Commit 6e6430a8 authored by Peter Maydell's avatar Peter Maydell
Browse files

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: default avatarPeter Maydell <peter.maydell@linaro.org>

# Conflicts:
#	target/i386/cpu.c
#	target/ppc/translate_init.c
parents 74d7fc7f e219c499
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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
+15 −0
Original line number Diff line number Diff line
@@ -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))

capstone @ 22ead3e0

Original line number Diff line number Diff line
Subproject commit 22ead3e0bfdb87516656453336160e0a37b066bf
+72 −0
Original line number Diff line number Diff line
@@ -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
+223 −85
Original line number Diff line number Diff line
@@ -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);
    }
    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);
Loading