1 //===- COFFMasmParser.cpp - COFF MASM Assembly Parser ---------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
30 class COFFMasmParser
: public MCAsmParserExtension
{
31 template <bool (COFFMasmParser::*HandlerMethod
)(StringRef
, SMLoc
)>
32 void addDirectiveHandler(StringRef Directive
) {
33 MCAsmParser::ExtensionDirectiveHandler Handler
=
34 std::make_pair(this, HandleDirective
<COFFMasmParser
, HandlerMethod
>);
35 getParser().addDirectiveHandler(Directive
, Handler
);
38 bool ParseSectionSwitch(StringRef SectionName
, unsigned Characteristics
,
41 bool ParseSectionSwitch(StringRef SectionName
, unsigned Characteristics
,
42 SectionKind Kind
, StringRef COMDATSymName
,
43 COFF::COMDATType Type
, Align Alignment
);
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
);
52 bool ParseDirectiveAlias(StringRef
, SMLoc
);
54 bool ParseSEHDirectiveAllocStack(StringRef
, SMLoc
);
55 bool ParseSEHDirectiveEndProlog(StringRef
, SMLoc
);
57 bool IgnoreDirective(StringRef
, SMLoc
) {
58 while (!getLexer().is(AsmToken::EndOfStatement
)) {
64 void Initialize(MCAsmParser
&Parser
) override
{
65 // Call the base implementation.
66 MCAsmParserExtension::Initialize(Parser
);
69 addDirectiveHandler
<&COFFMasmParser::ParseSEHDirectiveAllocStack
>(
71 addDirectiveHandler
<&COFFMasmParser::ParseSEHDirectiveEndProlog
>(
74 // Code label directives
78 // Conditional control flow directives
91 // Data allocation directives
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");
118 // Miscellaneous directives
119 addDirectiveHandler
<&COFFMasmParser::ParseDirectiveAlias
>("alias");
122 addDirectiveHandler
<&COFFMasmParser::ParseDirectiveIncludelib
>(
124 addDirectiveHandler
<&COFFMasmParser::ParseDirectiveOption
>("option");
129 // Procedure directives
130 addDirectiveHandler
<&COFFMasmParser::ParseDirectiveEndProc
>("endp");
131 // invoke (32-bit only)
132 addDirectiveHandler
<&COFFMasmParser::ParseDirectiveProc
>("proc");
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");
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");
161 // Simplified segment directives
162 addDirectiveHandler
<&COFFMasmParser::ParseSectionDirectiveCode
>(".code");
165 &COFFMasmParser::ParseSectionDirectiveInitializedData
>(".data");
167 &COFFMasmParser::ParseSectionDirectiveUninitializedData
>(".data?");
171 addDirectiveHandler
<&COFFMasmParser::IgnoreDirective
>(".model");
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>))
181 // Structure and record directives
186 bool ParseSectionDirectiveCode(StringRef
, SMLoc
) {
187 return ParseSectionSwitch(".text",
188 COFF::IMAGE_SCN_CNT_CODE
189 | COFF::IMAGE_SCN_MEM_EXECUTE
190 | COFF::IMAGE_SCN_MEM_READ
,
191 SectionKind::getText());
194 bool ParseSectionDirectiveInitializedData(StringRef
, SMLoc
) {
195 return ParseSectionSwitch(".data",
196 COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
197 | COFF::IMAGE_SCN_MEM_READ
198 | COFF::IMAGE_SCN_MEM_WRITE
,
199 SectionKind::getData());
202 bool ParseSectionDirectiveUninitializedData(StringRef
, SMLoc
) {
203 return ParseSectionSwitch(".bss",
204 COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA
205 | COFF::IMAGE_SCN_MEM_READ
206 | COFF::IMAGE_SCN_MEM_WRITE
,
207 SectionKind::getBSS());
210 /// Stack of active procedure definitions.
211 SmallVector
<StringRef
, 1> CurrentProcedures
;
212 SmallVector
<bool, 1> CurrentProceduresFramed
;
215 COFFMasmParser() = default;
218 } // end anonymous namespace.
220 bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName
,
221 unsigned Characteristics
,
223 return ParseSectionSwitch(SectionName
, Characteristics
, Kind
, "",
224 (COFF::COMDATType
)0, Align(16));
227 bool COFFMasmParser::ParseSectionSwitch(
228 StringRef SectionName
, unsigned Characteristics
, SectionKind Kind
,
229 StringRef COMDATSymName
, COFF::COMDATType Type
, Align Alignment
) {
230 if (getLexer().isNot(AsmToken::EndOfStatement
))
231 return TokError("unexpected token in section switching directive");
234 MCSection
*Section
= getContext().getCOFFSection(SectionName
, Characteristics
,
235 Kind
, COMDATSymName
, Type
);
236 Section
->setAlignment(Alignment
);
237 getStreamer().switchSection(Section
);
242 bool 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();
249 StringRef SectionName
= SegmentName
;
250 SmallVector
<char, 247> SectionNameVector
;
253 if (SegmentName
== "_TEXT" || SegmentName
.starts_with("_TEXT$")) {
254 if (SegmentName
.size() == 5) {
255 SectionName
= ".text";
258 (".text$" + SegmentName
.substr(6)).toStringRef(SectionNameVector
);
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;
269 // "obsolete" according to the documentation, but still supported.
270 bool Readonly
= false;
271 while (getLexer().isNot(AsmToken::EndOfStatement
)) {
272 switch (getTok().getKind()) {
275 case AsmToken::String
: {
276 // Class identifier; overrides Kind.
277 Class
= getTok().getStringContents();
281 case AsmToken::Identifier
: {
282 SMLoc KeywordLoc
= getTok().getLoc();
284 if (getParser().parseIdentifier(Keyword
)) {
285 llvm_unreachable("failed to parse identifier at an identifier token");
287 if (Keyword
.equals_insensitive("byte")) {
289 } else if (Keyword
.equals_insensitive("word")) {
291 } else if (Keyword
.equals_insensitive("dword")) {
293 } else if (Keyword
.equals_insensitive("para")) {
295 } else if (Keyword
.equals_insensitive("page")) {
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");
305 if (!isPowerOf2_64(Alignment
) || Alignment
> 8192) {
306 return Error(KeywordLoc
,
307 "ALIGN argument must be a power of 2 from 1 to 8192");
309 } else if (Keyword
.equals_insensitive("alias")) {
310 if (getParser().parseToken(AsmToken::LParen
) ||
311 !getTok().is(AsmToken::String
))
314 "Expected (string) following ALIAS in SEGMENT directive");
315 SectionName
= getTok().getStringContents();
317 if (getParser().parseToken(AsmToken::RParen
))
320 "Expected (string) following ALIAS in SEGMENT directive");
321 } else if (Keyword
.equals_insensitive("readonly")) {
324 unsigned Characteristic
=
325 StringSwitch
<unsigned>(Keyword
)
326 .CaseLower("info", COFF::IMAGE_SCN_LNK_INFO
)
327 .CaseLower("read", COFF::IMAGE_SCN_MEM_READ
)
328 .CaseLower("write", COFF::IMAGE_SCN_MEM_WRITE
)
329 .CaseLower("execute", COFF::IMAGE_SCN_MEM_EXECUTE
)
330 .CaseLower("shared", COFF::IMAGE_SCN_MEM_SHARED
)
331 .CaseLower("nopage", COFF::IMAGE_SCN_MEM_NOT_PAGED
)
332 .CaseLower("nocache", COFF::IMAGE_SCN_MEM_NOT_CACHED
)
333 .CaseLower("discard", COFF::IMAGE_SCN_MEM_DISCARDABLE
)
335 if (Characteristic
== static_cast<unsigned>(-1)) {
336 return Error(KeywordLoc
,
337 "Expected characteristic in SEGMENT directive; found '" +
340 Flags
|= Characteristic
;
341 DefaultCharacteristics
= false;
347 SectionKind Kind
= StringSwitch
<SectionKind
>(Class
)
348 .CaseLower("data", SectionKind::getData())
349 .CaseLower("code", SectionKind::getText())
350 .CaseLower("const", SectionKind::getReadOnly())
351 .Default(SectionKind::getData());
353 if (DefaultCharacteristics
) {
354 Flags
|= COFF::IMAGE_SCN_MEM_EXECUTE
| COFF::IMAGE_SCN_MEM_READ
;
356 Flags
|= COFF::IMAGE_SCN_CNT_CODE
;
358 if (DefaultCharacteristics
) {
359 Flags
|= COFF::IMAGE_SCN_MEM_READ
| COFF::IMAGE_SCN_MEM_WRITE
;
361 Flags
|= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
;
364 Flags
&= ~COFF::IMAGE_SCN_MEM_WRITE
;
367 MCSection
*Section
= getContext().getCOFFSection(SectionName
, Flags
, Kind
, "",
368 (COFF::COMDATType
)(0));
369 if (Alignment
!= 0) {
370 Section
->setAlignment(Align(Alignment
));
372 getStreamer().switchSection(Section
);
376 /// ParseDirectiveSegmentEnd
377 /// ::= identifier "ends"
378 bool 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();
384 // Ignore; no action necessary.
389 /// ParseDirectiveIncludelib
390 /// ::= "includelib" identifier
391 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive
, SMLoc Loc
) {
393 if (getParser().parseIdentifier(Lib
))
394 return TokError("expected identifier in includelib directive");
396 unsigned Flags
= COFF::IMAGE_SCN_MEM_PRELOAD
| COFF::IMAGE_SCN_MEM_16BIT
;
397 SectionKind Kind
= SectionKind::getData();
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();
408 /// ParseDirectiveOption
409 /// ::= "option" option-list
410 bool 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")) {
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
424 return TokError("OPTION PROLOGUE is currently unsupported");
426 if (Option
.equals_insensitive("epilogue")) {
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
435 return TokError("OPTION EPILOGUE is currently unsupported");
437 return TokError("OPTION '" + Option
+ "' is currently unsupported");
440 if (parseMany(parseOption
))
441 return addErrorSuffix(" in OPTION directive");
445 /// ParseDirectiveProc
446 /// TODO(epastor): Implement parameters and other attributes.
447 /// ::= label "proc" [[distance]]
450 bool 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.
460 return Error(nextLoc
, "far procedure definitions not yet supported");
461 } else if (nextVal
.equals_insensitive("near")) {
463 nextVal
= getTok().getString();
464 nextLoc
= getTok().getLoc();
467 MCSymbolCOFF
*Sym
= cast
<MCSymbolCOFF
>(getContext().getOrCreateSymbol(Label
));
469 // Define symbol as simple external function
470 Sym
->setExternal(true);
471 Sym
->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION
<< COFF::SCT_COMPLEX_TYPE_SHIFT
);
474 if (getLexer().is(AsmToken::Identifier
) &&
475 getTok().getString().equals_insensitive("frame")) {
478 getStreamer().emitWinCFIStartProc(Sym
, Loc
);
480 getStreamer().emitLabel(Sym
, Loc
);
482 CurrentProcedures
.push_back(Label
);
483 CurrentProceduresFramed
.push_back(Framed
);
486 bool 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");
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() + "'");
498 if (CurrentProceduresFramed
.back()) {
499 getStreamer().emitWinCFIEndProc(Loc
);
501 CurrentProcedures
.pop_back();
502 CurrentProceduresFramed
.pop_back();
506 bool 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>");
517 MCSymbol
*Alias
= getContext().getOrCreateSymbol(AliasName
);
518 MCSymbol
*Actual
= getContext().getOrCreateSymbol(ActualName
);
520 getStreamer().emitWeakReference(Alias
, Actual
);
525 bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive
,
528 SMLoc SizeLoc
= getTok().getLoc();
529 if (getParser().parseAbsoluteExpression(Size
))
530 return Error(SizeLoc
, "expected integer size");
532 return Error(SizeLoc
, "stack size must be a multiple of 8");
533 getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size
), Loc
);
537 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive
,
539 getStreamer().emitWinCFIEndProlog(Loc
);
545 MCAsmParserExtension
*createCOFFMasmParser() { return new COFFMasmParser
; }
547 } // end namespace llvm