1
2
3
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
14
15
16
17static __thread cs_insn *cap_insn;
18
19
20
21
22
23
24static size_t CAPSTONE_API
25cap_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
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
47static const cs_opt_skipdata cap_skipdata_s390x = {
48 .mnemonic = ".byte",
49 .callback = cap_skipdata_s390x_cb
50};
51
52
53
54
55
56
57
58
59
60
61
62static cs_err cap_disas_start(disassemble_info *info, csh *handle)
63{
64 cs_mode cap_mode = info->cap_mode;
65 cs_err err;
66
67 cap_mode += (info->endian == BFD_ENDIAN_BIG ? CS_MODE_BIG_ENDIAN
68 : CS_MODE_LITTLE_ENDIAN);
69
70 err = cs_open(info->cap_arch, cap_mode, handle);
71 if (err != CS_ERR_OK) {
72 return err;
73 }
74
75
76 cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON);
77
78 switch (info->cap_arch) {
79 case CS_ARCH_SYSZ:
80 cs_option(*handle, CS_OPT_SKIPDATA_SETUP,
81 (uintptr_t)&cap_skipdata_s390x);
82 break;
83
84 case CS_ARCH_X86:
85
86
87
88
89
90 cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
91 break;
92 }
93
94
95 if (cap_insn == NULL) {
96 cap_insn = cs_malloc(*handle);
97 if (cap_insn == NULL) {
98 cs_close(handle);
99 return CS_ERR_MEM;
100 }
101 }
102 return CS_ERR_OK;
103}
104
105static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn,
106 int i, int n)
107{
108 fprintf_function print = info->fprintf_func;
109 FILE *stream = info->stream;
110
111 switch (info->cap_insn_unit) {
112 case 4:
113 if (info->endian == BFD_ENDIAN_BIG) {
114 for (; i < n; i += 4) {
115 print(stream, " %08x", ldl_be_p(insn->bytes + i));
116
117 }
118 } else {
119 for (; i < n; i += 4) {
120 print(stream, " %08x", ldl_le_p(insn->bytes + i));
121 }
122 }
123 break;
124
125 case 2:
126 if (info->endian == BFD_ENDIAN_BIG) {
127 for (; i < n; i += 2) {
128 print(stream, " %04x", lduw_be_p(insn->bytes + i));
129 }
130 } else {
131 for (; i < n; i += 2) {
132 print(stream, " %04x", lduw_le_p(insn->bytes + i));
133 }
134 }
135 break;
136
137 default:
138 for (; i < n; i++) {
139 print(stream, " %02x", insn->bytes[i]);
140 }
141 break;
142 }
143}
144
145static void cap_dump_insn(disassemble_info *info, cs_insn *insn)
146{
147 fprintf_function print = info->fprintf_func;
148 FILE *stream = info->stream;
149 int i, n, split;
150
151 print(stream, "0x%08" PRIx64 ": ", insn->address);
152
153 n = insn->size;
154 split = info->cap_insn_split;
155
156
157 cap_dump_insn_units(info, insn, 0, MIN(n, split));
158
159
160 if (n < split) {
161 int width = (split - n) / info->cap_insn_unit;
162 width *= (2 * info->cap_insn_unit + 1);
163 print(stream, "%*s", width, "");
164 }
165
166
167 print(stream, " %-8s %s\n", insn->mnemonic, insn->op_str);
168
169
170 for (i = split; i < n; i += split) {
171 print(stream, "0x%08" PRIx64 ": ", insn->address + i);
172 cap_dump_insn_units(info, insn, i, MIN(n, i + split));
173 print(stream, "\n");
174 }
175}
176
177
178bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size)
179{
180 uint8_t cap_buf[1024];
181 csh handle;
182 cs_insn *insn;
183 size_t csize = 0;
184
185 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
186 return false;
187 }
188 insn = cap_insn;
189
190 while (1) {
191 size_t tsize = MIN(sizeof(cap_buf) - csize, size);
192 const uint8_t *cbuf = cap_buf;
193
194 info->read_memory_func(pc + csize, cap_buf + csize, tsize, info);
195 csize += tsize;
196 size -= tsize;
197
198 while (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) {
199 cap_dump_insn(info, insn);
200 }
201
202
203 if (size != 0) {
204
205
206
207
208 if (csize != 0) {
209 memmove(cap_buf, cbuf, csize);
210 }
211 continue;
212 }
213
214
215
216
217
218 if (csize != 0) {
219 info->fprintf_func(info->stream,
220 "Disassembler disagrees with translator "
221 "over instruction decoding\n"
222 "Please report this to qemu-devel@nongnu.org\n");
223 }
224 break;
225 }
226
227 cs_close(&handle);
228 return true;
229}
230
231
232bool cap_disas_host(disassemble_info *info, void *code, size_t size)
233{
234 csh handle;
235 const uint8_t *cbuf;
236 cs_insn *insn;
237 uint64_t pc;
238
239 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
240 return false;
241 }
242 insn = cap_insn;
243
244 cbuf = code;
245 pc = (uintptr_t)code;
246
247 while (cs_disasm_iter(handle, &cbuf, &size, &pc, insn)) {
248 cap_dump_insn(info, insn);
249 }
250 if (size != 0) {
251 info->fprintf_func(info->stream,
252 "Disassembler disagrees with TCG over instruction encoding\n"
253 "Please report this to qemu-devel@nongnu.org\n");
254 }
255
256 cs_close(&handle);
257 return true;
258}
259
260
261bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count)
262{
263 uint8_t cap_buf[32];
264 csh handle;
265 cs_insn *insn;
266 size_t csize = 0;
267
268 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
269 return false;
270 }
271 insn = cap_insn;
272
273 while (1) {
274
275
276
277
278
279
280
281
282
283 uint64_t epc = QEMU_ALIGN_UP(pc + csize + 1, 1024);
284 size_t tsize = MIN(sizeof(cap_buf) - csize, epc - pc);
285 const uint8_t *cbuf = cap_buf;
286
287
288 assert(tsize != 0);
289 info->read_memory_func(pc + csize, cap_buf + csize, tsize, info);
290 csize += tsize;
291
292 if (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) {
293 cap_dump_insn(info, insn);
294 if (--count <= 0) {
295 break;
296 }
297 }
298 memmove(cap_buf, cbuf, csize);
299 }
300
301 cs_close(&handle);
302 return true;
303}
304
305
306bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size)
307{
308 uint8_t cap_buf[32];
309 const uint8_t *cbuf = cap_buf;
310 csh handle;
311
312 if (cap_disas_start(info, &handle) != CS_ERR_OK) {
313 return false;
314 }
315
316 assert(size < sizeof(cap_buf));
317 info->read_memory_func(pc, cap_buf, size, info);
318
319 if (cs_disasm_iter(handle, &cbuf, &size, &pc, cap_insn)) {
320 info->fprintf_func(info->stream, "%s %s",
321 cap_insn->mnemonic, cap_insn->op_str);
322 }
323
324 cs_close(&handle);
325 return true;
326}
327