LLVM 20.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
40 bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics,
41 StringRef COMDATSymName, COFF::COMDATType Type,
42 Align Alignment);
43
44 bool ParseDirectiveProc(StringRef, SMLoc);
45 bool ParseDirectiveEndProc(StringRef, SMLoc);
46 bool ParseDirectiveSegment(StringRef, SMLoc);
47 bool ParseDirectiveSegmentEnd(StringRef, SMLoc);
48 bool ParseDirectiveIncludelib(StringRef, SMLoc);
49 bool ParseDirectiveOption(StringRef, SMLoc);
50
51 bool ParseDirectiveAlias(StringRef, SMLoc);
52
53 bool ParseSEHDirectiveAllocStack(StringRef, SMLoc);
54 bool ParseSEHDirectiveEndProlog(StringRef, SMLoc);
55
56 bool IgnoreDirective(StringRef, SMLoc) {
57 while (!getLexer().is(AsmToken::EndOfStatement)) {
58 Lex();
59 }
60 return false;
61 }
62
63 void Initialize(MCAsmParser &Parser) override {
64 // Call the base implementation.
66
67 // x64 directives
68 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>(
69 ".allocstack");
70 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>(
71 ".endprolog");
72
73 // Code label directives
74 // label
75 // org
76
77 // Conditional control flow directives
78 // .break
79 // .continue
80 // .else
81 // .elseif
82 // .endif
83 // .endw
84 // .if
85 // .repeat
86 // .until
87 // .untilcxz
88 // .while
89
90 // Data allocation directives
91 // align
92 // even
93 // mmword
94 // tbyte
95 // xmmword
96 // ymmword
97
98 // Listing control directives
99 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref");
100 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list");
101 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall");
102 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif");
103 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro");
104 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall");
105 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref");
106 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist");
107 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif");
108 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro");
109 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page");
110 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle");
111 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond");
112 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title");
113
114 // Macro directives
115 // goto
116
117 // Miscellaneous directives
118 addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias");
119 // assume
120 // .fpo
121 addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>(
122 "includelib");
123 addDirectiveHandler<&COFFMasmParser::ParseDirectiveOption>("option");
124 // popcontext
125 // pushcontext
126 // .safeseh
127
128 // Procedure directives
129 addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp");
130 // invoke (32-bit only)
131 addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc");
132 // proto
133
134 // Processor directives; all ignored
135 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386");
136 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386p");
137 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387");
138 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486");
139 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486p");
140 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586");
141 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586p");
142 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686");
143 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686p");
144 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d");
145 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx");
146 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm");
147
148 // Scope directives
149 // comm
150 // externdef
151
152 // Segment directives
153 // .alpha (32-bit only, order segments alphabetically)
154 // .dosseg (32-bit only, order segments in DOS convention)
155 // .seq (32-bit only, order segments sequentially)
156 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends");
157 // group (32-bit only)
158 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment");
159
160 // Simplified segment directives
161 addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code");
162 // .const
163 addDirectiveHandler<
164 &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data");
165 addDirectiveHandler<
166 &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?");
167 // .exit
168 // .fardata
169 // .fardata?
170 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");
171 // .stack
172 // .startup
173
174 // String directives, written <name> <directive> <params>
175 // catstr (equivalent to <name> TEXTEQU <params>)
176 // instr (equivalent to <name> = @InStr(<params>))
177 // sizestr (equivalent to <name> = @SizeStr(<params>))
178 // substr (equivalent to <name> TEXTEQU @SubStr(<params>))
179
180 // Structure and record directives
181 // record
182 // typedef
183 }
184
185 bool ParseSectionDirectiveCode(StringRef, SMLoc) {
186 return ParseSectionSwitch(".text", COFF::IMAGE_SCN_CNT_CODE |
189 }
190
191 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
192 return ParseSectionSwitch(".data", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
195 }
196
197 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
198 return ParseSectionSwitch(".bss", COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA |
201 }
202
203 /// Stack of active procedure definitions.
204 SmallVector<StringRef, 1> CurrentProcedures;
205 SmallVector<bool, 1> CurrentProceduresFramed;
206
207public:
208 COFFMasmParser() = default;
209};
210
211} // end anonymous namespace.
212
213bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,
214 unsigned Characteristics) {
215 return ParseSectionSwitch(SectionName, Characteristics, "",
216 (COFF::COMDATType)0, Align(16));
217}
218
219bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,
220 unsigned Characteristics,
221 StringRef COMDATSymName,
223 Align Alignment) {
224 if (getLexer().isNot(AsmToken::EndOfStatement))
225 return TokError("unexpected token in section switching directive");
226 Lex();
227
228 MCSection *Section = getContext().getCOFFSection(SectionName, Characteristics,
229 COMDATSymName, Type);
230 Section->setAlignment(Alignment);
231 getStreamer().switchSection(Section);
232
233 return false;
234}
235
236bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {
237 StringRef SegmentName;
238 if (!getLexer().is(AsmToken::Identifier))
239 return TokError("expected identifier in directive");
240 SegmentName = getTok().getIdentifier();
241 Lex();
242
243 StringRef SectionName = SegmentName;
244 SmallVector<char, 247> SectionNameVector;
245
247 if (SegmentName == "_TEXT" || SegmentName.starts_with("_TEXT$")) {
248 if (SegmentName.size() == 5) {
249 SectionName = ".text";
250 } else {
252 (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);
253 }
254 Class = "CODE";
255 }
256
257 // Parse all options to end of statement.
258 // Alignment defaults to PARA if unspecified.
259 int64_t Alignment = 16;
260 // Default flags are used only if no characteristics are set.
261 bool DefaultCharacteristics = true;
262 unsigned Flags = 0;
263 // "obsolete" according to the documentation, but still supported.
264 bool Readonly = false;
265 while (getLexer().isNot(AsmToken::EndOfStatement)) {
266 switch (getTok().getKind()) {
267 default:
268 break;
269 case AsmToken::String: {
270 // Class identifier; overrides Kind.
271 Class = getTok().getStringContents();
272 Lex();
273 break;
274 }
276 SMLoc KeywordLoc = getTok().getLoc();
278 if (getParser().parseIdentifier(Keyword)) {
279 llvm_unreachable("failed to parse identifier at an identifier token");
280 }
281 if (Keyword.equals_insensitive("byte")) {
282 Alignment = 1;
283 } else if (Keyword.equals_insensitive("word")) {
284 Alignment = 2;
285 } else if (Keyword.equals_insensitive("dword")) {
286 Alignment = 4;
287 } else if (Keyword.equals_insensitive("para")) {
288 Alignment = 16;
289 } else if (Keyword.equals_insensitive("page")) {
290 Alignment = 256;
291 } else if (Keyword.equals_insensitive("align")) {
292 if (getParser().parseToken(AsmToken::LParen) ||
293 getParser().parseIntToken(Alignment,
294 "Expected integer alignment") ||
295 getParser().parseToken(AsmToken::RParen)) {
296 return Error(getTok().getLoc(),
297 "Expected (n) following ALIGN in SEGMENT directive");
298 }
299 if (!isPowerOf2_64(Alignment) || Alignment > 8192) {
300 return Error(KeywordLoc,
301 "ALIGN argument must be a power of 2 from 1 to 8192");
302 }
303 } else if (Keyword.equals_insensitive("alias")) {
304 if (getParser().parseToken(AsmToken::LParen) ||
305 !getTok().is(AsmToken::String))
306 return Error(
307 getTok().getLoc(),
308 "Expected (string) following ALIAS in SEGMENT directive");
309 SectionName = getTok().getStringContents();
310 Lex();
311 if (getParser().parseToken(AsmToken::RParen))
312 return Error(
313 getTok().getLoc(),
314 "Expected (string) following ALIAS in SEGMENT directive");
315 } else if (Keyword.equals_insensitive("readonly")) {
316 Readonly = true;
317 } else {
318 unsigned Characteristic =
328 .Default(-1);
329 if (Characteristic == static_cast<unsigned>(-1)) {
330 return Error(KeywordLoc,
331 "Expected characteristic in SEGMENT directive; found '" +
332 Keyword + "'");
333 }
334 Flags |= Characteristic;
335 DefaultCharacteristics = false;
336 }
337 }
338 }
339 }
340
346 if (Kind.isText()) {
347 if (DefaultCharacteristics) {
349 }
351 } else {
352 if (DefaultCharacteristics) {
354 }
356 }
357 if (Readonly) {
358 Flags &= ~COFF::IMAGE_SCN_MEM_WRITE;
359 }
360
361 MCSection *Section = getContext().getCOFFSection(SectionName, Flags, "",
362 (COFF::COMDATType)(0));
363 if (Alignment != 0) {
364 Section->setAlignment(Align(Alignment));
365 }
366 getStreamer().switchSection(Section);
367 return false;
368}
369
370/// ParseDirectiveSegmentEnd
371/// ::= identifier "ends"
372bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {
373 StringRef SegmentName;
374 if (!getLexer().is(AsmToken::Identifier))
375 return TokError("expected identifier in directive");
376 SegmentName = getTok().getIdentifier();
377
378 // Ignore; no action necessary.
379 Lex();
380 return false;
381}
382
383/// ParseDirectiveIncludelib
384/// ::= "includelib" identifier
385bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
387 if (getParser().parseIdentifier(Lib))
388 return TokError("expected identifier in includelib directive");
389
391 getStreamer().pushSection();
392 getStreamer().switchSection(getContext().getCOFFSection(
393 ".drectve", Flags, "", (COFF::COMDATType)(0)));
394 getStreamer().emitBytes("/DEFAULTLIB:");
395 getStreamer().emitBytes(Lib);
396 getStreamer().emitBytes(" ");
397 getStreamer().popSection();
398 return false;
399}
400
401/// ParseDirectiveOption
402/// ::= "option" option-list
403bool COFFMasmParser::ParseDirectiveOption(StringRef Directive, SMLoc Loc) {
404 auto parseOption = [&]() -> bool {
406 if (getParser().parseIdentifier(Option))
407 return TokError("expected identifier for option name");
408 if (Option.equals_insensitive("prologue")) {
409 StringRef MacroId;
410 if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))
411 return TokError("expected :macroId after OPTION PROLOGUE");
412 if (MacroId.equals_insensitive("none")) {
413 // Since we currently don't implement prologues/epilogues, NONE is our
414 // default.
415 return false;
416 }
417 return TokError("OPTION PROLOGUE is currently unsupported");
418 }
419 if (Option.equals_insensitive("epilogue")) {
420 StringRef MacroId;
421 if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))
422 return TokError("expected :macroId after OPTION EPILOGUE");
423 if (MacroId.equals_insensitive("none")) {
424 // Since we currently don't implement prologues/epilogues, NONE is our
425 // default.
426 return false;
427 }
428 return TokError("OPTION EPILOGUE is currently unsupported");
429 }
430 return TokError("OPTION '" + Option + "' is currently unsupported");
431 };
432
433 if (parseMany(parseOption))
434 return addErrorSuffix(" in OPTION directive");
435 return false;
436}
437
438/// ParseDirectiveProc
439/// TODO(epastor): Implement parameters and other attributes.
440/// ::= label "proc" [[distance]]
441/// statements
442/// label "endproc"
443bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
445 if (getParser().parseIdentifier(Label))
446 return Error(Loc, "expected identifier for procedure");
447 if (getLexer().is(AsmToken::Identifier)) {
448 StringRef nextVal = getTok().getString();
449 SMLoc nextLoc = getTok().getLoc();
450 if (nextVal.equals_insensitive("far")) {
451 // TODO(epastor): Handle far procedure definitions.
452 Lex();
453 return Error(nextLoc, "far procedure definitions not yet supported");
454 } else if (nextVal.equals_insensitive("near")) {
455 Lex();
456 nextVal = getTok().getString();
457 nextLoc = getTok().getLoc();
458 }
459 }
460 MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));
461
462 // Define symbol as simple external function
463 Sym->setExternal(true);
465
466 bool Framed = false;
467 if (getLexer().is(AsmToken::Identifier) &&
468 getTok().getString().equals_insensitive("frame")) {
469 Lex();
470 Framed = true;
471 getStreamer().emitWinCFIStartProc(Sym, Loc);
472 }
473 getStreamer().emitLabel(Sym, Loc);
474
475 CurrentProcedures.push_back(Label);
476 CurrentProceduresFramed.push_back(Framed);
477 return false;
478}
479bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
481 SMLoc LabelLoc = getTok().getLoc();
482 if (getParser().parseIdentifier(Label))
483 return Error(LabelLoc, "expected identifier for procedure end");
484
485 if (CurrentProcedures.empty())
486 return Error(Loc, "endp outside of procedure block");
487 else if (!CurrentProcedures.back().equals_insensitive(Label))
488 return Error(LabelLoc, "endp does not match current procedure '" +
489 CurrentProcedures.back() + "'");
490
491 if (CurrentProceduresFramed.back()) {
492 getStreamer().emitWinCFIEndProc(Loc);
493 }
494 CurrentProcedures.pop_back();
495 CurrentProceduresFramed.pop_back();
496 return false;
497}
498
499bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) {
500 std::string AliasName, ActualName;
501 if (getTok().isNot(AsmToken::Less) ||
502 getParser().parseAngleBracketString(AliasName))
503 return Error(getTok().getLoc(), "expected <aliasName>");
504 if (getParser().parseToken(AsmToken::Equal))
505 return addErrorSuffix(" in " + Directive + " directive");
506 if (getTok().isNot(AsmToken::Less) ||
507 getParser().parseAngleBracketString(ActualName))
508 return Error(getTok().getLoc(), "expected <actualName>");
509
510 MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName);
511 MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName);
512
513 getStreamer().emitWeakReference(Alias, Actual);
514
515 return false;
516}
517
518bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive,
519 SMLoc Loc) {
520 int64_t Size;
521 SMLoc SizeLoc = getTok().getLoc();
522 if (getParser().parseAbsoluteExpression(Size))
523 return Error(SizeLoc, "expected integer size");
524 if (Size % 8 != 0)
525 return Error(SizeLoc, "stack size must be a multiple of 8");
526 getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);
527 return false;
528}
529
530bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
531 SMLoc Loc) {
532 getStreamer().emitWinCFIEndProlog(Loc);
533 return false;
534}
535
536namespace llvm {
537
538MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
539
540} // 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:36
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition: MCSymbol.h:41
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 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:556
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition: StringRef.h:250
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:163
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:435
@ 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:296
MCAsmParserExtension * createCOFFMasmParser()
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39