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 48 using namespace llvm; 49 using namespace llvm::opt; 50 51 namespace { 52 53 enum ID { 54 OPT_INVALID = 0, // This is not an option ID. 55 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 56 HELPTEXT, METAVAR, VALUES) \ 57 OPT_##ID, 58 #include "Opts.inc" 59 #undef OPTION 60 }; 61 62 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; 63 #include "Opts.inc" 64 #undef PREFIX 65 66 const opt::OptTable::Info InfoTable[] = { 67 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 68 HELPTEXT, METAVAR, VALUES) \ 69 { \ 70 PREFIX, NAME, HELPTEXT, \ 71 METAVAR, OPT_##ID, opt::Option::KIND##Class, \ 72 PARAM, FLAGS, OPT_##GROUP, \ 73 OPT_##ALIAS, ALIASARGS, VALUES}, 74 #include "Opts.inc" 75 #undef OPTION 76 }; 77 78 class MLOptTable : public opt::OptTable { 79 public: 80 MLOptTable() : OptTable(InfoTable, /*IgnoreCase=*/false) {} 81 }; 82 } // namespace 83 84 static 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.endswith("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 100 static 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 111 static 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 130 static 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 '" << TimestampStr 143 << "'; 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 192 int main(int Argc, char **Argv) { 193 InitLLVM X(Argc, Argv); 194 StringRef ProgName = sys::path::filename(Argv[0]); 195 196 // Initialize targets and assembly printers/parsers. 197 llvm::InitializeAllTargetInfos(); 198 llvm::InitializeAllTargetMCs(); 199 llvm::InitializeAllAsmParsers(); 200 llvm::InitializeAllDisassemblers(); 201 202 MLOptTable T; 203 unsigned MissingArgIndex, MissingArgCount; 204 ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); 205 opt::InputArgList InputArgs = 206 T.ParseArgs(ArgsArr, MissingArgIndex, MissingArgCount); 207 208 std::string InputFilename; 209 for (auto *Arg : InputArgs.filtered(OPT_INPUT)) { 210 std::string ArgString = Arg->getAsString(InputArgs); 211 StringRef ArgStringRef(ArgString); 212 if (ArgString == "-" || ArgStringRef.endswith(".asm") || 213 ArgStringRef.endswith(".S")) { 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 << "invalid option '" << ArgString << "'"; 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 \"" << ProgName 263 << " /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 271 Triple TheTriple = GetTriple(ProgName, InputArgs); 272 std::string Error; 273 const Target *TheTarget = TargetRegistry::lookupTarget("", TheTriple, Error); 274 if (!TheTarget) { 275 WithColor::error(errs(), ProgName) << Error; 276 return 1; 277 } 278 const std::string &TripleName = TheTriple.getTriple(); 279 280 bool SafeSEH = InputArgs.hasArg(OPT_safeseh); 281 if (SafeSEH && !(TheTriple.isArch32Bit() && TheTriple.isX86())) { 282 WithColor::warning() 283 << "/safeseh applies only to 32-bit X86 platforms; ignoring.\n"; 284 SafeSEH = false; 285 } 286 287 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr = 288 MemoryBuffer::getFileOrSTDIN(InputFilename); 289 if (std::error_code EC = BufferPtr.getError()) { 290 WithColor::error(errs(), ProgName) 291 << InputFilename << ": " << EC.message() << '\n'; 292 return 1; 293 } 294 295 SourceMgr SrcMgr; 296 297 // Tell SrcMgr about this buffer, which is what the parser will pick up. 298 SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); 299 300 // Record the location of the include directories so that the lexer can find 301 // included files later. 302 std::vector<std::string> IncludeDirs = 303 InputArgs.getAllArgValues(OPT_include_path); 304 if (!InputArgs.hasArg(OPT_ignore_include_envvar)) { 305 if (llvm::Optional<std::string> IncludeEnvVar = 306 llvm::sys::Process::GetEnv("INCLUDE")) { 307 SmallVector<StringRef, 8> Dirs; 308 StringRef(*IncludeEnvVar) 309 .split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false); 310 IncludeDirs.reserve(IncludeDirs.size() + Dirs.size()); 311 for (StringRef Dir : Dirs) 312 IncludeDirs.push_back(Dir.str()); 313 } 314 } 315 SrcMgr.setIncludeDirs(IncludeDirs); 316 317 std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); 318 assert(MRI && "Unable to create target register info!"); 319 320 std::unique_ptr<MCAsmInfo> MAI( 321 TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); 322 assert(MAI && "Unable to create target asm info!"); 323 324 MAI->setPreserveAsmComments(InputArgs.hasArg(OPT_preserve_comments)); 325 326 std::unique_ptr<MCSubtargetInfo> STI(TheTarget->createMCSubtargetInfo( 327 TripleName, /*CPU=*/"", /*Features=*/"")); 328 assert(STI && "Unable to create subtarget info!"); 329 330 // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and 331 // MCObjectFileInfo needs a MCContext reference in order to initialize itself. 332 MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); 333 std::unique_ptr<MCObjectFileInfo> MOFI(TheTarget->createMCObjectFileInfo( 334 Ctx, /*PIC=*/false, /*LargeCodeModel=*/true)); 335 Ctx.setObjectFileInfo(MOFI.get()); 336 337 if (InputArgs.hasArg(OPT_save_temp_labels)) 338 Ctx.setAllowTemporaryLabels(false); 339 340 // Set compilation information. 341 SmallString<128> CWD; 342 if (!sys::fs::current_path(CWD)) 343 Ctx.setCompilationDir(CWD); 344 Ctx.setMainFileName(InputFilename); 345 346 StringRef FileType = InputArgs.getLastArgValue(OPT_filetype, "obj"); 347 SmallString<255> DefaultOutputFilename; 348 if (InputArgs.hasArg(OPT_as_lex)) { 349 DefaultOutputFilename = "-"; 350 } else { 351 DefaultOutputFilename = InputFilename; 352 sys::path::replace_extension(DefaultOutputFilename, FileType); 353 } 354 const StringRef OutputFilename = 355 InputArgs.getLastArgValue(OPT_output_file, DefaultOutputFilename); 356 std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename); 357 if (!Out) 358 return 1; 359 360 std::unique_ptr<buffer_ostream> BOS; 361 raw_pwrite_stream *OS = &Out->os(); 362 std::unique_ptr<MCStreamer> Str; 363 364 std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); 365 assert(MCII && "Unable to create instruction info!"); 366 367 MCInstPrinter *IP = nullptr; 368 if (FileType == "s") { 369 const bool OutputATTAsm = InputArgs.hasArg(OPT_output_att_asm); 370 const unsigned OutputAsmVariant = OutputATTAsm ? 0U // ATT dialect 371 : 1U; // Intel dialect 372 IP = TheTarget->createMCInstPrinter(TheTriple, OutputAsmVariant, *MAI, 373 *MCII, *MRI); 374 375 if (!IP) { 376 WithColor::error() 377 << "unable to create instruction printer for target triple '" 378 << TheTriple.normalize() << "' with " 379 << (OutputATTAsm ? "ATT" : "Intel") << " assembly variant.\n"; 380 return 1; 381 } 382 383 // Set the display preference for hex vs. decimal immediates. 384 IP->setPrintImmHex(InputArgs.hasArg(OPT_print_imm_hex)); 385 386 // Set up the AsmStreamer. 387 std::unique_ptr<MCCodeEmitter> CE; 388 if (InputArgs.hasArg(OPT_show_encoding)) 389 CE.reset(TheTarget->createMCCodeEmitter(*MCII, Ctx)); 390 391 std::unique_ptr<MCAsmBackend> MAB( 392 TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); 393 auto FOut = std::make_unique<formatted_raw_ostream>(*OS); 394 Str.reset(TheTarget->createAsmStreamer( 395 Ctx, std::move(FOut), /*asmverbose*/ true, 396 /*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB), 397 InputArgs.hasArg(OPT_show_inst))); 398 399 } else if (FileType == "null") { 400 Str.reset(TheTarget->createNullStreamer(Ctx)); 401 } else if (FileType == "obj") { 402 if (!Out->os().supportsSeeking()) { 403 BOS = std::make_unique<buffer_ostream>(Out->os()); 404 OS = BOS.get(); 405 } 406 407 MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, Ctx); 408 MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions); 409 Str.reset(TheTarget->createMCObjectStreamer( 410 TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), 411 MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(CE), *STI, 412 MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, 413 /*DWARFMustBeAtTheEnd*/ false)); 414 } else { 415 llvm_unreachable("Invalid file type!"); 416 } 417 418 if (TheTriple.isOSBinFormatCOFF()) { 419 // Emit an absolute @feat.00 symbol. This is a features bitfield read by 420 // link.exe. 421 int64_t Feat00Flags = 0x2; 422 if (SafeSEH) { 423 // According to the PE-COFF spec, the LSB of this value marks the object 424 // for "registered SEH". This means that all SEH handler entry points 425 // must be registered in .sxdata. Use of any unregistered handlers will 426 // cause the process to terminate immediately. 427 Feat00Flags |= 0x1; 428 } 429 MCSymbol *Feat00Sym = Ctx.getOrCreateSymbol("@feat.00"); 430 Feat00Sym->setRedefinable(true); 431 Str->emitSymbolAttribute(Feat00Sym, MCSA_Global); 432 Str->emitAssignment(Feat00Sym, MCConstantExpr::create(Feat00Flags, Ctx)); 433 } 434 435 // Use Assembler information for parsing. 436 Str->setUseAssemblerInfoForParsing(true); 437 438 int Res = 1; 439 if (InputArgs.hasArg(OPT_as_lex)) { 440 // -as-lex; Lex only, and output a stream of tokens 441 Res = AsLexInput(SrcMgr, *MAI, Out->os()); 442 } else { 443 Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI, 444 *MCII, MCOptions, InputArgs); 445 } 446 447 // Keep output if no errors. 448 if (Res == 0) 449 Out->keep(); 450 return Res; 451 } 452