Update lightrec 20220910 (#686)
[pcsx_rearmed.git] / deps / lightning / lib / jit_disasm.c
CommitLineData
4a71579b
PC
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
30static int
31disasm_compare_symbols(const void *ap, const void *bp);
32
33static void
34disasm_print_address(bfd_vma addr, struct disassemble_info *info);
35
36#define disassemble(u, v) _disassemble(_jit, u, v)
37static 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
45static bfd *disasm_bfd;
46static disassemble_info disasm_info;
47static disassembler_ftype disasm_print;
48static asymbol **disasm_symbols;
49static asymbol *disasm_synthetic;
50static long disasm_num_symbols;
51static long disasm_num_synthetic;
52static jit_state_t *disasm_jit;
519a9ea1 53static FILE *disasm_stream;
4a71579b
PC
54#endif
55
ba3814c1
PC
56#if BINUTILS_2_38
57static int fprintf_styled(void *, enum disassembler_style, const char* fmt, ...)
58{
59 va_list args;
60 int r;
61
62 va_start(args, fmt);
63 r = vprintf(fmt, args);
64 va_end(args);
65
66 return r;
67}
68#endif
69
4a71579b
PC
70/*
71 * Implementation
72 */
73void
74jit_init_debug(const char *progname)
75{
40a44dcb 76 jit_init_print();
4a71579b
PC
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);
519a9ea1 91 if (!disasm_stream)
40a44dcb
PC
92 disasm_stream = stdout;
93
ba3814c1
PC
94#if BINUTILS_2_38
95 INIT_DISASSEMBLE_INFO(disasm_info, disasm_stream, fprintf, fprintf_styled);
96#else
4a71579b 97 INIT_DISASSEMBLE_INFO(disasm_info, disasm_stream, fprintf);
ba3814c1 98#endif
40a44dcb
PC
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
4a71579b 103 disassemble_init_for_target(&disasm_info);
40a44dcb
PC
104# endif
105
106# if defined(__powerpc64__)
4a71579b 107 disasm_info.disassembler_options = "64";
4a71579b 108# endif
40a44dcb 109# if defined(__sparc__) || defined(__s390__) || defined(__s390x__)
4a71579b
PC
110 disasm_info.endian = disasm_info.display_endian = BFD_ENDIAN_BIG;
111# endif
112# if defined(__s390__) || defined(__s390x__)
4a71579b 113 disasm_info.disassembler_options = "zarch";
4a71579b
PC
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
200void
201jit_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
213void
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 */
231static int
232disasm_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
251static void
252disasm_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, (long long)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
315static 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 __arm__
323 jit_int32_t offset;
324 jit_bool_t data_info;
325 jit_int32_t data_offset;
326#endif
327 bfd_vma pc = (jit_uword_t)code;
328 bfd_vma end = (jit_uword_t)code + length;
329 char buffer[address_buffer_length];
330#if DEVEL_DISASSEMBLER
331 jit_node_t *node;
332 jit_uword_t prevw;
333#endif
334
335#if __arm__
336 data_info = _jitc && _jitc->data_info.ptr;
337 data_offset = 0;
338#endif
339 disasm_info.buffer = code;
340 disasm_info.buffer_vma = (jit_uword_t)code;
341 disasm_info.buffer_length = length;
342 old_file = old_name = NULL;
343 old_line = 0;
344 disasm_jit = _jit;
345#if DEVEL_DISASSEMBLER
346 node = _jitc->head;
347 prevw = pc;
348#endif
349 while (pc < end) {
350#if DEVEL_DISASSEMBLER
351 while (node && (jit_uword_t)(prevw + node->offset) < (jit_uword_t)pc) {
352 prevw += node->offset;
353 node = node->next;
354 }
355 while (node && (jit_uword_t)(prevw + node->offset) == (jit_uword_t)pc) {
356 jit_print_node(node);
357 fputc('\n', stdout);
358 prevw += node->offset;
359 node = node->next;
360 }
361#endif
362#if __arm__
363 again:
364 if (data_info) {
365 while (_jitc->data_info.ptr[data_offset].code < pc) {
366 if (++data_offset >= _jitc->data_info.length) {
367 data_info = 0;
368 goto again;
369 }
370 }
371 if (pc == _jitc->data_info.ptr[data_offset].code) {
372 offset = _jitc->data_info.ptr[data_offset].length;
373 for (; offset >= 4; offset -= 4, pc += 4) {
374 bytes = sprintf(buffer, address_buffer_format, pc);
375 (*disasm_info.fprintf_func)(disasm_stream,
376 "%*c0x%s\t.data\t0x%08x\n",
377 16 - bytes, ' ', buffer,
378 *(jit_uint32_t *)
379 (jit_uint32_t)pc);
380 }
381 /* reset disassemble information instead of attempting
382 * to hack the arm specific backend data structures to
383 * tell it to forward the required number of bytes. */
384 disasm_info.buffer = (jit_pointer_t)(jit_uint32_t)pc;
385 disasm_info.buffer_vma = (jit_uword_t)pc;
386 if ((disasm_info.buffer_length = end - pc) <= 0)
387 break;
388 }
389 }
390#endif
391 if (jit_get_note((jit_uint8_t *)(jit_word_t)pc, &name, &file, &line) &&
392 (name != old_name || file != old_file || line != old_line)) {
393 (*disasm_info.fprintf_func)(disasm_stream, "# %s:%s:%d\n",
394 name ? name : "",
395 file ? file : "",
396 line);
397 old_name = name;
398 old_file = file;
399 old_line = line;
400 }
401
402 bytes = sprintf(buffer, address_buffer_format, (long long)pc);
403 (*disasm_info.fprintf_func)(disasm_stream, "%*c0x%s\t",
404 16 - bytes, ' ', buffer);
405 pc += (*disasm_print)(pc, &disasm_info);
406 putc('\n', disasm_stream);
407 }
408}
409#endif