Commit | Line | Data |
---|---|---|
4a71579b | 1 | /* |
79bfeef6 | 2 | * Copyright (C) 2012-2023 Free Software Foundation, Inc. |
4a71579b PC |
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; | |
519a9ea1 | 53 | static FILE *disasm_stream; |
4a71579b PC |
54 | #endif |
55 | ||
ba3814c1 | 56 | #if BINUTILS_2_38 |
79bfeef6 | 57 | static int fprintf_styled(void * stream, enum disassembler_style style, const char* fmt, ...) |
ba3814c1 PC |
58 | { |
59 | va_list args; | |
60 | int r; | |
61 | ||
62 | va_start(args, fmt); | |
c0c16242 | 63 | r = vfprintf(disasm_stream, fmt, args); |
ba3814c1 PC |
64 | va_end(args); |
65 | ||
66 | return r; | |
67 | } | |
68 | #endif | |
69 | ||
4a71579b PC |
70 | /* |
71 | * Implementation | |
72 | */ | |
73 | void | |
74 | jit_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 | ||
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 | ||
79bfeef6 | 259 | sprintf(buffer, address_buffer_format, addr); |
4a71579b PC |
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; | |
c0c16242 PC |
322 | #if __riscv && __WORDSIZE == 64 |
323 | jit_word_t *vector; | |
324 | jit_int32_t offset; | |
325 | #elif __arm__ | |
4a71579b PC |
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 | ||
c0c16242 PC |
338 | #if __riscv && __WORDSIZE == 64 |
339 | end -= _jitc->consts.hash.count * 8; | |
340 | #endif | |
341 | ||
4a71579b PC |
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); | |
c0c16242 | 364 | fputc('\n', disasm_stream); |
4a71579b PC |
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 | ||
79bfeef6 | 409 | bytes = sprintf(buffer, address_buffer_format, pc); |
4a71579b 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 | } | |
c0c16242 PC |
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 | |
4a71579b PC |
426 | } |
427 | #endif |