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