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