25983a68b185e41f82992d1c46e4e72029cb8513
[pcsx_rearmed.git] / deps / lightning / lib / jit_disasm.c
1 /*
2  * Copyright (C) 2012-2019  Free Software Foundation, Inc.
3  *
4  * This file is part of GNU lightning.
5  *
6  * GNU lightning is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 3, or (at your option)
9  * any later version.
10  *
11  * GNU lightning is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14  * License for more details.
15  *
16  * Authors:
17  *      Paulo Cesar Pereira de Andrade
18  */
19
20 #include <lightning.h>
21 #include <lightning/jit_private.h>
22 #if DISASSEMBLER
23 #  include <dis-asm.h>
24 #endif
25
26 /*
27  * Prototypes
28  */
29 #if DISASSEMBLER
30 static int
31 disasm_compare_symbols(const void *ap, const void *bp);
32
33 static void
34 disasm_print_address(bfd_vma addr, struct disassemble_info *info);
35
36 #define disassemble(u, v)       _disassemble(_jit, u, v)
37 static void
38 _disassemble(jit_state_t *_jit, jit_pointer_t code, jit_int32_t length);
39 #endif
40
41 /*
42  * Initialization
43  */
44 #if DISASSEMBLER
45 static bfd                       *disasm_bfd;
46 static disassemble_info           disasm_info;
47 static disassembler_ftype         disasm_print;
48 static asymbol                  **disasm_symbols;
49 static asymbol                   *disasm_synthetic;
50 static long                       disasm_num_symbols;
51 static long                       disasm_num_synthetic;
52 static jit_state_t               *disasm_jit;
53 static FILE                      *disasm_stream;
54 #endif
55
56 /*
57  * Implementation
58  */
59 void
60 jit_init_debug(const char *progname)
61 {
62     jit_init_print();
63 #if DISASSEMBLER
64     bfd_init();
65
66     if (progname)
67         disasm_bfd = bfd_openr(progname, NULL);
68     if (disasm_bfd == NULL) {
69 #if defined(__linux__)
70         disasm_bfd = bfd_openr("/proc/self/exe", NULL);
71         if (disasm_bfd == NULL)
72 #endif
73             return;
74     }
75     bfd_check_format(disasm_bfd, bfd_object);
76     bfd_check_format(disasm_bfd, bfd_archive);
77     if (!disasm_stream)
78         disasm_stream = stdout;
79
80     INIT_DISASSEMBLE_INFO(disasm_info, disasm_stream, fprintf);
81     disasm_info.arch = bfd_get_arch(disasm_bfd);
82     disasm_info.mach = bfd_get_mach(disasm_bfd);
83
84 #  if HAVE_DISASSEMBLE_INIT_FOR_TARGET
85     disassemble_init_for_target(&disasm_info);
86 #  endif
87
88 #  if defined(__powerpc64__)
89     disasm_info.disassembler_options = "64";
90 #  endif
91 #  if defined(__sparc__) || defined(__s390__) || defined(__s390x__)
92     disasm_info.endian = disasm_info.display_endian = BFD_ENDIAN_BIG;
93 #  endif
94 #  if defined(__s390__) || defined(__s390x__)
95     disasm_info.disassembler_options = "zarch";
96 #  endif
97     disasm_info.print_address_func = disasm_print_address;
98
99 # if BINUTILS_2_29
100     disasm_print = disassembler(disasm_info.arch, __BYTE_ORDER == __BIG_ENDIAN,
101                                 disasm_info.mach, disasm_bfd);
102 #  else
103     disasm_print = disassembler(disasm_bfd);
104 #  endif
105     assert(disasm_print);
106
107     if (bfd_get_file_flags(disasm_bfd) & HAS_SYMS) {
108         asymbol         **in;
109         asymbol         **out;
110         asymbol          *symbol;
111         long              offset;
112         long              sym_count;
113         long              dyn_count;
114         long              sym_storage;
115         long              dyn_storage;
116
117         if ((sym_storage = bfd_get_symtab_upper_bound(disasm_bfd)) >= 0) {
118
119             if (bfd_get_file_flags(disasm_bfd) & DYNAMIC) {
120                 dyn_storage = bfd_get_dynamic_symtab_upper_bound(disasm_bfd);
121 #  if defined(__alpha__)
122                 /* XXX */
123                 if (dyn_storage < 0)
124                     dyn_storage = 0;
125 #  else
126                 assert(dyn_storage >= 0);
127 #  endif
128             }
129             else
130                 dyn_storage = 0;
131
132             jit_alloc((jit_pointer_t *)&disasm_symbols,
133                       (sym_storage + dyn_storage) * sizeof(asymbol *));
134             sym_count = bfd_canonicalize_symtab(disasm_bfd, disasm_symbols);
135             assert(sym_count >= 0);
136             if (dyn_storage) {
137                 dyn_count = bfd_canonicalize_dynamic_symtab(disasm_bfd,
138                                                             disasm_symbols +
139                                                             sym_count);
140                 assert(dyn_count >= 0);
141             }
142             else
143                 dyn_count = 0;
144             disasm_num_symbols = sym_count + dyn_count;
145
146             disasm_num_synthetic = bfd_get_synthetic_symtab(disasm_bfd,
147                                                             sym_count,
148                                                             disasm_symbols,
149                                                             dyn_count,
150                                                             disasm_symbols +
151                                                             sym_count,
152                                                             &disasm_synthetic);
153             if (disasm_num_synthetic > 0) {
154                 jit_realloc((jit_pointer_t *)&disasm_symbols,
155                             (sym_storage + dyn_storage) * sizeof(asymbol *),
156                             (sym_storage + dyn_storage + disasm_num_synthetic) *
157                             sizeof(asymbol *));
158                 for (offset = 0; offset < disasm_num_synthetic; offset++)
159                     disasm_symbols[disasm_num_symbols++] =
160                         disasm_synthetic + offset;
161             }
162
163             /* remove symbols not useful for disassemble */
164             in = out = disasm_symbols;
165             for (offset = 0; offset < disasm_num_symbols; offset++) {
166                 symbol = *in++;
167                 if (symbol->name &&
168                     symbol->name[0] != '\0' &&
169                     !(symbol->flags & (BSF_DEBUGGING | BSF_SECTION_SYM)) &&
170                     !bfd_is_und_section(symbol->section) &&
171                     !bfd_is_com_section(symbol->section))
172                     *out++ = symbol;
173             }
174             disasm_num_symbols = out - disasm_symbols;
175             qsort(disasm_symbols, disasm_num_symbols,
176                   sizeof(asymbol *), disasm_compare_symbols);
177         }
178     }
179 #endif
180 }
181
182 void
183 jit_finish_debug(void)
184 {
185 #if DISASSEMBLER
186     if (disasm_synthetic)
187         jit_free((jit_pointer_t *)&disasm_synthetic);
188     if (disasm_symbols)
189         jit_free((jit_pointer_t *)&disasm_symbols);
190     if (disasm_bfd)
191         bfd_close (disasm_bfd);
192 #endif
193 }
194
195 void
196 _jit_disassemble(jit_state_t *_jit)
197 {
198 #if DISASSEMBLER
199     if (disasm_bfd) {
200 #  if defined(__arm__)
201         /* FIXME add mapping for prolog switching to arm and possible jump
202          * before first prolog also in arm mode */
203         disasm_info.disassembler_options = jit_cpu.thumb ? "force-thumb" : "";
204 #  endif
205
206         disassemble(_jit->code.ptr, _jit->pc.uc - _jit->code.ptr);
207     }
208 #endif
209 }
210
211 #if DISASSEMBLER
212 /* Based on objdump source */
213 static int
214 disasm_compare_symbols(const void *ap, const void *bp)
215 {
216     const asymbol       *a = *(const asymbol **)ap;
217     const asymbol       *b = *(const asymbol **)bp;
218
219     if (bfd_asymbol_value(a) > bfd_asymbol_value(b))
220         return (1);
221     if (bfd_asymbol_value(a) < bfd_asymbol_value(b))
222         return (-1);
223     return (0);
224 }
225
226 #if __WORDSIZE == 32
227 #  define address_buffer_length         16
228 #  define address_buffer_format         "%llx"
229 #else
230 #  define address_buffer_length         32
231 #  define address_buffer_format         "%lx"
232 #endif
233 static void
234 disasm_print_address(bfd_vma addr, struct disassemble_info *info)
235 {
236     char                *name;
237     char                *file;
238     int                  line;
239     char                 buffer[address_buffer_length];
240
241     sprintf(buffer, address_buffer_format, (long long)addr);
242     (*info->fprintf_func)(info->stream, "0x%s", buffer);
243
244 #  define _jit                          disasm_jit
245 #  undef jit_pointer_p
246 #  define jit_pointer_p(u)                                      \
247         ((u) >= _jit->code.ptr && (u) < _jit->pc.uc)
248     if (jit_pointer_p((jit_uint8_t *)(jit_word_t)addr)) {
249         if (jit_get_note((jit_uint8_t *)(jit_word_t)addr, &name, &file, &line))
250             (*info->fprintf_func)(info->stream, " %s:%s:%d",
251                                   name ? name : "",
252                                   file ? file : "",
253                                   line);
254     }
255 #  undef jit_pointer_p
256 #  undef _jit
257     else if (disasm_num_symbols) {
258         long             low;
259         long             high;
260         long             offset;
261         asymbol         *symbol;
262
263         low = 0;
264         high = disasm_num_symbols;
265         do {
266             offset = (low + high) >> 1;
267             symbol = disasm_symbols[offset];
268             if (bfd_asymbol_value(symbol) > addr)
269                 high = offset - 1;
270             else if (bfd_asymbol_value(symbol) < addr)
271                 low = offset + 1;
272             else
273                 break;
274         } while (low < high);
275
276         if (offset >= 0 && offset < disasm_num_symbols) {
277             if (bfd_asymbol_value(symbol) < addr) {
278                 while (++offset < disasm_num_symbols) {
279                     symbol = disasm_symbols[offset];
280                     if (bfd_asymbol_value(symbol) >= addr)
281                         break;
282                 }
283             }
284             else if (bfd_asymbol_value(symbol) > addr) {
285                 while (offset--) {
286                     if (bfd_asymbol_value(disasm_symbols[offset]) < addr)
287                         break;
288                     symbol = disasm_symbols[offset];
289                 }
290             }
291             if (bfd_asymbol_value(symbol) == addr)
292                 (*info->fprintf_func)(info->stream, " # %s", symbol->name);
293         }
294     }
295 }
296
297 static void
298 _disassemble(jit_state_t *_jit, jit_pointer_t code, jit_int32_t length)
299 {
300     int                  bytes;
301     char                *name, *old_name;
302     char                *file, *old_file;
303     int                  line,  old_line;
304 #if __arm__
305     jit_int32_t          offset;
306     jit_bool_t           data_info;
307     jit_int32_t          data_offset;
308 #endif
309     bfd_vma              pc = (jit_uword_t)code;
310     bfd_vma              end = (jit_uword_t)code + length;
311     char                 buffer[address_buffer_length];
312 #if DEVEL_DISASSEMBLER
313     jit_node_t          *node;
314     jit_uword_t          prevw;
315 #endif
316
317 #if __arm__
318     data_info = _jitc && _jitc->data_info.ptr;
319     data_offset = 0;
320 #endif
321     disasm_info.buffer = code;
322     disasm_info.buffer_vma = (jit_uword_t)code;
323     disasm_info.buffer_length = length;
324     old_file = old_name = NULL;
325     old_line = 0;
326     disasm_jit = _jit;
327 #if DEVEL_DISASSEMBLER
328     node = _jitc->head;
329     prevw = pc;
330 #endif
331     while (pc < end) {
332 #if DEVEL_DISASSEMBLER
333         while (node && (jit_uword_t)(prevw + node->offset) < (jit_uword_t)pc) {
334             prevw += node->offset;
335             node = node->next;
336         }
337         while (node && (jit_uword_t)(prevw + node->offset) == (jit_uword_t)pc) {
338             jit_print_node(node);
339             fputc('\n', stdout); 
340             prevw += node->offset;
341             node = node->next;
342         }
343 #endif
344 #if __arm__
345     again:
346         if (data_info) {
347             while (_jitc->data_info.ptr[data_offset].code < pc) {
348                 if (++data_offset >= _jitc->data_info.length) {
349                     data_info = 0;
350                     goto again;
351                 }
352             }
353             if (pc == _jitc->data_info.ptr[data_offset].code) {
354                 offset = _jitc->data_info.ptr[data_offset].length;
355                 for (; offset >= 4; offset -= 4, pc += 4) {
356                     bytes = sprintf(buffer, address_buffer_format, pc);
357                     (*disasm_info.fprintf_func)(disasm_stream,
358                                                 "%*c0x%s\t.data\t0x%08x\n",
359                                                 16 - bytes, ' ', buffer,
360                                                 *(jit_uint32_t *)
361                                                 (jit_uint32_t)pc);
362                 }
363                 /* reset disassemble information instead of attempting
364                  * to hack the arm specific backend data structures to
365                  * tell it to forward the required number of bytes. */
366                 disasm_info.buffer = (jit_pointer_t)(jit_uint32_t)pc;
367                 disasm_info.buffer_vma = (jit_uword_t)pc;
368                 if ((disasm_info.buffer_length = end - pc) <= 0)
369                     break;
370             }
371         }
372 #endif
373         if (jit_get_note((jit_uint8_t *)(jit_word_t)pc, &name, &file, &line) &&
374             (name != old_name || file != old_file || line != old_line)) {
375             (*disasm_info.fprintf_func)(disasm_stream, "# %s:%s:%d\n",
376                                         name ? name : "",
377                                         file ? file : "",
378                                         line);
379             old_name = name;
380             old_file = file;
381             old_line = line;
382         }
383
384         bytes = sprintf(buffer, address_buffer_format, (long long)pc);
385         (*disasm_info.fprintf_func)(disasm_stream, "%*c0x%s\t",
386                                     16 - bytes, ' ', buffer);
387         pc += (*disasm_print)(pc, &disasm_info);
388         putc('\n', disasm_stream);
389     }
390 }
391 #endif