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