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