xref: /freebsd/contrib/llvm-project/llvm/lib/MC/MCParser/COFFMasmParser.cpp (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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/MCAsmMacro.h"
13 #include "llvm/MC/MCContext.h"
14 #include "llvm/MC/MCParser/MCAsmLexer.h"
15 #include "llvm/MC/MCParser/MCAsmParserExtension.h"
16 #include "llvm/MC/MCParser/MCTargetAsmParser.h"
17 #include "llvm/MC/MCSectionCOFF.h"
18 #include "llvm/MC/MCStreamer.h"
19 #include "llvm/MC/MCSymbolCOFF.h"
20 #include "llvm/MC/SectionKind.h"
21 #include "llvm/Support/Casting.h"
22 #include "llvm/Support/SMLoc.h"
23 #include <cstdint>
24 #include <utility>
25 
26 using namespace llvm;
27 
28 namespace {
29 
30 class COFFMasmParser : public MCAsmParserExtension {
31   template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)>
32   void addDirectiveHandler(StringRef Directive) {
33     MCAsmParser::ExtensionDirectiveHandler Handler =
34         std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>);
35     getParser().addDirectiveHandler(Directive, Handler);
36   }
37 
38   bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics);
39 
40   bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics,
41                           StringRef COMDATSymName, COFF::COMDATType Type,
42                           Align Alignment);
43 
44   bool ParseDirectiveProc(StringRef, SMLoc);
45   bool ParseDirectiveEndProc(StringRef, SMLoc);
46   bool ParseDirectiveSegment(StringRef, SMLoc);
47   bool ParseDirectiveSegmentEnd(StringRef, SMLoc);
48   bool ParseDirectiveIncludelib(StringRef, SMLoc);
49   bool ParseDirectiveOption(StringRef, SMLoc);
50 
51   bool ParseDirectiveAlias(StringRef, SMLoc);
52 
53   bool ParseSEHDirectiveAllocStack(StringRef, SMLoc);
54   bool ParseSEHDirectiveEndProlog(StringRef, SMLoc);
55 
56   bool IgnoreDirective(StringRef, SMLoc) {
57     while (!getLexer().is(AsmToken::EndOfStatement)) {
58       Lex();
59     }
60     return false;
61   }
62 
63   void Initialize(MCAsmParser &Parser) override {
64     // Call the base implementation.
65     MCAsmParserExtension::Initialize(Parser);
66 
67     // x64 directives
68     addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>(
69         ".allocstack");
70     addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>(
71         ".endprolog");
72 
73     // Code label directives
74     // label
75     // org
76 
77     // Conditional control flow directives
78     // .break
79     // .continue
80     // .else
81     // .elseif
82     // .endif
83     // .endw
84     // .if
85     // .repeat
86     // .until
87     // .untilcxz
88     // .while
89 
90     // Data allocation directives
91     // align
92     // even
93     // mmword
94     // tbyte
95     // xmmword
96     // ymmword
97 
98     // Listing control directives
99     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref");
100     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list");
101     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall");
102     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif");
103     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro");
104     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall");
105     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref");
106     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist");
107     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif");
108     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro");
109     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page");
110     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle");
111     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond");
112     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title");
113 
114     // Macro directives
115     // goto
116 
117     // Miscellaneous directives
118     addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias");
119     // assume
120     // .fpo
121     addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>(
122         "includelib");
123     addDirectiveHandler<&COFFMasmParser::ParseDirectiveOption>("option");
124     // popcontext
125     // pushcontext
126     // .safeseh
127 
128     // Procedure directives
129     addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp");
130     // invoke (32-bit only)
131     addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc");
132     // proto
133 
134     // Processor directives; all ignored
135     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386");
136     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386p");
137     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387");
138     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486");
139     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486p");
140     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586");
141     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586p");
142     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686");
143     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686p");
144     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d");
145     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx");
146     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm");
147 
148     // Scope directives
149     // comm
150     // externdef
151 
152     // Segment directives
153     // .alpha (32-bit only, order segments alphabetically)
154     // .dosseg (32-bit only, order segments in DOS convention)
155     // .seq (32-bit only, order segments sequentially)
156     addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends");
157     // group (32-bit only)
158     addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment");
159 
160     // Simplified segment directives
161     addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code");
162     // .const
163     addDirectiveHandler<
164         &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data");
165     addDirectiveHandler<
166         &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?");
167     // .exit
168     // .fardata
169     // .fardata?
170     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");
171     // .stack
172     // .startup
173 
174     // String directives, written <name> <directive> <params>
175     // catstr (equivalent to <name> TEXTEQU <params>)
176     // instr (equivalent to <name> = @InStr(<params>))
177     // sizestr (equivalent to <name> = @SizeStr(<params>))
178     // substr (equivalent to <name> TEXTEQU @SubStr(<params>))
179 
180     // Structure and record directives
181     // record
182     // typedef
183   }
184 
185   bool ParseSectionDirectiveCode(StringRef, SMLoc) {
186     return ParseSectionSwitch(".text", COFF::IMAGE_SCN_CNT_CODE |
187                                            COFF::IMAGE_SCN_MEM_EXECUTE |
188                                            COFF::IMAGE_SCN_MEM_READ);
189   }
190 
191   bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
192     return ParseSectionSwitch(".data", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
193                                            COFF::IMAGE_SCN_MEM_READ |
194                                            COFF::IMAGE_SCN_MEM_WRITE);
195   }
196 
197   bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
198     return ParseSectionSwitch(".bss", COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA |
199                                           COFF::IMAGE_SCN_MEM_READ |
200                                           COFF::IMAGE_SCN_MEM_WRITE);
201   }
202 
203   /// Stack of active procedure definitions.
204   SmallVector<StringRef, 1> CurrentProcedures;
205   SmallVector<bool, 1> CurrentProceduresFramed;
206 
207 public:
208   COFFMasmParser() = default;
209 };
210 
211 } // end anonymous namespace.
212 
213 bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,
214                                         unsigned Characteristics) {
215   return ParseSectionSwitch(SectionName, Characteristics, "",
216                             (COFF::COMDATType)0, Align(16));
217 }
218 
219 bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,
220                                         unsigned Characteristics,
221                                         StringRef COMDATSymName,
222                                         COFF::COMDATType Type,
223                                         Align Alignment) {
224   if (getLexer().isNot(AsmToken::EndOfStatement))
225     return TokError("unexpected token in section switching directive");
226   Lex();
227 
228   MCSection *Section = getContext().getCOFFSection(SectionName, Characteristics,
229                                                    COMDATSymName, Type);
230   Section->setAlignment(Alignment);
231   getStreamer().switchSection(Section);
232 
233   return false;
234 }
235 
236 bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {
237   StringRef SegmentName;
238   if (!getLexer().is(AsmToken::Identifier))
239     return TokError("expected identifier in directive");
240   SegmentName = getTok().getIdentifier();
241   Lex();
242 
243   StringRef SectionName = SegmentName;
244   SmallVector<char, 247> SectionNameVector;
245 
246   StringRef Class;
247   if (SegmentName == "_TEXT" || SegmentName.starts_with("_TEXT$")) {
248     if (SegmentName.size() == 5) {
249       SectionName = ".text";
250     } else {
251       SectionName =
252           (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);
253     }
254     Class = "CODE";
255   }
256 
257   // Parse all options to end of statement.
258   // Alignment defaults to PARA if unspecified.
259   int64_t Alignment = 16;
260   // Default flags are used only if no characteristics are set.
261   bool DefaultCharacteristics = true;
262   unsigned Flags = 0;
263   // "obsolete" according to the documentation, but still supported.
264   bool Readonly = false;
265   while (getLexer().isNot(AsmToken::EndOfStatement)) {
266     switch (getTok().getKind()) {
267     default:
268       break;
269     case AsmToken::String: {
270       // Class identifier; overrides Kind.
271       Class = getTok().getStringContents();
272       Lex();
273       break;
274     }
275     case AsmToken::Identifier: {
276       SMLoc KeywordLoc = getTok().getLoc();
277       StringRef Keyword;
278       if (getParser().parseIdentifier(Keyword)) {
279         llvm_unreachable("failed to parse identifier at an identifier token");
280       }
281       if (Keyword.equals_insensitive("byte")) {
282         Alignment = 1;
283       } else if (Keyword.equals_insensitive("word")) {
284         Alignment = 2;
285       } else if (Keyword.equals_insensitive("dword")) {
286         Alignment = 4;
287       } else if (Keyword.equals_insensitive("para")) {
288         Alignment = 16;
289       } else if (Keyword.equals_insensitive("page")) {
290         Alignment = 256;
291       } else if (Keyword.equals_insensitive("align")) {
292         if (getParser().parseToken(AsmToken::LParen) ||
293             getParser().parseIntToken(Alignment,
294                                       "Expected integer alignment") ||
295             getParser().parseToken(AsmToken::RParen)) {
296           return Error(getTok().getLoc(),
297                        "Expected (n) following ALIGN in SEGMENT directive");
298         }
299         if (!isPowerOf2_64(Alignment) || Alignment > 8192) {
300           return Error(KeywordLoc,
301                        "ALIGN argument must be a power of 2 from 1 to 8192");
302         }
303       } else if (Keyword.equals_insensitive("alias")) {
304         if (getParser().parseToken(AsmToken::LParen) ||
305             !getTok().is(AsmToken::String))
306           return Error(
307               getTok().getLoc(),
308               "Expected (string) following ALIAS in SEGMENT directive");
309         SectionName = getTok().getStringContents();
310         Lex();
311         if (getParser().parseToken(AsmToken::RParen))
312           return Error(
313               getTok().getLoc(),
314               "Expected (string) following ALIAS in SEGMENT directive");
315       } else if (Keyword.equals_insensitive("readonly")) {
316         Readonly = true;
317       } else {
318         unsigned Characteristic =
319             StringSwitch<unsigned>(Keyword)
320                 .CaseLower("info", COFF::IMAGE_SCN_LNK_INFO)
321                 .CaseLower("read", COFF::IMAGE_SCN_MEM_READ)
322                 .CaseLower("write", COFF::IMAGE_SCN_MEM_WRITE)
323                 .CaseLower("execute", COFF::IMAGE_SCN_MEM_EXECUTE)
324                 .CaseLower("shared", COFF::IMAGE_SCN_MEM_SHARED)
325                 .CaseLower("nopage", COFF::IMAGE_SCN_MEM_NOT_PAGED)
326                 .CaseLower("nocache", COFF::IMAGE_SCN_MEM_NOT_CACHED)
327                 .CaseLower("discard", COFF::IMAGE_SCN_MEM_DISCARDABLE)
328                 .Default(-1);
329         if (Characteristic == static_cast<unsigned>(-1)) {
330           return Error(KeywordLoc,
331                        "Expected characteristic in SEGMENT directive; found '" +
332                            Keyword + "'");
333         }
334         Flags |= Characteristic;
335         DefaultCharacteristics = false;
336       }
337     }
338     }
339   }
340 
341   SectionKind Kind = StringSwitch<SectionKind>(Class)
342                          .CaseLower("data", SectionKind::getData())
343                          .CaseLower("code", SectionKind::getText())
344                          .CaseLower("const", SectionKind::getReadOnly())
345                          .Default(SectionKind::getData());
346   if (Kind.isText()) {
347     if (DefaultCharacteristics) {
348       Flags |= COFF::IMAGE_SCN_MEM_EXECUTE | COFF::IMAGE_SCN_MEM_READ;
349     }
350     Flags |= COFF::IMAGE_SCN_CNT_CODE;
351   } else {
352     if (DefaultCharacteristics) {
353       Flags |= COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;
354     }
355     Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
356   }
357   if (Readonly) {
358     Flags &= ~COFF::IMAGE_SCN_MEM_WRITE;
359   }
360 
361   MCSection *Section = getContext().getCOFFSection(SectionName, Flags, "",
362                                                    (COFF::COMDATType)(0));
363   if (Alignment != 0) {
364     Section->setAlignment(Align(Alignment));
365   }
366   getStreamer().switchSection(Section);
367   return false;
368 }
369 
370 /// ParseDirectiveSegmentEnd
371 ///  ::= identifier "ends"
372 bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {
373   StringRef SegmentName;
374   if (!getLexer().is(AsmToken::Identifier))
375     return TokError("expected identifier in directive");
376   SegmentName = getTok().getIdentifier();
377 
378   // Ignore; no action necessary.
379   Lex();
380   return false;
381 }
382 
383 /// ParseDirectiveIncludelib
384 ///  ::= "includelib" identifier
385 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
386   StringRef Lib;
387   if (getParser().parseIdentifier(Lib))
388     return TokError("expected identifier in includelib directive");
389 
390   unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;
391   getStreamer().pushSection();
392   getStreamer().switchSection(getContext().getCOFFSection(
393       ".drectve", Flags, "", (COFF::COMDATType)(0)));
394   getStreamer().emitBytes("/DEFAULTLIB:");
395   getStreamer().emitBytes(Lib);
396   getStreamer().emitBytes(" ");
397   getStreamer().popSection();
398   return false;
399 }
400 
401 /// ParseDirectiveOption
402 ///  ::= "option" option-list
403 bool COFFMasmParser::ParseDirectiveOption(StringRef Directive, SMLoc Loc) {
404   auto parseOption = [&]() -> bool {
405     StringRef Option;
406     if (getParser().parseIdentifier(Option))
407       return TokError("expected identifier for option name");
408     if (Option.equals_insensitive("prologue")) {
409       StringRef MacroId;
410       if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))
411         return TokError("expected :macroId after OPTION PROLOGUE");
412       if (MacroId.equals_insensitive("none")) {
413         // Since we currently don't implement prologues/epilogues, NONE is our
414         // default.
415         return false;
416       }
417       return TokError("OPTION PROLOGUE is currently unsupported");
418     }
419     if (Option.equals_insensitive("epilogue")) {
420       StringRef MacroId;
421       if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))
422         return TokError("expected :macroId after OPTION EPILOGUE");
423       if (MacroId.equals_insensitive("none")) {
424         // Since we currently don't implement prologues/epilogues, NONE is our
425         // default.
426         return false;
427       }
428       return TokError("OPTION EPILOGUE is currently unsupported");
429     }
430     return TokError("OPTION '" + Option + "' is currently unsupported");
431   };
432 
433   if (parseMany(parseOption))
434     return addErrorSuffix(" in OPTION directive");
435   return false;
436 }
437 
438 /// ParseDirectiveProc
439 /// TODO(epastor): Implement parameters and other attributes.
440 ///  ::= label "proc" [[distance]]
441 ///          statements
442 ///      label "endproc"
443 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
444   StringRef Label;
445   if (getParser().parseIdentifier(Label))
446     return Error(Loc, "expected identifier for procedure");
447   if (getLexer().is(AsmToken::Identifier)) {
448     StringRef nextVal = getTok().getString();
449     SMLoc nextLoc = getTok().getLoc();
450     if (nextVal.equals_insensitive("far")) {
451       // TODO(epastor): Handle far procedure definitions.
452       Lex();
453       return Error(nextLoc, "far procedure definitions not yet supported");
454     } else if (nextVal.equals_insensitive("near")) {
455       Lex();
456       nextVal = getTok().getString();
457       nextLoc = getTok().getLoc();
458     }
459   }
460   MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));
461 
462   // Define symbol as simple external function
463   Sym->setExternal(true);
464   Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
465 
466   bool Framed = false;
467   if (getLexer().is(AsmToken::Identifier) &&
468       getTok().getString().equals_insensitive("frame")) {
469     Lex();
470     Framed = true;
471     getStreamer().emitWinCFIStartProc(Sym, Loc);
472   }
473   getStreamer().emitLabel(Sym, Loc);
474 
475   CurrentProcedures.push_back(Label);
476   CurrentProceduresFramed.push_back(Framed);
477   return false;
478 }
479 bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
480   StringRef Label;
481   SMLoc LabelLoc = getTok().getLoc();
482   if (getParser().parseIdentifier(Label))
483     return Error(LabelLoc, "expected identifier for procedure end");
484 
485   if (CurrentProcedures.empty())
486     return Error(Loc, "endp outside of procedure block");
487   else if (!CurrentProcedures.back().equals_insensitive(Label))
488     return Error(LabelLoc, "endp does not match current procedure '" +
489                                CurrentProcedures.back() + "'");
490 
491   if (CurrentProceduresFramed.back()) {
492     getStreamer().emitWinCFIEndProc(Loc);
493   }
494   CurrentProcedures.pop_back();
495   CurrentProceduresFramed.pop_back();
496   return false;
497 }
498 
499 bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) {
500   std::string AliasName, ActualName;
501   if (getTok().isNot(AsmToken::Less) ||
502       getParser().parseAngleBracketString(AliasName))
503     return Error(getTok().getLoc(), "expected <aliasName>");
504   if (getParser().parseToken(AsmToken::Equal))
505     return addErrorSuffix(" in " + Directive + " directive");
506   if (getTok().isNot(AsmToken::Less) ||
507       getParser().parseAngleBracketString(ActualName))
508     return Error(getTok().getLoc(), "expected <actualName>");
509 
510   MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName);
511   MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName);
512 
513   getStreamer().emitWeakReference(Alias, Actual);
514 
515   return false;
516 }
517 
518 bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive,
519                                                  SMLoc Loc) {
520   int64_t Size;
521   SMLoc SizeLoc = getTok().getLoc();
522   if (getParser().parseAbsoluteExpression(Size))
523     return Error(SizeLoc, "expected integer size");
524   if (Size % 8 != 0)
525     return Error(SizeLoc, "stack size must be a multiple of 8");
526   getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);
527   return false;
528 }
529 
530 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
531                                                 SMLoc Loc) {
532   getStreamer().emitWinCFIEndProlog(Loc);
533   return false;
534 }
535 
536 namespace llvm {
537 
538 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
539 
540 } // end namespace llvm
541