From f343346b147b8871d628defa4fe038eb1a2e4e08 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 12 Sep 2020 10:47:33 -0700 Subject: disas: Split out capstone code to disas/capstone.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is nothing target-specific about this code, so it can be added to common_ss. This also requires that the base capstone dependency be added to common_ss, so that we get the correct include paths added to CFLAGS. Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- disas.c | 275 ---------------------------------------------------------------- 1 file changed, 275 deletions(-) (limited to 'disas.c') diff --git a/disas.c b/disas.c index cafd063d42..9dfbeededc 100644 --- a/disas.c +++ b/disas.c @@ -109,255 +109,6 @@ static int print_insn_od_target(bfd_vma pc, disassemble_info *info) return print_insn_objdump(pc, info, "OBJD-T"); } -#ifdef CONFIG_CAPSTONE -/* Temporary storage for the capstone library. This will be alloced via - malloc with a size private to the library; thus there's no reason not - to share this across calls and across host vs target disassembly. */ -static __thread cs_insn *cap_insn; - -/* Initialize the Capstone library. */ -/* ??? It would be nice to cache this. We would need one handle for the - host and one for the target. For most targets we can reset specific - parameters via cs_option(CS_OPT_MODE, new_mode), but we cannot change - CS_ARCH_* in this way. Thus we would need to be able to close and - re-open the target handle with a different arch for the target in order - to handle AArch64 vs AArch32 mode switching. */ -static cs_err cap_disas_start(disassemble_info *info, csh *handle) -{ - cs_mode cap_mode = info->cap_mode; - cs_err err; - - cap_mode += (info->endian == BFD_ENDIAN_BIG ? CS_MODE_BIG_ENDIAN - : CS_MODE_LITTLE_ENDIAN); - - err = cs_open(info->cap_arch, cap_mode, handle); - if (err != CS_ERR_OK) { - return err; - } - - /* ??? There probably ought to be a better place to put this. */ - if (info->cap_arch == CS_ARCH_X86) { - /* We don't care about errors (if for some reason the library - is compiled without AT&T syntax); the user will just have - to deal with the Intel syntax. */ - cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT); - } - - /* "Disassemble" unknown insns as ".byte W,X,Y,Z". */ - cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON); - - /* Allocate temp space for cs_disasm_iter. */ - if (cap_insn == NULL) { - cap_insn = cs_malloc(*handle); - if (cap_insn == NULL) { - cs_close(handle); - return CS_ERR_MEM; - } - } - return CS_ERR_OK; -} - -static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn, - int i, int n) -{ - fprintf_function print = info->fprintf_func; - FILE *stream = info->stream; - - switch (info->cap_insn_unit) { - case 4: - if (info->endian == BFD_ENDIAN_BIG) { - for (; i < n; i += 4) { - print(stream, " %08x", ldl_be_p(insn->bytes + i)); - - } - } else { - for (; i < n; i += 4) { - print(stream, " %08x", ldl_le_p(insn->bytes + i)); - } - } - break; - - case 2: - if (info->endian == BFD_ENDIAN_BIG) { - for (; i < n; i += 2) { - print(stream, " %04x", lduw_be_p(insn->bytes + i)); - } - } else { - for (; i < n; i += 2) { - print(stream, " %04x", lduw_le_p(insn->bytes + i)); - } - } - break; - - default: - for (; i < n; i++) { - print(stream, " %02x", insn->bytes[i]); - } - break; - } -} - -static void cap_dump_insn(disassemble_info *info, cs_insn *insn) -{ - fprintf_function print = info->fprintf_func; - int i, n, split; - - print(info->stream, "0x%08" PRIx64 ": ", insn->address); - - n = insn->size; - split = info->cap_insn_split; - - /* Dump the first SPLIT bytes of the instruction. */ - cap_dump_insn_units(info, insn, 0, MIN(n, split)); - - /* Add padding up to SPLIT so that mnemonics line up. */ - if (n < split) { - int width = (split - n) / info->cap_insn_unit; - width *= (2 * info->cap_insn_unit + 1); - print(info->stream, "%*s", width, ""); - } - - /* Print the actual instruction. */ - print(info->stream, " %-8s %s\n", insn->mnemonic, insn->op_str); - - /* Dump any remaining part of the insn on subsequent lines. */ - for (i = split; i < n; i += split) { - print(info->stream, "0x%08" PRIx64 ": ", insn->address + i); - cap_dump_insn_units(info, insn, i, MIN(n, i + split)); - print(info->stream, "\n"); - } -} - -/* Disassemble SIZE bytes at PC for the target. */ -static bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size) -{ - uint8_t cap_buf[1024]; - csh handle; - cs_insn *insn; - size_t csize = 0; - - if (cap_disas_start(info, &handle) != CS_ERR_OK) { - return false; - } - insn = cap_insn; - - while (1) { - size_t tsize = MIN(sizeof(cap_buf) - csize, size); - const uint8_t *cbuf = cap_buf; - - target_read_memory(pc + csize, cap_buf + csize, tsize, info); - csize += tsize; - size -= tsize; - - while (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { - cap_dump_insn(info, insn); - } - - /* If the target memory is not consumed, go back for more... */ - if (size != 0) { - /* ... taking care to move any remaining fractional insn - to the beginning of the buffer. */ - if (csize != 0) { - memmove(cap_buf, cbuf, csize); - } - continue; - } - - /* Since the target memory is consumed, we should not have - a remaining fractional insn. */ - if (csize != 0) { - (*info->fprintf_func)(info->stream, - "Disassembler disagrees with translator " - "over instruction decoding\n" - "Please report this to qemu-devel@nongnu.org\n"); - } - break; - } - - cs_close(&handle); - return true; -} - -/* Disassemble SIZE bytes at CODE for the host. */ -static bool cap_disas_host(disassemble_info *info, void *code, size_t size) -{ - csh handle; - const uint8_t *cbuf; - cs_insn *insn; - uint64_t pc; - - if (cap_disas_start(info, &handle) != CS_ERR_OK) { - return false; - } - insn = cap_insn; - - cbuf = code; - pc = (uintptr_t)code; - - while (cs_disasm_iter(handle, &cbuf, &size, &pc, insn)) { - cap_dump_insn(info, insn); - } - if (size != 0) { - (*info->fprintf_func)(info->stream, - "Disassembler disagrees with TCG over instruction encoding\n" - "Please report this to qemu-devel@nongnu.org\n"); - } - - cs_close(&handle); - return true; -} - -#if !defined(CONFIG_USER_ONLY) -/* Disassemble COUNT insns at PC for the target. */ -static bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count) -{ - uint8_t cap_buf[32]; - csh handle; - cs_insn *insn; - size_t csize = 0; - - if (cap_disas_start(info, &handle) != CS_ERR_OK) { - return false; - } - insn = cap_insn; - - while (1) { - /* We want to read memory for one insn, but generically we do not - know how much memory that is. We have a small buffer which is - known to be sufficient for all supported targets. Try to not - read beyond the page, Just In Case. For even more simplicity, - ignore the actual target page size and use a 1k boundary. If - that turns out to be insufficient, we'll come back around the - loop and read more. */ - uint64_t epc = QEMU_ALIGN_UP(pc + csize + 1, 1024); - size_t tsize = MIN(sizeof(cap_buf) - csize, epc - pc); - const uint8_t *cbuf = cap_buf; - - /* Make certain that we can make progress. */ - assert(tsize != 0); - info->read_memory_func(pc, cap_buf + csize, tsize, info); - csize += tsize; - - if (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { - cap_dump_insn(info, insn); - if (--count <= 0) { - break; - } - } - memmove(cap_buf, cbuf, csize); - } - - cs_close(&handle); - return true; -} -#endif /* !CONFIG_USER_ONLY */ -#else -# define cap_disas_target(i, p, s) false -# define cap_disas_host(i, p, s) false -# define cap_disas_monitor(i, p, c) false -# define cap_disas_plugin(i, p, c) false -#endif /* CONFIG_CAPSTONE */ - static void initialize_debug(CPUDebug *s) { memset(s, 0, sizeof(*s)); @@ -515,32 +266,6 @@ static void plugin_print_address(bfd_vma addr, struct disassemble_info *info) } -#ifdef CONFIG_CAPSTONE -/* Disassemble a single instruction directly into plugin output */ -static -bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size) -{ - uint8_t cap_buf[64]; - const uint8_t *cbuf = cap_buf; - csh handle; - - if (cap_disas_start(info, &handle) != CS_ERR_OK) { - return false; - } - - assert(size < sizeof(cap_buf)); - target_read_memory(pc, cap_buf, size, info); - - if (cs_disasm_iter(handle, &cbuf, &size, &pc, cap_insn)) { - GString *s = (GString *)info->stream; - g_string_printf(s, "%s %s", cap_insn->mnemonic, cap_insn->op_str); - } - - cs_close(&handle); - return true; -} -#endif - /* * We should only be dissembling one instruction at a time here. If * there is left over it usually indicates the front end has read more -- cgit v1.2.3