brintos

brintos / llvm-project-archived public Read only

0
0
Text · 19.3 KiB · db796a3 Raw
541 lines · cpp
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-exception6//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/AsmLexer.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/SMLoc.h"22#include <cstdint>23 24using namespace llvm;25 26namespace {27 28class 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 SectionName, unsigned Characteristics);37 38  bool parseSectionSwitch(StringRef SectionName, unsigned Characteristics,39                          StringRef COMDATSymName, COFF::COMDATType Type,40                          Align Alignment);41 42  bool parseDirectiveProc(StringRef, SMLoc);43  bool parseDirectiveEndProc(StringRef, SMLoc);44  bool parseDirectiveSegment(StringRef, SMLoc);45  bool parseDirectiveSegmentEnd(StringRef, SMLoc);46  bool parseDirectiveIncludelib(StringRef, SMLoc);47  bool parseDirectiveOption(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 directives66    addDirectiveHandler<&COFFMasmParser::parseSEHDirectiveAllocStack>(67        ".allocstack");68    addDirectiveHandler<&COFFMasmParser::parseSEHDirectiveEndProlog>(69        ".endprolog");70 71    // Code label directives72    // label73    // org74 75    // Conditional control flow directives76    // .break77    // .continue78    // .else79    // .elseif80    // .endif81    // .endw82    // .if83    // .repeat84    // .until85    // .untilcxz86    // .while87 88    // Data allocation directives89    // align90    // even91    // mmword92    // tbyte93    // xmmword94    // ymmword95 96    // Listing control directives97    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 directives113    // goto114 115    // Miscellaneous directives116    addDirectiveHandler<&COFFMasmParser::parseDirectiveAlias>("alias");117    // assume118    // .fpo119    addDirectiveHandler<&COFFMasmParser::parseDirectiveIncludelib>(120        "includelib");121    addDirectiveHandler<&COFFMasmParser::parseDirectiveOption>("option");122    // popcontext123    // pushcontext124    // .safeseh125 126    // Procedure directives127    addDirectiveHandler<&COFFMasmParser::parseDirectiveEndProc>("endp");128    // invoke (32-bit only)129    addDirectiveHandler<&COFFMasmParser::parseDirectiveProc>("proc");130    // proto131 132    // Processor directives; all ignored133    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 directives147    // comm148    // externdef149 150    // Segment directives151    // .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 directives159    addDirectiveHandler<&COFFMasmParser::parseSectionDirectiveCode>(".code");160    // .const161    addDirectiveHandler<&COFFMasmParser::parseSectionDirectiveInitializedData>(162        ".data");163    addDirectiveHandler<164        &COFFMasmParser::parseSectionDirectiveUninitializedData>(".data?");165    // .exit166    // .fardata167    // .fardata?168    addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");169    // .stack170    // .startup171 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 directives179    // record180    // typedef181  }182 183  bool parseSectionDirectiveCode(StringRef, SMLoc) {184    return parseSectionSwitch(".text", COFF::IMAGE_SCN_CNT_CODE |185                                           COFF::IMAGE_SCN_MEM_EXECUTE |186                                           COFF::IMAGE_SCN_MEM_READ);187  }188 189  bool parseSectionDirectiveInitializedData(StringRef, SMLoc) {190    return parseSectionSwitch(".data", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |191                                           COFF::IMAGE_SCN_MEM_READ |192                                           COFF::IMAGE_SCN_MEM_WRITE);193  }194 195  bool parseSectionDirectiveUninitializedData(StringRef, SMLoc) {196    return parseSectionSwitch(".bss", COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA |197                                          COFF::IMAGE_SCN_MEM_READ |198                                          COFF::IMAGE_SCN_MEM_WRITE);199  }200 201  /// Stack of active procedure definitions.202  SmallVector<StringRef, 1> CurrentProcedures;203  SmallVector<bool, 1> CurrentProceduresFramed;204 205public:206  COFFMasmParser() = default;207};208 209} // end anonymous namespace.210 211bool COFFMasmParser::parseSectionSwitch(StringRef SectionName,212                                        unsigned Characteristics) {213  return parseSectionSwitch(SectionName, Characteristics, "",214                            (COFF::COMDATType)0, Align(16));215}216 217bool COFFMasmParser::parseSectionSwitch(StringRef SectionName,218                                        unsigned Characteristics,219                                        StringRef COMDATSymName,220                                        COFF::COMDATType Type,221                                        Align Alignment) {222  if (getLexer().isNot(AsmToken::EndOfStatement))223    return TokError("unexpected token in section switching directive");224  Lex();225 226  MCSection *Section = getContext().getCOFFSection(SectionName, Characteristics,227                                                   COMDATSymName, Type);228  Section->setAlignment(Alignment);229  getStreamer().switchSection(Section);230 231  return false;232}233 234bool COFFMasmParser::parseDirectiveSegment(StringRef Directive, SMLoc Loc) {235  StringRef SegmentName;236  if (!getLexer().is(AsmToken::Identifier))237    return TokError("expected identifier in directive");238  SegmentName = getTok().getIdentifier();239  Lex();240 241  StringRef SectionName = SegmentName;242  SmallVector<char, 247> SectionNameVector;243 244  StringRef Class;245  if (SegmentName == "_TEXT" || SegmentName.starts_with("_TEXT$")) {246    if (SegmentName.size() == 5) {247      SectionName = ".text";248    } else {249      SectionName =250          (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);251    }252    Class = "CODE";253  }254 255  // Parse all options to end of statement.256  // Alignment defaults to PARA if unspecified.257  int64_t Alignment = 16;258  // Default flags are used only if no characteristics are set.259  bool DefaultCharacteristics = true;260  unsigned Flags = 0;261  // "obsolete" according to the documentation, but still supported.262  bool Readonly = false;263  while (getLexer().isNot(AsmToken::EndOfStatement)) {264    switch (getTok().getKind()) {265    default:266      break;267    case AsmToken::String: {268      // Class identifier; overrides Kind.269      Class = getTok().getStringContents();270      Lex();271      break;272    }273    case AsmToken::Identifier: {274      SMLoc KeywordLoc = getTok().getLoc();275      StringRef Keyword;276      if (getParser().parseIdentifier(Keyword)) {277        llvm_unreachable("failed to parse identifier at an identifier token");278      }279      if (Keyword.equals_insensitive("byte")) {280        Alignment = 1;281      } else if (Keyword.equals_insensitive("word")) {282        Alignment = 2;283      } else if (Keyword.equals_insensitive("dword")) {284        Alignment = 4;285      } else if (Keyword.equals_insensitive("para")) {286        Alignment = 16;287      } else if (Keyword.equals_insensitive("page")) {288        Alignment = 256;289      } else if (Keyword.equals_insensitive("align")) {290        if (getParser().parseToken(AsmToken::LParen) ||291            getParser().parseIntToken(Alignment,292                                      "Expected integer alignment") ||293            getParser().parseToken(AsmToken::RParen)) {294          return Error(getTok().getLoc(),295                       "Expected (n) following ALIGN in SEGMENT directive");296        }297        if (!isPowerOf2_64(Alignment) || Alignment > 8192) {298          return Error(KeywordLoc,299                       "ALIGN argument must be a power of 2 from 1 to 8192");300        }301      } else if (Keyword.equals_insensitive("alias")) {302        if (getParser().parseToken(AsmToken::LParen) ||303            !getTok().is(AsmToken::String))304          return Error(305              getTok().getLoc(),306              "Expected (string) following ALIAS in SEGMENT directive");307        SectionName = getTok().getStringContents();308        Lex();309        if (getParser().parseToken(AsmToken::RParen))310          return Error(311              getTok().getLoc(),312              "Expected (string) following ALIAS in SEGMENT directive");313      } else if (Keyword.equals_insensitive("readonly")) {314        Readonly = true;315      } else {316        unsigned Characteristic =317            StringSwitch<unsigned>(Keyword)318                .CaseLower("info", COFF::IMAGE_SCN_LNK_INFO)319                .CaseLower("read", COFF::IMAGE_SCN_MEM_READ)320                .CaseLower("write", COFF::IMAGE_SCN_MEM_WRITE)321                .CaseLower("execute", COFF::IMAGE_SCN_MEM_EXECUTE)322                .CaseLower("shared", COFF::IMAGE_SCN_MEM_SHARED)323                .CaseLower("nopage", COFF::IMAGE_SCN_MEM_NOT_PAGED)324                .CaseLower("nocache", COFF::IMAGE_SCN_MEM_NOT_CACHED)325                .CaseLower("discard", COFF::IMAGE_SCN_MEM_DISCARDABLE)326                .Default(-1);327        if (Characteristic == static_cast<unsigned>(-1)) {328          return Error(KeywordLoc,329                       "Expected characteristic in SEGMENT directive; found '" +330                           Keyword + "'");331        }332        Flags |= Characteristic;333        DefaultCharacteristics = false;334      }335    }336    }337  }338 339  SectionKind Kind = StringSwitch<SectionKind>(Class)340                         .CaseLower("data", SectionKind::getData())341                         .CaseLower("code", SectionKind::getText())342                         .CaseLower("const", SectionKind::getReadOnly())343                         .Default(SectionKind::getData());344  if (Kind.isText()) {345    if (DefaultCharacteristics) {346      Flags |= COFF::IMAGE_SCN_MEM_EXECUTE | COFF::IMAGE_SCN_MEM_READ;347    }348    Flags |= COFF::IMAGE_SCN_CNT_CODE;349  } else {350    if (DefaultCharacteristics) {351      Flags |= COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;352    }353    Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;354  }355  if (Readonly) {356    Flags &= ~COFF::IMAGE_SCN_MEM_WRITE;357  }358 359  MCSection *Section = getContext().getCOFFSection(SectionName, Flags, "",360                                                   (COFF::COMDATType)(0));361  if (Alignment != 0) {362    Section->setAlignment(Align(Alignment));363  }364  getStreamer().switchSection(Section);365  return false;366}367 368/// parseDirectiveSegmentEnd369///  ::= identifier "ends"370bool COFFMasmParser::parseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {371  StringRef SegmentName;372  if (!getLexer().is(AsmToken::Identifier))373    return TokError("expected identifier in directive");374  SegmentName = getTok().getIdentifier();375 376  // Ignore; no action necessary.377  Lex();378  return false;379}380 381/// parseDirectiveIncludelib382///  ::= "includelib" identifier383bool COFFMasmParser::parseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {384  StringRef Lib;385  if (getParser().parseIdentifier(Lib))386    return TokError("expected identifier in includelib directive");387 388  unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;389  getStreamer().pushSection();390  getStreamer().switchSection(getContext().getCOFFSection(391      ".drectve", Flags, "", (COFF::COMDATType)(0)));392  getStreamer().emitBytes("/DEFAULTLIB:");393  getStreamer().emitBytes(Lib);394  getStreamer().emitBytes(" ");395  getStreamer().popSection();396  return false;397}398 399/// parseDirectiveOption400///  ::= "option" option-list401bool COFFMasmParser::parseDirectiveOption(StringRef Directive, SMLoc Loc) {402  auto parseOption = [&]() -> bool {403    StringRef Option;404    if (getParser().parseIdentifier(Option))405      return TokError("expected identifier for option name");406    if (Option.equals_insensitive("prologue")) {407      StringRef MacroId;408      if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))409        return TokError("expected :macroId after OPTION PROLOGUE");410      if (MacroId.equals_insensitive("none")) {411        // Since we currently don't implement prologues/epilogues, NONE is our412        // default.413        return false;414      }415      return TokError("OPTION PROLOGUE is currently unsupported");416    }417    if (Option.equals_insensitive("epilogue")) {418      StringRef MacroId;419      if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))420        return TokError("expected :macroId after OPTION EPILOGUE");421      if (MacroId.equals_insensitive("none")) {422        // Since we currently don't implement prologues/epilogues, NONE is our423        // default.424        return false;425      }426      return TokError("OPTION EPILOGUE is currently unsupported");427    }428    return TokError("OPTION '" + Option + "' is currently unsupported");429  };430 431  if (parseMany(parseOption))432    return addErrorSuffix(" in OPTION directive");433  return false;434}435 436/// parseDirectiveProc437/// TODO(epastor): Implement parameters and other attributes.438///  ::= label "proc" [[distance]]439///          statements440///      label "endproc"441bool COFFMasmParser::parseDirectiveProc(StringRef Directive, SMLoc Loc) {442  if (!getStreamer().getCurrentFragment())443    return Error(getTok().getLoc(), "expected section directive");444 445  MCSymbol *Sym;446  if (getParser().parseSymbol(Sym))447    return Error(Loc, "expected identifier for procedure");448  if (getLexer().is(AsmToken::Identifier)) {449    StringRef nextVal = getTok().getString();450    SMLoc nextLoc = getTok().getLoc();451    if (nextVal.equals_insensitive("far")) {452      // TODO(epastor): Handle far procedure definitions.453      Lex();454      return Error(nextLoc, "far procedure definitions not yet supported");455    } else if (nextVal.equals_insensitive("near")) {456      Lex();457      nextVal = getTok().getString();458      nextLoc = getTok().getLoc();459    }460  }461 462  // Define symbol as simple external function463  auto *COFFSym = static_cast<MCSymbolCOFF *>(Sym);464  COFFSym->setExternal(true);465  COFFSym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION466                   << COFF::SCT_COMPLEX_TYPE_SHIFT);467 468  bool Framed = false;469  if (getLexer().is(AsmToken::Identifier) &&470      getTok().getString().equals_insensitive("frame")) {471    Lex();472    Framed = true;473    getStreamer().emitWinCFIStartProc(Sym, Loc);474  }475  getStreamer().emitLabel(Sym, Loc);476 477  CurrentProcedures.push_back(Sym->getName());478  CurrentProceduresFramed.push_back(Framed);479  return false;480}481bool COFFMasmParser::parseDirectiveEndProc(StringRef Directive, SMLoc Loc) {482  StringRef Label;483  SMLoc LabelLoc = getTok().getLoc();484  if (getParser().parseIdentifier(Label))485    return Error(LabelLoc, "expected identifier for procedure end");486 487  if (CurrentProcedures.empty())488    return Error(Loc, "endp outside of procedure block");489  else if (!CurrentProcedures.back().equals_insensitive(Label))490    return Error(LabelLoc, "endp does not match current procedure '" +491                               CurrentProcedures.back() + "'");492 493  if (CurrentProceduresFramed.back()) {494    getStreamer().emitWinCFIEndProc(Loc);495  }496  CurrentProcedures.pop_back();497  CurrentProceduresFramed.pop_back();498  return false;499}500 501bool COFFMasmParser::parseDirectiveAlias(StringRef Directive, SMLoc Loc) {502  std::string AliasName, ActualName;503  if (getTok().isNot(AsmToken::Less) ||504      getParser().parseAngleBracketString(AliasName))505    return Error(getTok().getLoc(), "expected <aliasName>");506  if (getParser().parseToken(AsmToken::Equal))507    return addErrorSuffix(" in " + Directive + " directive");508  if (getTok().isNot(AsmToken::Less) ||509      getParser().parseAngleBracketString(ActualName))510    return Error(getTok().getLoc(), "expected <actualName>");511 512  MCSymbol *Alias = getContext().parseSymbol(AliasName);513  MCSymbol *Actual = getContext().parseSymbol(ActualName);514 515  getStreamer().emitWeakReference(Alias, Actual);516 517  return false;518}519 520bool COFFMasmParser::parseSEHDirectiveAllocStack(StringRef Directive,521                                                 SMLoc Loc) {522  int64_t Size;523  SMLoc SizeLoc = getTok().getLoc();524  if (getParser().parseAbsoluteExpression(Size))525    return Error(SizeLoc, "expected integer size");526  if (Size % 8 != 0)527    return Error(SizeLoc, "stack size must be a multiple of 8");528  getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);529  return false;530}531 532bool COFFMasmParser::parseSEHDirectiveEndProlog(StringRef Directive,533                                                SMLoc Loc) {534  getStreamer().emitWinCFIEndProlog(Loc);535  return false;536}537 538MCAsmParserExtension *llvm::createCOFFMasmParser() {539  return new COFFMasmParser;540}541