diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2020-10-05 11:25:55 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-10-05 11:25:55 +0100 |
commit | 671ad7c4468f795b66b4cd8f376f1b1ce6701b63 (patch) | |
tree | a5b9838f62c5902965f82df211c2a018ae4b7a84 | |
parent | 469e72ab7dbbd7ff4ee601e5ea7c29545d46593b (diff) | |
parent | c6d3da962f058bca09b25f99da35436816fb6de8 (diff) |
Merge remote-tracking branch 'remotes/rth-gitlab/tags/pull-cap-20201003' into staging
Update capstone submodule from v3.0.5 to v5 ("next").
Convert submodule build to meson.
Enable capstone disassembly for s390x.
Code cleanups in disas.c
# gpg: Signature made Sat 03 Oct 2020 10:33:44 BST
# gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F
# gpg: issuer "richard.henderson@linaro.org"
# gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [full]
# Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F
* remotes/rth-gitlab/tags/pull-cap-20201003:
disas/capstone: Add skipdata hook for s390x
disas: Enable capstone disassembly for s390x
disas: Split out capstone code to disas/capstone.c
disas: Configure capstone for aarch64 host without libvixl
disas: Cleanup plugin_disas
disas: Use qemu/bswap.h for bfd endian loads
disas: Clean up CPUDebug initialization
disas: Move host asm annotations to tb_gen_code
capstone: Require version 4.0 from a system library
capstone: Update to upstream "next" branch
capstone: Convert Makefile bits to meson bits
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | Makefile | 18 | ||||
-rw-r--r-- | accel/tcg/translate-all.c | 24 | ||||
m--------- | capstone | 0 | ||||
-rwxr-xr-x | configure | 68 | ||||
-rw-r--r-- | disas.c | 695 | ||||
-rw-r--r-- | disas/capstone.c | 326 | ||||
-rw-r--r-- | disas/meson.build | 1 | ||||
-rw-r--r-- | include/disas/dis-asm.h | 104 | ||||
-rw-r--r-- | include/disas/disas.h | 2 | ||||
-rw-r--r-- | include/exec/log.h | 4 | ||||
-rw-r--r-- | meson.build | 123 | ||||
-rw-r--r-- | meson_options.txt | 4 | ||||
-rw-r--r-- | target/s390x/cpu.c | 4 | ||||
-rw-r--r-- | tcg/tcg.c | 4 |
14 files changed, 679 insertions, 698 deletions
@@ -156,21 +156,11 @@ dtc/all: .git-submodule-status dtc/libfdt dtc/%: .git-submodule-status @mkdir -p $@ -# Overriding CFLAGS causes us to lose defines added in the sub-makefile. -# Not overriding CFLAGS leads to mis-matches between compilation modes. -# Therefore we replicate some of the logic in the sub-makefile. -# Remove all the extra -Warning flags that QEMU uses that Capstone doesn't; -# no need to annoy QEMU developers with such things. -CAP_CFLAGS = $(patsubst -W%,,$(CFLAGS) $(QEMU_CFLAGS)) $(CAPSTONE_CFLAGS) -CAP_CFLAGS += -DCAPSTONE_USE_SYS_DYN_MEM -CAP_CFLAGS += -DCAPSTONE_HAS_ARM -CAP_CFLAGS += -DCAPSTONE_HAS_ARM64 -CAP_CFLAGS += -DCAPSTONE_HAS_POWERPC -CAP_CFLAGS += -DCAPSTONE_HAS_X86 - +# Retain for a while so that incremental build across this patch +# does not raise an error for missing target "capstone/all", which +# comes from the saved SUBDIRS value. .PHONY: capstone/all -capstone/all: .git-submodule-status - $(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE)) +capstone/all: .PHONY: slirp/all slirp/all: .git-submodule-status diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index f44ba9d46d..e82fc35b32 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1816,10 +1816,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu, qemu_log_in_addr_range(tb->pc)) { FILE *logfile = qemu_log_lock(); int code_size, data_size = 0; - g_autoptr(GString) note = g_string_new("[tb header & initial instruction]"); - size_t chunk_start = 0; + size_t chunk_start; int insn = 0; - qemu_log("OUT: [size=%d]\n", gen_code_size); + if (tcg_ctx->data_gen_ptr) { code_size = tcg_ctx->data_gen_ptr - tb->tc.ptr; data_size = gen_code_size - code_size; @@ -1828,26 +1827,33 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } /* Dump header and the first instruction */ + qemu_log("OUT: [size=%d]\n", gen_code_size); + qemu_log(" -- guest addr 0x" TARGET_FMT_lx " + tb prologue\n", + tcg_ctx->gen_insn_data[insn][0]); chunk_start = tcg_ctx->gen_insn_end_off[insn]; - log_disas(tb->tc.ptr, chunk_start, note->str); + log_disas(tb->tc.ptr, chunk_start); /* * Dump each instruction chunk, wrapping up empty chunks into * the next instruction. The whole array is offset so the * first entry is the beginning of the 2nd instruction. */ - while (insn <= tb->icount && chunk_start < code_size) { + while (insn < tb->icount) { size_t chunk_end = tcg_ctx->gen_insn_end_off[insn]; if (chunk_end > chunk_start) { - g_string_printf(note, "[guest addr: " TARGET_FMT_lx "]", - tcg_ctx->gen_insn_data[insn][0]); - log_disas(tb->tc.ptr + chunk_start, chunk_end - chunk_start, - note->str); + qemu_log(" -- guest addr 0x" TARGET_FMT_lx "\n", + tcg_ctx->gen_insn_data[insn][0]); + log_disas(tb->tc.ptr + chunk_start, chunk_end - chunk_start); chunk_start = chunk_end; } insn++; } + if (chunk_start < code_size) { + qemu_log(" -- tb slow paths + alignment\n"); + log_disas(tb->tc.ptr + chunk_start, code_size - chunk_start); + } + /* Finally dump any data we may have after the block */ if (data_size) { int i; diff --git a/capstone b/capstone -Subproject 22ead3e0bfdb87516656453336160e0a37b066b +Subproject f8b1b833015a4ae47110ed068e0deb7106ced66 @@ -478,7 +478,7 @@ opengl="" opengl_dmabuf="no" cpuid_h="no" avx2_opt="" -capstone="" +capstone="auto" lzo="" snappy="" bzip2="" @@ -1575,11 +1575,11 @@ for opt do ;; --enable-vhost-kernel) vhost_kernel="yes" ;; - --disable-capstone) capstone="no" + --disable-capstone) capstone="disabled" ;; - --enable-capstone) capstone="yes" + --enable-capstone) capstone="enabled" ;; - --enable-capstone=git) capstone="git" + --enable-capstone=git) capstone="internal" ;; --enable-capstone=system) capstone="system" ;; @@ -5017,51 +5017,11 @@ fi # capstone case "$capstone" in - "" | yes) - if $pkg_config capstone; then - capstone=system - elif test -e "${source_path}/.git" && test $git_update = 'yes' ; then - capstone=git - elif test -e "${source_path}/capstone/Makefile" ; then - capstone=internal - elif test -z "$capstone" ; then - capstone=no - else - feature_not_found "capstone" "Install capstone devel or git submodule" - fi - ;; - - system) - if ! $pkg_config capstone; then - feature_not_found "capstone" "Install capstone devel" - fi - ;; -esac - -case "$capstone" in - git | internal) - if test "$capstone" = git; then + auto | enabled | internal) + # Simpler to always update submodule, even if not needed. + if test -e "${source_path}/.git" && test $git_update = 'yes' ; then git_submodules="${git_submodules} capstone" fi - mkdir -p capstone - if test "$mingw32" = "yes"; then - LIBCAPSTONE=capstone.lib - else - LIBCAPSTONE=libcapstone.a - fi - capstone_libs="-Lcapstone -lcapstone" - capstone_cflags="-I${source_path}/capstone/include" - ;; - - system) - capstone_libs="$($pkg_config --libs capstone)" - capstone_cflags="$($pkg_config --cflags capstone)" - ;; - - no) - ;; - *) - error_exit "Unknown state for capstone: $capstone" ;; esac @@ -7142,11 +7102,6 @@ fi if test "$ivshmem" = "yes" ; then echo "CONFIG_IVSHMEM=y" >> $config_host_mak fi -if test "$capstone" != "no" ; then - echo "CONFIG_CAPSTONE=y" >> $config_host_mak - echo "CAPSTONE_CFLAGS=$capstone_cflags" >> $config_host_mak - echo "CAPSTONE_LIBS=$capstone_libs" >> $config_host_mak -fi if test "$debug_mutex" = "yes" ; then echo "CONFIG_DEBUG_MUTEX=y" >> $config_host_mak fi @@ -7664,13 +7619,7 @@ done # for target in $targets if [ "$fdt" = "git" ]; then subdirs="$subdirs dtc" fi -if [ "$capstone" = "git" -o "$capstone" = "internal" ]; then - subdirs="$subdirs capstone" -fi echo "SUBDIRS=$subdirs" >> $config_host_mak -if test -n "$LIBCAPSTONE"; then - echo "LIBCAPSTONE=$LIBCAPSTONE" >> $config_host_mak -fi if test "$numa" = "yes"; then echo "CONFIG_NUMA=y" >> $config_host_mak @@ -7846,7 +7795,8 @@ NINJA=${ninja:-$PWD/ninjatool} $meson setup \ -Dmalloc=$malloc -Dmalloc_trim=$malloc_trim \ -Dcocoa=$cocoa -Dmpath=$mpath -Dsdl=$sdl -Dsdl_image=$sdl_image \ -Dvnc=$vnc -Dvnc_sasl=$vnc_sasl -Dvnc_jpeg=$vnc_jpeg -Dvnc_png=$vnc_png \ - -Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f\ + -Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f \ + -Dcapstone=$capstone \ $cross_arg \ "$PWD" "$source_path" @@ -16,133 +16,68 @@ typedef struct CPUDebug { /* Filled in by elfload.c. Simplistic, but will do for now. */ struct syminfo *syminfos = NULL; -/* Get LENGTH bytes from info's buffer, at target address memaddr. - Transfer them to myaddr. */ -int -buffer_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, - struct disassemble_info *info) +/* + * Get LENGTH bytes from info's buffer, at host address memaddr. + * Transfer them to myaddr. + */ +static int host_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) { if (memaddr < info->buffer_vma - || memaddr + length > info->buffer_vma + info->buffer_length) + || memaddr + length > info->buffer_vma + info->buffer_length) { /* Out of bounds. Use EIO because GDB uses it. */ return EIO; + } memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length); return 0; } -/* Get LENGTH bytes from info's buffer, at target address memaddr. - Transfer them to myaddr. */ -static int -target_read_memory (bfd_vma memaddr, - bfd_byte *myaddr, - int length, - struct disassemble_info *info) +/* + * Get LENGTH bytes from info's buffer, at target address memaddr. + * Transfer them to myaddr. + */ +static int target_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) { CPUDebug *s = container_of(info, CPUDebug, info); - int r; - - r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); - + int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); return r ? EIO : 0; } -/* Print an error message. We can assume that this is in response to - an error return from buffer_read_memory. */ -void -perror_memory (int status, bfd_vma memaddr, struct disassemble_info *info) +/* + * Print an error message. We can assume that this is in response to + * an error return from {host,target}_read_memory. + */ +static void perror_memory(int status, bfd_vma memaddr, + struct disassemble_info *info) { - if (status != EIO) - /* Can't happen. */ - (*info->fprintf_func) (info->stream, "Unknown error %d\n", status); - else - /* Actually, address between memaddr and memaddr + len was - out of bounds. */ - (*info->fprintf_func) (info->stream, - "Address 0x%" PRIx64 " is out of bounds.\n", memaddr); + if (status != EIO) { + /* Can't happen. */ + info->fprintf_func(info->stream, "Unknown error %d\n", status); + } else { + /* Address between memaddr and memaddr + len was out of bounds. */ + info->fprintf_func(info->stream, + "Address 0x%" PRIx64 " is out of bounds.\n", + memaddr); + } } -/* This could be in a separate file, to save minuscule amounts of space - in statically linked executables. */ - -/* Just print the address is hex. This is included for completeness even - though both GDB and objdump provide their own (to print symbolic - addresses). */ - -void -generic_print_address (bfd_vma addr, struct disassemble_info *info) +/* Print address in hex. */ +static void print_address(bfd_vma addr, struct disassemble_info *info) { - (*info->fprintf_func) (info->stream, "0x%" PRIx64, addr); + info->fprintf_func(info->stream, "0x%" PRIx64, addr); } /* Print address in hex, truncated to the width of a host virtual address. */ -static void -generic_print_host_address(bfd_vma addr, struct disassemble_info *info) +static void host_print_address(bfd_vma addr, struct disassemble_info *info) { - uint64_t mask = ~0ULL >> (64 - (sizeof(void *) * 8)); - generic_print_address(addr & mask, info); + print_address((uintptr_t)addr, info); } -/* Just return the given address. */ - -int -generic_symbol_at_address (bfd_vma addr, struct disassemble_info *info) +/* Stub prevents some fruitless earching in optabs disassemblers. */ +static int symbol_at_address(bfd_vma addr, struct disassemble_info *info) { - return 1; -} - -bfd_vma bfd_getl64 (const bfd_byte *addr) -{ - unsigned long long v; - - v = (unsigned long long) addr[0]; - v |= (unsigned long long) addr[1] << 8; - v |= (unsigned long long) addr[2] << 16; - v |= (unsigned long long) addr[3] << 24; - v |= (unsigned long long) addr[4] << 32; - v |= (unsigned long long) addr[5] << 40; - v |= (unsigned long long) addr[6] << 48; - v |= (unsigned long long) addr[7] << 56; - return (bfd_vma) v; -} - -bfd_vma bfd_getl32 (const bfd_byte *addr) -{ - unsigned long v; - - v = (unsigned long) addr[0]; - v |= (unsigned long) addr[1] << 8; - v |= (unsigned long) addr[2] << 16; - v |= (unsigned long) addr[3] << 24; - return (bfd_vma) v; -} - -bfd_vma bfd_getb32 (const bfd_byte *addr) -{ - unsigned long v; - - v = (unsigned long) addr[0] << 24; - v |= (unsigned long) addr[1] << 16; - v |= (unsigned long) addr[2] << 8; - v |= (unsigned long) addr[3]; - return (bfd_vma) v; -} - -bfd_vma bfd_getl16 (const bfd_byte *addr) -{ - unsigned long v; - - v = (unsigned long) addr[0]; - v |= (unsigned long) addr[1] << 8; - return (bfd_vma) v; -} - -bfd_vma bfd_getb16 (const bfd_byte *addr) -{ - unsigned long v; - - v = (unsigned long) addr[0] << 24; - v |= (unsigned long) addr[1] << 16; - return (bfd_vma) v; + return 1; } static int print_insn_objdump(bfd_vma pc, disassemble_info *info, @@ -174,292 +109,121 @@ 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, - const char *note) +static void initialize_debug(CPUDebug *s) { - 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", insn->mnemonic, insn->op_str); - if (note) { - print(info->stream, "\t\t%s", note); - } - print(info->stream, "\n"); - - /* 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"); - } + memset(s, 0, sizeof(*s)); + s->info.arch = bfd_arch_unknown; + s->info.cap_arch = -1; + s->info.cap_insn_unit = 4; + s->info.cap_insn_split = 4; + s->info.memory_error_func = perror_memory; + s->info.symbol_at_address_func = symbol_at_address; } -/* Disassemble SIZE bytes at PC for the target. */ -static bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size) +static void initialize_debug_target(CPUDebug *s, CPUState *cpu) { - 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, NULL); - } - - /* 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; - } + initialize_debug(s); - /* 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, - const char *note) -{ - 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; + s->cpu = cpu; + s->info.read_memory_func = target_read_memory; + s->info.print_address_func = print_address; +#ifdef TARGET_WORDS_BIGENDIAN + s->info.endian = BFD_ENDIAN_BIG; +#else + s->info.endian = BFD_ENDIAN_LITTLE; +#endif - while (cs_disasm_iter(handle, &cbuf, &size, &pc, insn)) { - cap_dump_insn(info, insn, note); - note = NULL; - } - 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"); + CPUClass *cc = CPU_GET_CLASS(cpu); + if (cc->disas_set_info) { + cc->disas_set_info(cpu, &s->info); } - - 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) +static void initialize_debug_host(CPUDebug *s) { - uint8_t cap_buf[32]; - csh handle; - cs_insn *insn; - size_t csize = 0; + initialize_debug(s); - 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, NULL); - if (--count <= 0) { - break; - } - } - memmove(cap_buf, cbuf, csize); - } - - cs_close(&handle); - return true; -} -#endif /* !CONFIG_USER_ONLY */ + s->info.read_memory_func = host_read_memory; + s->info.print_address_func = host_print_address; +#ifdef HOST_WORDS_BIGENDIAN + s->info.endian = BFD_ENDIAN_BIG; #else -# define cap_disas_target(i, p, s) false -# define cap_disas_host(i, p, s, n) false -# define cap_disas_monitor(i, p, c) false -# define cap_disas_plugin(i, p, c) false -#endif /* CONFIG_CAPSTONE */ + s->info.endian = BFD_ENDIAN_LITTLE; +#endif +#if defined(CONFIG_TCG_INTERPRETER) + s->info.print_insn = print_insn_tci; +#elif defined(__i386__) + s->info.mach = bfd_mach_i386_i386; + s->info.print_insn = print_insn_i386; + s->info.cap_arch = CS_ARCH_X86; + s->info.cap_mode = CS_MODE_32; + s->info.cap_insn_unit = 1; + s->info.cap_insn_split = 8; +#elif defined(__x86_64__) + s->info.mach = bfd_mach_x86_64; + s->info.print_insn = print_insn_i386; + s->info.cap_arch = CS_ARCH_X86; + s->info.cap_mode = CS_MODE_64; + s->info.cap_insn_unit = 1; + s->info.cap_insn_split = 8; +#elif defined(_ARCH_PPC) + s->info.disassembler_options = (char *)"any"; + s->info.print_insn = print_insn_ppc; + s->info.cap_arch = CS_ARCH_PPC; +# ifdef _ARCH_PPC64 + s->info.cap_mode = CS_MODE_64; +# endif +#elif defined(__riscv) && defined(CONFIG_RISCV_DIS) +#if defined(_ILP32) || (__riscv_xlen == 32) + s->info.print_insn = print_insn_riscv32; +#elif defined(_LP64) + s->info.print_insn = print_insn_riscv64; +#else +#error unsupported RISC-V ABI +#endif +#elif defined(__aarch64__) + s->info.cap_arch = CS_ARCH_ARM64; +# ifdef CONFIG_ARM_A64_DIS + s->info.print_insn = print_insn_arm_a64; +# endif +#elif defined(__alpha__) + s->info.print_insn = print_insn_alpha; +#elif defined(__sparc__) + s->info.print_insn = print_insn_sparc; + s->info.mach = bfd_mach_sparc_v9b; +#elif defined(__arm__) + /* TCG only generates code for arm mode. */ + s->info.print_insn = print_insn_arm; + s->info.cap_arch = CS_ARCH_ARM; +#elif defined(__MIPSEB__) + s->info.print_insn = print_insn_big_mips; +#elif defined(__MIPSEL__) + s->info.print_insn = print_insn_little_mips; +#elif defined(__m68k__) + s->info.print_insn = print_insn_m68k; +#elif defined(__s390__) + s->info.print_insn = print_insn_s390; + s->info.cap_arch = CS_ARCH_SYSZ; + s->info.cap_insn_unit = 2; + s->info.cap_insn_split = 6; +#elif defined(__hppa__) + s->info.print_insn = print_insn_hppa; +#endif +} /* Disassemble this for me please... (debugging). */ void target_disas(FILE *out, CPUState *cpu, target_ulong code, target_ulong size) { - CPUClass *cc = CPU_GET_CLASS(cpu); target_ulong pc; int count; CPUDebug s; - INIT_DISASSEMBLE_INFO(s.info, out, fprintf); - - s.cpu = cpu; - s.info.read_memory_func = target_read_memory; + initialize_debug_target(&s, cpu); + s.info.fprintf_func = fprintf; + s.info.stream = out; s.info.buffer_vma = code; s.info.buffer_length = size; - s.info.print_address_func = generic_print_address; - s.info.cap_arch = -1; - s.info.cap_mode = 0; - s.info.cap_insn_unit = 4; - s.info.cap_insn_split = 4; - -#ifdef TARGET_WORDS_BIGENDIAN - s.info.endian = BFD_ENDIAN_BIG; -#else - s.info.endian = BFD_ENDIAN_LITTLE; -#endif - - if (cc->disas_set_info) { - cc->disas_set_info(cpu, &s.info); - } if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) { return; @@ -485,13 +249,12 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, } } -static __thread GString plugin_disas_output; - static int plugin_printf(FILE *stream, const char *fmt, ...) { - va_list va; - GString *s = &plugin_disas_output; + /* We abuse the FILE parameter to pass a GString. */ + GString *s = (GString *)stream; int initial_len = s->len; + va_list va; va_start(va, fmt); g_string_append_vprintf(s, fmt, va); @@ -506,40 +269,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[1024]; - csh handle; - cs_insn *insn; - size_t csize = 0; - int count; - GString *s = &plugin_disas_output; - - if (cap_disas_start(info, &handle) != CS_ERR_OK) { - return false; - } - insn = cap_insn; - - size_t tsize = MIN(sizeof(cap_buf) - csize, size); - const uint8_t *cbuf = cap_buf; - target_read_memory(pc, cap_buf, tsize, info); - - count = cs_disasm(handle, cbuf, size, 0, 1, &insn); - - if (count) { - g_string_printf(s, "%s %s", insn->mnemonic, insn->op_str); - } else { - g_string_printf(s, "cs_disasm failed"); - } - - 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 @@ -547,146 +276,52 @@ bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size) */ char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size) { - CPUClass *cc = CPU_GET_CLASS(cpu); - int count; CPUDebug s; - GString *ds = g_string_set_size(&plugin_disas_output, 0); - - g_assert(ds == &plugin_disas_output); + GString *ds = g_string_new(NULL); - INIT_DISASSEMBLE_INFO(s.info, NULL, plugin_printf); - - s.cpu = cpu; - s.info.read_memory_func = target_read_memory; + initialize_debug_target(&s, cpu); + s.info.fprintf_func = plugin_printf; + s.info.stream = (FILE *)ds; /* abuse this slot */ s.info.buffer_vma = addr; s.info.buffer_length = size; s.info.print_address_func = plugin_print_address; - s.info.cap_arch = -1; - s.info.cap_mode = 0; - s.info.cap_insn_unit = 4; - s.info.cap_insn_split = 4; - -#ifdef TARGET_WORDS_BIGENDIAN - s.info.endian = BFD_ENDIAN_BIG; -#else - s.info.endian = BFD_ENDIAN_LITTLE; -#endif - - if (cc->disas_set_info) { - cc->disas_set_info(cpu, &s.info); - } if (s.info.cap_arch >= 0 && cap_disas_plugin(&s.info, addr, size)) { - return g_strdup(ds->str); - } - - if (s.info.print_insn == NULL) { - s.info.print_insn = print_insn_od_target; - } - - count = s.info.print_insn(addr, &s.info); - - /* The decoder probably read more than it needed it's not critical */ - if (count < size) { - warn_report("%s: %zu bytes left over", __func__, size - count); + ; /* done */ + } else if (s.info.print_insn) { + s.info.print_insn(addr, &s.info); + } else { + ; /* cannot disassemble -- return empty string */ } - return g_strdup(ds->str); + /* Return the buffer, freeing the GString container. */ + return g_string_free(ds, false); } /* Disassemble this for me please... (debugging). */ -void disas(FILE *out, void *code, unsigned long size, const char *note) +void disas(FILE *out, void *code, unsigned long size) { uintptr_t pc; int count; CPUDebug s; - int (*print_insn)(bfd_vma pc, disassemble_info *info) = NULL; - - INIT_DISASSEMBLE_INFO(s.info, out, fprintf); - s.info.print_address_func = generic_print_host_address; + initialize_debug_host(&s); + s.info.fprintf_func = fprintf; + s.info.stream = out; s.info.buffer = code; s.info.buffer_vma = (uintptr_t)code; s.info.buffer_length = size; - s.info.cap_arch = -1; - s.info.cap_mode = 0; - s.info.cap_insn_unit = 4; - s.info.cap_insn_split = 4; - -#ifdef HOST_WORDS_BIGENDIAN - s.info.endian = BFD_ENDIAN_BIG; -#else - s.info.endian = BFD_ENDIAN_LITTLE; -#endif -#if defined(CONFIG_TCG_INTERPRETER) - print_insn = print_insn_tci; -#elif defined(__i386__) - s.info.mach = bfd_mach_i386_i386; - print_insn = print_insn_i386; - s.info.cap_arch = CS_ARCH_X86; - s.info.cap_mode = CS_MODE_32; - s.info.cap_insn_unit = 1; - s.info.cap_insn_split = 8; -#elif defined(__x86_64__) - s.info.mach = bfd_mach_x86_64; - print_insn = print_insn_i386; - s.info.cap_arch = CS_ARCH_X86; - s.info.cap_mode = CS_MODE_64; - s.info.cap_insn_unit = 1; - s.info.cap_insn_split = 8; -#elif defined(_ARCH_PPC) - s.info.disassembler_options = (char *)"any"; - print_insn = print_insn_ppc; - s.info.cap_arch = CS_ARCH_PPC; -# ifdef _ARCH_PPC64 - s.info.cap_mode = CS_MODE_64; -# endif -#elif defined(__riscv) && defined(CONFIG_RISCV_DIS) -#if defined(_ILP32) || (__riscv_xlen == 32) - print_insn = print_insn_riscv32; -#elif defined(_LP64) - print_insn = print_insn_riscv64; -#else -#error unsupported RISC-V ABI -#endif -#elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS) - print_insn = print_insn_arm_a64; - s.info.cap_arch = CS_ARCH_ARM64; -#elif defined(__alpha__) - print_insn = print_insn_alpha; -#elif defined(__sparc__) - print_insn = print_insn_sparc; - s.info.mach = bfd_mach_sparc_v9b; -#elif defined(__arm__) - print_insn = print_insn_arm; - s.info.cap_arch = CS_ARCH_ARM; - /* TCG only generates code for arm mode. */ -#elif defined(__MIPSEB__) - print_insn = print_insn_big_mips; -#elif defined(__MIPSEL__) - print_insn = print_insn_little_mips; -#elif defined(__m68k__) - print_insn = print_insn_m68k; -#elif defined(__s390__) - print_insn = print_insn_s390; -#elif defined(__hppa__) - print_insn = print_insn_hppa; -#endif - if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size, note)) { + if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) { return; } - if (print_insn == NULL) { - print_insn = print_insn_od_host; + if (s.info.print_insn == NULL) { + s.info.print_insn = print_insn_od_host; } for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) { fprintf(out, "0x%08" PRIxPTR ": ", pc); - count = print_insn(pc, &s.info); - if (note) { - fprintf(out, "\t\t%s", note); - note = NULL; - } + count = s.info.print_insn(pc, &s.info); fprintf(out, "\n"); if (count < 0) { break; @@ -731,31 +366,15 @@ physical_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, void monitor_disas(Monitor *mon, CPUState *cpu, target_ulong pc, int nb_insn, int is_physical) { - CPUClass *cc = CPU_GET_CLASS(cpu); int count, i; CPUDebug s; - INIT_DISASSEMBLE_INFO(s.info, NULL, qemu_fprintf); - - s.cpu = cpu; - s.info.read_memory_func - = (is_physical ? physical_read_memory : target_read_memory); - s.info.print_address_func = generic_print_address; - s.info.buffer_vma = pc; - s.info.cap_arch = -1; - s.info.cap_mode = 0; - s.info.cap_insn_unit = 4; - s.info.cap_insn_split = 4; - -#ifdef TARGET_WORDS_BIGENDIAN - s.info.endian = BFD_ENDIAN_BIG; -#else - s.info.endian = BFD_ENDIAN_LITTLE; -#endif - - if (cc->disas_set_info) { - cc->disas_set_info(cpu, &s.info); + initialize_debug_target(&s, cpu); + s.info.fprintf_func = qemu_fprintf; + if (is_physical) { + s.info.read_memory_func = physical_read_memory; } + s.info.buffer_vma = pc; if (s.info.cap_arch >= 0 && cap_disas_monitor(&s.info, pc, nb_insn)) { return; diff --git a/disas/capstone.c b/disas/capstone.c new file mode 100644 index 0000000000..0a9ef9c892 --- /dev/null +++ b/disas/capstone.c @@ -0,0 +1,326 @@ +/* + * Interface to the capstone disassembler. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "disas/dis-asm.h" +#include "disas/capstone.h" + + +/* + * 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; + +/* + * The capstone library always skips 2 bytes for S390X. + * This is less than ideal, since we can tell from the first two bits + * the size of the insn and thus stay in sync with the insn stream. + */ +static size_t CAPSTONE_API +cap_skipdata_s390x_cb(const uint8_t *code, size_t code_size, + size_t offset, void *user_data) +{ + size_t ilen; + + /* See get_ilen() in target/s390x/internal.h. */ + switch (code[offset] >> 6) { + case 0: + ilen = 2; + break; + case 1: + case 2: + ilen = 4; + break; + default: + ilen = 6; + break; + } + + return ilen; +} + +static const cs_opt_skipdata cap_skipdata_s390x = { + .mnemonic = ".byte", + .callback = cap_skipdata_s390x_cb +}; + +/* + * 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; + } + + /* "Disassemble" unknown insns as ".byte W,X,Y,Z". */ + cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON); + + switch (info->cap_arch) { + case CS_ARCH_SYSZ: + cs_option(*handle, CS_OPT_SKIPDATA_SETUP, + (uintptr_t)&cap_skipdata_s390x); + break; + + case 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); + break; + } + + /* 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; + FILE *stream = info->stream; + int i, n, split; + + print(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(stream, "%*s", width, ""); + } + + /* Print the actual instruction. */ + print(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(stream, "0x%08" PRIx64 ": ", insn->address + i); + cap_dump_insn_units(info, insn, i, MIN(n, i + split)); + print(stream, "\n"); + } +} + +/* Disassemble SIZE bytes at PC for the target. */ +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; + + info->read_memory_func(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. */ +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; +} + +/* Disassemble COUNT insns at PC for the target. */ +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; +} + +/* Disassemble a single instruction directly into plugin output */ +bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size) +{ + uint8_t cap_buf[32]; + const uint8_t *cbuf = cap_buf; + csh handle; + + if (cap_disas_start(info, &handle) != CS_ERR_OK) { + return false; + } + + assert(size < sizeof(cap_buf)); + info->read_memory_func(pc, cap_buf, size, info); + + if (cs_disasm_iter(handle, &cbuf, &size, &pc, cap_insn)) { + info->fprintf_func(info->stream, "%s %s", + cap_insn->mnemonic, cap_insn->op_str); + } + + cs_close(&handle); + return true; +} diff --git a/disas/meson.build b/disas/meson.build index bde8280c73..09a852742e 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -21,5 +21,6 @@ common_ss.add(when: 'CONFIG_S390_DIS', if_true: files('s390.c')) common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c')) common_ss.add(when: 'CONFIG_SPARC_DIS', if_true: files('sparc.c')) common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c')) +common_ss.add(when: capstone, if_true: files('capstone.c')) specific_ss.add(when: 'CONFIG_TCG_INTERPRETER', if_true: files('tci.c')) diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index 9856bf7921..2164762b46 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -406,7 +406,6 @@ typedef struct disassemble_info { } disassemble_info; - /* Standard disassemblers. Disassemble one instruction at the given target address. Return number of bytes processed. */ typedef int (*disassembler_ftype) (bfd_vma, disassemble_info *); @@ -461,66 +460,17 @@ int print_insn_riscv32 (bfd_vma, disassemble_info*); int print_insn_riscv64 (bfd_vma, disassemble_info*); int print_insn_rx(bfd_vma, disassemble_info *); -#if 0 -/* Fetch the disassembler for a given BFD, if that support is available. */ -disassembler_ftype disassembler(bfd *); -#endif - - -/* This block of definitions is for particular callers who read instructions - into a buffer before calling the instruction decoder. */ - -/* Here is a function which callers may wish to use for read_memory_func. - It gets bytes from a buffer. */ -int buffer_read_memory(bfd_vma, bfd_byte *, int, struct disassemble_info *); - -/* This function goes with buffer_read_memory. - It prints a message using info->fprintf_func and info->stream. */ -void perror_memory(int, bfd_vma, struct disassemble_info *); - - -/* Just print the address in hex. This is included for completeness even - though both GDB and objdump provide their own (to print symbolic - addresses). */ -void generic_print_address(bfd_vma, struct disassemble_info *); - -/* Always true. */ -int generic_symbol_at_address(bfd_vma, struct disassemble_info *); - -/* Macro to initialize a disassemble_info struct. This should be called - by all applications creating such a struct. */ -#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \ - (INFO).flavour = bfd_target_unknown_flavour, \ - (INFO).arch = bfd_arch_unknown, \ - (INFO).mach = 0, \ - (INFO).endian = BFD_ENDIAN_UNKNOWN, \ - INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) - -/* Call this macro to initialize only the internal variables for the - disassembler. Architecture dependent things such as byte order, or machine - variant are not touched by this macro. This makes things much easier for - GDB which must initialize these things separately. */ - -#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \ - (INFO).fprintf_func = (FPRINTF_FUNC), \ - (INFO).stream = (STREAM), \ - (INFO).symbols = NULL, \ - (INFO).num_symbols = 0, \ - (INFO).private_data = NULL, \ - (INFO).buffer = NULL, \ - (INFO).buffer_vma = 0, \ - (INFO).buffer_length = 0, \ - (INFO).read_memory_func = buffer_read_memory, \ - (INFO).memory_error_func = perror_memory, \ - (INFO).print_address_func = generic_print_address, \ - (INFO).print_insn = NULL, \ - (INFO).symbol_at_address_func = generic_symbol_at_address, \ - (INFO).flags = 0, \ - (INFO).bytes_per_line = 0, \ - (INFO).bytes_per_chunk = 0, \ - (INFO).display_endian = BFD_ENDIAN_UNKNOWN, \ - (INFO).disassembler_options = NULL, \ - (INFO).insn_info_valid = 0 +#ifdef CONFIG_CAPSTONE +bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size); +bool cap_disas_host(disassemble_info *info, void *code, size_t size); +bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count); +bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size); +#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 */ #ifndef ATTRIBUTE_UNUSED #define ATTRIBUTE_UNUSED __attribute__((unused)) @@ -528,11 +478,33 @@ int generic_symbol_at_address(bfd_vma, struct disassemble_info *); /* from libbfd */ -bfd_vma bfd_getl64 (const bfd_byte *addr); -bfd_vma bfd_getl32 (const bfd_byte *addr); -bfd_vma bfd_getb32 (const bfd_byte *addr); -bfd_vma bfd_getl16 (const bfd_byte *addr); -bfd_vma bfd_getb16 (const bfd_byte *addr); +#include "qemu/bswap.h" + +static inline bfd_vma bfd_getl64(const bfd_byte *addr) +{ + return ldq_le_p(addr); +} + +static inline bfd_vma bfd_getl32(const bfd_byte *addr) +{ + return (uint32_t)ldl_le_p(addr); +} + +static inline bfd_vma bfd_getl16(const bfd_byte *addr) +{ + return lduw_le_p(addr); +} + +static inline bfd_vma bfd_getb32(const bfd_byte *addr) +{ + return (uint32_t)ldl_be_p(addr); +} + +static inline bfd_vma bfd_getb16(const bfd_byte *addr) +{ + return lduw_be_p(addr); +} + typedef bool bfd_boolean; #endif /* DISAS_DIS_ASM_H */ diff --git a/include/disas/disas.h b/include/disas/disas.h index 1b6e035e32..36c33f6f19 100644 --- a/include/disas/disas.h +++ b/include/disas/disas.h @@ -7,7 +7,7 @@ #include "cpu.h" /* Disassemble this for me please... (debugging). */ -void disas(FILE *out, void *code, unsigned long size, const char *note); +void disas(FILE *out, void *code, unsigned long size); void target_disas(FILE *out, CPUState *cpu, target_ulong code, target_ulong size); diff --git a/include/exec/log.h b/include/exec/log.h index 86871f403a..e02fff5de1 100644 --- a/include/exec/log.h +++ b/include/exec/log.h @@ -56,13 +56,13 @@ static inline void log_target_disas(CPUState *cpu, target_ulong start, rcu_read_unlock(); } -static inline void log_disas(void *code, unsigned long size, const char *note) +static inline void log_disas(void *code, unsigned long size) { QemuLogFile *logfile; rcu_read_lock(); logfile = qatomic_rcu_read(&qemu_logfile); if (logfile) { - disas(logfile->fd, code, size, note); + disas(logfile->fd, code, size); } rcu_read_unlock(); } diff --git a/meson.build b/meson.build index 0f0cc21d16..20f6ad774a 100644 --- a/meson.build +++ b/meson.build @@ -10,6 +10,7 @@ else keyval = import('unstable-keyval') endif ss = import('sourceset') +fs = import('fs') sh = find_program('sh') cc = meson.get_compiler('c') @@ -495,11 +496,6 @@ if 'CONFIG_USB_LIBUSB' in config_host libusb = declare_dependency(compile_args: config_host['LIBUSB_CFLAGS'].split(), link_args: config_host['LIBUSB_LIBS'].split()) endif -capstone = not_found -if 'CONFIG_CAPSTONE' in config_host - capstone = declare_dependency(compile_args: config_host['CAPSTONE_CFLAGS'].split(), - link_args: config_host['CAPSTONE_LIBS'].split()) -endif libpmem = not_found if 'CONFIG_LIBPMEM' in config_host libpmem = declare_dependency(compile_args: config_host['LIBPMEM_CFLAGS'].split(), @@ -581,7 +577,6 @@ foreach k, v: config_host config_host_data.set(k, v == 'y' ? 1 : v) endif endforeach -genh += configure_file(output: 'config-host.h', configuration: config_host_data) minikconf = find_program('scripts/minikconf.py') config_all = {} @@ -736,6 +731,119 @@ config_all += { 'CONFIG_ALL': true, } +# Submodules + +capstone = not_found +capstone_opt = get_option('capstone') +if capstone_opt in ['enabled', 'auto', 'system'] + have_internal = fs.exists(meson.current_source_dir() / 'capstone/Makefile') + capstone = dependency('capstone', version: '>=4.0', + static: enable_static, method: 'pkg-config', + required: capstone_opt == 'system' or + capstone_opt == 'enabled' and not have_internal) + if capstone.found() + capstone_opt = 'system' + elif have_internal + capstone_opt = 'internal' + else + capstone_opt = 'disabled' + endif +endif +if capstone_opt == 'internal' + capstone_data = configuration_data() + capstone_data.set('CAPSTONE_USE_SYS_DYN_MEM', '1') + + capstone_files = files( + 'capstone/cs.c', + 'capstone/MCInst.c', + 'capstone/MCInstrDesc.c', + 'capstone/MCRegisterInfo.c', + 'capstone/SStream.c', + 'capstone/utils.c' + ) + + if 'CONFIG_ARM_DIS' in config_all_disas + capstone_data.set('CAPSTONE_HAS_ARM', '1') + capstone_files += files( + 'capstone/arch/ARM/ARMDisassembler.c', + 'capstone/arch/ARM/ARMInstPrinter.c', + 'capstone/arch/ARM/ARMMapping.c', + 'capstone/arch/ARM/ARMModule.c' + ) + endif + + # FIXME: This config entry currently depends on a c++ compiler. + # Which is needed for building libvixl, but not for capstone. + if 'CONFIG_ARM_A64_DIS' in config_all_disas + capstone_data.set('CAPSTONE_HAS_ARM64', '1') + capstone_files += files( + 'capstone/arch/AArch64/AArch64BaseInfo.c', + 'capstone/arch/AArch64/AArch64Disassembler.c', + 'capstone/arch/AArch64/AArch64InstPrinter.c', + 'capstone/arch/AArch64/AArch64Mapping.c', + 'capstone/arch/AArch64/AArch64Module.c' + ) + endif + + if 'CONFIG_PPC_DIS' in config_all_disas + capstone_data.set('CAPSTONE_HAS_POWERPC', '1') + capstone_files += files( + 'capstone/arch/PowerPC/PPCDisassembler.c', + 'capstone/arch/PowerPC/PPCInstPrinter.c', + 'capstone/arch/PowerPC/PPCMapping.c', + 'capstone/arch/PowerPC/PPCModule.c' + ) + endif + + if 'CONFIG_S390_DIS' in config_all_disas + capstone_data.set('CAPSTONE_HAS_SYSZ', '1') + capstone_files += files( + 'capstone/arch/SystemZ/SystemZDisassembler.c', + 'capstone/arch/SystemZ/SystemZInstPrinter.c', + 'capstone/arch/SystemZ/SystemZMapping.c', + 'capstone/arch/SystemZ/SystemZModule.c', + 'capstone/arch/SystemZ/SystemZMCTargetDesc.c' + ) + endif + + if 'CONFIG_I386_DIS' in config_all_disas + capstone_data.set('CAPSTONE_HAS_X86', 1) + capstone_files += files( + 'capstone/arch/X86/X86Disassembler.c', + 'capstone/arch/X86/X86DisassemblerDecoder.c', + 'capstone/arch/X86/X86ATTInstPrinter.c', + 'capstone/arch/X86/X86IntelInstPrinter.c', + 'capstone/arch/X86/X86InstPrinterCommon.c', + 'capstone/arch/X86/X86Mapping.c', + 'capstone/arch/X86/X86Module.c' + ) + endif + + configure_file(output: 'capstone-defs.h', configuration: capstone_data) + + capstone_cargs = [ + # FIXME: There does not seem to be a way to completely replace the c_args + # that come from add_project_arguments() -- we can only add to them. + # So: disable all warnings with a big hammer. + '-Wno-error', '-w', + + # Include all configuration defines via a header file, which will wind up + # as a dependency on the object file, and thus changes here will result + # in a rebuild. + '-include', 'capstone-defs.h' + ] + + libcapstone = static_library('capstone', + sources: capstone_files, + c_args: capstone_cargs, + include_directories: 'capstone/include') + capstone = declare_dependency(link_with: libcapstone, + include_directories: 'capstone/include/capstone') +endif +config_host_data.set('CONFIG_CAPSTONE', capstone.found()) + +genh += configure_file(output: 'config-host.h', configuration: config_host_data) + # Generators hxtool = find_program('scripts/hxtool') @@ -1007,6 +1115,7 @@ common_ss.add(files('cpus-common.c')) subdir('softmmu') +common_ss.add(capstone) specific_ss.add(files('disas.c', 'exec.c', 'gdbstub.c'), capstone, libpmem, libdaxctl) specific_ss.add(files('exec-vary.c')) specific_ss.add(when: 'CONFIG_TCG', if_true: files( @@ -1566,7 +1675,7 @@ summary_info += {'vvfat support': config_host.has_key('CONFIG_VVFAT')} summary_info += {'qed support': config_host.has_key('CONFIG_QED')} summary_info += {'parallels support': config_host.has_key('CONFIG_PARALLELS')} summary_info += {'sheepdog support': config_host.has_key('CONFIG_SHEEPDOG')} -summary_info += {'capstone': config_host.has_key('CONFIG_CAPSTONE')} +summary_info += {'capstone': capstone_opt == 'disabled' ? false : capstone_opt} summary_info += {'libpmem support': config_host.has_key('CONFIG_LIBPMEM')} summary_info += {'libdaxctl support': config_host.has_key('CONFIG_LIBDAXCTL')} summary_info += {'libudev': libudev.found()} diff --git a/meson_options.txt b/meson_options.txt index 46ea1d889a..a1228d29a9 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -31,3 +31,7 @@ option('vnc_sasl', type : 'feature', value : 'auto', description: 'SASL authentication for VNC server') option('xkbcommon', type : 'feature', value : 'auto', description: 'xkbcommon support') + +option('capstone', type: 'combo', value: 'auto', + choices: ['disabled', 'enabled', 'auto', 'system', 'internal'], + description: 'Whether and how to find the capstone library') diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 9cbd937ba2..66f5942b53 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -44,6 +44,7 @@ #include "sysemu/tcg.h" #endif #include "fpu/softfloat-helpers.h" +#include "disas/capstone.h" #define CR0_RESET 0xE0UL #define CR14_RESET 0xC2000000UL; @@ -182,6 +183,9 @@ static void s390_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { info->mach = bfd_mach_s390_64; info->print_insn = print_insn_s390; + info->cap_arch = CS_ARCH_SYSZ; + info->cap_insn_unit = 2; + info->cap_insn_split = 6; } static void s390_cpu_realizefn(DeviceState *dev, Error **errp) @@ -1101,7 +1101,7 @@ void tcg_prologue_init(TCGContext *s) size_t data_size = prologue_size - code_size; size_t i; - log_disas(buf0, code_size, NULL); + log_disas(buf0, code_size); for (i = 0; i < data_size; i += sizeof(tcg_target_ulong)) { if (sizeof(tcg_target_ulong) == 8) { @@ -1115,7 +1115,7 @@ void tcg_prologue_init(TCGContext *s) } } } else { - log_disas(buf0, prologue_size, NULL); + log_disas(buf0, prologue_size); } qemu_log("\n"); qemu_log_flush(); |