blob: 95128cf7d1845f47e08299a90aca73ca2445d0e3 [file] [log] [blame]
//===- COFFMasmParser.cpp - COFF MASM Assembly Parser ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDirectives.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCAsmParserExtension.h"
#include "llvm/MC/MCParser/MCAsmParserUtils.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSectionCOFF.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbolCOFF.h"
#include "llvm/MC/SectionKind.h"
#include "llvm/Support/SMLoc.h"
#include <cassert>
#include <cstdint>
#include <limits>
#include <utility>
using namespace llvm;
namespace {
class COFFMasmParser : public MCAsmParserExtension {
template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)>
void addDirectiveHandler(StringRef Directive) {
MCAsmParser::ExtensionDirectiveHandler Handler =
std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>);
getParser().addDirectiveHandler(Directive, Handler);
}
bool ParseSectionSwitch(StringRef Section, unsigned Characteristics,
SectionKind Kind);
bool ParseSectionSwitch(StringRef Section, unsigned Characteristics,
SectionKind Kind, StringRef COMDATSymName,
COFF::COMDATType Type);
bool ParseDirectiveProc(StringRef, SMLoc);
bool ParseDirectiveEndProc(StringRef, SMLoc);
bool ParseDirectiveSegment(StringRef, SMLoc);
bool ParseDirectiveSegmentEnd(StringRef, SMLoc);
bool ParseDirectiveIncludelib(StringRef, SMLoc);
bool ParseDirectiveAlias(StringRef, SMLoc);
bool ParseSEHDirectiveAllocStack(StringRef, SMLoc);
bool ParseSEHDirectiveEndProlog(StringRef, SMLoc);
bool IgnoreDirective(StringRef, SMLoc) {
while (!getLexer().is(AsmToken::EndOfStatement)) {
Lex();
}
return false;
}
void Initialize(MCAsmParser &Parser) override {
// Call the base implementation.
MCAsmParserExtension::Initialize(Parser);
// x64 directives
addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>(
".allocstack");
addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>(
".endprolog");
// Code label directives
// label
// org
// Conditional control flow directives
// .break
// .continue
// .else
// .elseif
// .endif
// .endw
// .if
// .repeat
// .until
// .untilcxz
// .while
// Data allocation directives
// align
// even
// mmword
// tbyte
// xmmword
// ymmword
// Listing control directives
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title");
// Macro directives
// goto
// Miscellaneous directives
addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias");
// assume
// .fpo
addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>(
"includelib");
// option
// popcontext
// pushcontext
// .safeseh
// Procedure directives
addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp");
// invoke (32-bit only)
addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc");
// proto
// Processor directives; all ignored
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386P");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486P");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586P");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686P");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm");
// Scope directives
// comm
// externdef
// Segment directives
// .alpha (32-bit only, order segments alphabetically)
// .dosseg (32-bit only, order segments in DOS convention)
// .seq (32-bit only, order segments sequentially)
addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends");
// group (32-bit only)
addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment");
// Simplified segment directives
addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code");
// .const
addDirectiveHandler<
&COFFMasmParser::ParseSectionDirectiveInitializedData>(".data");
addDirectiveHandler<
&COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?");
// .exit
// .fardata
// .fardata?
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");
// .stack
// .startup
// String directives, written <name> <directive> <params>
// catstr (equivalent to <name> TEXTEQU <params>)
// instr (equivalent to <name> = @InStr(<params>))
// sizestr (equivalent to <name> = @SizeStr(<params>))
// substr (equivalent to <name> TEXTEQU @SubStr(<params>))
// Structure and record directives
// record
// typedef
}
bool ParseSectionDirectiveCode(StringRef, SMLoc) {
return ParseSectionSwitch(".text",
COFF::IMAGE_SCN_CNT_CODE
| COFF::IMAGE_SCN_MEM_EXECUTE
| COFF::IMAGE_SCN_MEM_READ,
SectionKind::getText());
}
bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
return ParseSectionSwitch(".data",
COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
| COFF::IMAGE_SCN_MEM_READ
| COFF::IMAGE_SCN_MEM_WRITE,
SectionKind::getData());
}
bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
return ParseSectionSwitch(".bss",
COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA
| COFF::IMAGE_SCN_MEM_READ
| COFF::IMAGE_SCN_MEM_WRITE,
SectionKind::getBSS());
}
StringRef CurrentProcedure;
bool CurrentProcedureFramed;
public:
COFFMasmParser() = default;
};
} // end anonymous namespace.
static SectionKind computeSectionKind(unsigned Flags) {
if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)
return SectionKind::getText();
if (Flags & COFF::IMAGE_SCN_MEM_READ &&
(Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0)
return SectionKind::getReadOnly();
return SectionKind::getData();
}
bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
unsigned Characteristics,
SectionKind Kind) {
return ParseSectionSwitch(Section, Characteristics, Kind, "",
(COFF::COMDATType)0);
}
bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
unsigned Characteristics,
SectionKind Kind,
StringRef COMDATSymName,
COFF::COMDATType Type) {
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in section switching directive");
Lex();
getStreamer().SwitchSection(getContext().getCOFFSection(
Section, Characteristics, Kind, COMDATSymName, Type));
return false;
}
bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {
StringRef SegmentName;
if (!getLexer().is(AsmToken::Identifier))
return TokError("expected identifier in directive");
SegmentName = getTok().getIdentifier();
Lex();
StringRef SectionName = SegmentName;
SmallVector<char, 247> SectionNameVector;
unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;
if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) {
if (SegmentName.size() == 5) {
SectionName = ".text";
} else {
SectionName =
(".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);
}
Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE |
COFF::IMAGE_SCN_MEM_READ;
}
SectionKind Kind = computeSectionKind(Flags);
getStreamer().SwitchSection(getContext().getCOFFSection(
SectionName, Flags, Kind, "", (COFF::COMDATType)(0)));
return false;
}
/// ParseDirectiveSegmentEnd
/// ::= identifier "ends"
bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {
StringRef SegmentName;
if (!getLexer().is(AsmToken::Identifier))
return TokError("expected identifier in directive");
SegmentName = getTok().getIdentifier();
// Ignore; no action necessary.
Lex();
return false;
}
/// ParseDirectiveIncludelib
/// ::= "includelib" identifier
bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
StringRef Lib;
if (getParser().parseIdentifier(Lib))
return TokError("expected identifier in includelib directive");
unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;
SectionKind Kind = computeSectionKind(Flags);
getStreamer().PushSection();
getStreamer().SwitchSection(getContext().getCOFFSection(
".drectve", Flags, Kind, "", (COFF::COMDATType)(0)));
getStreamer().emitBytes("/DEFAULTLIB:");
getStreamer().emitBytes(Lib);
getStreamer().emitBytes(" ");
getStreamer().PopSection();
return false;
}
/// ParseDirectiveProc
/// TODO(epastor): Implement parameters and other attributes.
/// ::= label "proc" [[distance]]
/// statements
/// label "endproc"
bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
StringRef Label;
if (getParser().parseIdentifier(Label))
return Error(Loc, "expected identifier for procedure");
if (getLexer().is(AsmToken::Identifier)) {
StringRef nextVal = getTok().getString();
SMLoc nextLoc = getTok().getLoc();
if (nextVal.equals_lower("far")) {
// TODO(epastor): Handle far procedure definitions.
Lex();
return Error(nextLoc, "far procedure definitions not yet supported");
} else if (nextVal.equals_lower("near")) {
Lex();
nextVal = getTok().getString();
nextLoc = getTok().getLoc();
}
}
MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));
// Define symbol as simple external function
Sym->setExternal(true);
Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
bool Framed = false;
if (getLexer().is(AsmToken::Identifier) &&
getTok().getString().equals_lower("frame")) {
Lex();
Framed = true;
getStreamer().EmitWinCFIStartProc(Sym, Loc);
}
getStreamer().emitLabel(Sym, Loc);
CurrentProcedure = Label;
CurrentProcedureFramed = Framed;
return false;
}
bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
StringRef Label;
SMLoc LabelLoc = getTok().getLoc();
if (getParser().parseIdentifier(Label))
return Error(LabelLoc, "expected identifier for procedure end");
if (CurrentProcedure.empty())
return Error(Loc, "endp outside of procedure block");
else if (CurrentProcedure != Label)
return Error(LabelLoc, "endp does not match current procedure '" +
CurrentProcedure + "'");
if (CurrentProcedureFramed) {
getStreamer().EmitWinCFIEndProc(Loc);
}
CurrentProcedure = "";
CurrentProcedureFramed = false;
return false;
}
bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) {
std::string AliasName, ActualName;
if (getTok().isNot(AsmToken::Less) ||
getParser().parseAngleBracketString(AliasName))
return Error(getTok().getLoc(), "expected <aliasName>");
if (getParser().parseToken(AsmToken::Equal))
return addErrorSuffix(" in " + Directive + " directive");
if (getTok().isNot(AsmToken::Less) ||
getParser().parseAngleBracketString(ActualName))
return Error(getTok().getLoc(), "expected <actualName>");
MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName);
MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName);
getStreamer().emitWeakReference(Alias, Actual);
return false;
}
bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive,
SMLoc Loc) {
int64_t Size;
SMLoc SizeLoc = getTok().getLoc();
if (getParser().parseAbsoluteExpression(Size))
return Error(SizeLoc, "expected integer size");
if (Size % 8 != 0)
return Error(SizeLoc, "stack size must be a multiple of 8");
getStreamer().EmitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);
return false;
}
bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
SMLoc Loc) {
getStreamer().EmitWinCFIEndProlog(Loc);
return false;
}
namespace llvm {
MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
} // end namespace llvm