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_host(i, p, s) false
|
||||
# define cap_disas_monitor(i, p, c) false
|
||||
# define cap_disas_plugin(i, p, c) false
|
||||
#endif /* CONFIG_CAPSTONE */
|
||||
|
||||
/* 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). */
|
||||
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,
|
||||
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. */
|
||||
const char *lookup_symbol(target_ulong orig_addr);
|
||||
#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_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
|
||||
* @id: plugin ID
|
||||
|
@ -39,7 +39,8 @@
|
||||
#include "cpu.h"
|
||||
#include "sysemu/sysemu.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"
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
#include "qemu/plugin-memory.h"
|
||||
@ -212,6 +213,12 @@ void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn)
|
||||
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
|
||||
* memory access.
|
||||
|
@ -25,6 +25,7 @@
|
||||
qemu_plugin_insn_size;
|
||||
qemu_plugin_insn_vaddr;
|
||||
qemu_plugin_insn_haddr;
|
||||
qemu_plugin_insn_disas;
|
||||
qemu_plugin_mem_size_shift;
|
||||
qemu_plugin_mem_is_sign_extended;
|
||||
qemu_plugin_mem_is_big_endian;
|
||||
|
Loading…
Reference in New Issue
Block a user