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