1 //===- COFFMasmParser.cpp - COFF MASM Assembly Parser ---------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/ADT/StringRef.h"
10 #include "llvm/ADT/Twine.h"
11 #include "llvm/BinaryFormat/COFF.h"
12 #include "llvm/MC/MCContext.h"
13 #include "llvm/MC/MCParser/MCAsmLexer.h"
14 #include "llvm/MC/MCParser/MCAsmParserExtension.h"
15 #include "llvm/MC/MCSectionCOFF.h"
16 #include "llvm/MC/MCStreamer.h"
17 #include "llvm/MC/MCSymbolCOFF.h"
18 #include "llvm/MC/SectionKind.h"
19 #include "llvm/Support/Casting.h"
20 #include "llvm/Support/SMLoc.h"
21 #include <cstdint>
22 #include <utility>
23 
24 using namespace llvm;
25 
26 namespace {
27 
28 class COFFMasmParser : public MCAsmParserExtension {
29   template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)>
30   void addDirectiveHandler(StringRef Directive) {
31     MCAsmParser::ExtensionDirectiveHandler Handler =
32         std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>);
33     getParser().addDirectiveHandler(Directive, Handler);
34   }
35 
36   bool ParseSectionSwitch(StringRef Section, unsigned Characteristics,
37                           SectionKind Kind);
38 
39   bool ParseSectionSwitch(StringRef Section, unsigned Characteristics,
40                           SectionKind Kind, StringRef COMDATSymName,
41                           COFF::COMDATType Type);
42 
43   bool ParseDirectiveProc(StringRef, SMLoc);
44   bool ParseDirectiveEndProc(StringRef, SMLoc);
45   bool ParseDirectiveSegment(StringRef, SMLoc);
46   bool ParseDirectiveSegmentEnd(StringRef, SMLoc);
47   bool ParseDirectiveIncludelib(StringRef, SMLoc);
48 
49   bool ParseDirectiveAlias(StringRef, SMLoc);
50 
51   bool ParseSEHDirectiveAllocStack(StringRef, SMLoc);
52   bool ParseSEHDirectiveEndProlog(StringRef, SMLoc);
53 
54   bool IgnoreDirective(StringRef, SMLoc) {
55     while (!getLexer().is(AsmToken::EndOfStatement)) {
56       Lex();
57     }
58     return false;
59   }
60 
61   void Initialize(MCAsmParser &Parser) override {
62     // Call the base implementation.
63     MCAsmParserExtension::Initialize(Parser);
64 
65     // x64 directives
66     addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>(
67         ".allocstack");
68     addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>(
69         ".endprolog");
70 
71     // Code label directives
72     // label
73     // org
74 
75     // Conditional control flow directives
76     // .break
77     // .continue
78     // .else
79     // .elseif
80     // .endif
81     // .endw
82     // .if
83     // .repeat
84     // .until
85     // .untilcxz
86     // .while
87 
88     // Data allocation directives
89     // align
90     // even
91     // mmword
92     // tbyte
93     // xmmword
94     // ymmword
95 
96     // Listing control directives
97     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref");
98     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list");
99     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall");
100     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif");
101     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro");
102     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall");
103     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref");
104     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist");
105     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif");
106     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro");
107     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page");
108     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle");
109     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond");
110     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title");
111 
112     // Macro directives
113     // goto
114 
115     // Miscellaneous directives
116     addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias");
117     // assume
118     // .fpo
119     addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>(
120         "includelib");
121     // option
122     // popcontext
123     // pushcontext
124     // .safeseh
125 
126     // Procedure directives
127     addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp");
128     // invoke (32-bit only)
129     addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc");
130     // proto
131 
132     // Processor directives; all ignored
133     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386");
134     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386p");
135     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387");
136     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486");
137     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486p");
138     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586");
139     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586p");
140     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686");
141     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686p");
142     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d");
143     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx");
144     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm");
145 
146     // Scope directives
147     // comm
148     // externdef
149 
150     // Segment directives
151     // .alpha (32-bit only, order segments alphabetically)
152     // .dosseg (32-bit only, order segments in DOS convention)
153     // .seq (32-bit only, order segments sequentially)
154     addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends");
155     // group (32-bit only)
156     addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment");
157 
158     // Simplified segment directives
159     addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code");
160     // .const
161     addDirectiveHandler<
162         &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data");
163     addDirectiveHandler<
164         &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?");
165     // .exit
166     // .fardata
167     // .fardata?
168     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");
169     // .stack
170     // .startup
171 
172     // String directives, written <name> <directive> <params>
173     // catstr (equivalent to <name> TEXTEQU <params>)
174     // instr (equivalent to <name> = @InStr(<params>))
175     // sizestr (equivalent to <name> = @SizeStr(<params>))
176     // substr (equivalent to <name> TEXTEQU @SubStr(<params>))
177 
178     // Structure and record directives
179     // record
180     // typedef
181   }
182 
183   bool ParseSectionDirectiveCode(StringRef, SMLoc) {
184     return ParseSectionSwitch(".text",
185                               COFF::IMAGE_SCN_CNT_CODE
186                             | COFF::IMAGE_SCN_MEM_EXECUTE
187                             | COFF::IMAGE_SCN_MEM_READ,
188                               SectionKind::getText());
189   }
190 
191   bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
192     return ParseSectionSwitch(".data",
193                               COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
194                             | COFF::IMAGE_SCN_MEM_READ
195                             | COFF::IMAGE_SCN_MEM_WRITE,
196                               SectionKind::getData());
197   }
198 
199   bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
200     return ParseSectionSwitch(".bss",
201                               COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA
202                             | COFF::IMAGE_SCN_MEM_READ
203                             | COFF::IMAGE_SCN_MEM_WRITE,
204                               SectionKind::getBSS());
205   }
206 
207   StringRef CurrentProcedure;
208   bool CurrentProcedureFramed;
209 
210 public:
211   COFFMasmParser() = default;
212 };
213 
214 } // end anonymous namespace.
215 
216 static SectionKind computeSectionKind(unsigned Flags) {
217   if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)
218     return SectionKind::getText();
219   if (Flags & COFF::IMAGE_SCN_MEM_READ &&
220       (Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0)
221     return SectionKind::getReadOnly();
222   return SectionKind::getData();
223 }
224 
225 bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
226                                         unsigned Characteristics,
227                                         SectionKind Kind) {
228   return ParseSectionSwitch(Section, Characteristics, Kind, "",
229                             (COFF::COMDATType)0);
230 }
231 
232 bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
233                                         unsigned Characteristics,
234                                         SectionKind Kind,
235                                         StringRef COMDATSymName,
236                                         COFF::COMDATType Type) {
237   if (getLexer().isNot(AsmToken::EndOfStatement))
238     return TokError("unexpected token in section switching directive");
239   Lex();
240 
241   getStreamer().switchSection(getContext().getCOFFSection(
242       Section, Characteristics, Kind, COMDATSymName, Type));
243 
244   return false;
245 }
246 
247 bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {
248   StringRef SegmentName;
249   if (!getLexer().is(AsmToken::Identifier))
250     return TokError("expected identifier in directive");
251   SegmentName = getTok().getIdentifier();
252   Lex();
253 
254   StringRef SectionName = SegmentName;
255   SmallVector<char, 247> SectionNameVector;
256   unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
257                    COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;
258   if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) {
259     if (SegmentName.size() == 5) {
260       SectionName = ".text";
261     } else {
262       SectionName =
263           (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);
264     }
265     Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE |
266             COFF::IMAGE_SCN_MEM_READ;
267   }
268   SectionKind Kind = computeSectionKind(Flags);
269   getStreamer().switchSection(getContext().getCOFFSection(
270       SectionName, Flags, Kind, "", (COFF::COMDATType)(0)));
271   return false;
272 }
273 
274 /// ParseDirectiveSegmentEnd
275 ///  ::= identifier "ends"
276 bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {
277   StringRef SegmentName;
278   if (!getLexer().is(AsmToken::Identifier))
279     return TokError("expected identifier in directive");
280   SegmentName = getTok().getIdentifier();
281 
282   // Ignore; no action necessary.
283   Lex();
284   return false;
285 }
286 
287 /// ParseDirectiveIncludelib
288 ///  ::= "includelib" identifier
289 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
290   StringRef Lib;
291   if (getParser().parseIdentifier(Lib))
292     return TokError("expected identifier in includelib directive");
293 
294   unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;
295   SectionKind Kind = computeSectionKind(Flags);
296   getStreamer().pushSection();
297   getStreamer().switchSection(getContext().getCOFFSection(
298       ".drectve", Flags, Kind, "", (COFF::COMDATType)(0)));
299   getStreamer().emitBytes("/DEFAULTLIB:");
300   getStreamer().emitBytes(Lib);
301   getStreamer().emitBytes(" ");
302   getStreamer().popSection();
303   return false;
304 }
305 
306 /// ParseDirectiveProc
307 /// TODO(epastor): Implement parameters and other attributes.
308 ///  ::= label "proc" [[distance]]
309 ///          statements
310 ///      label "endproc"
311 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
312   StringRef Label;
313   if (getParser().parseIdentifier(Label))
314     return Error(Loc, "expected identifier for procedure");
315   if (getLexer().is(AsmToken::Identifier)) {
316     StringRef nextVal = getTok().getString();
317     SMLoc nextLoc = getTok().getLoc();
318     if (nextVal.equals_insensitive("far")) {
319       // TODO(epastor): Handle far procedure definitions.
320       Lex();
321       return Error(nextLoc, "far procedure definitions not yet supported");
322     } else if (nextVal.equals_insensitive("near")) {
323       Lex();
324       nextVal = getTok().getString();
325       nextLoc = getTok().getLoc();
326     }
327   }
328   MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));
329 
330   // Define symbol as simple external function
331   Sym->setExternal(true);
332   Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
333 
334   bool Framed = false;
335   if (getLexer().is(AsmToken::Identifier) &&
336       getTok().getString().equals_insensitive("frame")) {
337     Lex();
338     Framed = true;
339     getStreamer().emitWinCFIStartProc(Sym, Loc);
340   }
341   getStreamer().emitLabel(Sym, Loc);
342 
343   CurrentProcedure = Label;
344   CurrentProcedureFramed = Framed;
345   return false;
346 }
347 bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
348   StringRef Label;
349   SMLoc LabelLoc = getTok().getLoc();
350   if (getParser().parseIdentifier(Label))
351     return Error(LabelLoc, "expected identifier for procedure end");
352 
353   if (CurrentProcedure.empty())
354     return Error(Loc, "endp outside of procedure block");
355   else if (CurrentProcedure != Label)
356     return Error(LabelLoc, "endp does not match current procedure '" +
357                                CurrentProcedure + "'");
358 
359   if (CurrentProcedureFramed) {
360     getStreamer().emitWinCFIEndProc(Loc);
361   }
362   CurrentProcedure = "";
363   CurrentProcedureFramed = false;
364   return false;
365 }
366 
367 bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) {
368   std::string AliasName, ActualName;
369   if (getTok().isNot(AsmToken::Less) ||
370       getParser().parseAngleBracketString(AliasName))
371     return Error(getTok().getLoc(), "expected <aliasName>");
372   if (getParser().parseToken(AsmToken::Equal))
373     return addErrorSuffix(" in " + Directive + " directive");
374   if (getTok().isNot(AsmToken::Less) ||
375       getParser().parseAngleBracketString(ActualName))
376     return Error(getTok().getLoc(), "expected <actualName>");
377 
378   MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName);
379   MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName);
380 
381   getStreamer().emitWeakReference(Alias, Actual);
382 
383   return false;
384 }
385 
386 bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive,
387                                                  SMLoc Loc) {
388   int64_t Size;
389   SMLoc SizeLoc = getTok().getLoc();
390   if (getParser().parseAbsoluteExpression(Size))
391     return Error(SizeLoc, "expected integer size");
392   if (Size % 8 != 0)
393     return Error(SizeLoc, "stack size must be a multiple of 8");
394   getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);
395   return false;
396 }
397 
398 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
399                                                 SMLoc Loc) {
400   getStreamer().emitWinCFIEndProlog(Loc);
401   return false;
402 }
403 
404 namespace llvm {
405 
406 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
407 
408 } // end namespace llvm
409