master
c 364 lines 10.2 KB
Raw
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 * The capstone library always skips 2 bytes for S390X.
21 * This is less than ideal, since we can tell from the first two bits
22 * the size of the insn and thus stay in sync with the insn stream.
23 */
24 static size_t CAPSTONE_API
25 cap_skipdata_s390x_cb(const uint8_t *code, size_t code_size,
26 size_t offset, void *user_data)
27 {
28 size_t ilen;
29
30 /* See get_ilen() in target/s390x/internal.h. */
31 switch (code[offset] >> 6) {
32 case 0:
33 ilen = 2;
34 break;
35 case 1:
36 case 2:
37 ilen = 4;
38 break;
39 default:
40 ilen = 6;
41 break;
42 }
43
44 return ilen;
45 }
46
47 static const cs_opt_skipdata cap_skipdata_s390x = {
48 .mnemonic = ".byte",
49 .callback = cap_skipdata_s390x_cb
50 };
51
52 /* Similarly for RISCV */
53 static size_t CAPSTONE_API
54 cap_skipdata_riscv_cb(const uint8_t *code, size_t code_size,
55 size_t offset, void *user_data)
56 {
57 /* See insn_len() from target/riscv/internals.h */
58 return (code[offset] & 3) == 3 ? 4 : 2;
59 }
60
61 static const cs_opt_skipdata cap_skipdata_riscv = {
62 .mnemonic = ".byte",
63 .callback = cap_skipdata_riscv_cb
64 };
65
66 /*
67 * Initialize the Capstone library.
68 *
69 * ??? It would be nice to cache this. We would need one handle for the
70 * host and one for the target. For most targets we can reset specific
71 * parameters via cs_option(CS_OPT_MODE, new_mode), but we cannot change
72 * CS_ARCH_* in this way. Thus we would need to be able to close and
73 * re-open the target handle with a different arch for the target in order
74 * to handle AArch64 vs AArch32 mode switching.
75 */
76 static cs_err cap_disas_start(disassemble_info *info, csh *handle)
77 {
78 cs_mode cap_mode = info->cap_mode;
79 cs_err err;
80
81 cap_mode += (info->endian == BFD_ENDIAN_BIG ? CS_MODE_BIG_ENDIAN
82 : CS_MODE_LITTLE_ENDIAN);
83
84 err = cs_open(info->cap_arch, cap_mode, handle);
85 if (err != CS_ERR_OK) {
86 return err;
87 }
88
89 /* "Disassemble" unknown insns as ".byte W,X,Y,Z". */
90 cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON);
91
92 switch (info->cap_arch) {
93 case CS_ARCH_RISCV:
94 cs_option(*handle, CS_OPT_SKIPDATA_SETUP,
95 (uintptr_t)&cap_skipdata_riscv);
96 break;
97
98 case CS_ARCH_SYSTEMZ:
99 cs_option(*handle, CS_OPT_SKIPDATA_SETUP,
100 (uintptr_t)&cap_skipdata_s390x);
101 break;
102
103 case CS_ARCH_X86:
104 /*
105 * We don't care about errors (if for some reason the library
106 * is compiled without AT&T syntax); the user will just have
107 * to deal with the Intel syntax.
108 */
109 cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
110 break;
111 }
112
113 /* Allocate temp space for cs_disasm_iter. */
114 if (cap_insn == NULL) {
115 cap_insn = cs_malloc(*handle);
116 if (cap_insn == NULL) {
117 cs_close(handle);
118 return CS_ERR_MEM;
119 }
120 }
121 return CS_ERR_OK;
122 }
123
124 static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn,
125 int i, int n)
126 {
127 fprintf_function print = info->fprintf_func;
128 FILE *stream = info->stream;
129 int unit = MIN(info->cap_insn_unit, n - i);
130
131 switch (unit) {
132 case 4:
133 if (info->endian == BFD_ENDIAN_BIG) {
134 for (; i < n; i += 4) {
135 print(stream, " %08x", ldl_be_p(insn->bytes + i));
136
137 }
138 } else {
139 for (; i < n; i += 4) {
140 print(stream, " %08x", ldl_le_p(insn->bytes + i));
141 }
142 }
143 break;
144
145 case 2:
146 if (info->endian == BFD_ENDIAN_BIG) {
147 for (; i < n; i += 2) {
148 print(stream, " %04x", lduw_be_p(insn->bytes + i));
149 }
150 } else {
151 for (; i < n; i += 2) {
152 print(stream, " %04x", lduw_le_p(insn->bytes + i));
153 }
154 }
155 break;
156
157 default:
158 for (; i < n; i++) {
159 print(stream, " %02x", insn->bytes[i]);
160 }
161 break;
162 }
163
164 if (unit < info->cap_insn_unit) {
165 int width = (info->cap_insn_unit - unit) * 2;
166 print(stream, "%*s", width, "");
167 }
168 }
169
170 static void cap_dump_insn(disassemble_info *info, cs_insn *insn)
171 {
172 fprintf_function print = info->fprintf_func;
173 FILE *stream = info->stream;
174 int i, n, split;
175
176 print(stream, "0x%08" PRIx64 ": ", insn->address);
177
178 n = insn->size;
179 split = info->cap_insn_split;
180
181 /* Dump the first SPLIT bytes of the instruction. */
182 cap_dump_insn_units(info, insn, 0, MIN(n, split));
183
184 /* Add padding up to SPLIT so that mnemonics line up. */
185 if (n < split) {
186 int width = (split - n) / info->cap_insn_unit;
187 width *= (2 * info->cap_insn_unit + 1);
188 print(stream, "%*s", width, "");
189 }
190
191 /* Print the actual instruction. */
192 print(stream, " %-8s %s\n", insn->mnemonic, insn->op_str);
193
194 /* Dump any remaining part of the insn on subsequent lines. */
195 for (i = split; i < n; i += split) {
196 print(stream, "0x%08" PRIx64 ": ", insn->address + i);
197 cap_dump_insn_units(info, insn, i, MIN(n, i + split));
198 print(stream, "\n");
199 }
200 }
201
202 /* Disassemble SIZE bytes at PC for the target. */
203 bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size)
204 {
205 uint8_t cap_buf[1024];
206 csh handle;
207 cs_insn *insn;
208 size_t csize = 0;
209
210 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
211 return false;
212 }
213 insn = cap_insn;
214
215 while (1) {
216 size_t tsize = MIN(sizeof(cap_buf) - csize, size);
217 const uint8_t *cbuf = cap_buf;
218
219 if (info->read_memory_func(pc + csize, cap_buf + csize, tsize, info) == 0) {
220 csize += tsize;
221 size -= tsize;
222
223 while (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) {
224 cap_dump_insn(info, insn);
225 }
226
227 /* If the target memory is not consumed, go back for more... */
228 if (size != 0) {
229 /*
230 * ... taking care to move any remaining fractional insn
231 * to the beginning of the buffer.
232 */
233 if (csize != 0) {
234 memmove(cap_buf, cbuf, csize);
235 }
236 continue;
237 }
238
239 /*
240 * Since the target memory is consumed, we should not have
241 * a remaining fractional insn.
242 */
243 if (csize != 0) {
244 info->fprintf_func(info->stream,
245 "Disassembler disagrees with translator "
246 "over instruction decoding\n"
247 "Please report this to qemu-devel@nongnu.org\n");
248 }
249 break;
250
251 } else {
252 info->fprintf_func(info->stream,
253 "0x%08" PRIx64 ": unable to read memory\n", pc);
254 break;
255 }
256 }
257
258 cs_close(&handle);
259 return true;
260 }
261
262 /* Disassemble SIZE bytes at CODE for the host. */
263 bool cap_disas_host(disassemble_info *info, const void *code, size_t size)
264 {
265 csh handle;
266 const uint8_t *cbuf;
267 cs_insn *insn;
268 uint64_t pc;
269
270 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
271 return false;
272 }
273 insn = cap_insn;
274
275 cbuf = code;
276 pc = (uintptr_t)code;
277
278 while (cs_disasm_iter(handle, &cbuf, &size, &pc, insn)) {
279 cap_dump_insn(info, insn);
280 }
281 if (size != 0) {
282 info->fprintf_func(info->stream,
283 "Disassembler disagrees with TCG over instruction encoding\n"
284 "Please report this to qemu-devel@nongnu.org\n");
285 }
286
287 cs_close(&handle);
288 return true;
289 }
290
291 /* Disassemble COUNT insns at PC for the target. */
292 bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count)
293 {
294 uint8_t cap_buf[32];
295 csh handle;
296 cs_insn *insn;
297 size_t csize = 0;
298
299 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
300 return false;
301 }
302 insn = cap_insn;
303
304 while (1) {
305 /*
306 * We want to read memory for one insn, but generically we do not
307 * know how much memory that is. We have a small buffer which is
308 * known to be sufficient for all supported targets. Try to not
309 * read beyond the page, Just In Case. For even more simplicity,
310 * ignore the actual target page size and use a 1k boundary. If
311 * that turns out to be insufficient, we'll come back around the
312 * loop and read more.
313 */
314 uint64_t epc = QEMU_ALIGN_UP(pc + csize + 1, 1024);
315 size_t tsize = MIN(sizeof(cap_buf) - csize, epc - pc);
316 const uint8_t *cbuf = cap_buf;
317
318 /* Make certain that we can make progress. */
319 assert(tsize != 0);
320 if (info->read_memory_func(pc + csize, cap_buf + csize,
321 tsize, info) == 0)
322 {
323 csize += tsize;
324
325 if (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) {
326 cap_dump_insn(info, insn);
327 if (--count <= 0) {
328 break;
329 }
330 }
331 memmove(cap_buf, cbuf, csize);
332 } else {
333 info->fprintf_func(info->stream,
334 "0x%08" PRIx64 ": unable to read memory\n", pc);
335 break;
336 }
337 }
338
339 cs_close(&handle);
340 return true;
341 }
342
343 /* Disassemble a single instruction directly into plugin output */
344 bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size)
345 {
346 uint8_t cap_buf[32];
347 const uint8_t *cbuf = cap_buf;
348 csh handle;
349
350 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
351 return false;
352 }
353
354 assert(size < sizeof(cap_buf));
355 info->read_memory_func(pc, cap_buf, size, info);
356
357 if (cs_disasm_iter(handle, &cbuf, &size, &pc, cap_insn)) {
358 info->fprintf_func(info->stream, "%s %s",
359 cap_insn->mnemonic, cap_insn->op_str);
360 }
361
362 cs_close(&handle);
363 return true;
364 }