plugin: add qemu_plugin_insn_disas helper
Give the plugins access to the QEMU dissasembler so they don't have to re-invent the wheel. We generate a warning when there are spare bytes in the decode buffer. This is usually due to the front end loading in more bytes than decoded. Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
parent
5901b2e15b
commit
cbafa2362a
110
disas.c
110
disas.c
@ -418,6 +418,7 @@ static bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count)
|
|||||||
# define cap_disas_target(i, p, s) false
|
# define cap_disas_target(i, p, s) false
|
||||||
# define cap_disas_host(i, p, s) false
|
# define cap_disas_host(i, p, s) false
|
||||||
# define cap_disas_monitor(i, p, c) false
|
# define cap_disas_monitor(i, p, c) false
|
||||||
|
# define cap_disas_plugin(i, p, c) false
|
||||||
#endif /* CONFIG_CAPSTONE */
|
#endif /* CONFIG_CAPSTONE */
|
||||||
|
|
||||||
/* Disassemble this for me please... (debugging). */
|
/* Disassemble this for me please... (debugging). */
|
||||||
@ -475,6 +476,115 @@ 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;
|
||||||
|
int initial_len = s->len;
|
||||||
|
|
||||||
|
va_start(va, fmt);
|
||||||
|
g_string_append_vprintf(s, fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
return s->len - initial_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void plugin_print_address(bfd_vma addr, struct disassemble_info *info)
|
||||||
|
{
|
||||||
|
/* does nothing */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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
|
||||||
|
* bytes than it needed.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
INIT_DISASSEMBLE_INFO(s.info, NULL, plugin_printf);
|
||||||
|
|
||||||
|
s.cpu = cpu;
|
||||||
|
s.info.read_memory_func = target_read_memory;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_strdup(ds->str);
|
||||||
|
}
|
||||||
|
|
||||||
/* Disassemble this for me please... (debugging). */
|
/* Disassemble this for me please... (debugging). */
|
||||||
void disas(FILE *out, void *code, unsigned long size)
|
void disas(FILE *out, void *code, unsigned long size)
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,8 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code,
|
|||||||
void monitor_disas(Monitor *mon, CPUState *cpu,
|
void monitor_disas(Monitor *mon, CPUState *cpu,
|
||||||
target_ulong pc, int nb_insn, int is_physical);
|
target_ulong pc, int nb_insn, int is_physical);
|
||||||
|
|
||||||
|
char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size);
|
||||||
|
|
||||||
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
/* Look up symbol for debugging purpose. Returns "" if unknown. */
|
||||||
const char *lookup_symbol(target_ulong orig_addr);
|
const char *lookup_symbol(target_ulong orig_addr);
|
||||||
#endif
|
#endif
|
||||||
|
@ -351,6 +351,15 @@ qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id,
|
|||||||
qemu_plugin_vcpu_syscall_ret_cb_t cb);
|
qemu_plugin_vcpu_syscall_ret_cb_t cb);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qemu_plugin_insn_disas() - return disassembly string for instruction
|
||||||
|
* @insn: instruction reference
|
||||||
|
*
|
||||||
|
* Returns an allocated string containing the disassembly
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qemu_plugin_vcpu_for_each() - iterate over the existing vCPU
|
* qemu_plugin_vcpu_for_each() - iterate over the existing vCPU
|
||||||
* @id: plugin ID
|
* @id: plugin ID
|
||||||
|
@ -39,7 +39,8 @@
|
|||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "tcg/tcg.h"
|
#include "tcg/tcg.h"
|
||||||
#include "trace/mem-internal.h" /* mem_info macros */
|
#include "exec/exec-all.h"
|
||||||
|
#include "disas/disas.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
#include "qemu/plugin-memory.h"
|
#include "qemu/plugin-memory.h"
|
||||||
@ -212,6 +213,12 @@ void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn)
|
|||||||
return insn->haddr;
|
return insn->haddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn)
|
||||||
|
{
|
||||||
|
CPUState *cpu = current_cpu;
|
||||||
|
return plugin_disas(cpu, insn->vaddr, insn->data->len);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The memory queries allow the plugin to query information about a
|
* The memory queries allow the plugin to query information about a
|
||||||
* memory access.
|
* memory access.
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
qemu_plugin_insn_size;
|
qemu_plugin_insn_size;
|
||||||
qemu_plugin_insn_vaddr;
|
qemu_plugin_insn_vaddr;
|
||||||
qemu_plugin_insn_haddr;
|
qemu_plugin_insn_haddr;
|
||||||
|
qemu_plugin_insn_disas;
|
||||||
qemu_plugin_mem_size_shift;
|
qemu_plugin_mem_size_shift;
|
||||||
qemu_plugin_mem_is_sign_extended;
|
qemu_plugin_mem_is_sign_extended;
|
||||||
qemu_plugin_mem_is_big_endian;
|
qemu_plugin_mem_is_big_endian;
|
||||||
|
Loading…
Reference in New Issue
Block a user