11 #include "ruby/config.h"
27 #include <sys/types.h>
41 # define alloca __builtin_alloca
58 #ifdef HAVE_DL_ITERATE_PHDR
65 #define DW_LNS_copy 0x01
66 #define DW_LNS_advance_pc 0x02
67 #define DW_LNS_advance_line 0x03
68 #define DW_LNS_set_file 0x04
69 #define DW_LNS_set_column 0x05
70 #define DW_LNS_negate_stmt 0x06
71 #define DW_LNS_set_basic_block 0x07
72 #define DW_LNS_const_add_pc 0x08
73 #define DW_LNS_fixed_advance_pc 0x09
74 #define DW_LNS_set_prologue_end 0x0a
75 #define DW_LNS_set_epilogue_begin 0x0b
76 #define DW_LNS_set_isa 0x0c
79 #define DW_LNE_end_sequence 0x01
80 #define DW_LNE_set_address 0x02
81 #define DW_LNE_define_file 0x03
82 #define DW_LNE_set_discriminator 0x04
85 # if SIZEOF_VOIDP == 8
86 # define ElfW(x) Elf64##_##x
88 # define ElfW(x) Elf32##_##x
95 int kprintf(
const char *
fmt, ...);
105 unsigned long base_addr;
109 static char binary_filename[
PATH_MAX];
117 unsigned char b = *(
unsigned char *)(*p)++;
119 r += (
unsigned long)b << s;
122 r += (b & 0x7f) << s;
134 unsigned char b = *(
unsigned char *)(*p)++;
137 r -= (0x80 -
b) << s;
140 r += (b & 0x3f) << s;
144 r += (b & 0x7f) << s;
151 get_nth_dirname(
unsigned long dir,
char *p)
160 kprintf(
"Unexpected directory number %lu in %s\n",
161 dir, binary_filename);
169 fill_filename(
int file,
char *include_directories,
char *filenames,
176 for (i = 1; i <= file; i++) {
180 kprintf(
"Unexpected file number %d in %s\n",
181 file, binary_filename);
193 line->filename = filename;
194 line->dirname = get_nth_dirname(dir, include_directories);
200 get_path_from_symbol(
const char *symbol,
const char **p,
size_t *
len)
202 if (symbol[0] ==
'0') {
205 if (*p ==
NULL)
return 0;
213 if (q ==
NULL)
return 0;
220 fill_line(
int num_traces,
void **traces,
221 unsigned long addr,
int file,
int line,
222 char *include_directories,
char *filenames, line_info_t *lines)
225 for (i = 0; i < num_traces; i++) {
226 unsigned long a = (
unsigned long)traces[i] - lines[i].base_addr;
229 if (addr < a && a < addr + 100) {
230 fill_filename(file, include_directories, filenames, &lines[i]);
231 lines[
i].line = line;
237 parse_debug_line_cu(
int num_traces,
void **traces,
238 char **debug_line, line_info_t *lines)
240 char *
p, *cu_end, *cu_start, *include_directories, *filenames;
241 unsigned long unit_length;
242 int default_is_stmt, line_base;
243 unsigned int header_length, minimum_instruction_length, line_range,
248 unsigned long addr = 0;
249 unsigned int file = 1;
250 unsigned int line = 1;
261 unit_length = *(
unsigned int *)p;
262 p +=
sizeof(
unsigned int);
263 if (unit_length == 0xffffffff) {
264 unit_length = *(
unsigned long *)p;
265 p +=
sizeof(
unsigned long);
268 cu_end = p + unit_length;
273 header_length = *(
unsigned int *)p;
274 p +=
sizeof(
unsigned int);
276 cu_start = p + header_length;
278 minimum_instruction_length = *(
unsigned char *)p;
281 is_stmt = default_is_stmt = *(
unsigned char *)p;
284 line_base = *(
char *)p;
287 line_range = *(
unsigned char *)p;
290 opcode_base = *(
unsigned char *)p;
294 p += opcode_base - 1;
296 include_directories =
p;
309 #define FILL_LINE() \
311 fill_line(num_traces, traces, addr, file, line, \
312 include_directories, filenames, lines); \
318 unsigned char op = *p++;
323 case DW_LNS_advance_pc:
327 case DW_LNS_advance_line: {
328 long a = sleb128(&p);
332 case DW_LNS_set_file:
333 file = (
unsigned int)uleb128(&p);
335 case DW_LNS_set_column:
338 case DW_LNS_negate_stmt:
341 case DW_LNS_set_basic_block:
344 case DW_LNS_const_add_pc:
345 a = ((255 - opcode_base) / line_range) *
346 minimum_instruction_length;
349 case DW_LNS_fixed_advance_pc:
350 a = *(
unsigned char *)p++;
353 case DW_LNS_set_prologue_end:
356 case DW_LNS_set_epilogue_begin:
363 a = *(
unsigned char *)p++;
366 case DW_LNE_end_sequence:
373 is_stmt = default_is_stmt;
377 case DW_LNE_set_address:
378 addr = *(
unsigned long *)p;
379 p +=
sizeof(
unsigned long);
381 case DW_LNE_define_file:
382 kprintf(
"Unsupported operation in %s\n",
385 case DW_LNE_set_discriminator:
390 kprintf(
"Unknown extended opcode: %d in %s\n",
391 op, binary_filename);
395 unsigned long addr_incr;
396 unsigned long line_incr;
397 a = op - opcode_base;
398 addr_incr = (a / line_range) * minimum_instruction_length;
399 line_incr = line_base + (a % line_range);
400 addr += (
unsigned int)addr_incr;
401 line += (
unsigned int)line_incr;
410 parse_debug_line(
int num_traces,
void **traces,
411 char *debug_line,
unsigned long size, line_info_t *lines)
413 char *debug_line_end = debug_line +
size;
414 while (debug_line < debug_line_end) {
415 parse_debug_line_cu(num_traces, traces, &debug_line, lines);
417 if (debug_line != debug_line_end) {
418 kprintf(
"Unexpected size of .debug_line in %s\n",
425 fill_lines(
int num_traces,
void **traces,
char **syms,
int check_debuglink,
426 line_info_t *current_line, line_info_t *lines);
429 follow_debuglink(
char *debuglink,
int num_traces,
void **traces,
char **syms,
430 line_info_t *current_line, line_info_t *lines)
435 static const char global_debug_dir[] =
"/usr/lib/debug";
438 p =
strrchr(binary_filename,
'/');
445 strcpy(subdir, binary_filename);
446 strcpy(binary_filename, global_debug_dir);
447 strncat(binary_filename, subdir,
449 strncat(binary_filename, debuglink,
452 munmap(current_line->mapped, current_line->mapped_size);
453 close(current_line->fd);
454 fill_lines(num_traces, traces, syms, 0, current_line, lines);
459 fill_lines(
int num_traces,
void **traces,
char **syms,
int check_debuglink,
460 line_info_t *current_line, line_info_t *lines)
466 ElfW(Shdr) *shdr, *shstr_shdr;
467 ElfW(Shdr) *debug_line_shdr =
NULL, *gnu_debuglink_shdr =
NULL;
472 fd = open(binary_filename, O_RDONLY);
480 kprintf(
"lseek: %s\n",
strerror(e));
483 #if SIZEOF_OFF_T > SIZEOF_SIZE_T
486 kprintf(
"Too large file %s\n", binary_filename);
492 file = (
char *)mmap(
NULL, (
size_t)filesize, PROT_READ, MAP_SHARED, fd, 0);
493 if (file == MAP_FAILED) {
500 ehdr = (ElfW(Ehdr) *)file;
501 if (
memcmp(ehdr->e_ident,
"\177ELF", 4) != 0) {
510 current_line->fd = fd;
511 current_line->mapped = file;
512 current_line->mapped_size = (size_t)filesize;
514 for (i = 0; i < num_traces; i++) {
517 if (get_path_from_symbol(syms[i], &path, &len) &&
518 !strncmp(path, binary_filename, len)) {
523 shdr = (ElfW(Shdr) *)(file + ehdr->e_shoff);
525 shstr_shdr = shdr + ehdr->e_shstrndx;
526 shstr = file + shstr_shdr->sh_offset;
528 for (i = 0; i < ehdr->e_shnum; i++) {
529 section_name = shstr + shdr[
i].sh_name;
530 if (!strcmp(section_name,
".debug_line")) {
531 debug_line_shdr = shdr +
i;
533 }
else if (!strcmp(section_name,
".gnu_debuglink")) {
534 gnu_debuglink_shdr = shdr +
i;
538 if (!debug_line_shdr) {
541 if (gnu_debuglink_shdr && check_debuglink) {
542 follow_debuglink(file + gnu_debuglink_shdr->sh_offset,
543 num_traces, traces, syms,
544 current_line, lines);
549 parse_debug_line(num_traces, traces,
550 file + debug_line_shdr->sh_offset,
551 debug_line_shdr->sh_size,
555 #ifdef HAVE_DL_ITERATE_PHDR
561 } fill_base_addr_state_t;
564 fill_base_addr(
struct dl_phdr_info *
info,
size_t size,
void *
data)
567 fill_base_addr_state_t *
st = (fill_base_addr_state_t *)data;
568 for (i = 0; i < st->num_traces; i++) {
571 size_t name_len =
strlen(info->dlpi_name);
573 if (get_path_from_symbol(st->syms[i], &path, &len) &&
574 (len == name_len || (len > name_len && path[len-name_len-1] ==
'/')) &&
575 !strncmp(path+len-name_len, info->dlpi_name, name_len)) {
576 st->lines[
i].base_addr = info->dlpi_addr;
585 rb_dump_backtrace_with_lines(
int num_traces,
void **trace,
char **syms)
589 line_info_t *lines = (line_info_t *)
calloc(num_traces,
590 sizeof(line_info_t));
594 #ifdef HAVE_DL_ITERATE_PHDR
595 fill_base_addr_state_t fill_base_addr_state;
597 fill_base_addr_state.num_traces = num_traces;
598 fill_base_addr_state.syms = syms;
599 fill_base_addr_state.lines = lines;
601 dl_iterate_phdr(fill_base_addr, &fill_base_addr_state);
604 for (i = 0; i < num_traces; i++) {
611 if (!get_path_from_symbol(syms[i], &path, &len)) {
615 strncpy(binary_filename, path, len);
616 binary_filename[
len] =
'\0';
618 fill_lines(num_traces, trace, syms, 1, &lines[i], lines);
621 for (i = 0; i < num_traces; i++) {
622 line_info_t *line = &lines[
i];
624 if (line->line > 0) {
625 if (line->filename) {
626 if (line->dirname && line->dirname[0]) {
627 kprintf(
"%s %s/%s:%d\n", syms[i], line->dirname, line->filename, line->line);
630 kprintf(
"%s %s:%d\n", syms[i], line->filename, line->line);
633 kprintf(
"%s ???:%d\n", syms[i], line->line);
636 kprintf(
"%s\n", syms[i]);
640 for (i = 0; i < num_traces; i++) {
641 line_info_t *line = &lines[
i];
643 munmap(line->mapped, line->mapped_size);
688 #define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
690 #define toupper(c) rb_toupper(c)
691 #define hex2ascii(hex) (hex2ascii_data[hex])
692 char const hex2ascii_data[] =
"0123456789abcdefghijklmnopqrstuvwxyz";
693 static inline int imax(
int a,
int b) {
return (a > b ? a : b); }
694 static int kvprintf(
char const *
fmt,
void (*
func)(
int),
void *
arg,
int radix, va_list ap);
696 static void putce(
int c)
702 ret = write(2, s, 1);
707 kprintf(
const char *fmt, ...)
713 retval = kvprintf(fmt, putce,
NULL, 10, ap);
725 ksprintn(
char *nbuf, uintmax_t num,
int base,
int *lenp,
int upper)
732 c = hex2ascii(num % base);
733 *++p = upper ? toupper(c) : c;
734 }
while (num /= base);
736 *lenp = (
int)(p - nbuf);
767 kvprintf(
char const *fmt,
void (*
func)(
int),
void *
arg,
int radix, va_list ap)
769 #define PCHAR(c) {int cc=(c); if (func) (*func)(cc); else *d++ = cc; retval++; }
772 const char *
p, *percent, *
q;
776 int base, lflag, qflag,
tmp, width, ladjust, sharpflag,
neg, sign, dot;
777 int cflag, hflag, jflag, tflag, zflag;
789 fmt =
"(fmt null)\n";
791 if (radix < 2 || radix > 36)
797 while ((ch = (
unsigned char)*fmt++) !=
'%' || stop) {
803 qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
804 sign = 0; dot = 0; dwidth = 0; upper = 0;
805 cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
806 reswitch:
switch (ch = (
unsigned char)*fmt++) {
824 width = va_arg(ap,
int);
830 dwidth = va_arg(ap,
int);
838 case '1':
case '2':
case '3':
case '4':
839 case '5':
case '6':
case '7':
case '8':
case '9':
840 for (n = 0;; ++
fmt) {
841 n = n * 10 + ch -
'0';
843 if (ch < '0' || ch >
'9')
852 num = (
unsigned int)va_arg(ap,
int);
853 p = va_arg(ap,
char *);
854 for (q = ksprintn(nbuf, num, *p++,
NULL, 0); *
q;)
862 if (num & (1 << (n - 1))) {
863 PCHAR(tmp ?
',' :
'<');
864 for (; (n = *
p) >
' '; ++
p)
868 for (; *p >
' '; ++
p)
875 PCHAR(va_arg(ap,
int));
878 up = va_arg(ap,
unsigned char *);
879 p = va_arg(ap,
char *);
883 PCHAR(hex2ascii(*up >> 4));
884 PCHAR(hex2ascii(*up & 0x0f));
915 *(va_arg(ap, intmax_t *)) =
retval;
917 *(va_arg(ap, int64_t *)) =
retval;
919 *(va_arg(ap,
long *)) =
retval;
921 *(va_arg(ap,
size_t *)) =
retval;
923 *(va_arg(ap,
short *)) =
retval;
925 *(va_arg(ap,
char *)) =
retval;
927 *(va_arg(ap,
int *)) =
retval;
934 sharpflag = (width == 0);
947 p = va_arg(ap,
char *);
953 for (n = 0; n < dwidth && p[
n]; n++)
958 if (!ladjust && width > 0)
963 if (ladjust && width > 0)
988 num = va_arg(ap, uintmax_t);
992 num = va_arg(ap, ptrdiff_t);
994 num = va_arg(ap,
unsigned long);
996 num = va_arg(ap,
size_t);
998 num = (
unsigned short)va_arg(ap,
int);
1000 num = (
unsigned char)va_arg(ap,
int);
1002 num = va_arg(ap,
unsigned int);
1006 num = va_arg(ap, intmax_t);
1008 num = va_arg(ap, int64_t);
1010 num = va_arg(ap, ptrdiff_t);
1012 num = va_arg(ap,
long);
1014 num = va_arg(ap, ssize_t);
1016 num = (short)va_arg(ap,
int);
1018 num = (char)va_arg(ap,
int);
1020 num = va_arg(ap,
int);
1022 if (sign && (intmax_t)num < 0) {
1024 num = -(intmax_t)num;
1026 p = ksprintn(nbuf, num, base, &n, upper);
1028 if (sharpflag && num != 0) {
1031 else if (base == 16)
1037 if (!ladjust && padc ==
'0')
1038 dwidth = width -
tmp;
1039 width -= tmp + imax(dwidth, n);
1046 if (sharpflag && num != 0) {
1049 }
else if (base == 16) {
1054 while (dwidth-- > 0)
1066 while (percent < fmt)
1081 #error not supported
RUBY_SYMBOL_EXPORT_BEGIN void * alloca()
size_t strlen(const char *)
SSL_METHOD *(* func)(void)
unsigned long long uint64_t
char * strchr(char *, char)
int memcmp(const void *s1, const void *s2, size_t len)
RUBY_EXTERN char * strerror(int)
char * strrchr(const char *, const char)