1 //===-- llvm-ml.cpp - masm-compatible assembler -----------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // A simple driver around MasmParser; based on llvm-mc.
11 //===----------------------------------------------------------------------===//
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/MCTargetOptionsCommandFlags.h"
28 #include "llvm/MC/TargetRegistry.h"
29 #include "llvm/Option/Arg.h"
30 #include "llvm/Option/ArgList.h"
31 #include "llvm/Option/Option.h"
32 #include "llvm/Support/Compression.h"
33 #include "llvm/Support/FileUtilities.h"
34 #include "llvm/Support/FormatVariadic.h"
35 #include "llvm/Support/FormattedStream.h"
36 #include "llvm/Support/Host.h"
37 #include "llvm/Support/InitLLVM.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"
48 using namespace llvm::opt
;
53 OPT_INVALID
= 0, // This is not an option ID.
54 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
55 HELPTEXT, METAVAR, VALUES) \
61 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
65 const opt::OptTable::Info InfoTable
[] = {
66 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
67 HELPTEXT, METAVAR, VALUES) \
69 PREFIX, NAME, HELPTEXT, \
70 METAVAR, OPT_##ID, opt::Option::KIND##Class, \
71 PARAM, FLAGS, OPT_##GROUP, \
72 OPT_##ALIAS, ALIASARGS, VALUES},
77 class MLOptTable
: public opt::OptTable
{
79 MLOptTable() : OptTable(InfoTable
, /*IgnoreCase=*/false) {}
83 static 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(Program
, "");
88 if (Program
.endswith("ml64"))
89 DefaultBitness
= "64";
91 StringRef TripleName
=
92 StringSwitch
<StringRef
>(Args
.getLastArgValue(OPT_bitness
, DefaultBitness
))
93 .Case("32", "i386-pc-windows")
94 .Case("64", "x86_64-pc-windows")
96 return Triple(Triple::normalize(TripleName
));
99 static std::unique_ptr
<ToolOutputFile
> GetOutputStream(StringRef Path
) {
101 auto Out
= std::make_unique
<ToolOutputFile
>(Path
, EC
, sys::fs::OF_None
);
103 WithColor::error() << EC
.message() << '\n';
110 static int AsLexInput(SourceMgr
&SrcMgr
, MCAsmInfo
&MAI
, raw_ostream
&OS
) {
112 Lexer
.setBuffer(SrcMgr
.getMemoryBuffer(SrcMgr
.getMainFileID())->getBuffer());
113 Lexer
.setLexMasmIntegers(true);
114 Lexer
.useMasmDefaultRadix(true);
115 Lexer
.setLexMasmHexFloats(true);
116 Lexer
.setLexMasmStrings(true);
119 while (Lexer
.Lex().isNot(AsmToken::Eof
)) {
120 Lexer
.getTok().dump(OS
);
122 if (Lexer
.getTok().getKind() == AsmToken::Error
)
129 static 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
) {
136 if (InputArgs
.hasArg(OPT_timestamp
)) {
137 StringRef TimestampStr
= InputArgs
.getLastArgValue(OPT_timestamp
);
138 int64_t IntTimestamp
;
139 if (TimestampStr
.getAsInteger(10, IntTimestamp
)) {
140 WithColor::error(errs(), ProgName
)
141 << "invalid timestamp '" << TimestampStr
142 << "'; must be expressed in seconds since the UNIX epoch.\n";
145 Timestamp
= IntTimestamp
;
147 Timestamp
= time(nullptr);
149 if (InputArgs
.hasArg(OPT_utc
)) {
151 TM
= *gmtime(&Timestamp
);
154 TM
= *localtime(&Timestamp
);
157 std::unique_ptr
<MCAsmParser
> Parser(
158 createMCMasmParser(SrcMgr
, Ctx
, Str
, MAI
, TM
, 0));
159 std::unique_ptr
<MCTargetAsmParser
> TAP(
160 TheTarget
->createMCAsmParser(STI
, *Parser
, MCII
, MCOptions
));
163 WithColor::error(errs(), ProgName
)
164 << "this target does not support assembly parsing.\n";
168 Parser
->setShowParsedOperands(InputArgs
.hasArg(OPT_show_inst_operands
));
169 Parser
->setTargetParser(*TAP
);
170 Parser
->getLexer().setLexMasmIntegers(true);
171 Parser
->getLexer().useMasmDefaultRadix(true);
172 Parser
->getLexer().setLexMasmHexFloats(true);
173 Parser
->getLexer().setLexMasmStrings(true);
175 auto Defines
= InputArgs
.getAllArgValues(OPT_define
);
176 for (StringRef Define
: Defines
) {
177 const auto NameValue
= Define
.split('=');
178 StringRef Name
= NameValue
.first
, Value
= NameValue
.second
;
179 if (Parser
->defineMacro(Name
, Value
)) {
180 WithColor::error(errs(), ProgName
)
181 << "can't define macro '" << Name
<< "' = '" << Value
<< "'\n";
186 int Res
= Parser
->Run(/*NoInitialTextSection=*/true);
191 int main(int Argc
, char **Argv
) {
192 InitLLVM
X(Argc
, Argv
);
193 StringRef ProgName
= sys::path::filename(Argv
[0]);
195 // Initialize targets and assembly printers/parsers.
196 llvm::InitializeAllTargetInfos();
197 llvm::InitializeAllTargetMCs();
198 llvm::InitializeAllAsmParsers();
199 llvm::InitializeAllDisassemblers();
202 unsigned MissingArgIndex
, MissingArgCount
;
203 ArrayRef
<const char *> ArgsArr
= makeArrayRef(Argv
+ 1, Argc
- 1);
204 opt::InputArgList InputArgs
=
205 T
.ParseArgs(ArgsArr
, MissingArgIndex
, MissingArgCount
);
207 std::string InputFilename
;
208 for (auto *Arg
: InputArgs
.filtered(OPT_INPUT
)) {
209 std::string ArgString
= Arg
->getAsString(InputArgs
);
210 if (ArgString
== "-" || StringRef(ArgString
).endswith(".asm")) {
211 if (!InputFilename
.empty()) {
212 WithColor::warning(errs(), ProgName
)
213 << "does not support multiple assembly files in one command; "
214 << "ignoring '" << InputFilename
<< "'\n";
216 InputFilename
= ArgString
;
219 raw_string_ostream
OS(Diag
);
220 OS
<< "invalid option '" << ArgString
<< "'";
223 if (T
.findNearest(ArgString
, Nearest
) < 2)
224 OS
<< ", did you mean '" << Nearest
<< "'?";
226 WithColor::error(errs(), ProgName
) << OS
.str() << '\n';
230 for (auto *Arg
: InputArgs
.filtered(OPT_assembly_file
)) {
231 if (!InputFilename
.empty()) {
232 WithColor::warning(errs(), ProgName
)
233 << "does not support multiple assembly files in one command; "
234 << "ignoring '" << InputFilename
<< "'\n";
236 InputFilename
= Arg
->getAsString(InputArgs
);
239 for (auto *Arg
: InputArgs
.filtered(OPT_unsupported_Group
)) {
240 WithColor::warning(errs(), ProgName
)
241 << "ignoring unsupported '" << Arg
->getOption().getName()
245 if (InputArgs
.hasArg(OPT_help
)) {
246 std::string Usage
= llvm::formatv("{0} [ /options ] file", ProgName
).str();
247 T
.printHelp(outs(), Usage
.c_str(), "LLVM MASM Assembler",
248 /*ShowHidden=*/false);
250 } else if (InputFilename
.empty()) {
251 outs() << "USAGE: " << ProgName
<< " [ /options ] file\n"
252 << "Run \"" << ProgName
<< " /?\" or \"" << ProgName
253 << " /help\" for more info.\n";
257 MCTargetOptions MCOptions
;
258 MCOptions
.AssemblyLanguage
= "masm";
259 MCOptions
.MCFatalWarnings
= InputArgs
.hasArg(OPT_fatal_warnings
);
261 Triple TheTriple
= GetTriple(ProgName
, InputArgs
);
263 const Target
*TheTarget
= TargetRegistry::lookupTarget("", TheTriple
, Error
);
265 WithColor::error(errs(), ProgName
) << Error
;
268 const std::string
&TripleName
= TheTriple
.getTriple();
270 bool SafeSEH
= InputArgs
.hasArg(OPT_safeseh
);
271 if (SafeSEH
&& !(TheTriple
.isArch32Bit() && TheTriple
.isX86())) {
273 << "/safeseh applies only to 32-bit X86 platforms; ignoring.\n";
277 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufferPtr
=
278 MemoryBuffer::getFileOrSTDIN(InputFilename
);
279 if (std::error_code EC
= BufferPtr
.getError()) {
280 WithColor::error(errs(), ProgName
)
281 << InputFilename
<< ": " << EC
.message() << '\n';
287 // Tell SrcMgr about this buffer, which is what the parser will pick up.
288 SrcMgr
.AddNewSourceBuffer(std::move(*BufferPtr
), SMLoc());
290 // Record the location of the include directories so that the lexer can find
291 // included files later.
292 std::vector
<std::string
> IncludeDirs
=
293 InputArgs
.getAllArgValues(OPT_include_path
);
294 if (!InputArgs
.hasArg(OPT_ignore_include_envvar
)) {
295 if (llvm::Optional
<std::string
> IncludeEnvVar
=
296 llvm::sys::Process::GetEnv("INCLUDE")) {
297 SmallVector
<StringRef
, 8> Dirs
;
298 StringRef(*IncludeEnvVar
)
299 .split(Dirs
, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
300 IncludeDirs
.reserve(IncludeDirs
.size() + Dirs
.size());
301 for (StringRef Dir
: Dirs
)
302 IncludeDirs
.push_back(Dir
.str());
305 SrcMgr
.setIncludeDirs(IncludeDirs
);
307 std::unique_ptr
<MCRegisterInfo
> MRI(TheTarget
->createMCRegInfo(TripleName
));
308 assert(MRI
&& "Unable to create target register info!");
310 std::unique_ptr
<MCAsmInfo
> MAI(
311 TheTarget
->createMCAsmInfo(*MRI
, TripleName
, MCOptions
));
312 assert(MAI
&& "Unable to create target asm info!");
314 MAI
->setPreserveAsmComments(InputArgs
.hasArg(OPT_preserve_comments
));
316 std::unique_ptr
<MCSubtargetInfo
> STI(TheTarget
->createMCSubtargetInfo(
317 TripleName
, /*CPU=*/"", /*Features=*/""));
318 assert(STI
&& "Unable to create subtarget info!");
320 // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
321 // MCObjectFileInfo needs a MCContext reference in order to initialize itself.
322 MCContext
Ctx(TheTriple
, MAI
.get(), MRI
.get(), STI
.get(), &SrcMgr
);
323 std::unique_ptr
<MCObjectFileInfo
> MOFI(TheTarget
->createMCObjectFileInfo(
324 Ctx
, /*PIC=*/false, /*LargeCodeModel=*/true));
325 Ctx
.setObjectFileInfo(MOFI
.get());
327 if (InputArgs
.hasArg(OPT_save_temp_labels
))
328 Ctx
.setAllowTemporaryLabels(false);
330 // Set compilation information.
331 SmallString
<128> CWD
;
332 if (!sys::fs::current_path(CWD
))
333 Ctx
.setCompilationDir(CWD
);
334 Ctx
.setMainFileName(InputFilename
);
336 StringRef FileType
= InputArgs
.getLastArgValue(OPT_filetype
, "obj");
337 SmallString
<255> DefaultOutputFilename
;
338 if (InputArgs
.hasArg(OPT_as_lex
)) {
339 DefaultOutputFilename
= "-";
341 DefaultOutputFilename
= InputFilename
;
342 sys::path::replace_extension(DefaultOutputFilename
, FileType
);
344 const StringRef OutputFilename
=
345 InputArgs
.getLastArgValue(OPT_output_file
, DefaultOutputFilename
);
346 std::unique_ptr
<ToolOutputFile
> Out
= GetOutputStream(OutputFilename
);
350 std::unique_ptr
<buffer_ostream
> BOS
;
351 raw_pwrite_stream
*OS
= &Out
->os();
352 std::unique_ptr
<MCStreamer
> Str
;
354 std::unique_ptr
<MCInstrInfo
> MCII(TheTarget
->createMCInstrInfo());
355 assert(MCII
&& "Unable to create instruction info!");
357 MCInstPrinter
*IP
= nullptr;
358 if (FileType
== "s") {
359 const bool OutputATTAsm
= InputArgs
.hasArg(OPT_output_att_asm
);
360 const unsigned OutputAsmVariant
= OutputATTAsm
? 0U // ATT dialect
361 : 1U; // Intel dialect
362 IP
= TheTarget
->createMCInstPrinter(TheTriple
, OutputAsmVariant
, *MAI
,
367 << "unable to create instruction printer for target triple '"
368 << TheTriple
.normalize() << "' with "
369 << (OutputATTAsm
? "ATT" : "Intel") << " assembly variant.\n";
373 // Set the display preference for hex vs. decimal immediates.
374 IP
->setPrintImmHex(InputArgs
.hasArg(OPT_print_imm_hex
));
376 // Set up the AsmStreamer.
377 std::unique_ptr
<MCCodeEmitter
> CE
;
378 if (InputArgs
.hasArg(OPT_show_encoding
))
379 CE
.reset(TheTarget
->createMCCodeEmitter(*MCII
, *MRI
, Ctx
));
381 std::unique_ptr
<MCAsmBackend
> MAB(
382 TheTarget
->createMCAsmBackend(*STI
, *MRI
, MCOptions
));
383 auto FOut
= std::make_unique
<formatted_raw_ostream
>(*OS
);
384 Str
.reset(TheTarget
->createAsmStreamer(
385 Ctx
, std::move(FOut
), /*asmverbose*/ true,
386 /*useDwarfDirectory*/ true, IP
, std::move(CE
), std::move(MAB
),
387 InputArgs
.hasArg(OPT_show_inst
)));
389 } else if (FileType
== "null") {
390 Str
.reset(TheTarget
->createNullStreamer(Ctx
));
391 } else if (FileType
== "obj") {
392 if (!Out
->os().supportsSeeking()) {
393 BOS
= std::make_unique
<buffer_ostream
>(Out
->os());
397 MCCodeEmitter
*CE
= TheTarget
->createMCCodeEmitter(*MCII
, *MRI
, Ctx
);
398 MCAsmBackend
*MAB
= TheTarget
->createMCAsmBackend(*STI
, *MRI
, MCOptions
);
399 Str
.reset(TheTarget
->createMCObjectStreamer(
400 TheTriple
, Ctx
, std::unique_ptr
<MCAsmBackend
>(MAB
),
401 MAB
->createObjectWriter(*OS
), std::unique_ptr
<MCCodeEmitter
>(CE
), *STI
,
402 MCOptions
.MCRelaxAll
, MCOptions
.MCIncrementalLinkerCompatible
,
403 /*DWARFMustBeAtTheEnd*/ false));
405 llvm_unreachable("Invalid file type!");
408 if (TheTriple
.isOSBinFormatCOFF()) {
409 // Emit an absolute @feat.00 symbol. This is a features bitfield read by
411 int64_t Feat00Flags
= 0x2;
413 // According to the PE-COFF spec, the LSB of this value marks the object
414 // for "registered SEH". This means that all SEH handler entry points
415 // must be registered in .sxdata. Use of any unregistered handlers will
416 // cause the process to terminate immediately.
419 MCSymbol
*Feat00Sym
= Ctx
.getOrCreateSymbol("@feat.00");
420 Feat00Sym
->setRedefinable(true);
421 Str
->emitSymbolAttribute(Feat00Sym
, MCSA_Global
);
422 Str
->emitAssignment(Feat00Sym
, MCConstantExpr::create(Feat00Flags
, Ctx
));
425 // Use Assembler information for parsing.
426 Str
->setUseAssemblerInfoForParsing(true);
429 if (InputArgs
.hasArg(OPT_as_lex
)) {
430 // -as-lex; Lex only, and output a stream of tokens
431 Res
= AsLexInput(SrcMgr
, *MAI
, Out
->os());
433 Res
= AssembleInput(ProgName
, TheTarget
, SrcMgr
, Ctx
, *Str
, *MAI
, *STI
,
434 *MCII
, MCOptions
, InputArgs
);
437 // Keep output if no errors.