disas.c 11.4 KB
Newer Older
bellard's avatar
bellard committed
1
/* General "disassemble this chunk" code.  Used for debugging. */
Peter Maydell's avatar
Peter Maydell committed
2
#include "qemu/osdep.h"
3
#include "qemu-common.h"
4
#include "disas/bfd.h"
bellard's avatar
bellard committed
5 6
#include "elf.h"

7
#include "cpu.h"
8
#include "disas/disas.h"
9

10 11
typedef struct CPUDebug {
    struct disassemble_info info;
12
    CPUState *cpu;
13 14
} CPUDebug;

bellard's avatar
bellard committed
15
/* Filled in by elfload.c.  Simplistic, but will do for now. */
bellard's avatar
bellard committed
16
struct syminfo *syminfos = NULL;
bellard's avatar
bellard committed
17

18 19 20
/* Get LENGTH bytes from info's buffer, at target address memaddr.
   Transfer them to myaddr.  */
int
21 22
buffer_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length,
                   struct disassemble_info *info)
23
{
24 25 26 27 28 29
    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;
30 31
}

32 33 34
/* Get LENGTH bytes from info's buffer, at target address memaddr.
   Transfer them to myaddr.  */
static int
bellard's avatar
bellard committed
35 36 37 38
target_read_memory (bfd_vma memaddr,
                    bfd_byte *myaddr,
                    int length,
                    struct disassemble_info *info)
39
{
40 41
    CPUDebug *s = container_of(info, CPUDebug, info);

42
    cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0);
43 44 45
    return 0;
}

46 47 48
/* Print an error message.  We can assume that this is in response to
   an error return from buffer_read_memory.  */
void
49
perror_memory (int status, bfd_vma memaddr, struct disassemble_info *info)
50 51 52 53 54 55 56 57
{
  if (status != EIO)
    /* Can't happen.  */
    (*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
  else
    /* Actually, address between memaddr and memaddr + len was
       out of bounds.  */
    (*info->fprintf_func) (info->stream,
bellard's avatar
bellard committed
58
			   "Address 0x%" PRIx64 " is out of bounds.\n", memaddr);
59 60
}

Jim Meyering's avatar
Jim Meyering committed
61
/* This could be in a separate file, to save minuscule amounts of space
62 63 64 65 66 67 68
   in statically linked executables.  */

/* Just print the address is hex.  This is included for completeness even
   though both GDB and objdump provide their own (to print symbolic
   addresses).  */

void
69
generic_print_address (bfd_vma addr, struct disassemble_info *info)
70
{
bellard's avatar
bellard committed
71
    (*info->fprintf_func) (info->stream, "0x%" PRIx64, addr);
72 73
}

74 75 76 77 78 79 80 81
/* Print address in hex, truncated to the width of a host virtual address. */
static void
generic_print_host_address(bfd_vma addr, struct disassemble_info *info)
{
    uint64_t mask = ~0ULL >> (64 - (sizeof(void *) * 8));
    generic_print_address(addr & mask, info);
}

82 83 84
/* Just return the given address.  */

int
85
generic_symbol_at_address (bfd_vma addr, struct disassemble_info *info)
86 87 88 89
{
  return 1;
}

Aurelien Jarno's avatar
Aurelien Jarno committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
bfd_vma bfd_getl64 (const bfd_byte *addr)
{
  unsigned long long v;

  v = (unsigned long long) addr[0];
  v |= (unsigned long long) addr[1] << 8;
  v |= (unsigned long long) addr[2] << 16;
  v |= (unsigned long long) addr[3] << 24;
  v |= (unsigned long long) addr[4] << 32;
  v |= (unsigned long long) addr[5] << 40;
  v |= (unsigned long long) addr[6] << 48;
  v |= (unsigned long long) addr[7] << 56;
  return (bfd_vma) v;
}

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
bfd_vma bfd_getl32 (const bfd_byte *addr)
{
  unsigned long v;

  v = (unsigned long) addr[0];
  v |= (unsigned long) addr[1] << 8;
  v |= (unsigned long) addr[2] << 16;
  v |= (unsigned long) addr[3] << 24;
  return (bfd_vma) v;
}

bfd_vma bfd_getb32 (const bfd_byte *addr)
{
  unsigned long v;

  v = (unsigned long) addr[0] << 24;
  v |= (unsigned long) addr[1] << 16;
  v |= (unsigned long) addr[2] << 8;
  v |= (unsigned long) addr[3];
  return (bfd_vma) v;
}

bellard's avatar
bellard committed
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
bfd_vma bfd_getl16 (const bfd_byte *addr)
{
  unsigned long v;

  v = (unsigned long) addr[0];
  v |= (unsigned long) addr[1] << 8;
  return (bfd_vma) v;
}

bfd_vma bfd_getb16 (const bfd_byte *addr)
{
  unsigned long v;

  v = (unsigned long) addr[0] << 24;
  v |= (unsigned long) addr[1] << 16;
  return (bfd_vma) v;
}

145 146 147 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
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");
}

ths's avatar
ths committed
174
/* Disassemble this for me please... (debugging). 'flags' has the following
175
   values:
Frediano Ziglio's avatar
Frediano Ziglio committed
176
    i386 - 1 means 16 bit code, 2 means 64 bit code
Tom Musta's avatar
Tom Musta committed
177 178
    ppc  - bits 0:15 specify (optionally) the machine instruction set;
           bit 16 indicates little endian.
179 180
    other targets - unused
 */
181
void target_disas(FILE *out, CPUState *cpu, target_ulong code,
182
                  target_ulong size, int flags)
bellard's avatar
bellard committed
183
{
184
    CPUClass *cc = CPU_GET_CLASS(cpu);
bellard's avatar
bellard committed
185
    target_ulong pc;
bellard's avatar
bellard committed
186
    int count;
187
    CPUDebug s;
bellard's avatar
bellard committed
188

189
    INIT_DISASSEMBLE_INFO(s.info, out, fprintf);
bellard's avatar
bellard committed
190

191
    s.cpu = cpu;
192 193 194
    s.info.read_memory_func = target_read_memory;
    s.info.buffer_vma = code;
    s.info.buffer_length = size;
195
    s.info.print_address_func = generic_print_address;
bellard's avatar
bellard committed
196 197

#ifdef TARGET_WORDS_BIGENDIAN
198
    s.info.endian = BFD_ENDIAN_BIG;
bellard's avatar
bellard committed
199
#else
200
    s.info.endian = BFD_ENDIAN_LITTLE;
bellard's avatar
bellard committed
201
#endif
202 203 204 205 206

    if (cc->disas_set_info) {
        cc->disas_set_info(cpu, &s.info);
    }

bellard's avatar
bellard committed
207
#if defined(TARGET_I386)
208 209 210 211 212 213 214
    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;
    }
215
    s.info.print_insn = print_insn_i386;
bellard's avatar
bellard committed
216
#elif defined(TARGET_PPC)
Tom Musta's avatar
Tom Musta committed
217
    if ((flags >> 16) & 1) {
218 219
        s.info.endian = BFD_ENDIAN_LITTLE;
    }
220
    if (flags & 0xFFFF) {
Tom Musta's avatar
Tom Musta committed
221
        /* If we have a precise definition of the instruction set, use it. */
222
        s.info.mach = flags & 0xFFFF;
223
    } else {
bellard's avatar
bellard committed
224
#ifdef TARGET_PPC64
225
        s.info.mach = bfd_mach_ppc64;
bellard's avatar
bellard committed
226
#else
227
        s.info.mach = bfd_mach_ppc;
bellard's avatar
bellard committed
228
#endif
229
    }
230
    s.info.disassembler_options = (char *)"any";
231
    s.info.print_insn = print_insn_ppc;
232
#endif
233 234
    if (s.info.print_insn == NULL) {
        s.info.print_insn = print_insn_od_target;
235
    }
236

237
    for (pc = code; size > 0; pc += count, size -= count) {
bellard's avatar
bellard committed
238
	fprintf(out, "0x" TARGET_FMT_lx ":  ", pc);
239
	count = s.info.print_insn(pc, &s.info);
bellard's avatar
bellard committed
240 241 242 243 244 245
#if 0
        {
            int i;
            uint8_t b;
            fprintf(out, " {");
            for(i = 0; i < count; i++) {
246
                target_read_memory(pc + i, &b, 1, &s.info);
bellard's avatar
bellard committed
247 248 249 250 251 252 253 254
                fprintf(out, " %02x", b);
            }
            fprintf(out, " }");
        }
#endif
	fprintf(out, "\n");
	if (count < 0)
	    break;
255 256 257 258 259 260 261
        if (size < count) {
            fprintf(out,
                    "Disassembler disagrees with translator over instruction "
                    "decoding\n"
                    "Please report this to qemu-devel@nongnu.org\n");
            break;
        }
bellard's avatar
bellard committed
262 263 264 265 266 267
    }
}

/* Disassemble this for me please... (debugging). */
void disas(FILE *out, void *code, unsigned long size)
{
268
    uintptr_t pc;
bellard's avatar
bellard committed
269
    int count;
270
    CPUDebug s;
271
    int (*print_insn)(bfd_vma pc, disassemble_info *info) = NULL;
bellard's avatar
bellard committed
272

273 274
    INIT_DISASSEMBLE_INFO(s.info, out, fprintf);
    s.info.print_address_func = generic_print_host_address;
bellard's avatar
bellard committed
275

276 277 278
    s.info.buffer = code;
    s.info.buffer_vma = (uintptr_t)code;
    s.info.buffer_length = size;
bellard's avatar
bellard committed
279

280
#ifdef HOST_WORDS_BIGENDIAN
281
    s.info.endian = BFD_ENDIAN_BIG;
bellard's avatar
bellard committed
282
#else
283
    s.info.endian = BFD_ENDIAN_LITTLE;
bellard's avatar
bellard committed
284
#endif
Stefan Weil's avatar
Stefan Weil committed
285 286 287
#if defined(CONFIG_TCG_INTERPRETER)
    print_insn = print_insn_tci;
#elif defined(__i386__)
288
    s.info.mach = bfd_mach_i386_i386;
bellard's avatar
bellard committed
289
    print_insn = print_insn_i386;
290
#elif defined(__x86_64__)
291
    s.info.mach = bfd_mach_x86_64;
bellard's avatar
bellard committed
292
    print_insn = print_insn_i386;
malc's avatar
malc committed
293
#elif defined(_ARCH_PPC)
294
    s.info.disassembler_options = (char *)"any";
bellard's avatar
bellard committed
295
    print_insn = print_insn_ppc;
296 297
#elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS)
    print_insn = print_insn_arm_a64;
bellard's avatar
bellard committed
298
#elif defined(__alpha__)
bellard's avatar
bellard committed
299
    print_insn = print_insn_alpha;
300
#elif defined(__sparc__)
bellard's avatar
bellard committed
301
    print_insn = print_insn_sparc;
302
    s.info.mach = bfd_mach_sparc_v9b;
303
#elif defined(__arm__)
bellard's avatar
bellard committed
304
    print_insn = print_insn_arm;
bellard's avatar
bellard committed
305 306 307 308
#elif defined(__MIPSEB__)
    print_insn = print_insn_big_mips;
#elif defined(__MIPSEL__)
    print_insn = print_insn_little_mips;
bellard's avatar
bellard committed
309 310
#elif defined(__m68k__)
    print_insn = print_insn_m68k;
311 312
#elif defined(__s390__)
    print_insn = print_insn_s390;
aurel32's avatar
aurel32 committed
313 314
#elif defined(__hppa__)
    print_insn = print_insn_hppa;
Aurelien Jarno's avatar
Aurelien Jarno committed
315 316
#elif defined(__ia64__)
    print_insn = print_insn_ia64;
bellard's avatar
bellard committed
317
#endif
318 319 320
    if (print_insn == NULL) {
        print_insn = print_insn_od_host;
    }
321 322
    for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) {
        fprintf(out, "0x%08" PRIxPTR ":  ", pc);
323
        count = print_insn(pc, &s.info);
bellard's avatar
bellard committed
324 325 326 327 328 329 330
	fprintf(out, "\n");
	if (count < 0)
	    break;
    }
}

/* Look up symbol for debugging purpose.  Returns "" if unknown. */
bellard's avatar
bellard committed
331
const char *lookup_symbol(target_ulong orig_addr)
bellard's avatar
bellard committed
332
{
333
    const char *symbol = "";
bellard's avatar
bellard committed
334
    struct syminfo *s;
335

bellard's avatar
bellard committed
336
    for (s = syminfos; s; s = s->next) {
337 338 339 340
        symbol = s->lookup_symbol(s, orig_addr);
        if (symbol[0] != '\0') {
            break;
        }
bellard's avatar
bellard committed
341
    }
342 343

    return symbol;
bellard's avatar
bellard committed
344
}
345 346 347

#if !defined(CONFIG_USER_ONLY)

348
#include "monitor/monitor.h"
349

350 351 352
static int monitor_disas_is_physical;

static int
353 354
monitor_read_memory (bfd_vma memaddr, bfd_byte *myaddr, int length,
                     struct disassemble_info *info)
355
{
356 357
    CPUDebug *s = container_of(info, CPUDebug, info);

358
    if (monitor_disas_is_physical) {
359
        cpu_physical_memory_read(memaddr, myaddr, length);
360
    } else {
361
        cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0);
362 363 364 365
    }
    return 0;
}

366 367
/* Disassembler for the monitor.
   See target_disas for a description of flags. */
368
void monitor_disas(Monitor *mon, CPUState *cpu,
bellard's avatar
bellard committed
369
                   target_ulong pc, int nb_insn, int is_physical, int flags)
370
{
371
    CPUClass *cc = CPU_GET_CLASS(cpu);
372
    int count, i;
373
    CPUDebug s;
374

375
    INIT_DISASSEMBLE_INFO(s.info, (FILE *)mon, monitor_fprintf);
376

377
    s.cpu = cpu;
378
    monitor_disas_is_physical = is_physical;
379
    s.info.read_memory_func = monitor_read_memory;
380
    s.info.print_address_func = generic_print_address;
381

382
    s.info.buffer_vma = pc;
383 384

#ifdef TARGET_WORDS_BIGENDIAN
385
    s.info.endian = BFD_ENDIAN_BIG;
386
#else
387
    s.info.endian = BFD_ENDIAN_LITTLE;
388
#endif
389 390 391 392 393

    if (cc->disas_set_info) {
        cc->disas_set_info(cpu, &s.info);
    }

394
#if defined(TARGET_I386)
395 396 397 398 399 400 401
    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;
    }
402
    s.info.print_insn = print_insn_i386;
403
#elif defined(TARGET_PPC)
404 405 406 407
    if (flags & 0xFFFF) {
        /* If we have a precise definition of the instruction set, use it. */
        s.info.mach = flags & 0xFFFF;
    } else {
bellard's avatar
bellard committed
408
#ifdef TARGET_PPC64
409
        s.info.mach = bfd_mach_ppc64;
bellard's avatar
bellard committed
410
#else
411
        s.info.mach = bfd_mach_ppc;
bellard's avatar
bellard committed
412
#endif
413 414 415 416
    }
    if ((flags >> 16) & 1) {
        s.info.endian = BFD_ENDIAN_LITTLE;
    }
417
    s.info.print_insn = print_insn_ppc;
418
#endif
419 420 421 422 423
    if (!s.info.print_insn) {
        monitor_printf(mon, "0x" TARGET_FMT_lx
                       ": Asm output not supported on this arch\n", pc);
        return;
    }
424 425

    for(i = 0; i < nb_insn; i++) {
426
	monitor_printf(mon, "0x" TARGET_FMT_lx ":  ", pc);
427
        count = s.info.print_insn(pc, &s.info);
428
	monitor_printf(mon, "\n");
429 430 431 432 433 434
	if (count < 0)
	    break;
        pc += count;
    }
}
#endif