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 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(...) 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 69 static constexpr opt::OptTable::Info InfoTable[] = { 70 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 71 #include "Opts.inc" 72 #undef OPTION 73 }; 74 75 class MLOptTable : public opt::GenericOptTable { 76 public: 77 MLOptTable() 78 : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable, 79 /*IgnoreCase=*/false) {} 80 }; 81 } // namespace 82 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.ends_with("ml64")) 89 DefaultBitness = "64"; 90 91 StringRef TripleName = 92 StringSwitch<StringRef>(Args.getLastArgValue(OPT_bitness, DefaultBitness)) 93 .Case("32", "i386-pc-windows") 94 .Case("64", "x86_64-pc-windows") 95 .Default(""); 96 return Triple(Triple::normalize(TripleName)); 97 } 98 99 static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path) { 100 std::error_code EC; 101 auto Out = std::make_unique<ToolOutputFile>(Path, EC, sys::fs::OF_None); 102 if (EC) { 103 WithColor::error() << EC.message() << '\n'; 104 return nullptr; 105 } 106 107 return Out; 108 } 109 110 static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI, raw_ostream &OS) { 111 AsmLexer Lexer(MAI); 112 Lexer.setBuffer(SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID())->getBuffer()); 113 Lexer.setLexMasmIntegers(true); 114 Lexer.useMasmDefaultRadix(true); 115 Lexer.setLexMasmHexFloats(true); 116 Lexer.setLexMasmStrings(true); 117 118 bool Error = false; 119 while (Lexer.Lex().isNot(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 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) { 134 struct tm TM; 135 time_t Timestamp; 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"; 143 return 1; 144 } 145 Timestamp = IntTimestamp; 146 } else { 147 Timestamp = time(nullptr); 148 } 149 if (InputArgs.hasArg(OPT_utc)) { 150 // Not thread-safe. 151 TM = *gmtime(&Timestamp); 152 } else { 153 // Not thread-safe. 154 TM = *localtime(&Timestamp); 155 } 156 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)); 161 162 if (!TAP) { 163 WithColor::error(errs(), ProgName) 164 << "this target does not support assembly parsing.\n"; 165 return 1; 166 } 167 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); 174 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"; 182 return 1; 183 } 184 } 185 186 int Res = Parser->Run(/*NoInitialTextSection=*/true); 187 188 return Res; 189 } 190 191 int llvm_ml_main(int Argc, char **Argv, const llvm::ToolContext &) { 192 StringRef ProgName = sys::path::filename(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(ArgsArr, MissingArgIndex, MissingArgCount); 205 206 std::string InputFilename; 207 for (auto *Arg : InputArgs.filtered(OPT_INPUT)) { 208 std::string ArgString = Arg->getAsString(InputArgs); 209 bool IsFile = false; 210 std::error_code IsFileEC = 211 llvm::sys::fs::is_regular_file(ArgString, IsFile); 212 if (ArgString == "-" || IsFile) { 213 if (!InputFilename.empty()) { 214 WithColor::warning(errs(), 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(ArgString, Nearest) < 2) 226 OS << ", did you mean '" << Nearest << "'?"; 227 228 WithColor::error(errs(), ProgName) << OS.str() << '\n'; 229 exit(1); 230 } 231 } 232 for (auto *Arg : InputArgs.filtered(OPT_assembly_file)) { 233 if (!InputFilename.empty()) { 234 WithColor::warning(errs(), 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(OPT_unsupported_Group)) { 242 WithColor::warning(errs(), ProgName) 243 << "ignoring unsupported '" << Arg->getOption().getName() 244 << "' option\n"; 245 } 246 247 if (InputArgs.hasArg(OPT_debug)) { 248 DebugFlag = true; 249 } 250 for (auto *Arg : InputArgs.filtered(OPT_debug_only)) { 251 setCurrentDebugTypes(Arg->getValues().data(), Arg->getNumValues()); 252 } 253 254 if (InputArgs.hasArg(OPT_help)) { 255 std::string Usage = llvm::formatv("{0} [ /options ] file", ProgName).str(); 256 T.printHelp(outs(), Usage.c_str(), "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(OPT_fatal_warnings); 269 MCOptions.MCSaveTempLabels = InputArgs.hasArg(OPT_save_temp_labels); 270 MCOptions.ShowMCInst = InputArgs.hasArg(OPT_show_inst); 271 MCOptions.AsmVerbose = true; 272 273 Triple TheTriple = GetTriple(ProgName, InputArgs); 274 std::string Error; 275 const Target *TheTarget = TargetRegistry::lookupTarget("", TheTriple, Error); 276 if (!TheTarget) { 277 WithColor::error(errs(), ProgName) << Error; 278 return 1; 279 } 280 const std::string &TripleName = TheTriple.getTriple(); 281 282 bool SafeSEH = InputArgs.hasArg(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(InputFilename); 291 if (std::error_code EC = BufferPtr.getError()) { 292 WithColor::error(errs(), 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(std::move(*BufferPtr), 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(OPT_include_path); 306 if (!InputArgs.hasArg(OPT_ignore_include_envvar)) { 307 if (std::optional<std::string> IncludeEnvVar = 308 llvm::sys::Process::GetEnv("INCLUDE")) { 309 SmallVector<StringRef, 8> Dirs; 310 StringRef(*IncludeEnvVar) 311 .split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false); 312 IncludeDirs.reserve(IncludeDirs.size() + Dirs.size()); 313 for (StringRef Dir : Dirs) 314 IncludeDirs.push_back(Dir.str()); 315 } 316 } 317 SrcMgr.setIncludeDirs(IncludeDirs); 318 319 std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); 320 assert(MRI && "Unable to create target register info!"); 321 322 std::unique_ptr<MCAsmInfo> MAI( 323 TheTarget->createMCAsmInfo(*MRI, TripleName, 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(TheTarget->createMCSubtargetInfo( 329 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(CWD)) 342 Ctx.setCompilationDir(CWD); 343 Ctx.setMainFileName(InputFilename); 344 345 StringRef FileType = InputArgs.getLastArgValue(OPT_filetype, "obj"); 346 SmallString<255> DefaultOutputFilename; 347 if (InputArgs.hasArg(OPT_as_lex)) { 348 DefaultOutputFilename = "-"; 349 } else { 350 DefaultOutputFilename = InputFilename; 351 sys::path::replace_extension(DefaultOutputFilename, FileType); 352 } 353 const StringRef OutputFilename = 354 InputArgs.getLastArgValue(OPT_output_file, DefaultOutputFilename); 355 std::unique_ptr<ToolOutputFile> Out = GetOutputStream(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 MCInstPrinter *IP = nullptr; 367 if (FileType == "s") { 368 const bool OutputATTAsm = InputArgs.hasArg(OPT_output_att_asm); 369 const unsigned OutputAsmVariant = OutputATTAsm ? 0U // ATT dialect 370 : 1U; // Intel dialect 371 IP = TheTarget->createMCInstPrinter(TheTriple, OutputAsmVariant, *MAI, 372 *MCII, *MRI); 373 374 if (!IP) { 375 WithColor::error() 376 << "unable to create instruction printer for target triple '" 377 << TheTriple.normalize() << "' with " 378 << (OutputATTAsm ? "ATT" : "Intel") << " assembly variant.\n"; 379 return 1; 380 } 381 382 // Set the display preference for hex vs. decimal immediates. 383 IP->setPrintImmHex(InputArgs.hasArg(OPT_print_imm_hex)); 384 385 // Set up the AsmStreamer. 386 std::unique_ptr<MCCodeEmitter> CE; 387 if (InputArgs.hasArg(OPT_show_encoding)) 388 CE.reset(TheTarget->createMCCodeEmitter(*MCII, Ctx)); 389 390 std::unique_ptr<MCAsmBackend> MAB( 391 TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); 392 auto FOut = std::make_unique<formatted_raw_ostream>(*OS); 393 Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), IP, 394 std::move(CE), std::move(MAB))); 395 396 } else if (FileType == "null") { 397 Str.reset(TheTarget->createNullStreamer(Ctx)); 398 } else if (FileType == "obj") { 399 if (!Out->os().supportsSeeking()) { 400 BOS = std::make_unique<buffer_ostream>(Out->os()); 401 OS = BOS.get(); 402 } 403 404 MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, Ctx); 405 MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions); 406 Str.reset(TheTarget->createMCObjectStreamer( 407 TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), 408 MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(CE), 409 *STI)); 410 } else { 411 llvm_unreachable("Invalid file type!"); 412 } 413 414 if (TheTriple.isOSBinFormatCOFF()) { 415 // Emit an absolute @feat.00 symbol. This is a features bitfield read by 416 // link.exe. 417 int64_t Feat00Flags = 0x2; 418 if (SafeSEH) { 419 // According to the PE-COFF spec, the LSB of this value marks the object 420 // for "registered SEH". This means that all SEH handler entry points 421 // must be registered in .sxdata. Use of any unregistered handlers will 422 // cause the process to terminate immediately. 423 Feat00Flags |= 0x1; 424 } 425 MCSymbol *Feat00Sym = Ctx.getOrCreateSymbol("@feat.00"); 426 Feat00Sym->setRedefinable(true); 427 Str->emitSymbolAttribute(Feat00Sym, MCSA_Global); 428 Str->emitAssignment(Feat00Sym, MCConstantExpr::create(Feat00Flags, Ctx)); 429 } 430 431 int Res = 1; 432 if (InputArgs.hasArg(OPT_as_lex)) { 433 // -as-lex; Lex only, and output a stream of tokens 434 Res = AsLexInput(SrcMgr, *MAI, Out->os()); 435 } else { 436 Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI, 437 *MCII, MCOptions, InputArgs); 438 } 439 440 // Keep output if no errors. 441 if (Res == 0) 442 Out->keep(); 443 return Res; 444 } 445