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