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