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