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