add a thp-based huge page alloc fallback
[pcsx_rearmed.git] / deps / lightning / lib / jit_disasm.c
1 /*
2  * Copyright (C) 2012-2023  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 #if BINUTILS_2_38
57 static int fprintf_styled(void * stream, enum disassembler_style style, const char* fmt, ...)
58 {
59   va_list args;
60   int r;
61
62   va_start(args, fmt);
63   r = vfprintf(disasm_stream, fmt, args);
64   va_end(args);
65
66   return r;
67 }
68 #endif
69
70 /*
71  * Implementation
72  */
73 void
74 jit_init_debug(const char *progname)
75 {
76     jit_init_print();
77 #if DISASSEMBLER
78     bfd_init();
79
80     if (progname)
81         disasm_bfd = bfd_openr(progname, NULL);
82     if (disasm_bfd == NULL) {
83 #if defined(__linux__)
84         disasm_bfd = bfd_openr("/proc/self/exe", NULL);
85         if (disasm_bfd == NULL)
86 #endif
87             return;
88     }
89     bfd_check_format(disasm_bfd, bfd_object);
90     bfd_check_format(disasm_bfd, bfd_archive);
91     if (!disasm_stream)
92         disasm_stream = stdout;
93
94 #if BINUTILS_2_38
95     INIT_DISASSEMBLE_INFO(disasm_info, disasm_stream, fprintf, fprintf_styled);
96 #else
97     INIT_DISASSEMBLE_INFO(disasm_info, disasm_stream, fprintf);
98 #endif
99     disasm_info.arch = bfd_get_arch(disasm_bfd);
100     disasm_info.mach = bfd_get_mach(disasm_bfd);
101
102 #  if HAVE_DISASSEMBLE_INIT_FOR_TARGET
103     disassemble_init_for_target(&disasm_info);
104 #  endif
105
106 #  if defined(__powerpc64__)
107     disasm_info.disassembler_options = "64";
108 #  endif
109 #  if defined(__sparc__) || defined(__s390__) || defined(__s390x__)
110     disasm_info.endian = disasm_info.display_endian = BFD_ENDIAN_BIG;
111 #  endif
112 #  if defined(__s390__) || defined(__s390x__)
113     disasm_info.disassembler_options = "zarch";
114 #  endif
115     disasm_info.print_address_func = disasm_print_address;
116
117 # if BINUTILS_2_29
118     disasm_print = disassembler(disasm_info.arch, __BYTE_ORDER == __BIG_ENDIAN,
119                                 disasm_info.mach, disasm_bfd);
120 #  else
121     disasm_print = disassembler(disasm_bfd);
122 #  endif
123     assert(disasm_print);
124
125     if (bfd_get_file_flags(disasm_bfd) & HAS_SYMS) {
126         asymbol         **in;
127         asymbol         **out;
128         asymbol          *symbol;
129         long              offset;
130         long              sym_count;
131         long              dyn_count;
132         long              sym_storage;
133         long              dyn_storage;
134
135         if ((sym_storage = bfd_get_symtab_upper_bound(disasm_bfd)) >= 0) {
136
137             if (bfd_get_file_flags(disasm_bfd) & DYNAMIC) {
138                 dyn_storage = bfd_get_dynamic_symtab_upper_bound(disasm_bfd);
139 #  if defined(__alpha__)
140                 /* XXX */
141                 if (dyn_storage < 0)
142                     dyn_storage = 0;
143 #  else
144                 assert(dyn_storage >= 0);
145 #  endif
146             }
147             else
148                 dyn_storage = 0;
149
150             jit_alloc((jit_pointer_t *)&disasm_symbols,
151                       (sym_storage + dyn_storage) * sizeof(asymbol *));
152             sym_count = bfd_canonicalize_symtab(disasm_bfd, disasm_symbols);
153             assert(sym_count >= 0);
154             if (dyn_storage) {
155                 dyn_count = bfd_canonicalize_dynamic_symtab(disasm_bfd,
156                                                             disasm_symbols +
157                                                             sym_count);
158                 assert(dyn_count >= 0);
159             }
160             else
161                 dyn_count = 0;
162             disasm_num_symbols = sym_count + dyn_count;
163
164             disasm_num_synthetic = bfd_get_synthetic_symtab(disasm_bfd,
165                                                             sym_count,
166                                                             disasm_symbols,
167                                                             dyn_count,
168                                                             disasm_symbols +
169                                                             sym_count,
170                                                             &disasm_synthetic);
171             if (disasm_num_synthetic > 0) {
172                 jit_realloc((jit_pointer_t *)&disasm_symbols,
173                             (sym_storage + dyn_storage) * sizeof(asymbol *),
174                             (sym_storage + dyn_storage + disasm_num_synthetic) *
175                             sizeof(asymbol *));
176                 for (offset = 0; offset < disasm_num_synthetic; offset++)
177                     disasm_symbols[disasm_num_symbols++] =
178                         disasm_synthetic + offset;
179             }
180
181             /* remove symbols not useful for disassemble */
182             in = out = disasm_symbols;
183             for (offset = 0; offset < disasm_num_symbols; offset++) {
184                 symbol = *in++;
185                 if (symbol->name &&
186                     symbol->name[0] != '\0' &&
187                     !(symbol->flags & (BSF_DEBUGGING | BSF_SECTION_SYM)) &&
188                     !bfd_is_und_section(symbol->section) &&
189                     !bfd_is_com_section(symbol->section))
190                     *out++ = symbol;
191             }
192             disasm_num_symbols = out - disasm_symbols;
193             qsort(disasm_symbols, disasm_num_symbols,
194                   sizeof(asymbol *), disasm_compare_symbols);
195         }
196     }
197 #endif
198 }
199
200 void
201 jit_finish_debug(void)
202 {
203 #if DISASSEMBLER
204     if (disasm_synthetic)
205         jit_free((jit_pointer_t *)&disasm_synthetic);
206     if (disasm_symbols)
207         jit_free((jit_pointer_t *)&disasm_symbols);
208     if (disasm_bfd)
209         bfd_close (disasm_bfd);
210 #endif
211 }
212
213 void
214 _jit_disassemble(jit_state_t *_jit)
215 {
216 #if DISASSEMBLER
217     if (disasm_bfd) {
218 #  if defined(__arm__)
219         /* FIXME add mapping for prolog switching to arm and possible jump
220          * before first prolog also in arm mode */
221         disasm_info.disassembler_options = jit_cpu.thumb ? "force-thumb" : "";
222 #  endif
223
224         disassemble(_jit->code.ptr, _jit->pc.uc - _jit->code.ptr);
225     }
226 #endif
227 }
228
229 #if DISASSEMBLER
230 /* Based on objdump source */
231 static int
232 disasm_compare_symbols(const void *ap, const void *bp)
233 {
234     const asymbol       *a = *(const asymbol **)ap;
235     const asymbol       *b = *(const asymbol **)bp;
236
237     if (bfd_asymbol_value(a) > bfd_asymbol_value(b))
238         return (1);
239     if (bfd_asymbol_value(a) < bfd_asymbol_value(b))
240         return (-1);
241     return (0);
242 }
243
244 #if __WORDSIZE == 32
245 #  define address_buffer_length         16
246 #  define address_buffer_format         "%llx"
247 #else
248 #  define address_buffer_length         32
249 #  define address_buffer_format         "%lx"
250 #endif
251 static void
252 disasm_print_address(bfd_vma addr, struct disassemble_info *info)
253 {
254     char                *name;
255     char                *file;
256     int                  line;
257     char                 buffer[address_buffer_length];
258
259     sprintf(buffer, address_buffer_format, addr);
260     (*info->fprintf_func)(info->stream, "0x%s", buffer);
261
262 #  define _jit                          disasm_jit
263 #  undef jit_pointer_p
264 #  define jit_pointer_p(u)                                      \
265         ((u) >= _jit->code.ptr && (u) < _jit->pc.uc)
266     if (jit_pointer_p((jit_uint8_t *)(jit_word_t)addr)) {
267         if (jit_get_note((jit_uint8_t *)(jit_word_t)addr, &name, &file, &line))
268             (*info->fprintf_func)(info->stream, " %s:%s:%d",
269                                   name ? name : "",
270                                   file ? file : "",
271                                   line);
272     }
273 #  undef jit_pointer_p
274 #  undef _jit
275     else if (disasm_num_symbols) {
276         long             low;
277         long             high;
278         long             offset;
279         asymbol         *symbol;
280
281         low = 0;
282         high = disasm_num_symbols;
283         do {
284             offset = (low + high) >> 1;
285             symbol = disasm_symbols[offset];
286             if (bfd_asymbol_value(symbol) > addr)
287                 high = offset - 1;
288             else if (bfd_asymbol_value(symbol) < addr)
289                 low = offset + 1;
290             else
291                 break;
292         } while (low < high);
293
294         if (offset >= 0 && offset < disasm_num_symbols) {
295             if (bfd_asymbol_value(symbol) < addr) {
296                 while (++offset < disasm_num_symbols) {
297                     symbol = disasm_symbols[offset];
298                     if (bfd_asymbol_value(symbol) >= addr)
299                         break;
300                 }
301             }
302             else if (bfd_asymbol_value(symbol) > addr) {
303                 while (offset--) {
304                     if (bfd_asymbol_value(disasm_symbols[offset]) < addr)
305                         break;
306                     symbol = disasm_symbols[offset];
307                 }
308             }
309             if (bfd_asymbol_value(symbol) == addr)
310                 (*info->fprintf_func)(info->stream, " # %s", symbol->name);
311         }
312     }
313 }
314
315 static void
316 _disassemble(jit_state_t *_jit, jit_pointer_t code, jit_int32_t length)
317 {
318     int                  bytes;
319     char                *name, *old_name;
320     char                *file, *old_file;
321     int                  line,  old_line;
322 #if __riscv && __WORDSIZE == 64
323     jit_word_t          *vector;
324     jit_int32_t          offset;
325 #elif __arm__
326     jit_int32_t          offset;
327     jit_bool_t           data_info;
328     jit_int32_t          data_offset;
329 #endif
330     bfd_vma              pc = (jit_uword_t)code;
331     bfd_vma              end = (jit_uword_t)code + length;
332     char                 buffer[address_buffer_length];
333 #if DEVEL_DISASSEMBLER
334     jit_node_t          *node;
335     jit_uword_t          prevw;
336 #endif
337
338 #if __riscv && __WORDSIZE == 64
339     end -= _jitc->consts.hash.count * 8;
340 #endif
341
342 #if __arm__
343     data_info = _jitc && _jitc->data_info.ptr;
344     data_offset = 0;
345 #endif
346     disasm_info.buffer = code;
347     disasm_info.buffer_vma = (jit_uword_t)code;
348     disasm_info.buffer_length = length;
349     old_file = old_name = NULL;
350     old_line = 0;
351     disasm_jit = _jit;
352 #if DEVEL_DISASSEMBLER
353     node = _jitc->head;
354     prevw = pc;
355 #endif
356     while (pc < end) {
357 #if DEVEL_DISASSEMBLER
358         while (node && (jit_uword_t)(prevw + node->offset) < (jit_uword_t)pc) {
359             prevw += node->offset;
360             node = node->next;
361         }
362         while (node && (jit_uword_t)(prevw + node->offset) == (jit_uword_t)pc) {
363             jit_print_node(node);
364             fputc('\n', disasm_stream);
365             prevw += node->offset;
366             node = node->next;
367         }
368 #endif
369 #if __arm__
370     again:
371         if (data_info) {
372             while (_jitc->data_info.ptr[data_offset].code < pc) {
373                 if (++data_offset >= _jitc->data_info.length) {
374                     data_info = 0;
375                     goto again;
376                 }
377             }
378             if (pc == _jitc->data_info.ptr[data_offset].code) {
379                 offset = _jitc->data_info.ptr[data_offset].length;
380                 for (; offset >= 4; offset -= 4, pc += 4) {
381                     bytes = sprintf(buffer, address_buffer_format, pc);
382                     (*disasm_info.fprintf_func)(disasm_stream,
383                                                 "%*c0x%s\t.data\t0x%08x\n",
384                                                 16 - bytes, ' ', buffer,
385                                                 *(jit_uint32_t *)
386                                                 (jit_uint32_t)pc);
387                 }
388                 /* reset disassemble information instead of attempting
389                  * to hack the arm specific backend data structures to
390                  * tell it to forward the required number of bytes. */
391                 disasm_info.buffer = (jit_pointer_t)(jit_uint32_t)pc;
392                 disasm_info.buffer_vma = (jit_uword_t)pc;
393                 if ((disasm_info.buffer_length = end - pc) <= 0)
394                     break;
395             }
396         }
397 #endif
398         if (jit_get_note((jit_uint8_t *)(jit_word_t)pc, &name, &file, &line) &&
399             (name != old_name || file != old_file || line != old_line)) {
400             (*disasm_info.fprintf_func)(disasm_stream, "# %s:%s:%d\n",
401                                         name ? name : "",
402                                         file ? file : "",
403                                         line);
404             old_name = name;
405             old_file = file;
406             old_line = line;
407         }
408
409         bytes = sprintf(buffer, address_buffer_format, pc);
410         (*disasm_info.fprintf_func)(disasm_stream, "%*c0x%s\t",
411                                     16 - bytes, ' ', buffer);
412         pc += (*disasm_print)(pc, &disasm_info);
413         putc('\n', disasm_stream);
414     }
415 #if __riscv && __WORDSIZE == 64
416     for (vector = (jit_word_t *)end, offset = 0;
417          offset < _jitc->consts.hash.count; offset++) {
418         bytes = sprintf(buffer, address_buffer_format,
419                         (long long)end + offset * sizeof(jit_word_t));
420         (*disasm_info.fprintf_func)(disasm_stream,
421                                     "%*c0x%s\t.quad\t0x%016lx\t# (%ld)\n",
422                                     16 - bytes, ' ', buffer,
423                                     vector[offset], vector[offset]);
424     }
425 #endif
426 }
427 #endif