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