brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.6 KiB · a9382b6 Raw
490 lines · cpp
1//===- WasmAsmParser.cpp - Wasm 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// Note, this is for wasm, the binary format (analogous to ELF), not wasm,10// the instruction set (analogous to x86), for which parsing code lives in11// WebAssemblyAsmParser.12//13// This file contains processing for generic directives implemented using14// MCTargetStreamer, the ones that depend on WebAssemblyTargetStreamer are in15// WebAssemblyAsmParser.16//17//===----------------------------------------------------------------------===//18 19#include "llvm/ADT/StringExtras.h"20#include "llvm/BinaryFormat/Wasm.h"21#include "llvm/MC/MCContext.h"22#include "llvm/MC/MCExpr.h"23#include "llvm/MC/MCObjectFileInfo.h"24#include "llvm/MC/MCParser/AsmLexer.h"25#include "llvm/MC/MCParser/MCAsmParser.h"26#include "llvm/MC/MCParser/MCAsmParserExtension.h"27#include "llvm/MC/MCSectionWasm.h"28#include "llvm/MC/MCStreamer.h"29#include "llvm/MC/MCSymbol.h"30#include "llvm/MC/MCSymbolWasm.h"31#include <optional>32 33using namespace llvm;34 35namespace {36 37class WasmAsmParser : public MCAsmParserExtension {38  MCAsmParser *Parser = nullptr;39  AsmLexer *Lexer = nullptr;40 41  template<bool (WasmAsmParser::*HandlerMethod)(StringRef, SMLoc)>42  void addDirectiveHandler(StringRef Directive) {43    MCAsmParser::ExtensionDirectiveHandler Handler = std::make_pair(44        this, HandleDirective<WasmAsmParser, HandlerMethod>);45 46    getParser().addDirectiveHandler(Directive, Handler);47  }48 49public:50  WasmAsmParser() { BracketExpressionsSupported = true; }51 52  void Initialize(MCAsmParser &P) override {53    Parser = &P;54    Lexer = &Parser->getLexer();55    // Call the base implementation.56    this->MCAsmParserExtension::Initialize(*Parser);57 58    addDirectiveHandler<&WasmAsmParser::parseSectionDirectiveText>(".text");59    addDirectiveHandler<&WasmAsmParser::parseSectionDirectiveData>(".data");60    addDirectiveHandler<&WasmAsmParser::parseSectionDirective>(".section");61    addDirectiveHandler<&WasmAsmParser::parseDirectivePrevious>(".previous");62    addDirectiveHandler<&WasmAsmParser::parseDirectiveSize>(".size");63    addDirectiveHandler<&WasmAsmParser::parseDirectiveType>(".type");64    addDirectiveHandler<&WasmAsmParser::ParseDirectiveIdent>(".ident");65    addDirectiveHandler<66      &WasmAsmParser::ParseDirectiveSymbolAttribute>(".weak");67    addDirectiveHandler<68      &WasmAsmParser::ParseDirectiveSymbolAttribute>(".local");69    addDirectiveHandler<70      &WasmAsmParser::ParseDirectiveSymbolAttribute>(".internal");71    addDirectiveHandler<72      &WasmAsmParser::ParseDirectiveSymbolAttribute>(".hidden");73    addDirectiveHandler<&WasmAsmParser::parseDirectiveSymver>(".symver");74  }75 76  bool error(const StringRef &Msg, const AsmToken &Tok) {77    return Parser->Error(Tok.getLoc(), Msg + Tok.getString());78  }79 80  bool isNext(AsmToken::TokenKind Kind) {81    auto Ok = Lexer->is(Kind);82    if (Ok)83      Lex();84    return Ok;85  }86 87  bool expect(AsmToken::TokenKind Kind, const char *KindName) {88    if (!isNext(Kind))89      return error(std::string("Expected ") + KindName + ", instead got: ",90                   Lexer->getTok());91    return false;92  }93 94  bool parseSectionDirectiveText(StringRef, SMLoc) {95    // FIXME: .text currently no-op.96    return false;97  }98 99  bool parseSectionDirectiveData(StringRef, SMLoc) {100    auto *S = getContext().getObjectFileInfo()->getDataSection();101    getStreamer().switchSection(S);102    return false;103  }104 105  uint32_t parseSectionFlags(StringRef FlagStr, bool &Passive, bool &Group) {106    uint32_t flags = 0;107    for (char C : FlagStr) {108      switch (C) {109      case 'p':110        Passive = true;111        break;112      case 'G':113        Group = true;114        break;115      case 'T':116        flags |= wasm::WASM_SEG_FLAG_TLS;117        break;118      case 'S':119        flags |= wasm::WASM_SEG_FLAG_STRINGS;120        break;121      case 'R':122        flags |= wasm::WASM_SEG_FLAG_RETAIN;123        break;124      default:125        return -1U;126      }127    }128    return flags;129  }130 131  bool parseGroup(StringRef &GroupName) {132    if (Lexer->isNot(AsmToken::Comma))133      return TokError("expected group name");134    Lex();135    if (Lexer->is(AsmToken::Integer)) {136      GroupName = getTok().getString();137      Lex();138    } else if (Parser->parseIdentifier(GroupName)) {139      return TokError("invalid group name");140    }141    if (Lexer->is(AsmToken::Comma)) {142      Lex();143      StringRef Linkage;144      if (Parser->parseIdentifier(Linkage))145        return TokError("invalid linkage");146      if (Linkage != "comdat")147        return TokError("Linkage must be 'comdat'");148    }149    return false;150  }151 152  // A section name can contain '-' (e.g. glibc's .gnu.glibc-stub.* markers) and153  // other characters that are not part of a single lexer identifier token, so154  // -- exactly like ELFAsmParser::parseSectionName -- we cannot just use155  // parseIdentifier. Build the name from adjacent tokens up to the comma / end156  // of statement.157  bool parseSectionName(StringRef &SectionName) {158    SMLoc FirstLoc = Lexer->getLoc();159    unsigned Size = 0;160 161    if (Lexer->is(AsmToken::String)) {162      SectionName = getTok().getIdentifier();163      Lex();164      return false;165    }166 167    while (!Parser->hasPendingError()) {168      SMLoc PrevLoc = Lexer->getLoc();169      if (Lexer->is(AsmToken::Comma) || Lexer->is(AsmToken::EndOfStatement))170        break;171 172      unsigned CurSize;173      if (Lexer->is(AsmToken::String)) {174        CurSize = getTok().getIdentifier().size() + 2;175        Lex();176      } else if (Lexer->is(AsmToken::Identifier)) {177        CurSize = getTok().getIdentifier().size();178        Lex();179      } else {180        CurSize = getTok().getString().size();181        Lex();182      }183      Size += CurSize;184      SectionName = StringRef(FirstLoc.getPointer(), Size);185 186      // Make sure the following token is adjacent.187      if (PrevLoc.getPointer() + CurSize != getTok().getLoc().getPointer())188        break;189    }190    if (Size == 0)191      return true;192 193    return false;194  }195 196  bool parseSectionDirective(StringRef, SMLoc loc) {197    StringRef Name;198    if (parseSectionName(Name))199      return TokError("expected identifier in directive");200 201    auto Kind = StringSwitch<std::optional<SectionKind>>(Name)202                    .StartsWith(".data", SectionKind::getData())203                    .StartsWith(".tdata", SectionKind::getThreadData())204                    .StartsWith(".tbss", SectionKind::getThreadBSS())205                    .StartsWith(".rodata", SectionKind::getReadOnly())206                    .StartsWith(".text", SectionKind::getText())207                    .StartsWith(".custom_section", SectionKind::getMetadata())208                    .StartsWith(".bss", SectionKind::getBSS())209                    // See use of .init_array in WasmObjectWriter and210                    // TargetLoweringObjectFileWasm211                    .StartsWith(".init_array", SectionKind::getData())212                    .StartsWith(".debug_", SectionKind::getMetadata())213                    .Default(SectionKind::getData());214 215    // GNU single-operand form: `.section <name>` with no flags/type operand.216    // GNU as accepts this bare spelling; glibc emits it (via the217    // link_warning / stub_warning machinery in include/libc-symbols.h) for218    // unallocated diagnostic marker sections such as `.gnu.warning.<sym>` and219    // `.gnu.glibc-stub.<sym>`, each immediately followed by `.previous`. wasm220    // (the object format) and wasm-ld have no ELF-style .gnu.warning /221    // .gnu.glibc-stub linker-warning feature, so these markers are purely222    // diagnostic and carry no allocated content. Accept the form and switch to223    // the (empty, unallocated) section so that a following `.previous` returns224    // to the prior section via the normal section stack; the marker itself225    // contributes nothing to the output. This never errors on a representable226    // construct and never drops real content: anything subsequently emitted227    // here still has a home in this section, exactly as on ELF.228    if (Lexer->is(AsmToken::EndOfStatement)) {229      MCSectionWasm *WS = getContext().getWasmSection(Name, *Kind);230      getStreamer().switchSection(WS);231      return false;232    }233 234    if (expect(AsmToken::Comma, ","))235      return true;236 237    if (Lexer->isNot(AsmToken::String))238      return error("expected string in directive, instead got: ", Lexer->getTok());239 240    // Update section flags if present in this .section directive241    bool Passive = false;242    bool Group = false;243    uint32_t Flags =244        parseSectionFlags(getTok().getStringContents(), Passive, Group);245    if (Flags == -1U)246      return TokError("unknown flag");247 248    Lex();249 250    if (expect(AsmToken::Comma, ",") || expect(AsmToken::At, "@"))251      return true;252 253    StringRef GroupName;254    if (Group && parseGroup(GroupName))255      return true;256 257    if (expect(AsmToken::EndOfStatement, "eol"))258      return true;259 260    // TODO: Parse UniqueID261    MCSectionWasm *WS = getContext().getWasmSection(262        Name, *Kind, Flags, GroupName, MCSection::NonUniqueID);263 264    if (WS->getSegmentFlags() != Flags)265      Parser->Error(loc, "changed section flags for " + Name +266                             ", expected: 0x" +267                             utohexstr(WS->getSegmentFlags()));268 269    if (Passive) {270      if (!WS->isWasmData())271        return Parser->Error(loc, "Only data sections can be passive");272      WS->setPassive();273    }274 275    getStreamer().switchSection(WS);276    return false;277  }278 279  /// parseDirectivePrevious280  ///  ::= .previous281  ///282  /// Switch back to the section that was active before the most recent283  /// `.section` / section switch, using the streamer's section stack -- the284  /// same semantics as ELFAsmParser::parseDirectivePrevious. glibc pairs every285  /// `.section .gnu.warning.*` / `.section .gnu.glibc-stub.*` marker with a286  /// `.previous` to restore the prior section; this was previously rejected on287  /// wasm as an "unknown directive". Mirror ELF exactly so the round-trip288  /// (`.section A` ... `.previous` -> back in A's predecessor) is preserved.289  bool parseDirectivePrevious(StringRef, SMLoc) {290    MCSectionSubPair PreviousSection = getStreamer().getPreviousSection();291    if (PreviousSection.first == nullptr)292      return TokError(".previous without corresponding .section");293    getStreamer().switchSection(PreviousSection.first, PreviousSection.second);294    return false;295  }296 297  // TODO: This function is almost the same as ELFAsmParser::ParseDirectiveSize298  // so maybe could be shared somehow.299  bool parseDirectiveSize(StringRef, SMLoc Loc) {300    MCSymbol *Sym;301    if (Parser->parseSymbol(Sym))302      return TokError("expected identifier in directive");303    if (expect(AsmToken::Comma, ","))304      return true;305    const MCExpr *Expr;306    if (Parser->parseExpression(Expr))307      return true;308    if (expect(AsmToken::EndOfStatement, "eol"))309      return true;310    auto WasmSym = static_cast<const MCSymbolWasm *>(Sym);311    if (WasmSym->isFunction()) {312      // Ignore .size directives for function symbols.  They get their size313      // set automatically based on their content.314      Warning(Loc, ".size directive ignored for function symbols");315    } else {316      getStreamer().emitELFSize(Sym, Expr);317    }318    return false;319  }320 321  bool parseDirectiveType(StringRef, SMLoc) {322    // This could be the start of a function, check if followed by323    // "label,@function"324    if (!Lexer->is(AsmToken::Identifier))325      return error("Expected label after .type directive, got: ",326                   Lexer->getTok());327    auto *WasmSym = static_cast<MCSymbolWasm *>(328        getStreamer().getContext().parseSymbol(Lexer->getTok().getString()));329    Lex();330    // GNU `as` accepts both spellings of the symbol-type prefix: `@type` and331    // `%type` (e.g. `%object`, `%function`, `%gnu_indirect_function`). Stock332    // glibc's include/libc-symbols.h hardcodes the `%` form (e.g.333    // declare_object_symbol_alias_1 emits `.type sym, %object`), which the wasm334    // MC parser previously rejected, accepting only `@`. Accept `%` as an exact335    // synonym for `@` here -- mirroring ELFAsmParser, which accepts the `@`,336    // `#`, and `%` prefixes interchangeably. The prefix only selects the337    // spelling; the type name that follows is interpreted by the identical338    // switch below, so `%X` produces exactly the same wasm symbol type as `@X`339    // (and remains rejected for any name the `@` form would also reject). This340    // never errors on a representable construct and does not change `@`-form341    // behavior.342    if (!(isNext(AsmToken::Comma) &&343          (isNext(AsmToken::At) || isNext(AsmToken::Percent)) &&344          Lexer->is(AsmToken::Identifier)))345      return error("Expected label,@type declaration, got: ", Lexer->getTok());346    auto TypeName = Lexer->getTok().getString();347    if (TypeName == "function") {348      WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);349      auto *Current =350          static_cast<MCSectionWasm *>(getStreamer().getCurrentSectionOnly());351      if (Current->getGroup())352        WasmSym->setComdat(true);353    } else if (TypeName == "global")354      WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);355    else if (TypeName == "object")356      WasmSym->setType(wasm::WASM_SYMBOL_TYPE_DATA);357    else358      return error("Unknown WASM symbol type: ", Lexer->getTok());359    Lex();360    return expect(AsmToken::EndOfStatement, "EOL");361  }362 363  // FIXME: Shared with ELF.364  /// ParseDirectiveIdent365  ///  ::= .ident string366  bool ParseDirectiveIdent(StringRef, SMLoc) {367    if (getLexer().isNot(AsmToken::String))368      return TokError("unexpected token in '.ident' directive");369    StringRef Data = getTok().getIdentifier();370    Lex();371    if (getLexer().isNot(AsmToken::EndOfStatement))372      return TokError("unexpected token in '.ident' directive");373    Lex();374    getStreamer().emitIdent(Data);375    return false;376  }377 378  // FIXME: Shared with ELF.379  /// ParseDirectiveSymbolAttribute380  ///  ::= { ".local", ".weak", ... } [ identifier ( , identifier )* ]381  bool ParseDirectiveSymbolAttribute(StringRef Directive, SMLoc) {382    MCSymbolAttr Attr = StringSwitch<MCSymbolAttr>(Directive)383      .Case(".weak", MCSA_Weak)384      .Case(".local", MCSA_Local)385      .Case(".hidden", MCSA_Hidden)386      .Case(".internal", MCSA_Internal)387      .Case(".protected", MCSA_Protected)388      .Default(MCSA_Invalid);389    assert(Attr != MCSA_Invalid && "unexpected symbol attribute directive!");390    if (getLexer().isNot(AsmToken::EndOfStatement)) {391      while (true) {392        MCSymbol *Sym;393        if (getParser().parseSymbol(Sym))394          return TokError("expected identifier in directive");395        getStreamer().emitSymbolAttribute(Sym, Attr);396        if (getLexer().is(AsmToken::EndOfStatement))397          break;398        if (getLexer().isNot(AsmToken::Comma))399          return TokError("unexpected token in directive");400        Lex();401      }402    }403    Lex();404    return false;405  }406 407  /// parseDirectiveSymver408  ///  ::= .symver foo, bar@@@ver409  ///410  /// wasm (the object format) has no ELF-style symbol versioning, and this is a411  /// fresh ABI with no legacy binaries, so version selection collapses to "the412  /// default version only". We map the directive onto the existing assembler413  /// alias machinery (the same representation extended by the prior414  /// alias-to-undefined-target fix in WasmObjectWriter):415  ///416  ///  - default version (`bar@@ver`): make `bar` a public alias of `foo`,417  ///    dropping the version tag. `bar` then resolves to `foo` at link, exactly418  ///    like `.set bar, foo` followed by `.globl bar`.419  ///  - non-default / compat version (`bar@ver`, single `@`): a compat symbol420  ///    for OLD binaries, which do not exist on this fresh ABI. Emitting a421  ///    second public `bar` here would collide with the default `bar`, so the422  ///    compat alias is dropped. `foo` itself is left untouched (it remains a423  ///    defined symbol in its own right).424  ///425  /// This never errors with "unknown directive" and never mis-resolves: the426  /// default binding is always materialized correctly, and we never emit two427  /// conflicting public definitions of the base name.428  bool parseDirectiveSymver(StringRef, SMLoc Loc) {429    MCSymbol *OriginalSym;430    if (getParser().parseSymbol(OriginalSym))431      return TokError("expected identifier in .symver directive");432 433    if (getLexer().isNot(AsmToken::Comma))434      return TokError("expected a comma in .symver directive");435 436    // '@' is a comment in some assembly dialects, but is required inside the437    // versioned name here. Allow it for the next identifier only, mirroring438    // ELFAsmParser::parseDirectiveSymver.439    const bool AllowAtInIdentifier = getLexer().getAllowAtInIdentifier();440    getLexer().setAllowAtInIdentifier(true);441    Lex();442    getLexer().setAllowAtInIdentifier(AllowAtInIdentifier);443 444    StringRef Name;445    if (getParser().parseIdentifier(Name))446      return TokError("expected identifier in .symver directive");447 448    size_t Pos = Name.find('@');449    if (Pos == StringRef::npos)450      return Error(Loc, "expected a '@' in the .symver versioned name");451 452    // Accept (and ignore) the optional ", remove" tail and the end of line, so453    // every well-formed ELF `.symver` spelling is parsed rather than rejected.454    if (parseOptionalToken(AsmToken::Comma)) {455      StringRef Action;456      if (getParser().parseIdentifier(Action) || Action != "remove")457        return TokError("expected 'remove'");458    }459    (void)parseOptionalToken(AsmToken::EndOfStatement);460 461    StringRef BareName = Name.substr(0, Pos);462    StringRef Rest = Name.substr(Pos); // one or more leading '@'463    // "@@" (and "@@@") select the default version; a single "@" is a464    // non-default / compat version.465    bool IsDefault = Rest.starts_with("@@");466 467    if (!IsDefault) {468      // Compat version: drop it. No legacy binaries exist on this ABI, so469      // nothing consumes the compat alias, and creating a second public470      // `BareName` would collide with the default. Never-silent: we deliberately471      // omit a consumer-less compat alias rather than mis-resolve or duplicate.472      return false;473    }474 475    // Default version: BareName becomes a public alias of OriginalSym with the476    // version tag dropped. Reuse the assembler-alias representation so the477    // WasmObjectWriter resolves BareName to its base symbol at link time.478    MCSymbol *AliasSym = getContext().getOrCreateSymbol(BareName);479    const MCExpr *Value = MCSymbolRefExpr::create(OriginalSym, getContext());480    getStreamer().emitAssignment(AliasSym, Value);481    // The default version is the public/exported binding of the base name.482    getStreamer().emitSymbolAttribute(AliasSym, MCSA_Global);483    return false;484  }485};486 487} // end anonymous namespace488 489MCAsmParserExtension *llvm::createWasmAsmParser() { return new WasmAsmParser; }490