Richard Henderson | c0d691a | 2024-04-04 20:46:32 -1000 | [diff] [blame] | 1 | /* |
| 2 | * Routines for target instruction disassembly. |
| 3 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | */ |
| 5 | |
| 6 | #include "qemu/osdep.h" |
| 7 | #include "disas/disas.h" |
| 8 | #include "disas/capstone.h" |
Richard Henderson | 4c833c6 | 2024-04-02 23:44:53 -1000 | [diff] [blame] | 9 | #include "exec/translator.h" |
Richard Henderson | c0d691a | 2024-04-04 20:46:32 -1000 | [diff] [blame] | 10 | #include "disas-internal.h" |
| 11 | |
| 12 | |
Richard Henderson | 4c833c6 | 2024-04-02 23:44:53 -1000 | [diff] [blame] | 13 | static int translator_read_memory(bfd_vma memaddr, bfd_byte *myaddr, |
| 14 | int length, struct disassemble_info *info) |
Richard Henderson | c0d691a | 2024-04-04 20:46:32 -1000 | [diff] [blame] | 15 | { |
Richard Henderson | 4c833c6 | 2024-04-02 23:44:53 -1000 | [diff] [blame] | 16 | const DisasContextBase *db = info->application_data; |
| 17 | return translator_st(db, myaddr, memaddr, length) ? 0 : EIO; |
| 18 | } |
| 19 | |
| 20 | void target_disas(FILE *out, CPUState *cpu, const struct DisasContextBase *db) |
| 21 | { |
| 22 | uint64_t code = db->pc_first; |
| 23 | size_t size = translator_st_len(db); |
Richard Henderson | c0d691a | 2024-04-04 20:46:32 -1000 | [diff] [blame] | 24 | uint64_t pc; |
| 25 | int count; |
| 26 | CPUDebug s; |
| 27 | |
| 28 | disas_initialize_debug_target(&s, cpu); |
Richard Henderson | 4c833c6 | 2024-04-02 23:44:53 -1000 | [diff] [blame] | 29 | s.info.read_memory_func = translator_read_memory; |
| 30 | s.info.application_data = (void *)db; |
Richard Henderson | c0d691a | 2024-04-04 20:46:32 -1000 | [diff] [blame] | 31 | s.info.fprintf_func = fprintf; |
| 32 | s.info.stream = out; |
| 33 | s.info.buffer_vma = code; |
| 34 | s.info.buffer_length = size; |
| 35 | s.info.show_opcodes = true; |
| 36 | |
| 37 | if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) { |
| 38 | return; |
| 39 | } |
| 40 | |
| 41 | if (s.info.print_insn == NULL) { |
| 42 | s.info.print_insn = print_insn_od_target; |
| 43 | } |
| 44 | |
| 45 | for (pc = code; size > 0; pc += count, size -= count) { |
| 46 | fprintf(out, "0x%08" PRIx64 ": ", pc); |
| 47 | count = s.info.print_insn(pc, &s.info); |
| 48 | fprintf(out, "\n"); |
| 49 | if (count < 0) { |
| 50 | break; |
| 51 | } |
| 52 | if (size < count) { |
| 53 | fprintf(out, |
| 54 | "Disassembler disagrees with translator over instruction " |
| 55 | "decoding\n" |
| 56 | "Please report this to qemu-devel@nongnu.org\n"); |
| 57 | break; |
| 58 | } |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | #ifdef CONFIG_PLUGIN |
| 63 | static void plugin_print_address(bfd_vma addr, struct disassemble_info *info) |
| 64 | { |
| 65 | /* does nothing */ |
| 66 | } |
| 67 | |
| 68 | /* |
| 69 | * We should only be dissembling one instruction at a time here. If |
| 70 | * there is left over it usually indicates the front end has read more |
| 71 | * bytes than it needed. |
| 72 | */ |
Richard Henderson | 4c833c6 | 2024-04-02 23:44:53 -1000 | [diff] [blame] | 73 | char *plugin_disas(CPUState *cpu, const DisasContextBase *db, |
| 74 | uint64_t addr, size_t size) |
Richard Henderson | c0d691a | 2024-04-04 20:46:32 -1000 | [diff] [blame] | 75 | { |
| 76 | CPUDebug s; |
| 77 | GString *ds = g_string_new(NULL); |
| 78 | |
| 79 | disas_initialize_debug_target(&s, cpu); |
Richard Henderson | 4c833c6 | 2024-04-02 23:44:53 -1000 | [diff] [blame] | 80 | s.info.read_memory_func = translator_read_memory; |
| 81 | s.info.application_data = (void *)db; |
Richard Henderson | c0d691a | 2024-04-04 20:46:32 -1000 | [diff] [blame] | 82 | s.info.fprintf_func = disas_gstring_printf; |
| 83 | s.info.stream = (FILE *)ds; /* abuse this slot */ |
| 84 | s.info.buffer_vma = addr; |
| 85 | s.info.buffer_length = size; |
| 86 | s.info.print_address_func = plugin_print_address; |
| 87 | |
| 88 | if (s.info.cap_arch >= 0 && cap_disas_plugin(&s.info, addr, size)) { |
| 89 | ; /* done */ |
| 90 | } else if (s.info.print_insn) { |
| 91 | s.info.print_insn(addr, &s.info); |
| 92 | } else { |
| 93 | ; /* cannot disassemble -- return empty string */ |
| 94 | } |
| 95 | |
| 96 | /* Return the buffer, freeing the GString container. */ |
| 97 | return g_string_free(ds, false); |
| 98 | } |
| 99 | #endif /* CONFIG_PLUGIN */ |