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/StringSwitch.h"
11 #include "llvm/ADT/Triple.h"
12 #include "llvm/ADT/Twine.h"
13 #include "llvm/BinaryFormat/COFF.h"
14 #include "llvm/MC/MCContext.h"
15 #include "llvm/MC/MCDirectives.h"
16 #include "llvm/MC/MCObjectFileInfo.h"
17 #include "llvm/MC/MCParser/MCAsmLexer.h"
18 #include "llvm/MC/MCParser/MCAsmParserExtension.h"
19 #include "llvm/MC/MCParser/MCAsmParserUtils.h"
20 #include "llvm/MC/MCParser/MCTargetAsmParser.h"
21 #include "llvm/MC/MCRegisterInfo.h"
22 #include "llvm/MC/MCSectionCOFF.h"
23 #include "llvm/MC/MCStreamer.h"
24 #include "llvm/MC/MCSymbolCOFF.h"
25 #include "llvm/MC/SectionKind.h"
26 #include "llvm/Support/SMLoc.h"
27 #include <cassert>
28 #include <cstdint>
29 #include <limits>
30 #include <utility>
31
32 using namespace llvm;
33
34 namespace {
35
36 class COFFMasmParser : public MCAsmParserExtension {
37 template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)>
addDirectiveHandler(StringRef Directive)38 void addDirectiveHandler(StringRef Directive) {
39 MCAsmParser::ExtensionDirectiveHandler Handler =
40 std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>);
41 getParser().addDirectiveHandler(Directive, Handler);
42 }
43
44 bool ParseSectionSwitch(StringRef Section, unsigned Characteristics,
45 SectionKind Kind);
46
47 bool ParseSectionSwitch(StringRef Section, unsigned Characteristics,
48 SectionKind Kind, StringRef COMDATSymName,
49 COFF::COMDATType Type);
50
51 bool ParseDirectiveProc(StringRef, SMLoc);
52 bool ParseDirectiveEndProc(StringRef, SMLoc);
53 bool ParseDirectiveSegment(StringRef, SMLoc);
54 bool ParseDirectiveSegmentEnd(StringRef, SMLoc);
55 bool ParseDirectiveIncludelib(StringRef, SMLoc);
56
57 bool ParseDirectiveAlias(StringRef, SMLoc);
58
59 bool ParseSEHDirectiveAllocStack(StringRef, SMLoc);
60 bool ParseSEHDirectiveEndProlog(StringRef, SMLoc);
61
IgnoreDirective(StringRef,SMLoc)62 bool IgnoreDirective(StringRef, SMLoc) {
63 while (!getLexer().is(AsmToken::EndOfStatement)) {
64 Lex();
65 }
66 return false;
67 }
68
Initialize(MCAsmParser & Parser)69 void Initialize(MCAsmParser &Parser) override {
70 // Call the base implementation.
71 MCAsmParserExtension::Initialize(Parser);
72
73 // x64 directives
74 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>(
75 ".allocstack");
76 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>(
77 ".endprolog");
78
79 // Code label directives
80 // label
81 // org
82
83 // Conditional control flow directives
84 // .break
85 // .continue
86 // .else
87 // .elseif
88 // .endif
89 // .endw
90 // .if
91 // .repeat
92 // .until
93 // .untilcxz
94 // .while
95
96 // Data allocation directives
97 // align
98 // even
99 // mmword
100 // tbyte
101 // xmmword
102 // ymmword
103
104 // Listing control directives
105 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref");
106 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list");
107 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall");
108 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif");
109 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro");
110 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall");
111 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref");
112 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist");
113 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif");
114 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro");
115 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page");
116 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle");
117 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond");
118 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title");
119
120 // Macro directives
121 // goto
122
123 // Miscellaneous directives
124 addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias");
125 // assume
126 // .fpo
127 addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>(
128 "includelib");
129 // option
130 // popcontext
131 // pushcontext
132 // .safeseh
133
134 // Procedure directives
135 addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp");
136 // invoke (32-bit only)
137 addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc");
138 // proto
139
140 // Processor directives; all ignored
141 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386");
142 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386P");
143 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387");
144 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486");
145 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486P");
146 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586");
147 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586P");
148 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686");
149 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686P");
150 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d");
151 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx");
152 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm");
153
154 // Scope directives
155 // comm
156 // externdef
157
158 // Segment directives
159 // .alpha (32-bit only, order segments alphabetically)
160 // .dosseg (32-bit only, order segments in DOS convention)
161 // .seq (32-bit only, order segments sequentially)
162 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends");
163 // group (32-bit only)
164 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment");
165
166 // Simplified segment directives
167 addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code");
168 // .const
169 addDirectiveHandler<
170 &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data");
171 addDirectiveHandler<
172 &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?");
173 // .exit
174 // .fardata
175 // .fardata?
176 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");
177 // .stack
178 // .startup
179
180 // String directives, written <name> <directive> <params>
181 // catstr (equivalent to <name> TEXTEQU <params>)
182 // instr (equivalent to <name> = @InStr(<params>))
183 // sizestr (equivalent to <name> = @SizeStr(<params>))
184 // substr (equivalent to <name> TEXTEQU @SubStr(<params>))
185
186 // Structure and record directives
187 // record
188 // typedef
189 }
190
ParseSectionDirectiveCode(StringRef,SMLoc)191 bool ParseSectionDirectiveCode(StringRef, SMLoc) {
192 return ParseSectionSwitch(".text",
193 COFF::IMAGE_SCN_CNT_CODE
194 | COFF::IMAGE_SCN_MEM_EXECUTE
195 | COFF::IMAGE_SCN_MEM_READ,
196 SectionKind::getText());
197 }
198
ParseSectionDirectiveInitializedData(StringRef,SMLoc)199 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
200 return ParseSectionSwitch(".data",
201 COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
202 | COFF::IMAGE_SCN_MEM_READ
203 | COFF::IMAGE_SCN_MEM_WRITE,
204 SectionKind::getData());
205 }
206
ParseSectionDirectiveUninitializedData(StringRef,SMLoc)207 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
208 return ParseSectionSwitch(".bss",
209 COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA
210 | COFF::IMAGE_SCN_MEM_READ
211 | COFF::IMAGE_SCN_MEM_WRITE,
212 SectionKind::getBSS());
213 }
214
215 StringRef CurrentProcedure;
216 bool CurrentProcedureFramed;
217
218 public:
219 COFFMasmParser() = default;
220 };
221
222 } // end anonymous namespace.
223
computeSectionKind(unsigned Flags)224 static SectionKind computeSectionKind(unsigned Flags) {
225 if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)
226 return SectionKind::getText();
227 if (Flags & COFF::IMAGE_SCN_MEM_READ &&
228 (Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0)
229 return SectionKind::getReadOnly();
230 return SectionKind::getData();
231 }
232
ParseSectionSwitch(StringRef Section,unsigned Characteristics,SectionKind Kind)233 bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
234 unsigned Characteristics,
235 SectionKind Kind) {
236 return ParseSectionSwitch(Section, Characteristics, Kind, "",
237 (COFF::COMDATType)0);
238 }
239
ParseSectionSwitch(StringRef Section,unsigned Characteristics,SectionKind Kind,StringRef COMDATSymName,COFF::COMDATType Type)240 bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
241 unsigned Characteristics,
242 SectionKind Kind,
243 StringRef COMDATSymName,
244 COFF::COMDATType Type) {
245 if (getLexer().isNot(AsmToken::EndOfStatement))
246 return TokError("unexpected token in section switching directive");
247 Lex();
248
249 getStreamer().SwitchSection(getContext().getCOFFSection(
250 Section, Characteristics, Kind, COMDATSymName, Type));
251
252 return false;
253 }
254
ParseDirectiveSegment(StringRef Directive,SMLoc Loc)255 bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {
256 StringRef SegmentName;
257 if (!getLexer().is(AsmToken::Identifier))
258 return TokError("expected identifier in directive");
259 SegmentName = getTok().getIdentifier();
260 Lex();
261
262 StringRef SectionName = SegmentName;
263 SmallVector<char, 247> SectionNameVector;
264 unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
265 COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;
266 if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) {
267 if (SegmentName.size() == 5) {
268 SectionName = ".text";
269 } else {
270 SectionName =
271 (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);
272 }
273 Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE |
274 COFF::IMAGE_SCN_MEM_READ;
275 }
276 SectionKind Kind = computeSectionKind(Flags);
277 getStreamer().SwitchSection(getContext().getCOFFSection(
278 SectionName, Flags, Kind, "", (COFF::COMDATType)(0)));
279 return false;
280 }
281
282 /// ParseDirectiveSegmentEnd
283 /// ::= identifier "ends"
ParseDirectiveSegmentEnd(StringRef Directive,SMLoc Loc)284 bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {
285 StringRef SegmentName;
286 if (!getLexer().is(AsmToken::Identifier))
287 return TokError("expected identifier in directive");
288 SegmentName = getTok().getIdentifier();
289
290 // Ignore; no action necessary.
291 Lex();
292 return false;
293 }
294
295 /// ParseDirectiveIncludelib
296 /// ::= "includelib" identifier
ParseDirectiveIncludelib(StringRef Directive,SMLoc Loc)297 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
298 StringRef Lib;
299 if (getParser().parseIdentifier(Lib))
300 return TokError("expected identifier in includelib directive");
301
302 unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;
303 SectionKind Kind = computeSectionKind(Flags);
304 getStreamer().PushSection();
305 getStreamer().SwitchSection(getContext().getCOFFSection(
306 ".drectve", Flags, Kind, "", (COFF::COMDATType)(0)));
307 getStreamer().emitBytes("/DEFAULTLIB:");
308 getStreamer().emitBytes(Lib);
309 getStreamer().emitBytes(" ");
310 getStreamer().PopSection();
311 return false;
312 }
313
314 /// ParseDirectiveProc
315 /// TODO(epastor): Implement parameters and other attributes.
316 /// ::= label "proc" [[distance]]
317 /// statements
318 /// label "endproc"
ParseDirectiveProc(StringRef Directive,SMLoc Loc)319 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
320 StringRef Label;
321 if (getParser().parseIdentifier(Label))
322 return Error(Loc, "expected identifier for procedure");
323 if (getLexer().is(AsmToken::Identifier)) {
324 StringRef nextVal = getTok().getString();
325 SMLoc nextLoc = getTok().getLoc();
326 if (nextVal.equals_lower("far")) {
327 // TODO(epastor): Handle far procedure definitions.
328 Lex();
329 return Error(nextLoc, "far procedure definitions not yet supported");
330 } else if (nextVal.equals_lower("near")) {
331 Lex();
332 nextVal = getTok().getString();
333 nextLoc = getTok().getLoc();
334 }
335 }
336 MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));
337
338 // Define symbol as simple external function
339 Sym->setExternal(true);
340 Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
341
342 bool Framed = false;
343 if (getLexer().is(AsmToken::Identifier) &&
344 getTok().getString().equals_lower("frame")) {
345 Lex();
346 Framed = true;
347 getStreamer().EmitWinCFIStartProc(Sym, Loc);
348 }
349 getStreamer().emitLabel(Sym, Loc);
350
351 CurrentProcedure = Label;
352 CurrentProcedureFramed = Framed;
353 return false;
354 }
ParseDirectiveEndProc(StringRef Directive,SMLoc Loc)355 bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
356 StringRef Label;
357 SMLoc LabelLoc = getTok().getLoc();
358 if (getParser().parseIdentifier(Label))
359 return Error(LabelLoc, "expected identifier for procedure end");
360
361 if (CurrentProcedure.empty())
362 return Error(Loc, "endp outside of procedure block");
363 else if (CurrentProcedure != Label)
364 return Error(LabelLoc, "endp does not match current procedure '" +
365 CurrentProcedure + "'");
366
367 if (CurrentProcedureFramed) {
368 getStreamer().EmitWinCFIEndProc(Loc);
369 }
370 CurrentProcedure = "";
371 CurrentProcedureFramed = false;
372 return false;
373 }
374
ParseDirectiveAlias(StringRef Directive,SMLoc Loc)375 bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) {
376 std::string AliasName, ActualName;
377 if (getTok().isNot(AsmToken::Less) ||
378 getParser().parseAngleBracketString(AliasName))
379 return Error(getTok().getLoc(), "expected <aliasName>");
380 if (getParser().parseToken(AsmToken::Equal))
381 return addErrorSuffix(" in " + Directive + " directive");
382 if (getTok().isNot(AsmToken::Less) ||
383 getParser().parseAngleBracketString(ActualName))
384 return Error(getTok().getLoc(), "expected <actualName>");
385
386 MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName);
387 MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName);
388
389 getStreamer().emitWeakReference(Alias, Actual);
390
391 return false;
392 }
393
ParseSEHDirectiveAllocStack(StringRef Directive,SMLoc Loc)394 bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive,
395 SMLoc Loc) {
396 int64_t Size;
397 SMLoc SizeLoc = getTok().getLoc();
398 if (getParser().parseAbsoluteExpression(Size))
399 return Error(SizeLoc, "expected integer size");
400 if (Size % 8 != 0)
401 return Error(SizeLoc, "stack size must be a multiple of 8");
402 getStreamer().EmitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);
403 return false;
404 }
405
ParseSEHDirectiveEndProlog(StringRef Directive,SMLoc Loc)406 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
407 SMLoc Loc) {
408 getStreamer().EmitWinCFIEndProlog(Loc);
409 return false;
410 }
411
412 namespace llvm {
413
createCOFFMasmParser()414 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
415
416 } // end namespace llvm
417