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