input: fix a leak
[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 static void host_dasm_init(void)
209 {
210   bfd_init();
211   if (g_argv && g_argv[0])
212     slurp_symtab(g_argv[0]);
213
214   init_disassemble_info(&di, NULL, insn_printf);
215   di.flavour = bfd_target_unknown_flavour;
216   di.memory_error_func = dis_asm_memory_error; 
217   di.print_address_func = dis_asm_print_address;
218 //  di.symbol_at_address_func = dis_asm_symbol_at_address;
219   di.read_memory_func = dis_asm_read_memory;
220   di.arch = BFD_ARCH;
221   di.mach = BFD_MACH;
222   di.endian = BFD_ENDIAN_LITTLE;
223   di.disassembler_options = DASM_OPTS;
224   disassemble_init_for_target(&di);
225 #ifndef print_insn_func
226   print_insn_func = disassembler(BFD_ARCH, 0, BFD_MACH, NULL);
227   if (!print_insn_func) print_insn_func = print_insn_hex;
228 #endif
229   init_done = 1;
230 }
231
232 void host_dasm(void *addr, int len)
233 {
234   bfd_vma vma_end, vma = (bfd_vma)addr;
235   const char *name;
236
237   if (!init_done)
238     host_dasm_init();
239
240   vma_end = vma + len;
241   while (vma < vma_end) {
242     name = lookup_name(vma);
243     if (name != NULL)
244       printf("%s:\n", name);
245
246     printf("   %08lx ", (long)vma);
247     vma += print_insn_func(vma, &di);
248     printf("\n");
249   }
250 }
251
252 void host_dasm_new_symbol_(void *addr, const char *name)
253 {
254   bfd_vma vma = (bfd_vma)(long)addr;
255   asymbol *sym, **tmp;
256
257   if (!init_done)
258     host_dasm_init();
259   if (symbols == NULL)
260     return;
261   if (symstorage <= symcount * sizeof(symbols[0])) {
262     tmp = realloc(symbols, symstorage * 2);
263     if (tmp == NULL)
264       return;
265     symstorage *= 2;
266     symbols = tmp;
267   }
268
269   symbols[symcount] = calloc(sizeof(*symbols[0]), 1);
270   if (symbols[symcount] == NULL)
271     return;
272
273   // a HACK (should use correct section), but ohwell
274   sym = symbols[symcount];
275   sym->section = symbols[0]->section;
276   sym->value = vma - sym->section->vma;
277   sym->name = name;
278   symcount++;
279 }
280