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/MCContext.h"
13 #include "llvm/MC/MCParser/MCAsmLexer.h"
14 #include "llvm/MC/MCParser/MCAsmParserExtension.h"
15 #include "llvm/MC/MCSectionCOFF.h"
16 #include "llvm/MC/MCStreamer.h"
17 #include "llvm/MC/MCSymbolCOFF.h"
18 #include "llvm/MC/SectionKind.h"
19 #include "llvm/Support/Casting.h"
20 #include "llvm/Support/SMLoc.h"
21 #include <cstdint>
22 #include <utility>
23
24 using namespace llvm;
25
26 namespace {
27
28 class COFFMasmParser : public MCAsmParserExtension {
29 template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)>
addDirectiveHandler(StringRef Directive)30 void addDirectiveHandler(StringRef Directive) {
31 MCAsmParser::ExtensionDirectiveHandler Handler =
32 std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>);
33 getParser().addDirectiveHandler(Directive, Handler);
34 }
35
36 bool ParseSectionSwitch(StringRef Section, unsigned Characteristics,
37 SectionKind Kind);
38
39 bool ParseSectionSwitch(StringRef Section, unsigned Characteristics,
40 SectionKind Kind, StringRef COMDATSymName,
41 COFF::COMDATType Type);
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
49 bool ParseDirectiveAlias(StringRef, SMLoc);
50
51 bool ParseSEHDirectiveAllocStack(StringRef, SMLoc);
52 bool ParseSEHDirectiveEndProlog(StringRef, SMLoc);
53
IgnoreDirective(StringRef,SMLoc)54 bool IgnoreDirective(StringRef, SMLoc) {
55 while (!getLexer().is(AsmToken::EndOfStatement)) {
56 Lex();
57 }
58 return false;
59 }
60
Initialize(MCAsmParser & Parser)61 void Initialize(MCAsmParser &Parser) override {
62 // Call the base implementation.
63 MCAsmParserExtension::Initialize(Parser);
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 // 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<
162 &COFFMasmParser::ParseSectionDirectiveInitializedData>(".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
ParseSectionDirectiveCode(StringRef,SMLoc)183 bool ParseSectionDirectiveCode(StringRef, SMLoc) {
184 return ParseSectionSwitch(".text",
185 COFF::IMAGE_SCN_CNT_CODE
186 | COFF::IMAGE_SCN_MEM_EXECUTE
187 | COFF::IMAGE_SCN_MEM_READ,
188 SectionKind::getText());
189 }
190
ParseSectionDirectiveInitializedData(StringRef,SMLoc)191 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
192 return ParseSectionSwitch(".data",
193 COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
194 | COFF::IMAGE_SCN_MEM_READ
195 | COFF::IMAGE_SCN_MEM_WRITE,
196 SectionKind::getData());
197 }
198
ParseSectionDirectiveUninitializedData(StringRef,SMLoc)199 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
200 return ParseSectionSwitch(".bss",
201 COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA
202 | COFF::IMAGE_SCN_MEM_READ
203 | COFF::IMAGE_SCN_MEM_WRITE,
204 SectionKind::getBSS());
205 }
206
207 StringRef CurrentProcedure;
208 bool CurrentProcedureFramed;
209
210 public:
211 COFFMasmParser() = default;
212 };
213
214 } // end anonymous namespace.
215
computeSectionKind(unsigned Flags)216 static SectionKind computeSectionKind(unsigned Flags) {
217 if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)
218 return SectionKind::getText();
219 if (Flags & COFF::IMAGE_SCN_MEM_READ &&
220 (Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0)
221 return SectionKind::getReadOnly();
222 return SectionKind::getData();
223 }
224
ParseSectionSwitch(StringRef Section,unsigned Characteristics,SectionKind Kind)225 bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
226 unsigned Characteristics,
227 SectionKind Kind) {
228 return ParseSectionSwitch(Section, Characteristics, Kind, "",
229 (COFF::COMDATType)0);
230 }
231
ParseSectionSwitch(StringRef Section,unsigned Characteristics,SectionKind Kind,StringRef COMDATSymName,COFF::COMDATType Type)232 bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
233 unsigned Characteristics,
234 SectionKind Kind,
235 StringRef COMDATSymName,
236 COFF::COMDATType Type) {
237 if (getLexer().isNot(AsmToken::EndOfStatement))
238 return TokError("unexpected token in section switching directive");
239 Lex();
240
241 getStreamer().switchSection(getContext().getCOFFSection(
242 Section, Characteristics, Kind, COMDATSymName, Type));
243
244 return false;
245 }
246
ParseDirectiveSegment(StringRef Directive,SMLoc Loc)247 bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {
248 StringRef SegmentName;
249 if (!getLexer().is(AsmToken::Identifier))
250 return TokError("expected identifier in directive");
251 SegmentName = getTok().getIdentifier();
252 Lex();
253
254 StringRef SectionName = SegmentName;
255 SmallVector<char, 247> SectionNameVector;
256 unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
257 COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;
258 if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) {
259 if (SegmentName.size() == 5) {
260 SectionName = ".text";
261 } else {
262 SectionName =
263 (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);
264 }
265 Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE |
266 COFF::IMAGE_SCN_MEM_READ;
267 }
268 SectionKind Kind = computeSectionKind(Flags);
269 getStreamer().switchSection(getContext().getCOFFSection(
270 SectionName, Flags, Kind, "", (COFF::COMDATType)(0)));
271 return false;
272 }
273
274 /// ParseDirectiveSegmentEnd
275 /// ::= identifier "ends"
ParseDirectiveSegmentEnd(StringRef Directive,SMLoc Loc)276 bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {
277 StringRef SegmentName;
278 if (!getLexer().is(AsmToken::Identifier))
279 return TokError("expected identifier in directive");
280 SegmentName = getTok().getIdentifier();
281
282 // Ignore; no action necessary.
283 Lex();
284 return false;
285 }
286
287 /// ParseDirectiveIncludelib
288 /// ::= "includelib" identifier
ParseDirectiveIncludelib(StringRef Directive,SMLoc Loc)289 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
290 StringRef Lib;
291 if (getParser().parseIdentifier(Lib))
292 return TokError("expected identifier in includelib directive");
293
294 unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;
295 SectionKind Kind = computeSectionKind(Flags);
296 getStreamer().pushSection();
297 getStreamer().switchSection(getContext().getCOFFSection(
298 ".drectve", Flags, Kind, "", (COFF::COMDATType)(0)));
299 getStreamer().emitBytes("/DEFAULTLIB:");
300 getStreamer().emitBytes(Lib);
301 getStreamer().emitBytes(" ");
302 getStreamer().popSection();
303 return false;
304 }
305
306 /// ParseDirectiveProc
307 /// TODO(epastor): Implement parameters and other attributes.
308 /// ::= label "proc" [[distance]]
309 /// statements
310 /// label "endproc"
ParseDirectiveProc(StringRef Directive,SMLoc Loc)311 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
312 StringRef Label;
313 if (getParser().parseIdentifier(Label))
314 return Error(Loc, "expected identifier for procedure");
315 if (getLexer().is(AsmToken::Identifier)) {
316 StringRef nextVal = getTok().getString();
317 SMLoc nextLoc = getTok().getLoc();
318 if (nextVal.equals_insensitive("far")) {
319 // TODO(epastor): Handle far procedure definitions.
320 Lex();
321 return Error(nextLoc, "far procedure definitions not yet supported");
322 } else if (nextVal.equals_insensitive("near")) {
323 Lex();
324 nextVal = getTok().getString();
325 nextLoc = getTok().getLoc();
326 }
327 }
328 MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));
329
330 // Define symbol as simple external function
331 Sym->setExternal(true);
332 Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
333
334 bool Framed = false;
335 if (getLexer().is(AsmToken::Identifier) &&
336 getTok().getString().equals_insensitive("frame")) {
337 Lex();
338 Framed = true;
339 getStreamer().emitWinCFIStartProc(Sym, Loc);
340 }
341 getStreamer().emitLabel(Sym, Loc);
342
343 CurrentProcedure = Label;
344 CurrentProcedureFramed = Framed;
345 return false;
346 }
ParseDirectiveEndProc(StringRef Directive,SMLoc Loc)347 bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
348 StringRef Label;
349 SMLoc LabelLoc = getTok().getLoc();
350 if (getParser().parseIdentifier(Label))
351 return Error(LabelLoc, "expected identifier for procedure end");
352
353 if (CurrentProcedure.empty())
354 return Error(Loc, "endp outside of procedure block");
355 else if (CurrentProcedure != Label)
356 return Error(LabelLoc, "endp does not match current procedure '" +
357 CurrentProcedure + "'");
358
359 if (CurrentProcedureFramed) {
360 getStreamer().emitWinCFIEndProc(Loc);
361 }
362 CurrentProcedure = "";
363 CurrentProcedureFramed = false;
364 return false;
365 }
366
ParseDirectiveAlias(StringRef Directive,SMLoc Loc)367 bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) {
368 std::string AliasName, ActualName;
369 if (getTok().isNot(AsmToken::Less) ||
370 getParser().parseAngleBracketString(AliasName))
371 return Error(getTok().getLoc(), "expected <aliasName>");
372 if (getParser().parseToken(AsmToken::Equal))
373 return addErrorSuffix(" in " + Directive + " directive");
374 if (getTok().isNot(AsmToken::Less) ||
375 getParser().parseAngleBracketString(ActualName))
376 return Error(getTok().getLoc(), "expected <actualName>");
377
378 MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName);
379 MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName);
380
381 getStreamer().emitWeakReference(Alias, Actual);
382
383 return false;
384 }
385
ParseSEHDirectiveAllocStack(StringRef Directive,SMLoc Loc)386 bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive,
387 SMLoc Loc) {
388 int64_t Size;
389 SMLoc SizeLoc = getTok().getLoc();
390 if (getParser().parseAbsoluteExpression(Size))
391 return Error(SizeLoc, "expected integer size");
392 if (Size % 8 != 0)
393 return Error(SizeLoc, "stack size must be a multiple of 8");
394 getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);
395 return false;
396 }
397
ParseSEHDirectiveEndProlog(StringRef Directive,SMLoc Loc)398 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
399 SMLoc Loc) {
400 getStreamer().emitWinCFIEndProlog(Loc);
401 return false;
402 }
403
404 namespace llvm {
405
createCOFFMasmParser()406 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
407
408 } // end namespace llvm
409