LLVM 19.0.0git
COFFMasmParser.cpp
Go to the documentation of this file.
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
10#include "llvm/ADT/Twine.h"
12#include "llvm/MC/MCAsmMacro.h"
13#include "llvm/MC/MCContext.h"
18#include "llvm/MC/MCStreamer.h"
20#include "llvm/MC/SectionKind.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) {
34 std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>);
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.
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",
192 }
193
194 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
195 return ParseSectionSwitch(".data",
200 }
201
202 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
203 return ParseSectionSwitch(".bss",
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(
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
253 if (SegmentName == "_TEXT" || SegmentName.starts_with("_TEXT$")) {
254 if (SegmentName.size() == 5) {
255 SectionName = ".text";
256 } else {
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 }
282 SMLoc KeywordLoc = getTok().getLoc();
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 =
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
352 if (Kind.isText()) {
353 if (DefaultCharacteristics) {
355 }
357 } else {
358 if (DefaultCharacteristics) {
360 }
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) {
393 if (getParser().parseIdentifier(Lib))
394 return TokError("expected identifier in includelib directive");
395
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 {
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) {
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);
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) {
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
static bool isNot(const MachineRegisterInfo &MRI, const MachineInstr &MI)
COFFYAML::WeakExternalCharacteristics Characteristics
Definition: COFFYAML.cpp:331
uint64_t Size
Symbol * Sym
Definition: ELF_riscv.cpp:479
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
Generic interface for extending the MCAsmParser, which is implemented by target and object file assem...
virtual void Initialize(MCAsmParser &Parser)
Initialize the extension for parsing using the given Parser.
Generic assembler parser interface, for use by target specific assembly parsers.
Definition: MCAsmParser.h:123
std::pair< MCAsmParserExtension *, DirectiveHandler > ExtensionDirectiveHandler
Definition: MCAsmParser.h:127
virtual void addDirectiveHandler(StringRef Directive, ExtensionDirectiveHandler Handler)=0
Instances of this class represent a uniqued identifier for a section in the current translation unit.
Definition: MCSection.h:39
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition: MCSymbol.h:40
Represents a location in source code.
Definition: SMLoc.h:23
SectionKind - This is a simple POD value that classifies the properties of a section.
Definition: SectionKind.h:22
static SectionKind getText()
Definition: SectionKind.h:190
static SectionKind getData()
Definition: SectionKind.h:213
static SectionKind getBSS()
Definition: SectionKind.h:209
static SectionKind getReadOnly()
Definition: SectionKind.h:192
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1209
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition: StringRef.h:567
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition: StringRef.h:257
constexpr size_t size() const
size - Get the string size.
Definition: StringRef.h:137
bool equals_insensitive(StringRef RHS) const
Check for string equality, ignoring case.
Definition: StringRef.h:170
A switch()-like statement whose cases are string literals.
Definition: StringSwitch.h:44
StringSwitch & CaseLower(StringLiteral S, T Value)
Definition: StringSwitch.h:142
R Default(T Value)
Definition: StringSwitch.h:182
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ IMAGE_SCN_MEM_NOT_PAGED
Definition: COFF.h:332
@ IMAGE_SCN_MEM_SHARED
Definition: COFF.h:333
@ IMAGE_SCN_CNT_CODE
Definition: COFF.h:302
@ IMAGE_SCN_MEM_NOT_CACHED
Definition: COFF.h:331
@ IMAGE_SCN_MEM_READ
Definition: COFF.h:335
@ IMAGE_SCN_MEM_EXECUTE
Definition: COFF.h:334
@ IMAGE_SCN_CNT_UNINITIALIZED_DATA
Definition: COFF.h:304
@ IMAGE_SCN_MEM_DISCARDABLE
Definition: COFF.h:330
@ IMAGE_SCN_LNK_INFO
Definition: COFF.h:306
@ IMAGE_SCN_MEM_16BIT
Definition: COFF.h:311
@ IMAGE_SCN_CNT_INITIALIZED_DATA
Definition: COFF.h:303
@ IMAGE_SCN_MEM_PRELOAD
Definition: COFF.h:313
@ IMAGE_SCN_MEM_WRITE
Definition: COFF.h:336
COMDATType
Definition: COFF.h:420
@ IMAGE_SYM_DTYPE_FUNCTION
A function that returns a base type.
Definition: COFF.h:275
@ SCT_COMPLEX_TYPE_SHIFT
Type is formed as (base + (derived << SCT_COMPLEX_TYPE_SHIFT))
Definition: COFF.h:279
StringRef toStringRef(const std::optional< DWARFFormValue > &V, StringRef Default={})
Take an optional DWARFFormValue and try to extract a string value from it.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
constexpr bool isPowerOf2_64(uint64_t Value)
Return true if the argument is a power of two > 0 (64 bit edition.)
Definition: MathExtras.h:269
MCAsmParserExtension * createCOFFMasmParser()
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39