| 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/MCRegisterInfo.h" |
| 23 | #include "llvm/MC/MCSchedule.h" |
| 24 | #include "llvm/MC/MCSubtargetInfo.h" |
| 25 | #include "llvm/MC/MCTargetOptions.h" |
| 26 | #include "llvm/MC/TargetRegistry.h" |
| 27 | #include "llvm/Support/ErrorHandling.h" |
| 28 | #include "llvm/Support/FormattedStream.h" |
| 29 | #include "llvm/Support/raw_ostream.h" |
| 30 | #include "llvm/TargetParser/Triple.h" |
| 31 | #include <cassert> |
| 32 | #include <cstring> |
| 33 | |
| 34 | using namespace llvm; |
| 35 | |
| 36 | // LLVMCreateDisasm() creates a disassembler for the TripleName. Symbolic |
| 37 | // disassembly is supported by passing a block of information in the DisInfo |
| 38 | // parameter and specifying the TagType and callback functions as described in |
| 39 | // the header llvm-c/Disassembler.h . The pointer to the block and the |
| 40 | // functions can all be passed as NULL. If successful, this returns a |
| 41 | // disassembler context. If not, it returns NULL. |
| 42 | // |
| 43 | LLVMDisasmContextRef |
| 44 | LLVMCreateDisasmCPUFeatures(const char *TT, const char *CPU, |
| 45 | const char *Features, void *DisInfo, int TagType, |
| 46 | LLVMOpInfoCallback GetOpInfo, |
| 47 | LLVMSymbolLookupCallback SymbolLookUp) { |
| 48 | // Get the target. |
| 49 | std::string Error; |
| 50 | const Target *TheTarget = TargetRegistry::lookupTarget(TripleStr: TT, Error); |
| 51 | if (!TheTarget) |
| 52 | return nullptr; |
| 53 | |
| 54 | std::unique_ptr<const MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT)); |
| 55 | if (!MRI) |
| 56 | return nullptr; |
| 57 | |
| 58 | MCTargetOptions MCOptions; |
| 59 | // Get the assembler info needed to setup the MCContext. |
| 60 | std::unique_ptr<const MCAsmInfo> MAI( |
| 61 | TheTarget->createMCAsmInfo(MRI: *MRI, TheTriple: TT, Options: MCOptions)); |
| 62 | if (!MAI) |
| 63 | return nullptr; |
| 64 | |
| 65 | std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo()); |
| 66 | if (!MII) |
| 67 | return nullptr; |
| 68 | |
| 69 | std::unique_ptr<const MCSubtargetInfo> STI( |
| 70 | TheTarget->createMCSubtargetInfo(TheTriple: TT, CPU, Features)); |
| 71 | if (!STI) |
| 72 | return nullptr; |
| 73 | |
| 74 | // Set up the MCContext for creating symbols and MCExpr's. |
| 75 | std::unique_ptr<MCContext> Ctx( |
| 76 | new MCContext(Triple(TT), MAI.get(), MRI.get(), STI.get())); |
| 77 | if (!Ctx) |
| 78 | return nullptr; |
| 79 | |
| 80 | // Set up disassembler. |
| 81 | std::unique_ptr<MCDisassembler> DisAsm( |
| 82 | TheTarget->createMCDisassembler(STI: *STI, Ctx&: *Ctx)); |
| 83 | if (!DisAsm) |
| 84 | return nullptr; |
| 85 | |
| 86 | std::unique_ptr<MCRelocationInfo> RelInfo( |
| 87 | TheTarget->createMCRelocationInfo(TT, Ctx&: *Ctx)); |
| 88 | if (!RelInfo) |
| 89 | return nullptr; |
| 90 | |
| 91 | std::unique_ptr<MCSymbolizer> Symbolizer(TheTarget->createMCSymbolizer( |
| 92 | TT, GetOpInfo, SymbolLookUp, DisInfo, Ctx: Ctx.get(), RelInfo: std::move(RelInfo))); |
| 93 | DisAsm->setSymbolizer(std::move(Symbolizer)); |
| 94 | |
| 95 | // Set up the instruction printer. |
| 96 | int AsmPrinterVariant = MAI->getAssemblerDialect(); |
| 97 | std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( |
| 98 | T: Triple(TT), SyntaxVariant: AsmPrinterVariant, MAI: *MAI, MII: *MII, MRI: *MRI)); |
| 99 | if (!IP) |
| 100 | return nullptr; |
| 101 | |
| 102 | LLVMDisasmContext *DC = new LLVMDisasmContext( |
| 103 | TT, DisInfo, TagType, GetOpInfo, SymbolLookUp, TheTarget, std::move(MAI), |
| 104 | std::move(MRI), std::move(STI), std::move(MII), std::move(Ctx), |
| 105 | std::move(DisAsm), std::move(IP)); |
| 106 | if (!DC) |
| 107 | return nullptr; |
| 108 | |
| 109 | DC->setCPU(CPU); |
| 110 | return DC; |
| 111 | } |
| 112 | |
| 113 | LLVMDisasmContextRef |
| 114 | LLVMCreateDisasmCPU(const char *TT, const char *CPU, void *DisInfo, int TagType, |
| 115 | LLVMOpInfoCallback GetOpInfo, |
| 116 | LLVMSymbolLookupCallback SymbolLookUp) { |
| 117 | return LLVMCreateDisasmCPUFeatures(TT, CPU, Features: "" , DisInfo, TagType, GetOpInfo, |
| 118 | SymbolLookUp); |
| 119 | } |
| 120 | |
| 121 | LLVMDisasmContextRef LLVMCreateDisasm(const char *TT, void *DisInfo, |
| 122 | int TagType, LLVMOpInfoCallback GetOpInfo, |
| 123 | LLVMSymbolLookupCallback SymbolLookUp) { |
| 124 | return LLVMCreateDisasmCPUFeatures(TT, CPU: "" , Features: "" , DisInfo, TagType, GetOpInfo, |
| 125 | SymbolLookUp); |
| 126 | } |
| 127 | |
| 128 | // |
| 129 | // LLVMDisasmDispose() disposes of the disassembler specified by the context. |
| 130 | // |
| 131 | void LLVMDisasmDispose(LLVMDisasmContextRef DCR){ |
| 132 | LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); |
| 133 | delete DC; |
| 134 | } |
| 135 | |
| 136 | /// Emits the comments that are stored in \p DC comment stream. |
| 137 | /// Each comment in the comment stream must end with a newline. |
| 138 | static void (LLVMDisasmContext *DC, |
| 139 | formatted_raw_ostream &FormattedOS) { |
| 140 | // Flush the stream before taking its content. |
| 141 | StringRef = DC->CommentsToEmit.str(); |
| 142 | // Get the default information for printing a comment. |
| 143 | const MCAsmInfo *MAI = DC->getAsmInfo(); |
| 144 | StringRef = MAI->getCommentString(); |
| 145 | unsigned CommentColumn = MAI->getCommentColumn(); |
| 146 | bool IsFirst = true; |
| 147 | while (!Comments.empty()) { |
| 148 | if (!IsFirst) |
| 149 | FormattedOS << '\n'; |
| 150 | // Emit a line of comments. |
| 151 | FormattedOS.PadToColumn(NewCol: CommentColumn); |
| 152 | size_t Position = Comments.find(C: '\n'); |
| 153 | FormattedOS << CommentBegin << ' ' << Comments.substr(Start: 0, N: Position); |
| 154 | // Move after the newline character. |
| 155 | Comments = Comments.substr(Start: Position+1); |
| 156 | IsFirst = false; |
| 157 | } |
| 158 | FormattedOS.flush(); |
| 159 | |
| 160 | // Tell the comment stream that the vector changed underneath it. |
| 161 | DC->CommentsToEmit.clear(); |
| 162 | } |
| 163 | |
| 164 | /// Emits latency information in DC->CommentStream for \p Inst, based |
| 165 | /// on the information available in \p DC. |
| 166 | static void emitLatency(LLVMDisasmContext *DC, const MCInst &Inst) { |
| 167 | const MCSubtargetInfo *STI = DC->getSubtargetInfo(); |
| 168 | const MCInstrInfo *MCII = DC->getInstrInfo(); |
| 169 | const MCSchedModel &SCModel = STI->getSchedModel(); |
| 170 | int Latency = SCModel.computeInstrLatency(STI: *STI, MCII: *MCII, Inst); |
| 171 | |
| 172 | // Report only interesting latencies. |
| 173 | if (Latency < 2) |
| 174 | return; |
| 175 | |
| 176 | DC->CommentStream << "Latency: " << Latency << '\n'; |
| 177 | } |
| 178 | |
| 179 | // |
| 180 | // LLVMDisasmInstruction() disassembles a single instruction using the |
| 181 | // disassembler context specified in the parameter DC. The bytes of the |
| 182 | // instruction are specified in the parameter Bytes, and contains at least |
| 183 | // BytesSize number of bytes. The instruction is at the address specified by |
| 184 | // the PC parameter. If a valid instruction can be disassembled its string is |
| 185 | // returned indirectly in OutString which whos size is specified in the |
| 186 | // parameter OutStringSize. This function returns the number of bytes in the |
| 187 | // instruction or zero if there was no valid instruction. If this function |
| 188 | // returns zero the caller will have to pick how many bytes they want to step |
| 189 | // over by printing a .byte, .long etc. to continue. |
| 190 | // |
| 191 | size_t LLVMDisasmInstruction(LLVMDisasmContextRef DCR, uint8_t *Bytes, |
| 192 | uint64_t BytesSize, uint64_t PC, char *OutString, |
| 193 | size_t OutStringSize){ |
| 194 | LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); |
| 195 | // Wrap the pointer to the Bytes, BytesSize and PC in a MemoryObject. |
| 196 | ArrayRef<uint8_t> Data(Bytes, BytesSize); |
| 197 | |
| 198 | uint64_t Size; |
| 199 | MCInst Inst; |
| 200 | const MCDisassembler *DisAsm = DC->getDisAsm(); |
| 201 | MCInstPrinter *IP = DC->getIP(); |
| 202 | MCDisassembler::DecodeStatus S; |
| 203 | SmallVector<char, 64> InsnStr; |
| 204 | raw_svector_ostream Annotations(InsnStr); |
| 205 | S = DisAsm->getInstruction(Instr&: Inst, Size, Bytes: Data, Address: PC, CStream&: Annotations); |
| 206 | switch (S) { |
| 207 | case MCDisassembler::Fail: |
| 208 | case MCDisassembler::SoftFail: |
| 209 | // FIXME: Do something different for soft failure modes? |
| 210 | return 0; |
| 211 | |
| 212 | case MCDisassembler::Success: { |
| 213 | StringRef AnnotationsStr = Annotations.str(); |
| 214 | |
| 215 | SmallVector<char, 64> InsnStr; |
| 216 | raw_svector_ostream OS(InsnStr); |
| 217 | formatted_raw_ostream FormattedOS(OS); |
| 218 | |
| 219 | if (DC->getOptions() & LLVMDisassembler_Option_Color) { |
| 220 | FormattedOS.enable_colors(enable: true); |
| 221 | IP->setUseColor(true); |
| 222 | } |
| 223 | |
| 224 | IP->printInst(MI: &Inst, Address: PC, Annot: AnnotationsStr, STI: *DC->getSubtargetInfo(), |
| 225 | OS&: FormattedOS); |
| 226 | |
| 227 | if (DC->getOptions() & LLVMDisassembler_Option_PrintLatency) |
| 228 | emitLatency(DC, Inst); |
| 229 | |
| 230 | emitComments(DC, FormattedOS); |
| 231 | |
| 232 | assert(OutStringSize != 0 && "Output buffer cannot be zero size" ); |
| 233 | size_t OutputSize = std::min(a: OutStringSize-1, b: InsnStr.size()); |
| 234 | std::memcpy(dest: OutString, src: InsnStr.data(), n: OutputSize); |
| 235 | OutString[OutputSize] = '\0'; // Terminate string. |
| 236 | |
| 237 | return Size; |
| 238 | } |
| 239 | } |
| 240 | llvm_unreachable("Invalid DecodeStatus!" ); |
| 241 | } |
| 242 | |
| 243 | // |
| 244 | // LLVMSetDisasmOptions() sets the disassembler's options. It returns 1 if it |
| 245 | // can set all the Options and 0 otherwise. |
| 246 | // |
| 247 | int LLVMSetDisasmOptions(LLVMDisasmContextRef DCR, uint64_t Options){ |
| 248 | if (Options & LLVMDisassembler_Option_UseMarkup){ |
| 249 | LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); |
| 250 | MCInstPrinter *IP = DC->getIP(); |
| 251 | IP->setUseMarkup(true); |
| 252 | DC->addOptions(LLVMDisassembler_Option_UseMarkup); |
| 253 | Options &= ~LLVMDisassembler_Option_UseMarkup; |
| 254 | } |
| 255 | if (Options & LLVMDisassembler_Option_PrintImmHex){ |
| 256 | LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); |
| 257 | MCInstPrinter *IP = DC->getIP(); |
| 258 | IP->setPrintImmHex(true); |
| 259 | DC->addOptions(LLVMDisassembler_Option_PrintImmHex); |
| 260 | Options &= ~LLVMDisassembler_Option_PrintImmHex; |
| 261 | } |
| 262 | if (Options & LLVMDisassembler_Option_AsmPrinterVariant){ |
| 263 | LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); |
| 264 | // Try to set up the new instruction printer. |
| 265 | const MCAsmInfo *MAI = DC->getAsmInfo(); |
| 266 | const MCInstrInfo *MII = DC->getInstrInfo(); |
| 267 | const MCRegisterInfo *MRI = DC->getRegisterInfo(); |
| 268 | int AsmPrinterVariant = MAI->getAssemblerDialect(); |
| 269 | AsmPrinterVariant = AsmPrinterVariant == 0 ? 1 : 0; |
| 270 | MCInstPrinter *IP = DC->getTarget()->createMCInstPrinter( |
| 271 | T: Triple(DC->getTripleName()), SyntaxVariant: AsmPrinterVariant, MAI: *MAI, MII: *MII, MRI: *MRI); |
| 272 | if (IP) { |
| 273 | DC->setIP(IP); |
| 274 | DC->addOptions(LLVMDisassembler_Option_AsmPrinterVariant); |
| 275 | Options &= ~LLVMDisassembler_Option_AsmPrinterVariant; |
| 276 | } |
| 277 | } |
| 278 | if (Options & LLVMDisassembler_Option_SetInstrComments) { |
| 279 | LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); |
| 280 | MCInstPrinter *IP = DC->getIP(); |
| 281 | IP->setCommentStream(DC->CommentStream); |
| 282 | DC->addOptions(LLVMDisassembler_Option_SetInstrComments); |
| 283 | Options &= ~LLVMDisassembler_Option_SetInstrComments; |
| 284 | } |
| 285 | if (Options & LLVMDisassembler_Option_PrintLatency) { |
| 286 | LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); |
| 287 | DC->addOptions(LLVMDisassembler_Option_PrintLatency); |
| 288 | Options &= ~LLVMDisassembler_Option_PrintLatency; |
| 289 | } |
| 290 | if (Options & LLVMDisassembler_Option_Color) { |
| 291 | LLVMDisasmContext *DC = static_cast<LLVMDisasmContext *>(DCR); |
| 292 | DC->addOptions(LLVMDisassembler_Option_Color); |
| 293 | Options &= ~LLVMDisassembler_Option_Color; |
| 294 | } |
| 295 | return (Options == 0); |
| 296 | } |
| 297 | |