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
9 #include "llvm/ADT/StringRef.h"
10 #include "llvm/ADT/Twine.h"
11 #include "llvm/BinaryFormat/COFF.h"
12 #include "llvm/MC/MCAsmMacro.h"
13 #include "llvm/MC/MCContext.h"
14 #include "llvm/MC/MCParser/MCAsmLexer.h"
15 #include "llvm/MC/MCParser/MCAsmParserExtension.h"
16 #include "llvm/MC/MCParser/MCTargetAsmParser.h"
17 #include "llvm/MC/MCSectionCOFF.h"
18 #include "llvm/MC/MCStreamer.h"
19 #include "llvm/MC/MCSymbolCOFF.h"
20 #include "llvm/MC/SectionKind.h"
21 #include "llvm/Support/Casting.h"
22 #include "llvm/Support/SMLoc.h"
23 #include <cstdint>
24 #include <utility>
25
26 using namespace llvm;
27
28 namespace {
29
30 class COFFMasmParser : public MCAsmParserExtension {
31 template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)>
addDirectiveHandler(StringRef Directive)32 void addDirectiveHandler(StringRef Directive) {
33 MCAsmParser::ExtensionDirectiveHandler Handler =
34 std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>);
35 getParser().addDirectiveHandler(Directive, Handler);
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
IgnoreDirective(StringRef,SMLoc)56 bool IgnoreDirective(StringRef, SMLoc) {
57 while (!getLexer().is(AsmToken::EndOfStatement)) {
58 Lex();
59 }
60 return false;
61 }
62
Initialize(MCAsmParser & Parser)63 void Initialize(MCAsmParser &Parser) override {
64 // Call the base implementation.
65 MCAsmParserExtension::Initialize(Parser);
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
ParseSectionDirectiveCode(StringRef,SMLoc)185 bool ParseSectionDirectiveCode(StringRef, SMLoc) {
186 return ParseSectionSwitch(".text", COFF::IMAGE_SCN_CNT_CODE |
187 COFF::IMAGE_SCN_MEM_EXECUTE |
188 COFF::IMAGE_SCN_MEM_READ);
189 }
190
ParseSectionDirectiveInitializedData(StringRef,SMLoc)191 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
192 return ParseSectionSwitch(".data", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
193 COFF::IMAGE_SCN_MEM_READ |
194 COFF::IMAGE_SCN_MEM_WRITE);
195 }
196
ParseSectionDirectiveUninitializedData(StringRef,SMLoc)197 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
198 return ParseSectionSwitch(".bss", COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA |
199 COFF::IMAGE_SCN_MEM_READ |
200 COFF::IMAGE_SCN_MEM_WRITE);
201 }
202
203 /// Stack of active procedure definitions.
204 SmallVector<StringRef, 1> CurrentProcedures;
205 SmallVector<bool, 1> CurrentProceduresFramed;
206
207 public:
208 COFFMasmParser() = default;
209 };
210
211 } // end anonymous namespace.
212
ParseSectionSwitch(StringRef SectionName,unsigned Characteristics)213 bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,
214 unsigned Characteristics) {
215 return ParseSectionSwitch(SectionName, Characteristics, "",
216 (COFF::COMDATType)0, Align(16));
217 }
218
ParseSectionSwitch(StringRef SectionName,unsigned Characteristics,StringRef COMDATSymName,COFF::COMDATType Type,Align Alignment)219 bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,
220 unsigned Characteristics,
221 StringRef COMDATSymName,
222 COFF::COMDATType Type,
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
ParseDirectiveSegment(StringRef Directive,SMLoc Loc)236 bool 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
246 StringRef Class;
247 if (SegmentName == "_TEXT" || SegmentName.starts_with("_TEXT$")) {
248 if (SegmentName.size() == 5) {
249 SectionName = ".text";
250 } else {
251 SectionName =
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 }
275 case AsmToken::Identifier: {
276 SMLoc KeywordLoc = getTok().getLoc();
277 StringRef Keyword;
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 =
319 StringSwitch<unsigned>(Keyword)
320 .CaseLower("info", COFF::IMAGE_SCN_LNK_INFO)
321 .CaseLower("read", COFF::IMAGE_SCN_MEM_READ)
322 .CaseLower("write", COFF::IMAGE_SCN_MEM_WRITE)
323 .CaseLower("execute", COFF::IMAGE_SCN_MEM_EXECUTE)
324 .CaseLower("shared", COFF::IMAGE_SCN_MEM_SHARED)
325 .CaseLower("nopage", COFF::IMAGE_SCN_MEM_NOT_PAGED)
326 .CaseLower("nocache", COFF::IMAGE_SCN_MEM_NOT_CACHED)
327 .CaseLower("discard", COFF::IMAGE_SCN_MEM_DISCARDABLE)
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
341 SectionKind Kind = StringSwitch<SectionKind>(Class)
342 .CaseLower("data", SectionKind::getData())
343 .CaseLower("code", SectionKind::getText())
344 .CaseLower("const", SectionKind::getReadOnly())
345 .Default(SectionKind::getData());
346 if (Kind.isText()) {
347 if (DefaultCharacteristics) {
348 Flags |= COFF::IMAGE_SCN_MEM_EXECUTE | COFF::IMAGE_SCN_MEM_READ;
349 }
350 Flags |= COFF::IMAGE_SCN_CNT_CODE;
351 } else {
352 if (DefaultCharacteristics) {
353 Flags |= COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;
354 }
355 Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
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"
ParseDirectiveSegmentEnd(StringRef Directive,SMLoc Loc)372 bool 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
ParseDirectiveIncludelib(StringRef Directive,SMLoc Loc)385 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
386 StringRef Lib;
387 if (getParser().parseIdentifier(Lib))
388 return TokError("expected identifier in includelib directive");
389
390 unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;
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
ParseDirectiveOption(StringRef Directive,SMLoc Loc)403 bool COFFMasmParser::ParseDirectiveOption(StringRef Directive, SMLoc Loc) {
404 auto parseOption = [&]() -> bool {
405 StringRef Option;
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"
ParseDirectiveProc(StringRef Directive,SMLoc Loc)443 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
444 StringRef Label;
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);
464 Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
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 }
ParseDirectiveEndProc(StringRef Directive,SMLoc Loc)479 bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
480 StringRef Label;
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
ParseDirectiveAlias(StringRef Directive,SMLoc Loc)499 bool 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
ParseSEHDirectiveAllocStack(StringRef Directive,SMLoc Loc)518 bool 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
ParseSEHDirectiveEndProlog(StringRef Directive,SMLoc Loc)530 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
531 SMLoc Loc) {
532 getStreamer().emitWinCFIEndProlog(Loc);
533 return false;
534 }
535
536 namespace llvm {
537
createCOFFMasmParser()538 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
539
540 } // end namespace llvm
541