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