disas: Split out capstone code to disas/capstone.c
[qemu.git] / disas / capstone.c
1 /*
2 * Interface to the capstone disassembler.
3 * SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
6 #include "qemu/osdep.h"
7 #include "qemu/bswap.h"
8 #include "disas/dis-asm.h"
9 #include "disas/capstone.h"
10
11
12 /*
13 * Temporary storage for the capstone library. This will be alloced via
14 * malloc with a size private to the library; thus there's no reason not
15 * to share this across calls and across host vs target disassembly.
16 */
17 static __thread cs_insn *cap_insn;
18
19 /*
20 * Initialize the Capstone library.
21 *
22 * ??? It would be nice to cache this. We would need one handle for the
23 * host and one for the target. For most targets we can reset specific
24 * parameters via cs_option(CS_OPT_MODE, new_mode), but we cannot change
25 * CS_ARCH_* in this way. Thus we would need to be able to close and
26 * re-open the target handle with a different arch for the target in order
27 * to handle AArch64 vs AArch32 mode switching.
28 */
29 static cs_err cap_disas_start(disassemble_info *info, csh *handle)
30 {
31 cs_mode cap_mode = info->cap_mode;
32 cs_err err;
33
34 cap_mode += (info->endian == BFD_ENDIAN_BIG ? CS_MODE_BIG_ENDIAN
35 : CS_MODE_LITTLE_ENDIAN);
36
37 err = cs_open(info->cap_arch, cap_mode, handle);
38 if (err != CS_ERR_OK) {
39 return err;
40 }
41
42 /* "Disassemble" unknown insns as ".byte W,X,Y,Z". */
43 cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON);
44
45 if (info->cap_arch == CS_ARCH_X86) {
46 /*
47 * We don't care about errors (if for some reason the library
48 * is compiled without AT&T syntax); the user will just have
49 * to deal with the Intel syntax.
50 */
51 cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
52 }
53
54 /* Allocate temp space for cs_disasm_iter. */
55 if (cap_insn == NULL) {
56 cap_insn = cs_malloc(*handle);
57 if (cap_insn == NULL) {
58 cs_close(handle);
59 return CS_ERR_MEM;
60 }
61 }
62 return CS_ERR_OK;
63 }
64
65 static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn,
66 int i, int n)
67 {
68 fprintf_function print = info->fprintf_func;
69 FILE *stream = info->stream;
70
71 switch (info->cap_insn_unit) {
72 case 4:
73 if (info->endian == BFD_ENDIAN_BIG) {
74 for (; i < n; i += 4) {
75 print(stream, " %08x", ldl_be_p(insn->bytes + i));
76
77 }
78 } else {
79 for (; i < n; i += 4) {
80 print(stream, " %08x", ldl_le_p(insn->bytes + i));
81 }
82 }
83 break;
84
85 case 2:
86 if (info->endian == BFD_ENDIAN_BIG) {
87 for (; i < n; i += 2) {
88 print(stream, " %04x", lduw_be_p(insn->bytes + i));
89 }
90 } else {
91 for (; i < n; i += 2) {
92 print(stream, " %04x", lduw_le_p(insn->bytes + i));
93 }
94 }
95 break;
96
97 default:
98 for (; i < n; i++) {
99 print(stream, " %02x", insn->bytes[i]);
100 }
101 break;
102 }
103 }
104
105 static void cap_dump_insn(disassemble_info *info, cs_insn *insn)
106 {
107 fprintf_function print = info->fprintf_func;
108 FILE *stream = info->stream;
109 int i, n, split;
110
111 print(stream, "0x%08" PRIx64 ": ", insn->address);
112
113 n = insn->size;
114 split = info->cap_insn_split;
115
116 /* Dump the first SPLIT bytes of the instruction. */
117 cap_dump_insn_units(info, insn, 0, MIN(n, split));
118
119 /* Add padding up to SPLIT so that mnemonics line up. */
120 if (n < split) {
121 int width = (split - n) / info->cap_insn_unit;
122 width *= (2 * info->cap_insn_unit + 1);
123 print(stream, "%*s", width, "");
124 }
125
126 /* Print the actual instruction. */
127 print(stream, " %-8s %s\n", insn->mnemonic, insn->op_str);
128
129 /* Dump any remaining part of the insn on subsequent lines. */
130 for (i = split; i < n; i += split) {
131 print(stream, "0x%08" PRIx64 ": ", insn->address + i);
132 cap_dump_insn_units(info, insn, i, MIN(n, i + split));
133 print(stream, "\n");
134 }
135 }
136
137 /* Disassemble SIZE bytes at PC for the target. */
138 bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size)
139 {
140 uint8_t cap_buf[1024];
141 csh handle;
142 cs_insn *insn;
143 size_t csize = 0;
144
145 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
146 return false;
147 }
148 insn = cap_insn;
149
150 while (1) {
151 size_t tsize = MIN(sizeof(cap_buf) - csize, size);
152 const uint8_t *cbuf = cap_buf;
153
154 info->read_memory_func(pc + csize, cap_buf + csize, tsize, info);
155 csize += tsize;
156 size -= tsize;
157
158 while (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) {
159 cap_dump_insn(info, insn);
160 }
161
162 /* If the target memory is not consumed, go back for more... */
163 if (size != 0) {
164 /*
165 * ... taking care to move any remaining fractional insn
166 * to the beginning of the buffer.
167 */
168 if (csize != 0) {
169 memmove(cap_buf, cbuf, csize);
170 }
171 continue;
172 }
173
174 /*
175 * Since the target memory is consumed, we should not have
176 * a remaining fractional insn.
177 */
178 if (csize != 0) {
179 info->fprintf_func(info->stream,
180 "Disassembler disagrees with translator "
181 "over instruction decoding\n"
182 "Please report this to qemu-devel@nongnu.org\n");
183 }
184 break;
185 }
186
187 cs_close(&handle);
188 return true;
189 }
190
191 /* Disassemble SIZE bytes at CODE for the host. */
192 bool cap_disas_host(disassemble_info *info, void *code, size_t size)
193 {
194 csh handle;
195 const uint8_t *cbuf;
196 cs_insn *insn;
197 uint64_t pc;
198
199 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
200 return false;
201 }
202 insn = cap_insn;
203
204 cbuf = code;
205 pc = (uintptr_t)code;
206
207 while (cs_disasm_iter(handle, &cbuf, &size, &pc, insn)) {
208 cap_dump_insn(info, insn);
209 }
210 if (size != 0) {
211 info->fprintf_func(info->stream,
212 "Disassembler disagrees with TCG over instruction encoding\n"
213 "Please report this to qemu-devel@nongnu.org\n");
214 }
215
216 cs_close(&handle);
217 return true;
218 }
219
220 /* Disassemble COUNT insns at PC for the target. */
221 bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count)
222 {
223 uint8_t cap_buf[32];
224 csh handle;
225 cs_insn *insn;
226 size_t csize = 0;
227
228 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
229 return false;
230 }
231 insn = cap_insn;
232
233 while (1) {
234 /*
235 * We want to read memory for one insn, but generically we do not
236 * know how much memory that is. We have a small buffer which is
237 * known to be sufficient for all supported targets. Try to not
238 * read beyond the page, Just In Case. For even more simplicity,
239 * ignore the actual target page size and use a 1k boundary. If
240 * that turns out to be insufficient, we'll come back around the
241 * loop and read more.
242 */
243 uint64_t epc = QEMU_ALIGN_UP(pc + csize + 1, 1024);
244 size_t tsize = MIN(sizeof(cap_buf) - csize, epc - pc);
245 const uint8_t *cbuf = cap_buf;
246
247 /* Make certain that we can make progress. */
248 assert(tsize != 0);
249 info->read_memory_func(pc, cap_buf + csize, tsize, info);
250 csize += tsize;
251
252 if (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) {
253 cap_dump_insn(info, insn);
254 if (--count <= 0) {
255 break;
256 }
257 }
258 memmove(cap_buf, cbuf, csize);
259 }
260
261 cs_close(&handle);
262 return true;
263 }
264
265 /* Disassemble a single instruction directly into plugin output */
266 bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size)
267 {
268 uint8_t cap_buf[32];
269 const uint8_t *cbuf = cap_buf;
270 csh handle;
271
272 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
273 return false;
274 }
275
276 assert(size < sizeof(cap_buf));
277 info->read_memory_func(pc, cap_buf, size, info);
278
279 if (cs_disasm_iter(handle, &cbuf, &size, &pc, cap_insn)) {
280 info->fprintf_func(info->stream, "%s %s",
281 cap_insn->mnemonic, cap_insn->op_str);
282 }
283
284 cs_close(&handle);
285 return true;
286 }