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