Newer
Older
/* General "disassemble this chunk" code. Used for debugging. */
#include "disas/dis-asm.h"
#include "qemu/qemu-print.h"
#include "disas/capstone.h"
typedef struct CPUDebug {
struct disassemble_info info;
/* Filled in by elfload.c. Simplistic, but will do for now. */
/*
* Get LENGTH bytes from info's buffer, at host address memaddr.
* Transfer them to myaddr.
*/
static int host_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length,
struct disassemble_info *info)
if (memaddr < info->buffer_vma
|| memaddr + length > info->buffer_vma + info->buffer_length) {
/* Out of bounds. Use EIO because GDB uses it. */
return EIO;
memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
return 0;
/*
* Get LENGTH bytes from info's buffer, at target address memaddr.
* Transfer them to myaddr.
*/
static int target_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length,
struct disassemble_info *info)
CPUDebug *s = container_of(info, CPUDebug, info);
int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0);
return r ? EIO : 0;
/*
* Print an error message. We can assume that this is in response to
* an error return from {host,target}_read_memory.
*/
static void perror_memory(int status, bfd_vma memaddr,
struct disassemble_info *info)
if (status != EIO) {
/* Can't happen. */
info->fprintf_func(info->stream, "Unknown error %d\n", status);
} else {
/* Address between memaddr and memaddr + len was out of bounds. */
info->fprintf_func(info->stream,
"Address 0x%" PRIx64 " is out of bounds.\n",
memaddr);
}
/* Print address in hex. */
static void print_address(bfd_vma addr, struct disassemble_info *info)
info->fprintf_func(info->stream, "0x%" PRIx64, addr);
/* Print address in hex, truncated to the width of a host virtual address. */
static void host_print_address(bfd_vma addr, struct disassemble_info *info)
print_address((uintptr_t)addr, info);
/* Stub prevents some fruitless earching in optabs disassemblers. */
static int symbol_at_address(bfd_vma addr, struct disassemble_info *info)
static int print_insn_objdump(bfd_vma pc, disassemble_info *info,
const char *prefix)
{
int i, n = info->buffer_length;
uint8_t *buf = g_malloc(n);
info->read_memory_func(pc, buf, n, info);
for (i = 0; i < n; ++i) {
if (i % 32 == 0) {
info->fprintf_func(info->stream, "\n%s: ", prefix);
}
info->fprintf_func(info->stream, "%02x", buf[i]);
}
g_free(buf);
return n;
}
static int print_insn_od_host(bfd_vma pc, disassemble_info *info)
{
return print_insn_objdump(pc, info, "OBJD-H");
}
static int print_insn_od_target(bfd_vma pc, disassemble_info *info)
{
return print_insn_objdump(pc, info, "OBJD-T");
}
static void initialize_debug(CPUDebug *s)
{
memset(s, 0, sizeof(*s));
s->info.arch = bfd_arch_unknown;
s->info.cap_arch = -1;
s->info.cap_insn_unit = 4;
s->info.cap_insn_split = 4;
s->info.memory_error_func = perror_memory;
s->info.symbol_at_address_func = symbol_at_address;
}
static void initialize_debug_target(CPUDebug *s, CPUState *cpu)
{
initialize_debug(s);
s->cpu = cpu;
s->info.read_memory_func = target_read_memory;
s->info.print_address_func = print_address;
s->info.endian = BFD_ENDIAN_BIG;
#else
s->info.endian = BFD_ENDIAN_LITTLE;
#endif
CPUClass *cc = CPU_GET_CLASS(cpu);
if (cc->disas_set_info) {
cc->disas_set_info(cpu, &s->info);
}
}
static void initialize_debug_host(CPUDebug *s)
{
initialize_debug(s);
s->info.read_memory_func = host_read_memory;
s->info.print_address_func = host_print_address;
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
s->info.endian = BFD_ENDIAN_BIG;
#else
s->info.endian = BFD_ENDIAN_LITTLE;
#endif
#if defined(CONFIG_TCG_INTERPRETER)
s->info.print_insn = print_insn_tci;
#elif defined(__i386__)
s->info.mach = bfd_mach_i386_i386;
s->info.cap_arch = CS_ARCH_X86;
s->info.cap_mode = CS_MODE_32;
s->info.cap_insn_unit = 1;
s->info.cap_insn_split = 8;
#elif defined(__x86_64__)
s->info.mach = bfd_mach_x86_64;
s->info.cap_arch = CS_ARCH_X86;
s->info.cap_mode = CS_MODE_64;
s->info.cap_insn_unit = 1;
s->info.cap_insn_split = 8;
#elif defined(_ARCH_PPC)
s->info.cap_arch = CS_ARCH_PPC;
# ifdef _ARCH_PPC64
s->info.cap_mode = CS_MODE_64;
# endif
#elif defined(__riscv) && defined(CONFIG_RISCV_DIS)
#if defined(_ILP32) || (__riscv_xlen == 32)
s->info.print_insn = print_insn_riscv32;
#elif defined(_LP64)
s->info.print_insn = print_insn_riscv64;
#else
#error unsupported RISC-V ABI
#endif
#elif defined(__aarch64__)
s->info.cap_arch = CS_ARCH_ARM64;
#elif defined(__alpha__)
s->info.print_insn = print_insn_alpha;
#elif defined(__sparc__)
s->info.print_insn = print_insn_sparc;
s->info.mach = bfd_mach_sparc_v9b;
#elif defined(__arm__)
/* TCG only generates code for arm mode. */
s->info.cap_arch = CS_ARCH_ARM;
#elif defined(__MIPSEB__)
s->info.print_insn = print_insn_big_mips;
#elif defined(__MIPSEL__)
s->info.print_insn = print_insn_little_mips;
#elif defined(__m68k__)
s->info.print_insn = print_insn_m68k;
#elif defined(__s390__)
s->info.cap_arch = CS_ARCH_SYSZ;
s->info.cap_insn_unit = 2;
s->info.cap_insn_split = 6;
#elif defined(__hppa__)
s->info.print_insn = print_insn_hppa;
#endif
}
/* Disassemble this for me please... (debugging). */
void target_disas(FILE *out, CPUState *cpu, target_ulong code,
initialize_debug_target(&s, cpu);
s.info.fprintf_func = fprintf;
s.info.stream = out;
s.info.buffer_vma = code;
s.info.buffer_length = size;
if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) {
return;
}
if (s.info.print_insn == NULL) {
s.info.print_insn = print_insn_od_target;
for (pc = code; size > 0; pc += count, size -= count) {
count = s.info.print_insn(pc, &s.info);
fprintf(out, "\n");
if (count < 0)
break;

malc
committed
if (size < count) {
fprintf(out,
"Disassembler disagrees with translator over instruction "
"decoding\n"
"Please report this to qemu-devel@nongnu.org\n");
break;
}
static int plugin_printf(FILE *stream, const char *fmt, ...)
{
/* We abuse the FILE parameter to pass a GString. */
GString *s = (GString *)stream;
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 */
}
/*
* 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)
{
CPUDebug s;
initialize_debug_target(&s, cpu);
s.info.fprintf_func = plugin_printf;
s.info.stream = (FILE *)ds; /* abuse this slot */
s.info.buffer_vma = addr;
s.info.buffer_length = size;
s.info.print_address_func = plugin_print_address;
if (s.info.cap_arch >= 0 && cap_disas_plugin(&s.info, addr, size)) {
; /* done */
} else if (s.info.print_insn) {
s.info.print_insn(addr, &s.info);
} else {
; /* cannot disassemble -- return empty string */
/* Return the buffer, freeing the GString container. */
return g_string_free(ds, false);
/* Disassemble this for me please... (debugging). */
void disas(FILE *out, const void *code, unsigned long size)
initialize_debug_host(&s);
s.info.fprintf_func = fprintf;
s.info.stream = out;
s.info.buffer = code;
s.info.buffer_vma = (uintptr_t)code;
s.info.buffer_length = size;
if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) {
if (s.info.print_insn == NULL) {
s.info.print_insn = print_insn_od_host;
for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) {
fprintf(out, "0x%08" PRIxPTR ": ", pc);
count = s.info.print_insn(pc, &s.info);
fprintf(out, "\n");
if (count < 0) {
break;
}
}
/* Look up symbol for debugging purpose. Returns "" if unknown. */
const char *lookup_symbol(target_ulong orig_addr)
const char *symbol = "";

Thiemo Seufer
committed
symbol = s->lookup_symbol(s, orig_addr);
if (symbol[0] != '\0') {
break;
}
return symbol;

Fabrice Bellard
committed
#if !defined(CONFIG_USER_ONLY)
#include "monitor/monitor.h"

Fabrice Bellard
committed
static int
physical_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length,
struct disassemble_info *info)

Fabrice Bellard
committed
{
CPUDebug *s = container_of(info, CPUDebug, info);
MemTxResult res;
res = address_space_read(s->cpu->as, memaddr, MEMTXATTRS_UNSPECIFIED,
myaddr, length);
return res == MEMTX_OK ? 0 : EIO;

Fabrice Bellard
committed
}
/* Disassembler for the monitor. */
void monitor_disas(Monitor *mon, CPUState *cpu,
target_ulong pc, int nb_insn, int is_physical)

Fabrice Bellard
committed
{
int count, i;

Fabrice Bellard
committed
initialize_debug_target(&s, cpu);
s.info.fprintf_func = qemu_fprintf;
if (is_physical) {
s.info.read_memory_func = physical_read_memory;
if (s.info.cap_arch >= 0 && cap_disas_monitor(&s.info, pc, nb_insn)) {
return;
}
if (!s.info.print_insn) {
monitor_printf(mon, "0x" TARGET_FMT_lx
": Asm output not supported on this arch\n", pc);
return;
}

Fabrice Bellard
committed
for(i = 0; i < nb_insn; i++) {
monitor_printf(mon, "0x" TARGET_FMT_lx ": ", pc);
count = s.info.print_insn(pc, &s.info);

Fabrice Bellard
committed
if (count < 0)
break;
pc += count;
}
}
#endif