diff --git a/.gitmodules b/.gitmodules index 7c981a42b66a..1500579638db 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,3 +37,6 @@ [submodule "ui/keycodemapdb"] path = ui/keycodemapdb url = git://git.qemu.org/keycodemapdb.git +[submodule "capstone"] + path = capstone + url = git://git.qemu.org/capstone.git diff --git a/Makefile b/Makefile index 9372742f86fb..ec73acfa9a91 100644 --- a/Makefile +++ b/Makefile @@ -383,6 +383,21 @@ subdir-dtc: .git-submodule-status dtc/libfdt dtc/tests 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)) +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 + +subdir-capstone: .git-submodule-status + $(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE)) + $(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \ $(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) diff --git a/capstone b/capstone new file mode 160000 index 000000000000..22ead3e0bfdb --- /dev/null +++ b/capstone @@ -0,0 +1 @@ +Subproject commit 22ead3e0bfdb87516656453336160e0a37b066bf diff --git a/configure b/configure index 03547cea6a78..23eddc2f5033 100755 --- a/configure +++ b/configure @@ -375,6 +375,7 @@ opengl_dmabuf="no" cpuid_h="no" avx2_opt="no" zlib="yes" +capstone="" lzo="" snappy="" bzip2="" @@ -1294,6 +1295,14 @@ for opt do error_exit "vhost-user isn't available on win32" fi ;; + --disable-capstone) capstone="no" + ;; + --enable-capstone) capstone="yes" + ;; + --enable-capstone=git) capstone="git" + ;; + --enable-capstone=system) capstone="system" + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1541,6 +1550,7 @@ disabled with --disable-FEATURE, default is enabled if available: vxhs Veritas HyperScale vDisk backend support crypto-afalg Linux AF_ALG crypto backend driver vhost-user vhost-user support + capstone capstone disassembler support NOTE: The object files are built at the place where configure is launched EOF @@ -4410,6 +4420,58 @@ EOF fi fi +########################################## +# capstone + +case "$capstone" in + "" | yes) + if $pkg_config capstone; then + capstone=system + elif test -e "${source_path}/.git" ; 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 + git_submodules="${git_submodules} capstone" + fi + mkdir -p capstone + QEMU_CFLAGS="$QEMU_CFLAGS -I\$(SRC_PATH)/capstone/include" + if test "$mingw32" = "yes"; then + LIBCAPSTONE=capstone.lib + else + LIBCAPSTONE=libcapstone.a + fi + LIBS="-L\$(BUILD_DIR)/capstone -lcapstone $LIBS" + ;; + + system) + QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags capstone)" + LIBS="$($pkg_config --libs capstone) $LIBS" + ;; + + no) + ;; + *) + error_exit "Unknown state for capstone: $capstone" + ;; +esac + ########################################## # check if we have fdatasync @@ -5468,6 +5530,7 @@ echo "jemalloc support $jemalloc" echo "avx2 optimization $avx2_opt" echo "replication support $replication" echo "VxHS block device $vxhs" +echo "capstone $capstone" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -6142,6 +6205,9 @@ 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 +fi # Hold two types of flag: # CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on @@ -6624,6 +6690,12 @@ done # for target in $targets if [ "$dtc_internal" = "yes" ]; then echo "config-host.h: subdir-dtc" >> $config_host_mak fi +if [ "$capstone" = "git" -o "$capstone" = "internal" ]; then + echo "config-host.h: subdir-capstone" >> $config_host_mak +fi +if test -n "$LIBCAPSTONE"; then + echo "LIBCAPSTONE=$LIBCAPSTONE" >> $config_host_mak +fi if test "$numa" = "yes"; then echo "CONFIG_NUMA=y" >> $config_host_mak diff --git a/disas.c b/disas.c index 54eea3f9c94a..92b389d25f85 100644 --- a/disas.c +++ b/disas.c @@ -6,6 +6,7 @@ #include "cpu.h" #include "disas/disas.h" +#include "disas/capstone.h" typedef struct CPUDebug { struct disassemble_info info; @@ -171,15 +172,195 @@ static int print_insn_od_target(bfd_vma pc, disassemble_info *info) return print_insn_objdump(pc, info, "OBJD-T"); } -/* Disassemble this for me please... (debugging). 'flags' has the following - values: - i386 - 1 means 16 bit code, 2 means 64 bit code - ppc - bits 0:15 specify (optionally) the machine instruction set; - bit 16 indicates little endian. - other targets - unused - */ +#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; +} + +/* 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)) { + (*info->fprintf_func)(info->stream, + "0x%08" PRIx64 ": %-12s %s\n", + insn->address, insn->mnemonic, + insn->op_str); + } + + /* 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)) { + (*info->fprintf_func)(info->stream, + "0x%08" PRIx64 ": %-12s %s\n", + insn->address, insn->mnemonic, + insn->op_str); + } + 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)) { + (*info->fprintf_func)(info->stream, + "0x%08" PRIx64 ": %-12s %s\n", + insn->address, insn->mnemonic, + insn->op_str); + 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 +#endif /* CONFIG_CAPSTONE */ + +/* Disassemble this for me please... (debugging). */ void target_disas(FILE *out, CPUState *cpu, target_ulong code, - target_ulong size, int flags) + target_ulong size) { CPUClass *cc = CPU_GET_CLASS(cpu); target_ulong pc; @@ -193,6 +374,8 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, 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; #ifdef TARGET_WORDS_BIGENDIAN s.info.endian = BFD_ENDIAN_BIG; @@ -204,32 +387,10 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, cc->disas_set_info(cpu, &s.info); } -#if defined(TARGET_I386) - if (flags == 2) { - s.info.mach = bfd_mach_x86_64; - } else if (flags == 1) { - s.info.mach = bfd_mach_i386_i8086; - } else { - s.info.mach = bfd_mach_i386_i386; - } - s.info.print_insn = print_insn_i386; -#elif defined(TARGET_PPC) - if ((flags >> 16) & 1) { - s.info.endian = BFD_ENDIAN_LITTLE; - } - if (flags & 0xFFFF) { - /* If we have a precise definition of the instruction set, use it. */ - s.info.mach = flags & 0xFFFF; - } else { -#ifdef TARGET_PPC64 - s.info.mach = bfd_mach_ppc64; -#else - s.info.mach = bfd_mach_ppc; -#endif + if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) { + return; } - s.info.disassembler_options = (char *)"any"; - s.info.print_insn = print_insn_ppc; -#endif + if (s.info.print_insn == NULL) { s.info.print_insn = print_insn_od_target; } @@ -237,18 +398,6 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, for (pc = code; size > 0; pc += count, size -= count) { fprintf(out, "0x" TARGET_FMT_lx ": ", pc); count = s.info.print_insn(pc, &s.info); -#if 0 - { - int i; - uint8_t b; - fprintf(out, " {"); - for(i = 0; i < count; i++) { - target_read_memory(pc + i, &b, 1, &s.info); - fprintf(out, " %02x", b); - } - fprintf(out, " }"); - } -#endif fprintf(out, "\n"); if (count < 0) break; @@ -276,6 +425,8 @@ void disas(FILE *out, void *code, unsigned long size) 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; #ifdef HOST_WORDS_BIGENDIAN s.info.endian = BFD_ENDIAN_BIG; @@ -287,14 +438,23 @@ void disas(FILE *out, void *code, unsigned long size) #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; #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; #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(__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__) @@ -302,6 +462,8 @@ void disas(FILE *out, void *code, unsigned long size) 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__) @@ -313,6 +475,11 @@ void disas(FILE *out, void *code, unsigned long size) #elif defined(__hppa__) print_insn = print_insn_hppa; #endif + + if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) { + return; + } + if (print_insn == NULL) { print_insn = print_insn_od_host; } @@ -345,26 +512,17 @@ const char *lookup_symbol(target_ulong orig_addr) #include "monitor/monitor.h" -static int monitor_disas_is_physical; - static int -monitor_read_memory (bfd_vma memaddr, bfd_byte *myaddr, int length, +physical_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, struct disassemble_info *info) { - CPUDebug *s = container_of(info, CPUDebug, info); - - if (monitor_disas_is_physical) { - cpu_physical_memory_read(memaddr, myaddr, length); - } else { - cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); - } + cpu_physical_memory_read(memaddr, myaddr, length); return 0; } -/* Disassembler for the monitor. - See target_disas for a description of flags. */ +/* Disassembler for the monitor. */ void monitor_disas(Monitor *mon, CPUState *cpu, - target_ulong pc, int nb_insn, int is_physical, int flags) + target_ulong pc, int nb_insn, int is_physical) { CPUClass *cc = CPU_GET_CLASS(cpu); int count, i; @@ -373,11 +531,12 @@ void monitor_disas(Monitor *mon, CPUState *cpu, INIT_DISASSEMBLE_INFO(s.info, (FILE *)mon, monitor_fprintf); s.cpu = cpu; - monitor_disas_is_physical = is_physical; - s.info.read_memory_func = monitor_read_memory; + 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; #ifdef TARGET_WORDS_BIGENDIAN s.info.endian = BFD_ENDIAN_BIG; @@ -389,31 +548,10 @@ void monitor_disas(Monitor *mon, CPUState *cpu, cc->disas_set_info(cpu, &s.info); } -#if defined(TARGET_I386) - if (flags == 2) { - s.info.mach = bfd_mach_x86_64; - } else if (flags == 1) { - s.info.mach = bfd_mach_i386_i8086; - } else { - s.info.mach = bfd_mach_i386_i386; - } - s.info.print_insn = print_insn_i386; -#elif defined(TARGET_PPC) - if (flags & 0xFFFF) { - /* If we have a precise definition of the instruction set, use it. */ - s.info.mach = flags & 0xFFFF; - } else { -#ifdef TARGET_PPC64 - s.info.mach = bfd_mach_ppc64; -#else - s.info.mach = bfd_mach_ppc; -#endif - } - if ((flags >> 16) & 1) { - s.info.endian = BFD_ENDIAN_LITTLE; + if (s.info.cap_arch >= 0 && cap_disas_monitor(&s.info, pc, nb_insn)) { + return; } - s.info.print_insn = print_insn_ppc; -#endif + if (!s.info.print_insn) { monitor_printf(mon, "0x" TARGET_FMT_lx ": Asm output not supported on this arch\n", pc); diff --git a/disas/arm.c b/disas/arm.c index 27396dd3e16e..9967c45990c8 100644 --- a/disas/arm.c +++ b/disas/arm.c @@ -70,6 +70,17 @@ static void floatformat_to_double (unsigned char *data, double *dest) *dest = u.f; } +static int arm_read_memory(bfd_vma memaddr, bfd_byte *b, int length, + struct disassemble_info *info) +{ + assert((info->flags & INSN_ARM_BE32) == 0 || length == 2 || length == 4); + + if ((info->flags & INSN_ARM_BE32) != 0 && length == 2) { + memaddr ^= 2; + } + return info->read_memory_func(memaddr, b, length, info); +} + /* End of qemu specific additions. */ struct opcode32 @@ -3810,7 +3821,7 @@ find_ifthen_state (bfd_vma pc, struct disassemble_info *info, return; } addr -= 2; - status = info->read_memory_func (addr, (bfd_byte *)b, 2, info); + status = arm_read_memory (addr, (bfd_byte *)b, 2, info); if (status) return; @@ -3882,7 +3893,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info) info->bytes_per_chunk = size; printer = print_insn_data; - status = info->read_memory_func (pc, (bfd_byte *)b, size, info); + status = arm_read_memory (pc, (bfd_byte *)b, size, info); given = 0; if (little) for (i = size - 1; i >= 0; i--) @@ -3899,7 +3910,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info) info->bytes_per_chunk = 4; size = 4; - status = info->read_memory_func (pc, (bfd_byte *)b, 4, info); + status = arm_read_memory (pc, (bfd_byte *)b, 4, info); if (little) given = (b[0]) | (b[1] << 8) | (b[2] << 16) | ((unsigned)b[3] << 24); else @@ -3915,7 +3926,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info) info->bytes_per_chunk = 2; size = 2; - status = info->read_memory_func (pc, (bfd_byte *)b, 2, info); + status = arm_read_memory (pc, (bfd_byte *)b, 2, info); if (little) given = (b[0]) | (b[1] << 8); else @@ -3929,7 +3940,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info) || (given & 0xF800) == 0xF000 || (given & 0xF800) == 0xE800) { - status = info->read_memory_func (pc + 2, (bfd_byte *)b, 2, info); + status = arm_read_memory (pc + 2, (bfd_byte *)b, 2, info); if (little) given = (b[0]) | (b[1] << 8) | (given << 16); else diff --git a/include/disas/bfd.h b/include/disas/bfd.h index d99da68267d8..1f88c9e9d526 100644 --- a/include/disas/bfd.h +++ b/include/disas/bfd.h @@ -307,12 +307,6 @@ typedef struct disassemble_info { (bfd_vma memaddr, bfd_byte *myaddr, int length, struct disassemble_info *info); - /* A place to stash the real read_memory_func if read_memory_func wants to - do some funky address arithmetic or similar (e.g. for ARM BE32 mode). */ - int (*read_memory_inner_func) - (bfd_vma memaddr, bfd_byte *myaddr, int length, - struct disassemble_info *info); - /* Function which should be called if we get an error that we can't recover from. STATUS is the errno value from read_memory_func and MEMADDR is the address that we were trying to read. INFO is a @@ -377,6 +371,10 @@ typedef struct disassemble_info { /* Command line options specific to the target disassembler. */ char * disassembler_options; + /* Options for Capstone disassembly. */ + int cap_arch; + int cap_mode; + } disassemble_info; @@ -479,7 +477,6 @@ int generic_symbol_at_address(bfd_vma, struct disassemble_info *); (INFO).buffer_vma = 0, \ (INFO).buffer_length = 0, \ (INFO).read_memory_func = buffer_read_memory, \ - (INFO).read_memory_inner_func = NULL, \ (INFO).memory_error_func = perror_memory, \ (INFO).print_address_func = generic_print_address, \ (INFO).print_insn = NULL, \ diff --git a/include/disas/capstone.h b/include/disas/capstone.h new file mode 100644 index 000000000000..84e214956d00 --- /dev/null +++ b/include/disas/capstone.h @@ -0,0 +1,38 @@ +#ifndef QEMU_CAPSTONE_H +#define QEMU_CAPSTONE_H 1 + +#ifdef CONFIG_CAPSTONE + +#include + +#else + +/* Just enough to allow backends to init without ifdefs. */ + +#define CS_ARCH_ARM -1 +#define CS_ARCH_ARM64 -1 +#define CS_ARCH_MIPS -1 +#define CS_ARCH_X86 -1 +#define CS_ARCH_PPC -1 +#define CS_ARCH_SPARC -1 +#define CS_ARCH_SYSZ -1 + +#define CS_MODE_LITTLE_ENDIAN 0 +#define CS_MODE_BIG_ENDIAN 0 +#define CS_MODE_ARM 0 +#define CS_MODE_16 0 +#define CS_MODE_32 0 +#define CS_MODE_64 0 +#define CS_MODE_THUMB 0 +#define CS_MODE_MCLASS 0 +#define CS_MODE_V8 0 +#define CS_MODE_MICRO 0 +#define CS_MODE_MIPS3 0 +#define CS_MODE_MIPS32R6 0 +#define CS_MODE_MIPSGP64 0 +#define CS_MODE_V9 0 +#define CS_MODE_MIPS32 0 +#define CS_MODE_MIPS64 0 + +#endif /* CONFIG_CAPSTONE */ +#endif /* QEMU_CAPSTONE_H */ diff --git a/include/disas/disas.h b/include/disas/disas.h index e549ca24a152..4d48c13c65fa 100644 --- a/include/disas/disas.h +++ b/include/disas/disas.h @@ -9,10 +9,10 @@ /* Disassemble this for me please... (debugging). */ void disas(FILE *out, void *code, unsigned long size); void target_disas(FILE *out, CPUState *cpu, target_ulong code, - target_ulong size, int flags); + target_ulong size); void monitor_disas(Monitor *mon, CPUState *cpu, - target_ulong pc, int nb_insn, int is_physical, int flags); + target_ulong pc, int nb_insn, int is_physical); /* Look up symbol for debugging purpose. Returns "" if unknown. */ const char *lookup_symbol(target_ulong orig_addr); diff --git a/include/exec/log.h b/include/exec/log.h index ba1c9b568238..c249307911cc 100644 --- a/include/exec/log.h +++ b/include/exec/log.h @@ -38,9 +38,9 @@ static inline void log_cpu_state_mask(int mask, CPUState *cpu, int flags) #ifdef NEED_CPU_H /* disas() and target_disas() to qemu_logfile: */ static inline void log_target_disas(CPUState *cpu, target_ulong start, - target_ulong len, int flags) + target_ulong len) { - target_disas(qemu_logfile, cpu, start, len, flags); + target_disas(qemu_logfile, cpu, start, len); } static inline void log_disas(void *code, unsigned long size) diff --git a/monitor.c b/monitor.c index fe0d1bdbb461..7a802a345eb5 100644 --- a/monitor.c +++ b/monitor.c @@ -1309,34 +1309,7 @@ static void memory_dump(Monitor *mon, int count, int format, int wsize, } if (format == 'i') { - int flags = 0; -#ifdef TARGET_I386 - CPUArchState *env = mon_get_cpu_env(); - if (wsize == 2) { - flags = 1; - } else if (wsize == 4) { - flags = 0; - } else { - /* as default we use the current CS size */ - flags = 0; - if (env) { -#ifdef TARGET_X86_64 - if ((env->efer & MSR_EFER_LMA) && - (env->segs[R_CS].flags & DESC_L_MASK)) - flags = 2; - else -#endif - if (!(env->segs[R_CS].flags & DESC_B_MASK)) - flags = 1; - } - } -#endif -#ifdef TARGET_PPC - CPUArchState *env = mon_get_cpu_env(); - flags = msr_le << 16; - flags |= env->bfd_mach; -#endif - monitor_disas(mon, cs, addr, count, is_physical, flags); + monitor_disas(mon, cs, addr, count, is_physical); return; } diff --git a/target/alpha/translate.c b/target/alpha/translate.c index cfd63d5c1f94..629f35ec8ef2 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -3038,7 +3038,7 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) static void alpha_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) { qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size, 1); + log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); } static const TranslatorOps alpha_tr_ops = { diff --git a/target/arm/cpu.c b/target/arm/cpu.c index a7f03eabe8d8..47c8b2a85c09 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -33,6 +33,7 @@ #include "sysemu/sysemu.h" #include "sysemu/hw_accel.h" #include "kvm_arm.h" +#include "disas/capstone.h" static void arm_cpu_set_pc(CPUState *cs, vaddr value) { @@ -473,25 +474,11 @@ print_insn_thumb1(bfd_vma pc, disassemble_info *info) return print_insn_arm(pc | 1, info); } -static int arm_read_memory_func(bfd_vma memaddr, bfd_byte *b, - int length, struct disassemble_info *info) -{ - assert(info->read_memory_inner_func); - assert((info->flags & INSN_ARM_BE32) == 0 || length == 2 || length == 4); - - if ((info->flags & INSN_ARM_BE32) != 0 && length == 2) { - assert(info->endian == BFD_ENDIAN_LITTLE); - return info->read_memory_inner_func(memaddr ^ 2, (bfd_byte *)b, 2, - info); - } else { - return info->read_memory_inner_func(memaddr, b, length, info); - } -} - static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) { ARMCPU *ac = ARM_CPU(cpu); CPUARMState *env = &ac->env; + bool sctlr_b; if (is_a64(env)) { /* We might not be compiled with the A64 disassembler @@ -501,26 +488,40 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) #if defined(CONFIG_ARM_A64_DIS) info->print_insn = print_insn_arm_a64; #endif - } else if (env->thumb) { - info->print_insn = print_insn_thumb1; + info->cap_arch = CS_ARCH_ARM64; } else { - info->print_insn = print_insn_arm; + int cap_mode; + if (env->thumb) { + info->print_insn = print_insn_thumb1; + cap_mode = CS_MODE_THUMB; + } else { + info->print_insn = print_insn_arm; + cap_mode = CS_MODE_ARM; + } + if (arm_feature(env, ARM_FEATURE_V8)) { + cap_mode |= CS_MODE_V8; + } + if (arm_feature(env, ARM_FEATURE_M)) { + cap_mode |= CS_MODE_MCLASS; + } + info->cap_arch = CS_ARCH_ARM; + info->cap_mode = cap_mode; } - if (bswap_code(arm_sctlr_b(env))) { + + sctlr_b = arm_sctlr_b(env); + if (bswap_code(sctlr_b)) { #ifdef TARGET_WORDS_BIGENDIAN info->endian = BFD_ENDIAN_LITTLE; #else info->endian = BFD_ENDIAN_BIG; #endif } - if (info->read_memory_inner_func == NULL) { - info->read_memory_inner_func = info->read_memory_func; - info->read_memory_func = arm_read_memory_func; - } info->flags &= ~INSN_ARM_BE32; - if (arm_sctlr_b(env)) { +#ifndef CONFIG_USER_ONLY + if (sctlr_b) { info->flags |= INSN_ARM_BE32; } +#endif } uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index f6b364c04b4f..e98fbcf26128 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -11423,8 +11423,7 @@ static void aarch64_tr_disas_log(const DisasContextBase *dcbase, DisasContext *dc = container_of(dcbase, DisasContext, base); qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); - log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size, - 4 | (bswap_code(dc->sctlr_b) ? 2 : 0)); + log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size); } const TranslatorOps aarch64_translator_ops = { diff --git a/target/arm/translate.c b/target/arm/translate.c index a252429e6895..6ba4ae92dc9c 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -12372,8 +12372,7 @@ static void arm_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) DisasContext *dc = container_of(dcbase, DisasContext, base); qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); - log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size, - dc->thumb | (dc->sctlr_b << 1)); + log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size); } static const TranslatorOps arm_translator_ops = { diff --git a/target/cris/translate.c b/target/cris/translate.c index aa95f6701a38..283141984528 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -3296,8 +3296,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) qemu_log_lock(); qemu_log("--------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc->pc - pc_start, - env->pregs[PR_VR]); + log_target_disas(cs, pc_start, dc->pc - pc_start); qemu_log("\nisize=%d osize=%d\n", dc->pc - pc_start, tcg_op_buf_count()); qemu_log_unlock(); diff --git a/target/hppa/translate.c b/target/hppa/translate.c index dbd4cd861577..53aa1f88c40c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3902,7 +3902,7 @@ static void hppa_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) break; default: qemu_log("IN: %s\n", lookup_symbol(tb->pc)); - log_target_disas(cs, tb->pc, tb->size, 1); + log_target_disas(cs, tb->pc, tb->size); break; } } diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 2f556c0ea38a..6f21a5e5181d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -51,6 +51,8 @@ #include "hw/i386/apic_internal.h" #endif +#include "disas/capstone.h" + /* Cache topology CPUID constants: */ @@ -4093,6 +4095,22 @@ static bool x86_cpu_has_work(CPUState *cs) !(env->hflags & HF_SMM_MASK)); } +static void x86_disas_set_info(CPUState *cs, disassemble_info *info) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + info->mach = (env->hflags & HF_CS64_MASK ? bfd_mach_x86_64 + : env->hflags & HF_CS32_MASK ? bfd_mach_i386_i386 + : bfd_mach_i386_i8086); + info->print_insn = print_insn_i386; + + info->cap_arch = CS_ARCH_X86; + info->cap_mode = (env->hflags & HF_CS64_MASK ? CS_MODE_64 + : env->hflags & HF_CS32_MASK ? CS_MODE_32 + : CS_MODE_16); +} + static Property x86_cpu_properties[] = { #ifdef CONFIG_USER_ONLY /* apic_id = 0 by default for *-user, see commit 9886e834 */ @@ -4215,6 +4233,7 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) #ifdef CONFIG_TCG cc->tcg_initialize = tcg_x86_init; #endif + cc->disas_set_info = x86_disas_set_info; dc->user_creatable = true; } diff --git a/target/i386/translate.c b/target/i386/translate.c index 7df9233ded1d..088a9d9766a9 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -8548,15 +8548,9 @@ static void i386_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - int disas_flags = !dc->code32; qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); -#ifdef TARGET_X86_64 - if (dc->code64) { - disas_flags = 2; - } -#endif - log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size, disas_flags); + log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size); } static const TranslatorOps i386_tr_ops = { diff --git a/target/lm32/translate.c b/target/lm32/translate.c index 02ad3edad375..b8b2b13e36fb 100644 --- a/target/lm32/translate.c +++ b/target/lm32/translate.c @@ -1155,7 +1155,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) && qemu_log_in_addr_range(pc_start)) { qemu_log_lock(); qemu_log("\n"); - log_target_disas(cs, pc_start, dc->pc - pc_start, 0); + log_target_disas(cs, pc_start, dc->pc - pc_start); qemu_log("\nisize=%d osize=%d\n", dc->pc - pc_start, tcg_op_buf_count()); qemu_log_unlock(); diff --git a/target/m68k/translate.c b/target/m68k/translate.c index e7eaf03e5540..b60909222c33 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -5623,7 +5623,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) qemu_log_lock(); qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc->pc - pc_start, 0); + log_target_disas(cs, pc_start, dc->pc - pc_start); qemu_log("\n"); qemu_log_unlock(); } diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index e51821d6bd81..e7b5597c46c6 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1809,7 +1809,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) qemu_log_lock(); qemu_log("--------------\n"); #if DISAS_GNU - log_target_disas(cs, pc_start, dc->pc - pc_start, 0); + log_target_disas(cs, pc_start, dc->pc - pc_start); #endif qemu_log("\nisize=%d osize=%d\n", dc->pc - pc_start, tcg_op_buf_count()); diff --git a/target/mips/translate.c b/target/mips/translate.c index 82622c550eee..d0690f7df6cc 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -20369,7 +20369,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) && qemu_log_in_addr_range(pc_start)) { qemu_log_lock(); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, ctx.pc - pc_start, 0); + log_target_disas(cs, pc_start, ctx.pc - pc_start); qemu_log("\n"); qemu_log_unlock(); } diff --git a/target/nios2/translate.c b/target/nios2/translate.c index b5aaf56e862f..72329002ac9e 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -906,7 +906,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) && qemu_log_in_addr_range(tb->pc)) { qemu_log_lock(); qemu_log("IN: %s\n", lookup_symbol(tb->pc)); - log_target_disas(cs, tb->pc, dc->pc - tb->pc, 0); + log_target_disas(cs, tb->pc, dc->pc - tb->pc); qemu_log("\n"); qemu_log_unlock(); } diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index c9cbd2319ff8..2747b24cf060 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -1650,7 +1650,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(pc_start)) { - log_target_disas(cs, pc_start, tb->size, 0); + log_target_disas(cs, pc_start, tb->size); qemu_log("\n"); qemu_log_unlock(); } diff --git a/target/ppc/translate.c b/target/ppc/translate.c index e7e4983cbffd..998fbed84877 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -7397,12 +7397,9 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) #if defined(DEBUG_DISAS) if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(pc_start)) { - int flags; - flags = env->bfd_mach; - flags |= ctx.le_mode << 16; qemu_log_lock(); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, ctx.nip - pc_start, flags); + log_target_disas(cs, pc_start, ctx.nip - pc_start); qemu_log("\n"); qemu_log_unlock(); } diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 61a9552c1cf9..b9c49c22f29f 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -35,6 +35,7 @@ #include "mmu-book3s-v3.h" #include "sysemu/qtest.h" #include "qemu/cutils.h" +#include "disas/capstone.h" //#define PPC_DUMP_CPU //#define PPC_DEBUG_SPR @@ -10515,6 +10516,31 @@ static gchar *ppc_gdb_arch_name(CPUState *cs) #endif } +static void ppc_disas_set_info(CPUState *cs, disassemble_info *info) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + if ((env->hflags >> MSR_LE) & 1) { + info->endian = BFD_ENDIAN_LITTLE; + } + info->mach = env->bfd_mach; + if (!env->bfd_mach) { +#ifdef TARGET_PPC64 + info->mach = bfd_mach_ppc64; +#else + info->mach = bfd_mach_ppc; +#endif + } + info->disassembler_options = (char *)"any"; + info->print_insn = print_insn_ppc; + + info->cap_arch = CS_ARCH_PPC; +#ifdef TARGET_PPC64 + info->cap_mode = CS_MODE_64; +#endif +} + static Property ppc_cpu_properties[] = { DEFINE_PROP_BOOL("pre-2.8-migration", PowerPCCPU, pre_2_8_migration, false), DEFINE_PROP_BOOL("pre-2.10-migration", PowerPCCPU, pre_2_10_migration, @@ -10581,7 +10607,8 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) #ifdef CONFIG_TCG cc->tcg_initialize = ppc_translate_init; #endif - + cc->disas_set_info = ppc_disas_set_info; + dc->fw_name = "PowerPC,UNKNOWN"; } diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 55db8f3446f9..dee72a787d4c 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -5982,7 +5982,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) qemu_log("IN: EXECUTE %016" PRIx64 "\n", dc.ex_value); } else { qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc.pc - pc_start, 1); + log_target_disas(cs, pc_start, dc.pc - pc_start); qemu_log("\n"); } qemu_log_unlock(); diff --git a/target/sh4/translate.c b/target/sh4/translate.c index c98f8d31e39b..703020fe87f5 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -2336,7 +2336,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) && qemu_log_in_addr_range(pc_start)) { qemu_log_lock(); qemu_log("IN:\n"); /* , lookup_symbol(pc_start)); */ - log_target_disas(cs, pc_start, ctx.pc - pc_start, 0); + log_target_disas(cs, pc_start, ctx.pc - pc_start); qemu_log("\n"); qemu_log_unlock(); } diff --git a/target/sparc/translate.c b/target/sparc/translate.c index d5e866fe0d50..849a02aebdb9 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5849,7 +5849,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) qemu_log_lock(); qemu_log("--------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, last_pc + 4 - pc_start, 0); + log_target_disas(cs, pc_start, last_pc + 4 - pc_start); qemu_log("\n"); qemu_log_unlock(); } diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 18102e54cb1c..4e5b0836654c 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -8837,7 +8837,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) && qemu_log_in_addr_range(pc_start)) { qemu_log_lock(); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, ctx.pc - pc_start, 0); + log_target_disas(cs, pc_start, ctx.pc - pc_start); qemu_log("\n"); qemu_log_unlock(); } diff --git a/target/unicore32/translate.c b/target/unicore32/translate.c index de2a7ceee744..384aa86027eb 100644 --- a/target/unicore32/translate.c +++ b/target/unicore32/translate.c @@ -2027,7 +2027,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) qemu_log_lock(); qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc->pc - pc_start, 0); + log_target_disas(cs, pc_start, dc->pc - pc_start); qemu_log("\n"); qemu_log_unlock(); } diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 32c41599496b..20f7ddf042f8 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -3247,7 +3247,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) qemu_log_lock(); qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc.pc - pc_start, 0); + log_target_disas(cs, pc_start, dc.pc - pc_start); qemu_log("\n"); qemu_log_unlock(); }