Capstone disassembler

-----BEGIN PGP SIGNATURE-----
 
 iQEcBAABAgAGBQJZ8bGHAAoJEGTfOOivfiFfOXQH/jc3BbQ+ulxvQSgA3rI2JE1e
 Ww5FK5HEs4qZU3hz4EtE2Cd5p7qV5I4tWRtbxzc6BGBwLsfz3a60Abx7726sZiH0
 ZuULTsWXQ/71XfZHQysgOSoy36G8xj/1yvrMWHjDCfWp/pzz479YXWSSn2TWEHpI
 jI6nKP5ALdv5XTAaglGaNzqVeWgjKXJn4O8qZFS7axj7hndzLFguymfm8rV8DAdd
 LRuYWOizzzJ0dcaO/HHyLTzSl7rR0g+DmcOAuFCREy4f+r6tXijwiirB5f7ZJiqc
 hgEBq/6NfztW2+pAUSxqI2Kuq1zVETTpZORH1+UxvVk9GPu1ouYldMx0NrYhDtc=
 =fC5W
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/rth/tags/pull-dis-20171026' into staging

Capstone disassembler

# gpg: Signature made Thu 26 Oct 2017 10:57:27 BST
# gpg:                using RSA key 0x64DF38E8AF7E215F
# gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>"
# Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A  05C0 64DF 38E8 AF7E 215F

* remotes/rth/tags/pull-dis-20171026:
  disas: Add capstone as submodule
  disas: Remove monitor_disas_is_physical
  ppc: Support Capstone in disas_set_info
  arm: Support Capstone in disas_set_info
  i386: Support Capstone in disas_set_info
  disas: Support the Capstone disassembler library
  disas: Remove unused flags arguments
  target/arm: Don't set INSN_ARM_BE32 for CONFIG_USER_ONLY
  target/arm: Move BE32 disassembler fixup
  target/ppc: Convert to disas_set_info hook
  target/i386: Convert to disas_set_info hook

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

# Conflicts:
#	target/i386/cpu.c
#	target/ppc/translate_init.c
This commit is contained in:
Peter Maydell 2017-10-27 08:04:51 +01:00
commit 6e6430a821
33 changed files with 468 additions and 185 deletions

3
.gitmodules vendored
View file

@ -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

View file

@ -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))

1
capstone Submodule

@ -0,0 +1 @@
Subproject commit 22ead3e0bfdb87516656453336160e0a37b066bf

72
configure vendored
View file

@ -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

308
disas.c
View file

@ -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;
if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) {
return;
}
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
}
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;
if (s.info.cap_arch >= 0 && cap_disas_monitor(&s.info, pc, nb_insn)) {
return;
}
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;
}
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);

View file

@ -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

View file

@ -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, \

38
include/disas/capstone.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef QEMU_CAPSTONE_H
#define QEMU_CAPSTONE_H 1
#ifdef CONFIG_CAPSTONE
#include <capstone.h>
#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 */

View file

@ -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);

View file

@ -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)

View file

@ -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;
}

View file

@ -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 = {

View file

@ -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)

View file

@ -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 = {

View file

@ -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 = {

View file

@ -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();

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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 = {

View file

@ -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();

View file

@ -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();
}

View file

@ -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());

View file

@ -20369,7 +20369,7 @@ done_generating:
&& 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();
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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";
}

View file

@ -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();

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -2027,7 +2027,7 @@ done_generating:
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();
}

View file

@ -3247,7 +3247,7 @@ done:
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();
}