1 //===-- lib/MC/Disassembler.cpp - Disassembler Public C Interface ---------===// 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 #include "Disassembler.h" 10 #include "llvm-c/Disassembler.h" 11 #include "llvm/ADT/ArrayRef.h" 12 #include "llvm/ADT/SmallVector.h" 13 #include "llvm/ADT/Triple.h" 14 #include "llvm/MC/MCAsmInfo.h" 15 #include "llvm/MC/MCContext.h" 16 #include "llvm/MC/MCDisassembler/MCDisassembler.h" 17 #include "llvm/MC/MCDisassembler/MCRelocationInfo.h" 18 #include "llvm/MC/MCDisassembler/MCSymbolizer.h" 19 #include "llvm/MC/MCInst.h" 20 #include "llvm/MC/MCInstPrinter.h" 21 #include "llvm/MC/MCInstrDesc.h" 22 #include "llvm/MC/MCInstrInfo.h" 23 #include "llvm/MC/MCInstrItineraries.h" 24 #include "llvm/MC/MCRegisterInfo.h" 25 #include "llvm/MC/MCSchedule.h" 26 #include "llvm/MC/MCSubtargetInfo.h" 27 #include "llvm/Support/ErrorHandling.h" 28 #include "llvm/Support/FormattedStream.h" 29 #include "llvm/Support/TargetRegistry.h" 30 #include "llvm/Support/raw_ostream.h" 31 #include <cassert> 32 #include <cstddef> 33 #include <cstring> 34 35 using namespace llvm; 36 37 // LLVMCreateDisasm() creates a disassembler for the TripleName. Symbolic 38 // disassembly is supported by passing a block of information in the DisInfo 39 // parameter and specifying the TagType and callback functions as described in 40 // the header llvm-c/Disassembler.h . The pointer to the block and the 41 // functions can all be passed as NULL. If successful, this returns a 42 // disassembler context. If not, it returns NULL. 43 // 44 LLVMDisasmContextRef 45 LLVMCreateDisasmCPUFeatures(const char *TT, const char *CPU, 46 const char *Features, void *DisInfo, int TagType, 47 LLVMOpInfoCallback GetOpInfo, 48 LLVMSymbolLookupCallback SymbolLookUp) { 49 // Get the target. 50 std::string Error; 51 const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error); 52 if (!TheTarget) 53 return nullptr; 54 55 std::unique_ptr<const MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT)); 56 if (!MRI) 57 return nullptr; 58 59 // Get the assembler info needed to setup the MCContext. 60 std::unique_ptr<const MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TT)); 61 if (!MAI) 62 return nullptr; 63 64 std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo()); 65 if (!MII) 66 return nullptr; 67 68 std::unique_ptr<const MCSubtargetInfo> STI( 69 TheTarget->createMCSubtargetInfo(TT, CPU, Features)); 70 if (!STI) 71 return nullptr; 72 73 // Set up the MCContext for creating symbols and MCExpr's. 74 std::unique_ptr<MCContext> Ctx(new MCContext(MAI.get(), MRI.get(), nullptr)); 75 if (!Ctx) 76 return nullptr; 77 78 // Set up disassembler. 79 std::unique_ptr<MCDisassembler> DisAsm( 80 TheTarget->createMCDisassembler(*STI, *Ctx)); 81 if (!DisAsm) 82 return nullptr; 83 84 std::unique_ptr<MCRelocationInfo> RelInfo( 85 TheTarget->createMCRelocationInfo(TT, *Ctx)); 86 if (!RelInfo) 87 return nullptr; 88 89 std::unique_ptr<MCSymbolizer> Symbolizer(TheTarget->createMCSymbolizer( 90 TT, GetOpInfo, SymbolLookUp, DisInfo, Ctx.get(), std::move(RelInfo))); 91 DisAsm->setSymbolizer(std::move(Symbolizer)); 92 93 // Set up the instruction printer. 94 int AsmPrinterVariant = MAI->getAssemblerDialect(); 95 std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( 96 Triple(TT), AsmPrinterVariant, *MAI, *MII, *MRI)); 97 if (!IP) 98 return nullptr; 99 100 LLVMDisasmContext *DC = new LLVMDisasmContext( 101 TT, DisInfo, TagType, GetOpInfo, SymbolLookUp, TheTarget, std::move(MAI), 102 std::move(MRI), std::move(STI), std::move(MII), std::move(Ctx), 103 std::move(DisAsm), std::move(IP)); 104 if (!DC) 105 return nullptr; 106 107 DC->setCPU(CPU); 108 return DC; 109 } 110 111 LLVMDisasmContextRef 112 LLVMCreateDisasmCPU(const char *TT, const char *CPU, void *DisInfo, int TagType, 113 LLVMOpInfoCallback GetOpInfo, 114 LLVMSymbolLookupCallback SymbolLookUp) { 115 return LLVMCreateDisasmCPUFeatures(TT, CPU, "", DisInfo, TagType, GetOpInfo, 116 SymbolLookUp); 117 } 118 119 LLVMDisasmContextRef LLVMCreateDisasm(const char *TT, void *DisInfo, 120 int TagType, LLVMOpInfoCallback GetOpInfo, 121 LLVMSymbolLookupCallback SymbolLookUp) { 122 return LLVMCreateDisasmCPUFeatures(TT, "", "", DisInfo, TagType, GetOpInfo, 123 SymbolLookUp); 124 } 125 126 // 127 // LLVMDisasmDispose() disposes of the disassembler specified by the context. 128 // 129 void LLVMDisasmDispose(LLVMDisasmContextRef DCR){ 130 LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); 131 delete DC; 132 } 133 134 /// Emits the comments that are stored in \p DC comment stream. 135 /// Each comment in the comment stream must end with a newline. 136 static void emitComments(LLVMDisasmContext *DC, 137 formatted_raw_ostream &FormattedOS) { 138 // Flush the stream before taking its content. 139 StringRef Comments = DC->CommentsToEmit.str(); 140 // Get the default information for printing a comment. 141 const MCAsmInfo *MAI = DC->getAsmInfo(); 142 StringRef CommentBegin = MAI->getCommentString(); 143 unsigned CommentColumn = MAI->getCommentColumn(); 144 bool IsFirst = true; 145 while (!Comments.empty()) { 146 if (!IsFirst) 147 FormattedOS << '\n'; 148 // Emit a line of comments. 149 FormattedOS.PadToColumn(CommentColumn); 150 size_t Position = Comments.find('\n'); 151 FormattedOS << CommentBegin << ' ' << Comments.substr(0, Position); 152 // Move after the newline character. 153 Comments = Comments.substr(Position+1); 154 IsFirst = false; 155 } 156 FormattedOS.flush(); 157 158 // Tell the comment stream that the vector changed underneath it. 159 DC->CommentsToEmit.clear(); 160 } 161 162 /// Gets latency information for \p Inst from the itinerary 163 /// scheduling model, based on \p DC information. 164 /// \return The maximum expected latency over all the operands or -1 165 /// if no information is available. 166 static int getItineraryLatency(LLVMDisasmContext *DC, const MCInst &Inst) { 167 const int NoInformationAvailable = -1; 168 169 // Check if we have a CPU to get the itinerary information. 170 if (DC->getCPU().empty()) 171 return NoInformationAvailable; 172 173 // Get itinerary information. 174 const MCSubtargetInfo *STI = DC->getSubtargetInfo(); 175 InstrItineraryData IID = STI->getInstrItineraryForCPU(DC->getCPU()); 176 // Get the scheduling class of the requested instruction. 177 const MCInstrDesc& Desc = DC->getInstrInfo()->get(Inst.getOpcode()); 178 unsigned SCClass = Desc.getSchedClass(); 179 180 int Latency = 0; 181 for (unsigned OpIdx = 0, OpIdxEnd = Inst.getNumOperands(); OpIdx != OpIdxEnd; 182 ++OpIdx) 183 Latency = std::max(Latency, IID.getOperandCycle(SCClass, OpIdx)); 184 185 return Latency; 186 } 187 188 /// Gets latency information for \p Inst, based on \p DC information. 189 /// \return The maximum expected latency over all the definitions or -1 190 /// if no information is available. 191 static int getLatency(LLVMDisasmContext *DC, const MCInst &Inst) { 192 // Try to compute scheduling information. 193 const MCSubtargetInfo *STI = DC->getSubtargetInfo(); 194 const MCSchedModel SCModel = STI->getSchedModel(); 195 const int NoInformationAvailable = -1; 196 197 // Check if we have a scheduling model for instructions. 198 if (!SCModel.hasInstrSchedModel()) 199 // Try to fall back to the itinerary model if the scheduling model doesn't 200 // have a scheduling table. Note the default does not have a table. 201 return getItineraryLatency(DC, Inst); 202 203 // Get the scheduling class of the requested instruction. 204 const MCInstrDesc& Desc = DC->getInstrInfo()->get(Inst.getOpcode()); 205 unsigned SCClass = Desc.getSchedClass(); 206 const MCSchedClassDesc *SCDesc = SCModel.getSchedClassDesc(SCClass); 207 // Resolving the variant SchedClass requires an MI to pass to 208 // SubTargetInfo::resolveSchedClass. 209 if (!SCDesc || !SCDesc->isValid() || SCDesc->isVariant()) 210 return NoInformationAvailable; 211 212 // Compute output latency. 213 int16_t Latency = 0; 214 for (unsigned DefIdx = 0, DefEnd = SCDesc->NumWriteLatencyEntries; 215 DefIdx != DefEnd; ++DefIdx) { 216 // Lookup the definition's write latency in SubtargetInfo. 217 const MCWriteLatencyEntry *WLEntry = STI->getWriteLatencyEntry(SCDesc, 218 DefIdx); 219 Latency = std::max(Latency, WLEntry->Cycles); 220 } 221 222 return Latency; 223 } 224 225 /// Emits latency information in DC->CommentStream for \p Inst, based 226 /// on the information available in \p DC. 227 static void emitLatency(LLVMDisasmContext *DC, const MCInst &Inst) { 228 int Latency = getLatency(DC, Inst); 229 230 // Report only interesting latencies. 231 if (Latency < 2) 232 return; 233 234 DC->CommentStream << "Latency: " << Latency << '\n'; 235 } 236 237 // 238 // LLVMDisasmInstruction() disassembles a single instruction using the 239 // disassembler context specified in the parameter DC. The bytes of the 240 // instruction are specified in the parameter Bytes, and contains at least 241 // BytesSize number of bytes. The instruction is at the address specified by 242 // the PC parameter. If a valid instruction can be disassembled its string is 243 // returned indirectly in OutString which whos size is specified in the 244 // parameter OutStringSize. This function returns the number of bytes in the 245 // instruction or zero if there was no valid instruction. If this function 246 // returns zero the caller will have to pick how many bytes they want to step 247 // over by printing a .byte, .long etc. to continue. 248 // 249 size_t LLVMDisasmInstruction(LLVMDisasmContextRef DCR, uint8_t *Bytes, 250 uint64_t BytesSize, uint64_t PC, char *OutString, 251 size_t OutStringSize){ 252 LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); 253 // Wrap the pointer to the Bytes, BytesSize and PC in a MemoryObject. 254 ArrayRef<uint8_t> Data(Bytes, BytesSize); 255 256 uint64_t Size; 257 MCInst Inst; 258 const MCDisassembler *DisAsm = DC->getDisAsm(); 259 MCInstPrinter *IP = DC->getIP(); 260 MCDisassembler::DecodeStatus S; 261 SmallVector<char, 64> InsnStr; 262 raw_svector_ostream Annotations(InsnStr); 263 S = DisAsm->getInstruction(Inst, Size, Data, PC, 264 /*REMOVE*/ nulls(), Annotations); 265 switch (S) { 266 case MCDisassembler::Fail: 267 case MCDisassembler::SoftFail: 268 // FIXME: Do something different for soft failure modes? 269 return 0; 270 271 case MCDisassembler::Success: { 272 StringRef AnnotationsStr = Annotations.str(); 273 274 SmallVector<char, 64> InsnStr; 275 raw_svector_ostream OS(InsnStr); 276 formatted_raw_ostream FormattedOS(OS); 277 IP->printInst(&Inst, FormattedOS, AnnotationsStr, *DC->getSubtargetInfo()); 278 279 if (DC->getOptions() & LLVMDisassembler_Option_PrintLatency) 280 emitLatency(DC, Inst); 281 282 emitComments(DC, FormattedOS); 283 284 assert(OutStringSize != 0 && "Output buffer cannot be zero size"); 285 size_t OutputSize = std::min(OutStringSize-1, InsnStr.size()); 286 std::memcpy(OutString, InsnStr.data(), OutputSize); 287 OutString[OutputSize] = '\0'; // Terminate string. 288 289 return Size; 290 } 291 } 292 llvm_unreachable("Invalid DecodeStatus!"); 293 } 294 295 // 296 // LLVMSetDisasmOptions() sets the disassembler's options. It returns 1 if it 297 // can set all the Options and 0 otherwise. 298 // 299 int LLVMSetDisasmOptions(LLVMDisasmContextRef DCR, uint64_t Options){ 300 if (Options & LLVMDisassembler_Option_UseMarkup){ 301 LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); 302 MCInstPrinter *IP = DC->getIP(); 303 IP->setUseMarkup(true); 304 DC->addOptions(LLVMDisassembler_Option_UseMarkup); 305 Options &= ~LLVMDisassembler_Option_UseMarkup; 306 } 307 if (Options & LLVMDisassembler_Option_PrintImmHex){ 308 LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); 309 MCInstPrinter *IP = DC->getIP(); 310 IP->setPrintImmHex(true); 311 DC->addOptions(LLVMDisassembler_Option_PrintImmHex); 312 Options &= ~LLVMDisassembler_Option_PrintImmHex; 313 } 314 if (Options & LLVMDisassembler_Option_AsmPrinterVariant){ 315 LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); 316 // Try to set up the new instruction printer. 317 const MCAsmInfo *MAI = DC->getAsmInfo(); 318 const MCInstrInfo *MII = DC->getInstrInfo(); 319 const MCRegisterInfo *MRI = DC->getRegisterInfo(); 320 int AsmPrinterVariant = MAI->getAssemblerDialect(); 321 AsmPrinterVariant = AsmPrinterVariant == 0 ? 1 : 0; 322 MCInstPrinter *IP = DC->getTarget()->createMCInstPrinter( 323 Triple(DC->getTripleName()), AsmPrinterVariant, *MAI, *MII, *MRI); 324 if (IP) { 325 DC->setIP(IP); 326 DC->addOptions(LLVMDisassembler_Option_AsmPrinterVariant); 327 Options &= ~LLVMDisassembler_Option_AsmPrinterVariant; 328 } 329 } 330 if (Options & LLVMDisassembler_Option_SetInstrComments) { 331 LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); 332 MCInstPrinter *IP = DC->getIP(); 333 IP->setCommentStream(DC->CommentStream); 334 DC->addOptions(LLVMDisassembler_Option_SetInstrComments); 335 Options &= ~LLVMDisassembler_Option_SetInstrComments; 336 } 337 if (Options & LLVMDisassembler_Option_PrintLatency) { 338 LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); 339 DC->addOptions(LLVMDisassembler_Option_PrintLatency); 340 Options &= ~LLVMDisassembler_Option_PrintLatency; 341 } 342 return (Options == 0); 343 } 344