a46a073539e2d1061353d96cf9c2465f1575f869
[libpicofe.git] / linux / host_dasm.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2009-2010
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  *  - MAME license.
9  * See the COPYING file in the top-level directory.
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <stdarg.h>
16 #include <bfd.h>
17 #include <dis-asm.h>
18
19 #include "host_dasm.h"
20
21 extern char **g_argv;
22
23 static struct disassemble_info di;
24 static disassembler_ftype print_insn_func;
25
26 #if defined __arm__
27 //#define print_insn_func print_insn_little_arm
28 #define BFD_ARCH bfd_arch_arm
29 #define BFD_MACH bfd_mach_arm_unknown
30 #define DASM_OPTS "reg-names-std"
31 #elif defined __aarch64__
32 //#define print_insn_func print_insn_aarch64
33 #define BFD_ARCH bfd_arch_aarch64
34 #define BFD_MACH bfd_mach_aarch64
35 #define DASM_OPTS NULL
36 #elif defined __mips__
37 //#define print_insn_func print_insn_little_mips
38 #define BFD_ARCH bfd_arch_mips
39 #define BFD_MACH bfd_mach_mipsisa64r2
40 #define DASM_OPTS NULL
41 #elif defined __riscv
42 //#define print_insn_func print_insn_riscv
43 #define BFD_ARCH bfd_arch_riscv
44 #define BFD_MACH bfd_mach_riscv64
45 #define DASM_OPTS NULL
46 #elif defined __powerpc__
47 //#define print_insn_func print_insn_little_powerpc
48 #define BFD_ARCH bfd_arch_powerpc
49 #define BFD_MACH bfd_mach_ppc64
50 #define DASM_OPTS NULL
51 #elif defined(__x86_64__) || defined(__i386__)
52 //#define print_insn_func print_insn_i386_intel
53 #define BFD_ARCH bfd_arch_i386
54 #ifdef __x86_64__
55 #define BFD_MACH bfd_mach_x86_64_intel_syntax
56 #else
57 #define BFD_MACH bfd_mach_i386_i386_intel_syntax
58 #endif
59 #define DASM_OPTS NULL
60 #else
61 #error "missing arch support"
62 #endif
63
64 /* symbols */
65 static asymbol **symbols;
66 static long symcount, symstorage;
67 static int init_done;
68
69 /* Filter out (in place) symbols that are useless for disassembly.
70    COUNT is the number of elements in SYMBOLS.
71    Return the number of useful symbols.  */
72 static long
73 remove_useless_symbols (asymbol **symbols, long count)
74 {
75   asymbol **in_ptr = symbols, **out_ptr = symbols;
76
77   while (--count >= 0)
78     {
79       asymbol *sym = *in_ptr++;
80
81       if (sym->name == NULL || sym->name[0] == '\0' || sym->name[0] == '$')
82         continue;
83       if (sym->flags & (BSF_DEBUGGING | BSF_SECTION_SYM))
84         continue;
85       if (bfd_is_und_section (sym->section)
86           || bfd_is_com_section (sym->section))
87         continue;
88       if (sym->value + sym->section->vma == 0)
89         continue;
90 /*
91       printf("sym: %08lx %04x %08x v %08x \"%s\"\n",
92         (unsigned int)sym->value, (unsigned int)sym->flags, (unsigned int)sym->udata.i,
93         (unsigned int)sym->section->vma, sym->name);
94 */
95       *out_ptr++ = sym;
96     }
97
98   return out_ptr - symbols;
99 }
100
101 static void slurp_symtab(const char *filename)
102 {
103   bfd *abfd;
104
105   symcount = 0;
106
107   abfd = bfd_openr(filename, NULL);
108   if (abfd == NULL) {
109     fprintf(stderr, "failed to open: %s\n", filename);
110     goto no_symbols;
111   }
112
113   if (!bfd_check_format(abfd, bfd_object))
114     goto no_symbols;
115
116   if (!(bfd_get_file_flags(abfd) & HAS_SYMS))
117     goto no_symbols;
118
119   symstorage = bfd_get_symtab_upper_bound(abfd);
120   if (symstorage <= 0)
121     goto no_symbols;
122
123   symbols = malloc(symstorage);
124   if (symbols == NULL)
125     goto no_symbols;
126
127   symcount = bfd_canonicalize_symtab(abfd, symbols);
128   if (symcount < 0)
129     goto no_symbols;
130
131   symcount = remove_useless_symbols(symbols, symcount);
132 //  bfd_close(abfd);
133   return;
134
135 no_symbols:
136   fprintf(stderr, "no symbols in %s\n", bfd_get_filename(abfd));
137   if (symbols != NULL)
138     free(symbols);
139   symbols = NULL;
140   if (abfd != NULL)
141     bfd_close(abfd);
142 }
143
144 static const char *lookup_name(bfd_vma addr)
145 {
146   asymbol **sptr = symbols;
147   int i;
148
149   for (i = 0; i < symcount; i++) {
150     asymbol *sym = *sptr++;
151
152     if (addr == sym->value + sym->section->vma)
153       return sym->name;
154   }
155
156   return NULL;
157 }
158
159 /* Like target_read_memory, but slightly different parameters.  */
160 static int
161 dis_asm_read_memory(bfd_vma memaddr, bfd_byte *myaddr, unsigned int len,
162                      struct disassemble_info *info)
163 {
164   memcpy(myaddr, (void *)memaddr, len);
165   return 0;
166 }
167
168 static void
169 dis_asm_memory_error(int status, bfd_vma memaddr,
170                       struct disassemble_info *info)
171 {
172   fprintf(stderr, "memory_error %p\n", (void *)(long)memaddr);
173 }
174
175 static void
176 dis_asm_print_address(bfd_vma addr, struct disassemble_info *info)
177 {
178   const char *name;
179
180   printf("%08x", (int)addr);
181
182   name = lookup_name(addr);
183   if (name != NULL)
184     printf(" <%s>", name);
185 }
186
187 static int insn_printf(void *f, const char *format, ...)
188 {
189   va_list args;
190   size_t n;
191
192   va_start(args, format);
193   n = vprintf(format, args);
194   va_end(args);
195
196   return n;
197 }
198
199 static int print_insn_hex(bfd_vma addr, struct disassemble_info *info)
200 {
201   unsigned op;
202
203   dis_asm_read_memory(addr, (bfd_byte *)&op, 4, info);
204   printf("%p %08lx",(void *)addr, (long)op);
205   return 4;
206 }
207
208 #ifdef BFD_COMPRESS_ZSTD
209 static int insn_printf_styled(void *f, enum disassembler_style style, const char* format, ...)
210 {
211   va_list args;
212   size_t n;
213
214   va_start(args, format);
215   n = vprintf(format, args);
216   va_end(args);
217
218   return n;
219 }
220 #endif
221
222 static void host_dasm_init(void)
223 {
224   bfd_init();
225   if (g_argv && g_argv[0])
226     slurp_symtab(g_argv[0]);
227
228 #ifdef BFD_COMPRESS_ZSTD
229   init_disassemble_info(&di, NULL, insn_printf, insn_printf_styled);
230 #else
231   init_disassemble_info(&di, NULL, insn_printf);
232 #endif
233   di.flavour = bfd_target_unknown_flavour;
234   di.memory_error_func = dis_asm_memory_error; 
235   di.print_address_func = dis_asm_print_address;
236 //  di.symbol_at_address_func = dis_asm_symbol_at_address;
237   di.read_memory_func = dis_asm_read_memory;
238   di.arch = BFD_ARCH;
239   di.mach = BFD_MACH;
240   di.endian = BFD_ENDIAN_LITTLE;
241   di.disassembler_options = DASM_OPTS;
242   disassemble_init_for_target(&di);
243 #ifndef print_insn_func
244   print_insn_func = disassembler(BFD_ARCH, 0, BFD_MACH, NULL);
245   if (!print_insn_func) print_insn_func = print_insn_hex;
246 #endif
247   init_done = 1;
248 }
249
250 void host_dasm(void *addr, int len)
251 {
252   bfd_vma vma_end, vma = (bfd_vma)addr;
253   const char *name;
254
255   if (!init_done)
256     host_dasm_init();
257
258   vma_end = vma + len;
259   while (vma < vma_end) {
260     name = lookup_name(vma);
261     if (name != NULL)
262       printf("%s:\n", name);
263
264     printf("   %08lx ", (long)vma);
265     vma += print_insn_func(vma, &di);
266     printf("\n");
267   }
268 }
269
270 void host_dasm_new_symbol_(void *addr, const char *name)
271 {
272   bfd_vma vma = (bfd_vma)(long)addr;
273   asymbol *sym, **tmp;
274
275   if (!init_done)
276     host_dasm_init();
277   if (symbols == NULL)
278     return;
279   if (symstorage <= symcount * sizeof(symbols[0])) {
280     tmp = realloc(symbols, symstorage * 2);
281     if (tmp == NULL)
282       return;
283     symstorage *= 2;
284     symbols = tmp;
285   }
286
287   symbols[symcount] = calloc(sizeof(*symbols[0]), 1);
288   if (symbols[symcount] == NULL)
289     return;
290
291   // a HACK (should use correct section), but ohwell
292   sym = symbols[symcount];
293   sym->section = symbols[0]->section;
294   sym->value = vma - sym->section->vma;
295   sym->name = name;
296   symcount++;
297 }
298