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