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
31using namespace llvm;
32
33namespace {
34
35class 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
238public:
239  COFFMasmParser() = default;
240};
241
242} // end anonymous namespace.
243
244static 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
253bool COFFMasmParser::ParseSectionSwitch(StringRef Section,
254                                        unsigned Characteristics,
255                                        SectionKind Kind) {
256  return ParseSectionSwitch(Section, Characteristics, Kind, "",
257                            (COFF::COMDATType)0);
258}
259
260bool 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
275bool 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"
304bool 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
317bool 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"
339bool 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}
368bool 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
382namespace llvm {
383
384MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
385
386} // end namespace llvm
387