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