1//===-- llvm-ml.cpp - masm-compatible assembler -----------------*- C++ -*-===//
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// A simple driver around MasmParser; based on llvm-mc.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/ADT/StringSwitch.h"
14#include "llvm/MC/MCAsmBackend.h"
15#include "llvm/MC/MCAsmInfo.h"
16#include "llvm/MC/MCCodeEmitter.h"
17#include "llvm/MC/MCContext.h"
18#include "llvm/MC/MCInstPrinter.h"
19#include "llvm/MC/MCInstrInfo.h"
20#include "llvm/MC/MCObjectFileInfo.h"
21#include "llvm/MC/MCObjectWriter.h"
22#include "llvm/MC/MCParser/AsmLexer.h"
23#include "llvm/MC/MCParser/MCTargetAsmParser.h"
24#include "llvm/MC/MCRegisterInfo.h"
25#include "llvm/MC/MCStreamer.h"
26#include "llvm/MC/MCSubtargetInfo.h"
27#include "llvm/MC/MCSymbol.h"
28#include "llvm/MC/MCTargetOptionsCommandFlags.h"
29#include "llvm/MC/TargetRegistry.h"
30#include "llvm/Option/Arg.h"
31#include "llvm/Option/ArgList.h"
32#include "llvm/Option/Option.h"
33#include "llvm/Support/Compression.h"
34#include "llvm/Support/FileUtilities.h"
35#include "llvm/Support/FormatVariadic.h"
36#include "llvm/Support/FormattedStream.h"
37#include "llvm/Support/LLVMDriver.h"
38#include "llvm/Support/MemoryBuffer.h"
39#include "llvm/Support/Path.h"
40#include "llvm/Support/Process.h"
41#include "llvm/Support/SourceMgr.h"
42#include "llvm/Support/TargetSelect.h"
43#include "llvm/Support/ToolOutputFile.h"
44#include "llvm/Support/WithColor.h"
45#include "llvm/TargetParser/Host.h"
46#include <ctime>
47#include <optional>
48
49using namespace llvm;
50using namespace llvm::opt;
51
52namespace {
53
54enum ID {
55 OPT_INVALID = 0, // This is not an option ID.
56#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
57#include "Opts.inc"
58#undef OPTION
59};
60
61#define OPTTABLE_STR_TABLE_CODE
62#include "Opts.inc"
63#undef OPTTABLE_STR_TABLE_CODE
64
65#define OPTTABLE_PREFIXES_TABLE_CODE
66#include "Opts.inc"
67#undef OPTTABLE_PREFIXES_TABLE_CODE
68
69static constexpr opt::OptTable::Info InfoTable[] = {
70#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
71#include "Opts.inc"
72#undef OPTION
73};
74
75class MLOptTable : public opt::GenericOptTable {
76public:
77 MLOptTable()
78 : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
79 /*IgnoreCase=*/false) {}
80};
81} // namespace
82
83static Triple GetTriple(StringRef ProgName, opt::InputArgList &Args) {
84 // Figure out the target triple.
85 StringRef DefaultBitness = "32";
86 SmallString<255> Program = ProgName;
87 sys::path::replace_extension(path&: Program, extension: "");
88 if (Program.ends_with(Suffix: "ml64"))
89 DefaultBitness = "64";
90
91 StringRef TripleName =
92 StringSwitch<StringRef>(Args.getLastArgValue(Id: OPT_bitness, Default: DefaultBitness))
93 .Case(S: "32", Value: "i386-pc-windows")
94 .Case(S: "64", Value: "x86_64-pc-windows")
95 .Default(Value: "");
96 return Triple(Triple::normalize(Str: TripleName));
97}
98
99static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path) {
100 std::error_code EC;
101 auto Out = std::make_unique<ToolOutputFile>(args&: Path, args&: EC, args: sys::fs::OF_None);
102 if (EC) {
103 WithColor::error() << EC.message() << '\n';
104 return nullptr;
105 }
106
107 return Out;
108}
109
110static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI, raw_ostream &OS) {
111 AsmLexer Lexer(MAI);
112 Lexer.setBuffer(Buf: SrcMgr.getMemoryBuffer(i: SrcMgr.getMainFileID())->getBuffer());
113 Lexer.setLexMasmIntegers(true);
114 Lexer.useMasmDefaultRadix(V: true);
115 Lexer.setLexMasmHexFloats(true);
116 Lexer.setLexMasmStrings(true);
117
118 bool Error = false;
119 while (Lexer.Lex().isNot(K: AsmToken::Eof)) {
120 Lexer.getTok().dump(OS);
121 OS << "\n";
122 if (Lexer.getTok().getKind() == AsmToken::Error)
123 Error = true;
124 }
125
126 return Error;
127}
128
129static int AssembleInput(StringRef ProgName, const Target *TheTarget,
130 SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str,
131 MCAsmInfo &MAI, MCSubtargetInfo &STI,
132 MCInstrInfo &MCII, MCTargetOptions &MCOptions,
133 const opt::ArgList &InputArgs) {
134 struct tm TM;
135 time_t Timestamp;
136 if (InputArgs.hasArg(Ids: OPT_timestamp)) {
137 StringRef TimestampStr = InputArgs.getLastArgValue(Id: OPT_timestamp);
138 int64_t IntTimestamp;
139 if (TimestampStr.getAsInteger(Radix: 10, Result&: IntTimestamp)) {
140 WithColor::error(OS&: errs(), Prefix: ProgName)
141 << "invalid timestamp '" << TimestampStr
142 << "'; must be expressed in seconds since the UNIX epoch.\n";
143 return 1;
144 }
145 Timestamp = IntTimestamp;
146 } else {
147 Timestamp = time(timer: nullptr);
148 }
149 if (InputArgs.hasArg(Ids: OPT_utc)) {
150 // Not thread-safe.
151 TM = *gmtime(timer: &Timestamp);
152 } else {
153 // Not thread-safe.
154 TM = *localtime(timer: &Timestamp);
155 }
156
157 std::unique_ptr<MCAsmParser> Parser(
158 createMCMasmParser(SrcMgr, Ctx, Str, MAI, TM, CB: 0));
159 std::unique_ptr<MCTargetAsmParser> TAP(
160 TheTarget->createMCAsmParser(STI, Parser&: *Parser, MII: MCII, Options: MCOptions));
161
162 if (!TAP) {
163 WithColor::error(OS&: errs(), Prefix: ProgName)
164 << "this target does not support assembly parsing.\n";
165 return 1;
166 }
167
168 Parser->setShowParsedOperands(InputArgs.hasArg(Ids: OPT_show_inst_operands));
169 Parser->setTargetParser(*TAP);
170 Parser->getLexer().setLexMasmIntegers(true);
171 Parser->getLexer().useMasmDefaultRadix(V: true);
172 Parser->getLexer().setLexMasmHexFloats(true);
173 Parser->getLexer().setLexMasmStrings(true);
174
175 auto Defines = InputArgs.getAllArgValues(Id: OPT_define);
176 for (StringRef Define : Defines) {
177 const auto NameValue = Define.split(Separator: '=');
178 StringRef Name = NameValue.first, Value = NameValue.second;
179 if (Parser->defineMacro(Name, Value)) {
180 WithColor::error(OS&: errs(), Prefix: ProgName)
181 << "can't define macro '" << Name << "' = '" << Value << "'\n";
182 return 1;
183 }
184 }
185
186 int Res = Parser->Run(/*NoInitialTextSection=*/true);
187
188 return Res;
189}
190
191int llvm_ml_main(int Argc, char **Argv, const llvm::ToolContext &) {
192 StringRef ProgName = sys::path::filename(path: Argv[0]);
193
194 // Initialize targets and assembly printers/parsers.
195 llvm::InitializeAllTargetInfos();
196 llvm::InitializeAllTargetMCs();
197 llvm::InitializeAllAsmParsers();
198 llvm::InitializeAllDisassemblers();
199
200 MLOptTable T;
201 unsigned MissingArgIndex, MissingArgCount;
202 ArrayRef<const char *> ArgsArr = ArrayRef(Argv + 1, Argc - 1);
203 opt::InputArgList InputArgs =
204 T.ParseArgs(Args: ArgsArr, MissingArgIndex, MissingArgCount);
205
206 std::string InputFilename;
207 for (auto *Arg : InputArgs.filtered(Ids: OPT_INPUT)) {
208 std::string ArgString = Arg->getAsString(Args: InputArgs);
209 bool IsFile = false;
210 std::error_code IsFileEC =
211 llvm::sys::fs::is_regular_file(path: ArgString, result&: IsFile);
212 if (ArgString == "-" || IsFile) {
213 if (!InputFilename.empty()) {
214 WithColor::warning(OS&: errs(), Prefix: ProgName)
215 << "does not support multiple assembly files in one command; "
216 << "ignoring '" << InputFilename << "'\n";
217 }
218 InputFilename = ArgString;
219 } else {
220 std::string Diag;
221 raw_string_ostream OS(Diag);
222 OS << ArgString << ": " << IsFileEC.message();
223
224 std::string Nearest;
225 if (T.findNearest(Option: ArgString, NearestString&: Nearest) < 2)
226 OS << ", did you mean '" << Nearest << "'?";
227
228 WithColor::error(OS&: errs(), Prefix: ProgName) << OS.str() << '\n';
229 exit(status: 1);
230 }
231 }
232 for (auto *Arg : InputArgs.filtered(Ids: OPT_assembly_file)) {
233 if (!InputFilename.empty()) {
234 WithColor::warning(OS&: errs(), Prefix: ProgName)
235 << "does not support multiple assembly files in one command; "
236 << "ignoring '" << InputFilename << "'\n";
237 }
238 InputFilename = Arg->getValue();
239 }
240
241 for (auto *Arg : InputArgs.filtered(Ids: OPT_unsupported_Group)) {
242 WithColor::warning(OS&: errs(), Prefix: ProgName)
243 << "ignoring unsupported '" << Arg->getOption().getName()
244 << "' option\n";
245 }
246
247 if (InputArgs.hasArg(Ids: OPT_debug)) {
248 DebugFlag = true;
249 }
250 for (auto *Arg : InputArgs.filtered(Ids: OPT_debug_only)) {
251 setCurrentDebugTypes(Arg->getValues().data(), Arg->getNumValues());
252 }
253
254 if (InputArgs.hasArg(Ids: OPT_help)) {
255 std::string Usage = llvm::formatv(Fmt: "{0} [ /options ] file", Vals&: ProgName).str();
256 T.printHelp(OS&: outs(), Usage: Usage.c_str(), Title: "LLVM MASM Assembler",
257 /*ShowHidden=*/false);
258 return 0;
259 } else if (InputFilename.empty()) {
260 outs() << "USAGE: " << ProgName << " [ /options ] file\n"
261 << "Run \"" << ProgName << " /?\" or \"" << ProgName
262 << " /help\" for more info.\n";
263 return 0;
264 }
265
266 MCTargetOptions MCOptions;
267 MCOptions.AssemblyLanguage = "masm";
268 MCOptions.MCFatalWarnings = InputArgs.hasArg(Ids: OPT_fatal_warnings);
269 MCOptions.MCSaveTempLabels = InputArgs.hasArg(Ids: OPT_save_temp_labels);
270 MCOptions.ShowMCInst = InputArgs.hasArg(Ids: OPT_show_inst);
271 MCOptions.AsmVerbose = true;
272
273 Triple TheTriple = GetTriple(ProgName, Args&: InputArgs);
274 std::string Error;
275 const Target *TheTarget = TargetRegistry::lookupTarget(ArchName: "", TheTriple, Error);
276 if (!TheTarget) {
277 WithColor::error(OS&: errs(), Prefix: ProgName) << Error;
278 return 1;
279 }
280 const std::string &TripleName = TheTriple.getTriple();
281
282 bool SafeSEH = InputArgs.hasArg(Ids: OPT_safeseh);
283 if (SafeSEH && !(TheTriple.isArch32Bit() && TheTriple.isX86())) {
284 WithColor::warning()
285 << "/safeseh applies only to 32-bit X86 platforms; ignoring.\n";
286 SafeSEH = false;
287 }
288
289 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
290 MemoryBuffer::getFileOrSTDIN(Filename: InputFilename);
291 if (std::error_code EC = BufferPtr.getError()) {
292 WithColor::error(OS&: errs(), Prefix: ProgName)
293 << InputFilename << ": " << EC.message() << '\n';
294 return 1;
295 }
296
297 SourceMgr SrcMgr;
298
299 // Tell SrcMgr about this buffer, which is what the parser will pick up.
300 SrcMgr.AddNewSourceBuffer(F: std::move(*BufferPtr), IncludeLoc: SMLoc());
301
302 // Record the location of the include directories so that the lexer can find
303 // included files later.
304 std::vector<std::string> IncludeDirs =
305 InputArgs.getAllArgValues(Id: OPT_include_path);
306 if (!InputArgs.hasArg(Ids: OPT_ignore_include_envvar)) {
307 if (std::optional<std::string> IncludeEnvVar =
308 llvm::sys::Process::GetEnv(name: "INCLUDE")) {
309 SmallVector<StringRef, 8> Dirs;
310 StringRef(*IncludeEnvVar)
311 .split(A&: Dirs, Separator: ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
312 IncludeDirs.reserve(n: IncludeDirs.size() + Dirs.size());
313 for (StringRef Dir : Dirs)
314 IncludeDirs.push_back(x: Dir.str());
315 }
316 }
317 SrcMgr.setIncludeDirs(IncludeDirs);
318
319 std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT: TripleName));
320 assert(MRI && "Unable to create target register info!");
321
322 std::unique_ptr<MCAsmInfo> MAI(
323 TheTarget->createMCAsmInfo(MRI: *MRI, TheTriple: TripleName, Options: MCOptions));
324 assert(MAI && "Unable to create target asm info!");
325
326 MAI->setPreserveAsmComments(InputArgs.hasArg(Ids: OPT_preserve_comments));
327
328 std::unique_ptr<MCSubtargetInfo> STI(TheTarget->createMCSubtargetInfo(
329 TheTriple: TripleName, /*CPU=*/"", /*Features=*/""));
330 assert(STI && "Unable to create subtarget info!");
331
332 // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
333 // MCObjectFileInfo needs a MCContext reference in order to initialize itself.
334 MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr);
335 std::unique_ptr<MCObjectFileInfo> MOFI(TheTarget->createMCObjectFileInfo(
336 Ctx, /*PIC=*/false, /*LargeCodeModel=*/true));
337 Ctx.setObjectFileInfo(MOFI.get());
338
339 // Set compilation information.
340 SmallString<128> CWD;
341 if (!sys::fs::current_path(result&: CWD))
342 Ctx.setCompilationDir(CWD);
343 Ctx.setMainFileName(InputFilename);
344
345 StringRef FileType = InputArgs.getLastArgValue(Id: OPT_filetype, Default: "obj");
346 SmallString<255> DefaultOutputFilename;
347 if (InputArgs.hasArg(Ids: OPT_as_lex)) {
348 DefaultOutputFilename = "-";
349 } else {
350 DefaultOutputFilename = InputFilename;
351 sys::path::replace_extension(path&: DefaultOutputFilename, extension: FileType);
352 }
353 const StringRef OutputFilename =
354 InputArgs.getLastArgValue(Id: OPT_output_file, Default: DefaultOutputFilename);
355 std::unique_ptr<ToolOutputFile> Out = GetOutputStream(Path: OutputFilename);
356 if (!Out)
357 return 1;
358
359 std::unique_ptr<buffer_ostream> BOS;
360 raw_pwrite_stream *OS = &Out->os();
361 std::unique_ptr<MCStreamer> Str;
362
363 std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
364 assert(MCII && "Unable to create instruction info!");
365
366 if (FileType == "s") {
367 const bool OutputATTAsm = InputArgs.hasArg(Ids: OPT_output_att_asm);
368 const unsigned OutputAsmVariant = OutputATTAsm ? 0U // ATT dialect
369 : 1U; // Intel dialect
370 std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
371 T: TheTriple, SyntaxVariant: OutputAsmVariant, MAI: *MAI, MII: *MCII, MRI: *MRI));
372
373 if (!IP) {
374 WithColor::error()
375 << "unable to create instruction printer for target triple '"
376 << TheTriple.normalize() << "' with "
377 << (OutputATTAsm ? "ATT" : "Intel") << " assembly variant.\n";
378 return 1;
379 }
380
381 // Set the display preference for hex vs. decimal immediates.
382 IP->setPrintImmHex(InputArgs.hasArg(Ids: OPT_print_imm_hex));
383
384 // Set up the AsmStreamer.
385 std::unique_ptr<MCCodeEmitter> CE;
386 if (InputArgs.hasArg(Ids: OPT_show_encoding))
387 CE.reset(p: TheTarget->createMCCodeEmitter(II: *MCII, Ctx));
388
389 std::unique_ptr<MCAsmBackend> MAB(
390 TheTarget->createMCAsmBackend(STI: *STI, MRI: *MRI, Options: MCOptions));
391 auto FOut = std::make_unique<formatted_raw_ostream>(args&: *OS);
392 Str.reset(p: TheTarget->createAsmStreamer(Ctx, OS: std::move(FOut), IP: std::move(IP),
393 CE: std::move(CE), TAB: std::move(MAB)));
394
395 } else if (FileType == "null") {
396 Str.reset(p: TheTarget->createNullStreamer(Ctx));
397 } else if (FileType == "obj") {
398 if (!Out->os().supportsSeeking()) {
399 BOS = std::make_unique<buffer_ostream>(args&: Out->os());
400 OS = BOS.get();
401 }
402
403 MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(II: *MCII, Ctx);
404 MCAsmBackend *MAB = TheTarget->createMCAsmBackend(STI: *STI, MRI: *MRI, Options: MCOptions);
405 Str.reset(p: TheTarget->createMCObjectStreamer(
406 T: TheTriple, Ctx, TAB: std::unique_ptr<MCAsmBackend>(MAB),
407 OW: MAB->createObjectWriter(OS&: *OS), Emitter: std::unique_ptr<MCCodeEmitter>(CE),
408 STI: *STI));
409 } else {
410 llvm_unreachable("Invalid file type!");
411 }
412
413 if (TheTriple.isOSBinFormatCOFF()) {
414 // Emit an absolute @feat.00 symbol. This is a features bitfield read by
415 // link.exe.
416 int64_t Feat00Flags = 0x2;
417 if (SafeSEH) {
418 // According to the PE-COFF spec, the LSB of this value marks the object
419 // for "registered SEH". This means that all SEH handler entry points
420 // must be registered in .sxdata. Use of any unregistered handlers will
421 // cause the process to terminate immediately.
422 Feat00Flags |= 0x1;
423 }
424 MCSymbol *Feat00Sym = Ctx.getOrCreateSymbol(Name: "@feat.00");
425 Feat00Sym->setRedefinable(true);
426 Str->emitSymbolAttribute(Symbol: Feat00Sym, Attribute: MCSA_Global);
427 Str->emitAssignment(Symbol: Feat00Sym, Value: MCConstantExpr::create(Value: Feat00Flags, Ctx));
428 }
429
430 int Res = 1;
431 if (InputArgs.hasArg(Ids: OPT_as_lex)) {
432 // -as-lex; Lex only, and output a stream of tokens
433 Res = AsLexInput(SrcMgr, MAI&: *MAI, OS&: Out->os());
434 } else {
435 Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, Str&: *Str, MAI&: *MAI, STI&: *STI,
436 MCII&: *MCII, MCOptions, InputArgs);
437 }
438
439 // Keep output if no errors.
440 if (Res == 0)
441 Out->keep();
442 return Res;
443}
444