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)>
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 
61   bool IgnoreDirective(StringRef, SMLoc) {
62     while (!getLexer().is(AsmToken::EndOfStatement)) {
63       Lex();
64     }
65     return false;
66   }
67 
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 
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 
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 
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 
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 
232 bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
233                                         unsigned Characteristics,
234                                         SectionKind Kind) {
235   return ParseSectionSwitch(Section, Characteristics, Kind, "",
236                             (COFF::COMDATType)0);
237 }
238 
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 
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"
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
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"
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_insensitive("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_insensitive("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_insensitive("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 }
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 
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 
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 
405 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
406                                                 SMLoc Loc) {
407   getStreamer().EmitWinCFIEndProlog(Loc);
408   return false;
409 }
410 
411 namespace llvm {
412 
413 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
414 
415 } // end namespace llvm
416