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
26using namespace llvm;
27
28namespace {
29
30class 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
214public:
215  COFFMasmParser() = default;
216};
217
218} // end anonymous namespace.
219
220bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,
221                                        unsigned Characteristics,
222                                        SectionKind Kind) {
223  return ParseSectionSwitch(SectionName, Characteristics, Kind, "",
224                            (COFF::COMDATType)0, Align(16));
225}
226
227bool 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
242bool 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"
378bool 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
391bool 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
410bool 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"
450bool 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}
486bool 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
506bool 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
525bool 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
537bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
538                                                SMLoc Loc) {
539  getStreamer().emitWinCFIEndProlog(Loc);
540  return false;
541}
542
543namespace llvm {
544
545MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
546
547} // end namespace llvm
548