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)>
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 
62   bool IgnoreDirective(StringRef, SMLoc) {
63     while (!getLexer().is(AsmToken::EndOfStatement)) {
64       Lex();
65     }
66     return false;
67   }
68 
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     // exitm
122     // goto
123     // purge
124 
125     // Miscellaneous directives
126     addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias");
127     // assume
128     // .fpo
129     addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>(
130         "includelib");
131     // option
132     // popcontext
133     // pushcontext
134     // .safeseh
135 
136     // Procedure directives
137     addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp");
138     // invoke (32-bit only)
139     addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc");
140     // proto
141 
142     // Processor directives; all ignored
143     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386");
144     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386P");
145     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387");
146     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486");
147     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486P");
148     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586");
149     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586P");
150     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686");
151     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686P");
152     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d");
153     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx");
154     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm");
155 
156     // Repeat blocks directives
157     // goto
158 
159     // Scope directives
160     // comm
161     // externdef
162 
163     // Segment directives
164     // .alpha (32-bit only, order segments alphabetically)
165     // .dosseg (32-bit only, order segments in DOS convention)
166     // .seq (32-bit only, order segments sequentially)
167     addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends");
168     // group (32-bit only)
169     addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment");
170 
171     // Simplified segment directives
172     addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code");
173     // .const
174     addDirectiveHandler<
175         &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data");
176     addDirectiveHandler<
177         &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?");
178     // .exit
179     // .fardata
180     // .fardata?
181     addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");
182     // .stack
183     // .startup
184 
185     // String directives, written <name> <directive> <params>
186     // catstr (equivalent to <name> TEXTEQU <params>)
187     // instr (equivalent to <name> = @InStr(<params>))
188     // sizestr (equivalent to <name> = @SizeStr(<params>))
189     // substr (equivalent to <name> TEXTEQU @SubStr(<params>))
190 
191     // Structure and record directives
192     // record
193     // typedef
194   }
195 
196   bool ParseSectionDirectiveCode(StringRef, SMLoc) {
197     return ParseSectionSwitch(".text",
198                               COFF::IMAGE_SCN_CNT_CODE
199                             | COFF::IMAGE_SCN_MEM_EXECUTE
200                             | COFF::IMAGE_SCN_MEM_READ,
201                               SectionKind::getText());
202   }
203 
204   bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
205     return ParseSectionSwitch(".data",
206                               COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
207                             | COFF::IMAGE_SCN_MEM_READ
208                             | COFF::IMAGE_SCN_MEM_WRITE,
209                               SectionKind::getData());
210   }
211 
212   bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
213     return ParseSectionSwitch(".bss",
214                               COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA
215                             | COFF::IMAGE_SCN_MEM_READ
216                             | COFF::IMAGE_SCN_MEM_WRITE,
217                               SectionKind::getBSS());
218   }
219 
220   StringRef CurrentProcedure;
221   bool CurrentProcedureFramed;
222 
223 public:
224   COFFMasmParser() = default;
225 };
226 
227 } // end anonymous namespace.
228 
229 static SectionKind computeSectionKind(unsigned Flags) {
230   if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)
231     return SectionKind::getText();
232   if (Flags & COFF::IMAGE_SCN_MEM_READ &&
233       (Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0)
234     return SectionKind::getReadOnly();
235   return SectionKind::getData();
236 }
237 
238 bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
239                                         unsigned Characteristics,
240                                         SectionKind Kind) {
241   return ParseSectionSwitch(Section, Characteristics, Kind, "",
242                             (COFF::COMDATType)0);
243 }
244 
245 bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
246                                         unsigned Characteristics,
247                                         SectionKind Kind,
248                                         StringRef COMDATSymName,
249                                         COFF::COMDATType Type) {
250   if (getLexer().isNot(AsmToken::EndOfStatement))
251     return TokError("unexpected token in section switching directive");
252   Lex();
253 
254   getStreamer().SwitchSection(getContext().getCOFFSection(
255       Section, Characteristics, Kind, COMDATSymName, Type));
256 
257   return false;
258 }
259 
260 bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {
261   StringRef SegmentName;
262   if (!getLexer().is(AsmToken::Identifier))
263     return TokError("expected identifier in directive");
264   SegmentName = getTok().getIdentifier();
265   Lex();
266 
267   StringRef SectionName = SegmentName;
268   SmallVector<char, 247> SectionNameVector;
269   unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
270                    COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;
271   if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) {
272     if (SegmentName.size() == 5) {
273       SectionName = ".text";
274     } else {
275       SectionName =
276           (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);
277     }
278     Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE |
279             COFF::IMAGE_SCN_MEM_READ;
280   }
281   SectionKind Kind = computeSectionKind(Flags);
282   getStreamer().SwitchSection(getContext().getCOFFSection(
283       SectionName, Flags, Kind, "", (COFF::COMDATType)(0)));
284   return false;
285 }
286 
287 /// ParseDirectiveSegmentEnd
288 ///  ::= identifier "ends"
289 bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {
290   StringRef SegmentName;
291   if (!getLexer().is(AsmToken::Identifier))
292     return TokError("expected identifier in directive");
293   SegmentName = getTok().getIdentifier();
294 
295   // Ignore; no action necessary.
296   Lex();
297   return false;
298 }
299 
300 /// ParseDirectiveIncludelib
301 ///  ::= "includelib" identifier
302 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
303   StringRef Lib;
304   if (getParser().parseIdentifier(Lib))
305     return TokError("expected identifier in includelib directive");
306 
307   unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;
308   SectionKind Kind = computeSectionKind(Flags);
309   getStreamer().PushSection();
310   getStreamer().SwitchSection(getContext().getCOFFSection(
311       ".drectve", Flags, Kind, "", (COFF::COMDATType)(0)));
312   getStreamer().emitBytes("/DEFAULTLIB:");
313   getStreamer().emitBytes(Lib);
314   getStreamer().emitBytes(" ");
315   getStreamer().PopSection();
316   return false;
317 }
318 
319 /// ParseDirectiveProc
320 /// TODO(epastor): Implement parameters and other attributes.
321 ///  ::= label "proc" [[distance]]
322 ///          statements
323 ///      label "endproc"
324 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
325   StringRef Label;
326   if (getParser().parseIdentifier(Label))
327     return Error(Loc, "expected identifier for procedure");
328   if (getLexer().is(AsmToken::Identifier)) {
329     StringRef nextVal = getTok().getString();
330     SMLoc nextLoc = getTok().getLoc();
331     if (nextVal.equals_lower("far")) {
332       // TODO(epastor): Handle far procedure definitions.
333       Lex();
334       return Error(nextLoc, "far procedure definitions not yet supported");
335     } else if (nextVal.equals_lower("near")) {
336       Lex();
337       nextVal = getTok().getString();
338       nextLoc = getTok().getLoc();
339     }
340   }
341   MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));
342 
343   // Define symbol as simple external function
344   Sym->setExternal(true);
345   Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
346 
347   bool Framed = false;
348   if (getLexer().is(AsmToken::Identifier) &&
349       getTok().getString().equals_lower("frame")) {
350     Lex();
351     Framed = true;
352     getStreamer().EmitWinCFIStartProc(Sym, Loc);
353   }
354   getStreamer().emitLabel(Sym, Loc);
355 
356   CurrentProcedure = Label;
357   CurrentProcedureFramed = Framed;
358   return false;
359 }
360 bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
361   StringRef Label;
362   SMLoc LabelLoc = getTok().getLoc();
363   if (getParser().parseIdentifier(Label))
364     return Error(LabelLoc, "expected identifier for procedure end");
365 
366   if (CurrentProcedure.empty())
367     return Error(Loc, "endp outside of procedure block");
368   else if (CurrentProcedure != Label)
369     return Error(LabelLoc, "endp does not match current procedure '" +
370                                CurrentProcedure + "'");
371 
372   if (CurrentProcedureFramed) {
373     getStreamer().EmitWinCFIEndProc(Loc);
374   }
375   CurrentProcedure = "";
376   CurrentProcedureFramed = false;
377   return false;
378 }
379 
380 bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) {
381   std::string AliasName, ActualName;
382   if (getTok().isNot(AsmToken::Less) ||
383       getParser().parseAngleBracketString(AliasName))
384     return Error(getTok().getLoc(), "expected <aliasName>");
385   if (getParser().parseToken(AsmToken::Equal))
386     return addErrorSuffix(" in " + Directive + " directive");
387   if (getTok().isNot(AsmToken::Less) ||
388       getParser().parseAngleBracketString(ActualName))
389     return Error(getTok().getLoc(), "expected <actualName>");
390 
391   MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName);
392   MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName);
393 
394   getStreamer().emitWeakReference(Alias, Actual);
395 
396   return false;
397 }
398 
399 bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive,
400                                                  SMLoc Loc) {
401   int64_t Size;
402   SMLoc SizeLoc = getTok().getLoc();
403   if (getParser().parseAbsoluteExpression(Size))
404     return Error(SizeLoc, "expected integer size");
405   if (Size % 8 != 0)
406     return Error(SizeLoc, "stack size must be a multiple of 8");
407   getStreamer().EmitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);
408   return false;
409 }
410 
411 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
412                                                 SMLoc Loc) {
413   getStreamer().EmitWinCFIEndProlog(Loc);
414   return false;
415 }
416 
417 namespace llvm {
418 
419 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
420 
421 } // end namespace llvm
422