• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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