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