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 "Disassembler.h" 14 15 #include "llvm/MC/MCAsmBackend.h" 16 #include "llvm/MC/MCAsmInfo.h" 17 #include "llvm/MC/MCCodeEmitter.h" 18 #include "llvm/MC/MCContext.h" 19 #include "llvm/MC/MCInstPrinter.h" 20 #include "llvm/MC/MCInstrInfo.h" 21 #include "llvm/MC/MCObjectFileInfo.h" 22 #include "llvm/MC/MCObjectWriter.h" 23 #include "llvm/MC/MCParser/AsmLexer.h" 24 #include "llvm/MC/MCParser/MCTargetAsmParser.h" 25 #include "llvm/MC/MCRegisterInfo.h" 26 #include "llvm/MC/MCStreamer.h" 27 #include "llvm/MC/MCSubtargetInfo.h" 28 #include "llvm/MC/MCTargetOptionsCommandFlags.h" 29 #include "llvm/Support/CommandLine.h" 30 #include "llvm/Support/Compression.h" 31 #include "llvm/Support/FileUtilities.h" 32 #include "llvm/Support/FormattedStream.h" 33 #include "llvm/Support/Host.h" 34 #include "llvm/Support/InitLLVM.h" 35 #include "llvm/Support/MemoryBuffer.h" 36 #include "llvm/Support/SourceMgr.h" 37 #include "llvm/Support/TargetRegistry.h" 38 #include "llvm/Support/TargetSelect.h" 39 #include "llvm/Support/ToolOutputFile.h" 40 #include "llvm/Support/WithColor.h" 41 42 using namespace llvm; 43 44 static mc::RegisterMCTargetOptionsFlags MOF; 45 46 static cl::opt<std::string> 47 InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-")); 48 49 static cl::opt<std::string> 50 OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), 51 cl::init("-")); 52 53 static cl::opt<bool> 54 ShowEncoding("show-encoding", cl::desc("Show instruction encodings")); 55 56 static cl::opt<bool> 57 ShowInst("show-inst", cl::desc("Show internal instruction representation")); 58 59 static cl::opt<bool> 60 ShowInstOperands("show-inst-operands", 61 cl::desc("Show instructions operands as parsed")); 62 63 static cl::opt<bool> 64 OutputATTAsm("output-att-asm", cl::desc("Use ATT syntax for output printing")); 65 66 static cl::opt<bool> 67 PrintImmHex("print-imm-hex", cl::init(false), 68 cl::desc("Prefer hex format for immediate values")); 69 70 static cl::opt<bool> 71 PreserveComments("preserve-comments", 72 cl::desc("Preserve Comments in outputted assembly")); 73 74 enum OutputFileType { 75 OFT_Null, 76 OFT_AssemblyFile, 77 OFT_ObjectFile 78 }; 79 static cl::opt<OutputFileType> 80 FileType("filetype", cl::init(OFT_ObjectFile), 81 cl::desc("Choose an output file type:"), 82 cl::values( 83 clEnumValN(OFT_AssemblyFile, "asm", 84 "Emit an assembly ('.s') file"), 85 clEnumValN(OFT_Null, "null", 86 "Don't emit anything (for timing purposes)"), 87 clEnumValN(OFT_ObjectFile, "obj", 88 "Emit a native object ('.o') file"))); 89 90 static cl::list<std::string> 91 IncludeDirs("I", cl::desc("Directory of include files"), 92 cl::value_desc("directory"), cl::Prefix); 93 94 enum BitnessType { 95 m32, 96 m64, 97 }; 98 cl::opt<BitnessType> Bitness(cl::desc("Choose bitness:"), cl::init(m64), 99 cl::values(clEnumVal(m32, "32-bit"), 100 clEnumVal(m64, "64-bit (default)"))); 101 102 static cl::opt<std::string> 103 TripleName("triple", cl::desc("Target triple to assemble for, " 104 "see -version for available targets")); 105 106 static cl::opt<std::string> 107 DebugCompilationDir("fdebug-compilation-dir", 108 cl::desc("Specifies the debug info's compilation dir")); 109 110 static cl::list<std::string> 111 DebugPrefixMap("fdebug-prefix-map", 112 cl::desc("Map file source paths in debug info"), 113 cl::value_desc("= separated key-value pairs")); 114 115 static cl::opt<std::string> 116 MainFileName("main-file-name", 117 cl::desc("Specifies the name we should consider the input file")); 118 119 static cl::opt<bool> SaveTempLabels("save-temp-labels", 120 cl::desc("Don't discard temporary labels")); 121 122 enum ActionType { 123 AC_AsLex, 124 AC_Assemble, 125 AC_Disassemble, 126 AC_MDisassemble, 127 }; 128 129 static cl::opt<ActionType> 130 Action(cl::desc("Action to perform:"), 131 cl::init(AC_Assemble), 132 cl::values(clEnumValN(AC_AsLex, "as-lex", 133 "Lex tokens from a .asm file"), 134 clEnumValN(AC_Assemble, "assemble", 135 "Assemble a .asm file (default)"), 136 clEnumValN(AC_Disassemble, "disassemble", 137 "Disassemble strings of hex bytes"), 138 clEnumValN(AC_MDisassemble, "mdis", 139 "Marked up disassembly of strings of hex bytes"))); 140 141 static const Target *GetTarget(const char *ProgName) { 142 // Figure out the target triple. 143 if (TripleName.empty()) { 144 if (Bitness == m32) 145 TripleName = "i386-pc-windows"; 146 else if (Bitness == m64) 147 TripleName = "x86_64-pc-windows"; 148 } 149 Triple TheTriple(Triple::normalize(TripleName)); 150 151 // Get the target specific parser. 152 std::string Error; 153 const Target *TheTarget = TargetRegistry::lookupTarget("", TheTriple, Error); 154 if (!TheTarget) { 155 WithColor::error(errs(), ProgName) << Error; 156 return nullptr; 157 } 158 159 // Update the triple name and return the found target. 160 TripleName = TheTriple.getTriple(); 161 return TheTarget; 162 } 163 164 static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path) { 165 std::error_code EC; 166 auto Out = std::make_unique<ToolOutputFile>(Path, EC, sys::fs::F_None); 167 if (EC) { 168 WithColor::error() << EC.message() << '\n'; 169 return nullptr; 170 } 171 172 return Out; 173 } 174 175 static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI, raw_ostream &OS) { 176 AsmLexer Lexer(MAI); 177 Lexer.setBuffer(SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID())->getBuffer()); 178 179 bool Error = false; 180 while (Lexer.Lex().isNot(AsmToken::Eof)) { 181 Lexer.getTok().dump(OS); 182 OS << "\n"; 183 if (Lexer.getTok().getKind() == AsmToken::Error) 184 Error = true; 185 } 186 187 return Error; 188 } 189 190 static int AssembleInput(const char *ProgName, const Target *TheTarget, 191 SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str, 192 MCAsmInfo &MAI, MCSubtargetInfo &STI, 193 MCInstrInfo &MCII, MCTargetOptions &MCOptions) { 194 std::unique_ptr<MCAsmParser> Parser( 195 createMCMasmParser(SrcMgr, Ctx, Str, MAI)); 196 std::unique_ptr<MCTargetAsmParser> TAP( 197 TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions)); 198 199 if (!TAP) { 200 WithColor::error(errs(), ProgName) 201 << "this target does not support assembly parsing.\n"; 202 return 1; 203 } 204 205 Parser->setShowParsedOperands(ShowInstOperands); 206 Parser->setTargetParser(*TAP); 207 Parser->getLexer().setLexMasmIntegers(true); 208 209 int Res = Parser->Run(/*NoInitialTextSection=*/true); 210 211 return Res; 212 } 213 214 int main(int argc, char **argv) { 215 InitLLVM X(argc, argv); 216 217 // Initialize targets and assembly printers/parsers. 218 llvm::InitializeAllTargetInfos(); 219 llvm::InitializeAllTargetMCs(); 220 llvm::InitializeAllAsmParsers(); 221 llvm::InitializeAllDisassemblers(); 222 223 // Register the target printer for --version. 224 cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); 225 226 cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n"); 227 MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); 228 MCOptions.AssemblyLanguage = "masm"; 229 230 const char *ProgName = argv[0]; 231 const Target *TheTarget = GetTarget(ProgName); 232 if (!TheTarget) 233 return 1; 234 // Now that GetTarget() has (potentially) replaced TripleName, it's safe to 235 // construct the Triple object. 236 Triple TheTriple(TripleName); 237 238 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr = 239 MemoryBuffer::getFileOrSTDIN(InputFilename); 240 if (std::error_code EC = BufferPtr.getError()) { 241 WithColor::error(errs(), ProgName) 242 << InputFilename << ": " << EC.message() << '\n'; 243 return 1; 244 } 245 MemoryBuffer *Buffer = BufferPtr->get(); 246 247 SourceMgr SrcMgr; 248 249 // Tell SrcMgr about this buffer, which is what the parser will pick up. 250 SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); 251 252 // Record the location of the include directories so that the lexer can find 253 // it later. 254 SrcMgr.setIncludeDirs(IncludeDirs); 255 256 std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); 257 assert(MRI && "Unable to create target register info!"); 258 259 std::unique_ptr<MCAsmInfo> MAI( 260 TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); 261 assert(MAI && "Unable to create target asm info!"); 262 263 MAI->setPreserveAsmComments(PreserveComments); 264 265 // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and 266 // MCObjectFileInfo needs a MCContext reference in order to initialize itself. 267 MCObjectFileInfo MOFI; 268 MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); 269 MOFI.InitMCObjectFileInfo(TheTriple, /*PIC=*/false, Ctx, 270 /*LargeCodeModel=*/true); 271 272 if (SaveTempLabels) 273 Ctx.setAllowTemporaryLabels(false); 274 275 if (!DebugCompilationDir.empty()) { 276 Ctx.setCompilationDir(DebugCompilationDir); 277 } else { 278 // If no compilation dir is set, try to use the current directory. 279 SmallString<128> CWD; 280 if (!sys::fs::current_path(CWD)) 281 Ctx.setCompilationDir(CWD); 282 } 283 for (const auto &Arg : DebugPrefixMap) { 284 const auto &KV = StringRef(Arg).split('='); 285 Ctx.addDebugPrefixMapEntry(std::string(KV.first), std::string(KV.second)); 286 } 287 if (!MainFileName.empty()) 288 Ctx.setMainFileName(MainFileName); 289 290 std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename); 291 if (!Out) 292 return 1; 293 294 std::unique_ptr<buffer_ostream> BOS; 295 raw_pwrite_stream *OS = &Out->os(); 296 std::unique_ptr<MCStreamer> Str; 297 298 std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); 299 std::unique_ptr<MCSubtargetInfo> STI(TheTarget->createMCSubtargetInfo( 300 TripleName, /*CPU=*/"", /*Features=*/"")); 301 302 MCInstPrinter *IP = nullptr; 303 if (FileType == OFT_AssemblyFile) { 304 const unsigned OutputAsmVariant = OutputATTAsm ? 0U // ATT dialect 305 : 1U; // Intel dialect 306 IP = TheTarget->createMCInstPrinter(Triple(TripleName), OutputAsmVariant, 307 *MAI, *MCII, *MRI); 308 309 if (!IP) { 310 WithColor::error() 311 << "unable to create instruction printer for target triple '" 312 << TheTriple.normalize() << "' with " 313 << (OutputATTAsm ? "ATT" : "Intel") << " assembly variant.\n"; 314 return 1; 315 } 316 317 // Set the display preference for hex vs. decimal immediates. 318 IP->setPrintImmHex(PrintImmHex); 319 320 // Set up the AsmStreamer. 321 std::unique_ptr<MCCodeEmitter> CE; 322 if (ShowEncoding) 323 CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); 324 325 std::unique_ptr<MCAsmBackend> MAB( 326 TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); 327 auto FOut = std::make_unique<formatted_raw_ostream>(*OS); 328 Str.reset( 329 TheTarget->createAsmStreamer(Ctx, std::move(FOut), /*asmverbose*/ true, 330 /*useDwarfDirectory*/ true, IP, 331 std::move(CE), std::move(MAB), ShowInst)); 332 333 } else if (FileType == OFT_Null) { 334 Str.reset(TheTarget->createNullStreamer(Ctx)); 335 } else { 336 assert(FileType == OFT_ObjectFile && "Invalid file type!"); 337 338 if (!Out->os().supportsSeeking()) { 339 BOS = std::make_unique<buffer_ostream>(Out->os()); 340 OS = BOS.get(); 341 } 342 343 MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); 344 MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions); 345 Str.reset(TheTarget->createMCObjectStreamer( 346 TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), 347 MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(CE), *STI, 348 MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, 349 /*DWARFMustBeAtTheEnd*/ false)); 350 } 351 352 // Use Assembler information for parsing. 353 Str->setUseAssemblerInfoForParsing(true); 354 355 int Res = 1; 356 bool disassemble = false; 357 switch (Action) { 358 case AC_AsLex: 359 Res = AsLexInput(SrcMgr, *MAI, Out->os()); 360 break; 361 case AC_Assemble: 362 Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI, 363 *MCII, MCOptions); 364 break; 365 case AC_MDisassemble: 366 assert(IP && "Expected assembly output"); 367 IP->setUseMarkup(1); 368 disassemble = true; 369 break; 370 case AC_Disassemble: 371 disassemble = true; 372 break; 373 } 374 if (disassemble) 375 Res = Disassembler::disassemble(*TheTarget, TripleName, *STI, *Str, *Buffer, 376 SrcMgr, Out->os()); 377 378 // Keep output if no errors. 379 if (Res == 0) 380 Out->keep(); 381 return Res; 382 } 383