//===-- llvm-ml.cpp - masm-compatible assembler -----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // A simple driver around MasmParser; based on llvm-mc. // //===----------------------------------------------------------------------===// #include "llvm/ADT/StringSwitch.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCParser/AsmLexer.h" #include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Compression.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/WithColor.h" #include using namespace llvm; using namespace llvm::opt; namespace { enum ID { OPT_INVALID = 0, // This is not an option ID. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ OPT_##ID, #include "Opts.inc" #undef OPTION }; #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; #include "Opts.inc" #undef PREFIX const opt::OptTable::Info InfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ { \ PREFIX, NAME, HELPTEXT, \ METAVAR, OPT_##ID, opt::Option::KIND##Class, \ PARAM, FLAGS, OPT_##GROUP, \ OPT_##ALIAS, ALIASARGS, VALUES}, #include "Opts.inc" #undef OPTION }; class MLOptTable : public opt::OptTable { public: MLOptTable() : OptTable(InfoTable, /*IgnoreCase=*/false) {} }; } // namespace static Triple GetTriple(StringRef ProgName, opt::InputArgList &Args) { // Figure out the target triple. StringRef DefaultBitness = "32"; SmallString<255> Program = ProgName; sys::path::replace_extension(Program, ""); if (Program.endswith("ml64")) DefaultBitness = "64"; StringRef TripleName = StringSwitch(Args.getLastArgValue(OPT_bitness, DefaultBitness)) .Case("32", "i386-pc-windows") .Case("64", "x86_64-pc-windows") .Default(""); return Triple(Triple::normalize(TripleName)); } static std::unique_ptr GetOutputStream(StringRef Path) { std::error_code EC; auto Out = std::make_unique(Path, EC, sys::fs::OF_None); if (EC) { WithColor::error() << EC.message() << '\n'; return nullptr; } return Out; } static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI, raw_ostream &OS) { AsmLexer Lexer(MAI); Lexer.setBuffer(SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID())->getBuffer()); Lexer.setLexMasmIntegers(true); Lexer.useMasmDefaultRadix(true); Lexer.setLexMasmHexFloats(true); Lexer.setLexMasmStrings(true); bool Error = false; while (Lexer.Lex().isNot(AsmToken::Eof)) { Lexer.getTok().dump(OS); OS << "\n"; if (Lexer.getTok().getKind() == AsmToken::Error) Error = true; } return Error; } static int AssembleInput(StringRef ProgName, const Target *TheTarget, SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str, MCAsmInfo &MAI, MCSubtargetInfo &STI, MCInstrInfo &MCII, MCTargetOptions &MCOptions, const opt::ArgList &InputArgs) { struct tm TM; time_t Timestamp; if (InputArgs.hasArg(OPT_timestamp)) { StringRef TimestampStr = InputArgs.getLastArgValue(OPT_timestamp); int64_t IntTimestamp; if (TimestampStr.getAsInteger(10, IntTimestamp)) { WithColor::error(errs(), ProgName) << "invalid timestamp '" << TimestampStr << "'; must be expressed in seconds since the UNIX epoch.\n"; return 1; } Timestamp = IntTimestamp; } else { Timestamp = time(nullptr); } if (InputArgs.hasArg(OPT_utc)) { // Not thread-safe. TM = *gmtime(&Timestamp); } else { // Not thread-safe. TM = *localtime(&Timestamp); } std::unique_ptr Parser( createMCMasmParser(SrcMgr, Ctx, Str, MAI, TM, 0)); std::unique_ptr TAP( TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions)); if (!TAP) { WithColor::error(errs(), ProgName) << "this target does not support assembly parsing.\n"; return 1; } Parser->setShowParsedOperands(InputArgs.hasArg(OPT_show_inst_operands)); Parser->setTargetParser(*TAP); Parser->getLexer().setLexMasmIntegers(true); Parser->getLexer().useMasmDefaultRadix(true); Parser->getLexer().setLexMasmHexFloats(true); Parser->getLexer().setLexMasmStrings(true); auto Defines = InputArgs.getAllArgValues(OPT_define); for (StringRef Define : Defines) { const auto NameValue = Define.split('='); StringRef Name = NameValue.first, Value = NameValue.second; if (Parser->defineMacro(Name, Value)) { WithColor::error(errs(), ProgName) << "can't define macro '" << Name << "' = '" << Value << "'\n"; return 1; } } int Res = Parser->Run(/*NoInitialTextSection=*/true); return Res; } int main(int Argc, char **Argv) { InitLLVM X(Argc, Argv); StringRef ProgName = sys::path::filename(Argv[0]); // Initialize targets and assembly printers/parsers. llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmParsers(); llvm::InitializeAllDisassemblers(); MLOptTable T; unsigned MissingArgIndex, MissingArgCount; ArrayRef ArgsArr = makeArrayRef(Argv + 1, Argc - 1); opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgIndex, MissingArgCount); std::string InputFilename; for (auto *Arg : InputArgs.filtered(OPT_INPUT)) { std::string ArgString = Arg->getAsString(InputArgs); if (ArgString == "-" || StringRef(ArgString).endswith(".asm")) { if (!InputFilename.empty()) { WithColor::warning(errs(), ProgName) << "does not support multiple assembly files in one command; " << "ignoring '" << InputFilename << "'\n"; } InputFilename = ArgString; } else { std::string Diag; raw_string_ostream OS(Diag); OS << "invalid option '" << ArgString << "'"; std::string Nearest; if (T.findNearest(ArgString, Nearest) < 2) OS << ", did you mean '" << Nearest << "'?"; WithColor::error(errs(), ProgName) << OS.str() << '\n'; exit(1); } } for (auto *Arg : InputArgs.filtered(OPT_assembly_file)) { if (!InputFilename.empty()) { WithColor::warning(errs(), ProgName) << "does not support multiple assembly files in one command; " << "ignoring '" << InputFilename << "'\n"; } InputFilename = Arg->getAsString(InputArgs); } for (auto *Arg : InputArgs.filtered(OPT_unsupported_Group)) { WithColor::warning(errs(), ProgName) << "ignoring unsupported '" << Arg->getOption().getName() << "' option\n"; } if (InputArgs.hasArg(OPT_help)) { std::string Usage = llvm::formatv("{0} [ /options ] file", ProgName).str(); T.printHelp(outs(), Usage.c_str(), "LLVM MASM Assembler", /*ShowHidden=*/false); return 0; } else if (InputFilename.empty()) { outs() << "USAGE: " << ProgName << " [ /options ] file\n" << "Run \"" << ProgName << " /?\" or \"" << ProgName << " /help\" for more info.\n"; return 0; } MCTargetOptions MCOptions; MCOptions.AssemblyLanguage = "masm"; MCOptions.MCFatalWarnings = InputArgs.hasArg(OPT_fatal_warnings); Triple TheTriple = GetTriple(ProgName, InputArgs); std::string Error; const Target *TheTarget = TargetRegistry::lookupTarget("", TheTriple, Error); if (!TheTarget) { WithColor::error(errs(), ProgName) << Error; return 1; } const std::string &TripleName = TheTriple.getTriple(); bool SafeSEH = InputArgs.hasArg(OPT_safeseh); if (SafeSEH && !(TheTriple.isArch32Bit() && TheTriple.isX86())) { WithColor::warning() << "/safeseh applies only to 32-bit X86 platforms; ignoring.\n"; SafeSEH = false; } ErrorOr> BufferPtr = MemoryBuffer::getFileOrSTDIN(InputFilename); if (std::error_code EC = BufferPtr.getError()) { WithColor::error(errs(), ProgName) << InputFilename << ": " << EC.message() << '\n'; return 1; } SourceMgr SrcMgr; // Tell SrcMgr about this buffer, which is what the parser will pick up. SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); // Record the location of the include directories so that the lexer can find // included files later. std::vector IncludeDirs = InputArgs.getAllArgValues(OPT_include_path); if (!InputArgs.hasArg(OPT_ignore_include_envvar)) { if (llvm::Optional IncludeEnvVar = llvm::sys::Process::GetEnv("INCLUDE")) { SmallVector Dirs; StringRef(*IncludeEnvVar) .split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false); IncludeDirs.reserve(IncludeDirs.size() + Dirs.size()); for (StringRef Dir : Dirs) IncludeDirs.push_back(Dir.str()); } } SrcMgr.setIncludeDirs(IncludeDirs); std::unique_ptr MRI(TheTarget->createMCRegInfo(TripleName)); assert(MRI && "Unable to create target register info!"); std::unique_ptr MAI( TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); assert(MAI && "Unable to create target asm info!"); MAI->setPreserveAsmComments(InputArgs.hasArg(OPT_preserve_comments)); std::unique_ptr STI(TheTarget->createMCSubtargetInfo( TripleName, /*CPU=*/"", /*Features=*/"")); assert(STI && "Unable to create subtarget info!"); // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and // MCObjectFileInfo needs a MCContext reference in order to initialize itself. MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); std::unique_ptr MOFI(TheTarget->createMCObjectFileInfo( Ctx, /*PIC=*/false, /*LargeCodeModel=*/true)); Ctx.setObjectFileInfo(MOFI.get()); if (InputArgs.hasArg(OPT_save_temp_labels)) Ctx.setAllowTemporaryLabels(false); // Set compilation information. SmallString<128> CWD; if (!sys::fs::current_path(CWD)) Ctx.setCompilationDir(CWD); Ctx.setMainFileName(InputFilename); StringRef FileType = InputArgs.getLastArgValue(OPT_filetype, "obj"); SmallString<255> DefaultOutputFilename; if (InputArgs.hasArg(OPT_as_lex)) { DefaultOutputFilename = "-"; } else { DefaultOutputFilename = InputFilename; sys::path::replace_extension(DefaultOutputFilename, FileType); } const StringRef OutputFilename = InputArgs.getLastArgValue(OPT_output_file, DefaultOutputFilename); std::unique_ptr Out = GetOutputStream(OutputFilename); if (!Out) return 1; std::unique_ptr BOS; raw_pwrite_stream *OS = &Out->os(); std::unique_ptr Str; std::unique_ptr MCII(TheTarget->createMCInstrInfo()); assert(MCII && "Unable to create instruction info!"); MCInstPrinter *IP = nullptr; if (FileType == "s") { const bool OutputATTAsm = InputArgs.hasArg(OPT_output_att_asm); const unsigned OutputAsmVariant = OutputATTAsm ? 0U // ATT dialect : 1U; // Intel dialect IP = TheTarget->createMCInstPrinter(TheTriple, OutputAsmVariant, *MAI, *MCII, *MRI); if (!IP) { WithColor::error() << "unable to create instruction printer for target triple '" << TheTriple.normalize() << "' with " << (OutputATTAsm ? "ATT" : "Intel") << " assembly variant.\n"; return 1; } // Set the display preference for hex vs. decimal immediates. IP->setPrintImmHex(InputArgs.hasArg(OPT_print_imm_hex)); // Set up the AsmStreamer. std::unique_ptr CE; if (InputArgs.hasArg(OPT_show_encoding)) CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); std::unique_ptr MAB( TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); auto FOut = std::make_unique(*OS); Str.reset(TheTarget->createAsmStreamer( Ctx, std::move(FOut), /*asmverbose*/ true, /*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB), InputArgs.hasArg(OPT_show_inst))); } else if (FileType == "null") { Str.reset(TheTarget->createNullStreamer(Ctx)); } else if (FileType == "obj") { if (!Out->os().supportsSeeking()) { BOS = std::make_unique(Out->os()); OS = BOS.get(); } MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions); Str.reset(TheTarget->createMCObjectStreamer( TheTriple, Ctx, std::unique_ptr(MAB), MAB->createObjectWriter(*OS), std::unique_ptr(CE), *STI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, /*DWARFMustBeAtTheEnd*/ false)); } else { llvm_unreachable("Invalid file type!"); } if (TheTriple.isOSBinFormatCOFF()) { // Emit an absolute @feat.00 symbol. This is a features bitfield read by // link.exe. int64_t Feat00Flags = 0x2; if (SafeSEH) { // According to the PE-COFF spec, the LSB of this value marks the object // for "registered SEH". This means that all SEH handler entry points // must be registered in .sxdata. Use of any unregistered handlers will // cause the process to terminate immediately. Feat00Flags |= 0x1; } MCSymbol *Feat00Sym = Ctx.getOrCreateSymbol("@feat.00"); Feat00Sym->setRedefinable(true); Str->emitSymbolAttribute(Feat00Sym, MCSA_Global); Str->emitAssignment(Feat00Sym, MCConstantExpr::create(Feat00Flags, Ctx)); } // Use Assembler information for parsing. Str->setUseAssemblerInfoForParsing(true); int Res = 1; if (InputArgs.hasArg(OPT_as_lex)) { // -as-lex; Lex only, and output a stream of tokens Res = AsLexInput(SrcMgr, *MAI, Out->os()); } else { Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI, *MCII, MCOptions, InputArgs); } // Keep output if no errors. if (Res == 0) Out->keep(); return Res; }