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 | |