11 #include "ruby/config.h"
30 #include <sys/types.h>
34 #if defined(HAVE_ALLOCA_H)
38 #ifdef HAVE_DL_ITERATE_PHDR
45 #define DW_LNS_copy 0x01
46 #define DW_LNS_advance_pc 0x02
47 #define DW_LNS_advance_line 0x03
48 #define DW_LNS_set_file 0x04
49 #define DW_LNS_set_column 0x05
50 #define DW_LNS_negate_stmt 0x06
51 #define DW_LNS_set_basic_block 0x07
52 #define DW_LNS_const_add_pc 0x08
53 #define DW_LNS_fixed_advance_pc 0x09
54 #define DW_LNS_set_prologue_end 0x0a
55 #define DW_LNS_set_epilogue_begin 0x0b
56 #define DW_LNS_set_isa 0x0c
59 #define DW_LNE_end_sequence 0x01
60 #define DW_LNE_set_address 0x02
61 #define DW_LNE_define_file 0x03
62 #define DW_LNE_set_discriminator 0x04
65 # if SIZEOF_VOIDP == 8
66 # define ElfW(x) Elf64##_##x
68 # define ElfW(x) Elf32##_##x
80 unsigned long base_addr;
84 static char binary_filename[
PATH_MAX];
91 unsigned char b = *(
unsigned char *)(*p)++;
93 r += (
unsigned long)b << s;
107 unsigned char b = *(
unsigned char *)(*p)++;
110 r -= (0x80 -
b) << s;
113 r += (b & 0x3f) << s;
117 r += (b & 0x7f) << s;
124 get_nth_dirname(
unsigned long dir,
char *p)
133 fprintf(stderr,
"Unexpected directory number %lu in %s\n",
134 dir, binary_filename);
142 fill_filename(
int file,
char *include_directories,
char *filenames,
149 for (i = 1; i <= file; i++) {
153 fprintf(stderr,
"Unexpected file number %d in %s\n",
154 file, binary_filename);
166 line->filename = filename;
167 line->dirname = get_nth_dirname(dir, include_directories);
173 get_path_from_symbol(
const char *symbol,
const char **p,
size_t *
len)
175 if (symbol[0] ==
'0') {
178 if (*p ==
NULL)
return 0;
186 if (q ==
NULL)
return 0;
193 fill_line(
int num_traces,
void **traces,
194 unsigned long addr,
int file,
int line,
195 char *include_directories,
char *filenames, line_info_t *lines)
198 for (i = 0; i < num_traces; i++) {
199 unsigned long a = (
unsigned long)traces[i] - lines[i].base_addr;
202 if (addr < a && a < addr + 100) {
203 fill_filename(file, include_directories, filenames, &lines[i]);
204 lines[
i].line = line;
210 parse_debug_line_cu(
int num_traces,
void **traces,
211 char **debug_line, line_info_t *lines)
213 char *
p, *cu_end, *cu_start, *include_directories, *filenames;
214 unsigned long unit_length;
215 int default_is_stmt, line_base;
216 unsigned int header_length, minimum_instruction_length, line_range,
218 unsigned char *standard_opcode_lengths;
221 unsigned long addr = 0;
222 unsigned int file = 1;
223 unsigned int line = 1;
224 unsigned int column = 0;
228 int prologue_end = 0;
229 int epilogue_begin = 0;
230 unsigned int isa = 0;
234 unit_length = *(
unsigned int *)p;
235 p +=
sizeof(
unsigned int);
236 if (unit_length == 0xffffffff) {
237 unit_length = *(
unsigned long *)p;
238 p +=
sizeof(
unsigned long);
241 cu_end = p + unit_length;
246 header_length = *(
unsigned int *)p;
247 p +=
sizeof(
unsigned int);
249 cu_start = p + header_length;
251 minimum_instruction_length = *(
unsigned char *)p;
254 is_stmt = default_is_stmt = *(
unsigned char *)p;
257 line_base = *(
char *)p;
260 line_range = *(
unsigned char *)p;
263 opcode_base = *(
unsigned char *)p;
266 standard_opcode_lengths = (
unsigned char *)p - 1;
267 p += opcode_base - 1;
269 include_directories =
p;
282 #define FILL_LINE() \
284 fill_line(num_traces, traces, addr, file, line, \
285 include_directories, filenames, lines); \
286 basic_block = prologue_end = epilogue_begin = 0; \
291 unsigned char op = *p++;
296 case DW_LNS_advance_pc:
300 case DW_LNS_advance_line: {
301 long a = sleb128(&p);
305 case DW_LNS_set_file:
306 file = (
unsigned int)uleb128(&p);
308 case DW_LNS_set_column:
309 column = (
unsigned int)uleb128(&p);
311 case DW_LNS_negate_stmt:
314 case DW_LNS_set_basic_block:
317 case DW_LNS_const_add_pc:
318 a = ((255 - opcode_base) / line_range) *
319 minimum_instruction_length;
322 case DW_LNS_fixed_advance_pc:
323 a = *(
unsigned char *)p++;
326 case DW_LNS_set_prologue_end:
329 case DW_LNS_set_epilogue_begin:
333 isa = (
unsigned int)uleb128(&p);
336 a = *(
unsigned char *)p++;
339 case DW_LNE_end_sequence:
346 is_stmt = default_is_stmt;
350 case DW_LNE_set_address:
351 addr = *(
unsigned long *)p;
352 p +=
sizeof(
unsigned long);
354 case DW_LNE_define_file:
355 fprintf(stderr,
"Unsupported operation in %s\n",
358 case DW_LNE_set_discriminator:
363 fprintf(stderr,
"Unknown extended opcode: %d in %s\n",
364 op, binary_filename);
368 unsigned long addr_incr;
369 unsigned long line_incr;
370 a = op - opcode_base;
371 addr_incr = (a / line_range) * minimum_instruction_length;
372 line_incr = line_base + (a % line_range);
373 addr += (
unsigned int)addr_incr;
374 line += (
unsigned int)line_incr;
383 parse_debug_line(
int num_traces,
void **traces,
384 char *debug_line,
unsigned long size, line_info_t *lines)
386 char *debug_line_end = debug_line +
size;
387 while (debug_line < debug_line_end) {
388 parse_debug_line_cu(num_traces, traces, &debug_line, lines);
390 if (debug_line != debug_line_end) {
391 fprintf(stderr,
"Unexpected size of .debug_line in %s\n",
398 fill_lines(
int num_traces,
void **traces,
char **syms,
int check_debuglink,
399 line_info_t *current_line, line_info_t *lines);
402 follow_debuglink(
char *debuglink,
int num_traces,
void **traces,
char **syms,
403 line_info_t *current_line, line_info_t *lines)
408 static const char global_debug_dir[] =
"/usr/lib/debug";
411 p =
strrchr(binary_filename,
'/');
418 strcpy(subdir, binary_filename);
419 strcpy(binary_filename, global_debug_dir);
420 strncat(binary_filename, subdir,
422 strncat(binary_filename, debuglink,
425 munmap(current_line->mapped, current_line->mapped_size);
426 close(current_line->fd);
427 fill_lines(num_traces, traces, syms, 0, current_line, lines);
432 fill_lines(
int num_traces,
void **traces,
char **syms,
int check_debuglink,
433 line_info_t *current_line, line_info_t *lines)
439 ElfW(Shdr) *shdr, *shstr_shdr;
440 ElfW(Shdr) *debug_line_shdr =
NULL, *gnu_debuglink_shdr =
NULL;
445 fd = open(binary_filename, O_RDONLY);
453 fprintf(stderr,
"lseek: %s\n",
strerror(e));
458 file = (
char *)mmap(
NULL, filesize, PROT_READ, MAP_SHARED, fd, 0);
459 if (file == MAP_FAILED) {
462 fprintf(stderr,
"mmap: %s\n",
strerror(e));
466 current_line->fd = fd;
467 current_line->mapped = file;
468 current_line->mapped_size = filesize;
470 for (i = 0; i < num_traces; i++) {
473 if (get_path_from_symbol(syms[i], &path, &len) &&
474 !strncmp(path, binary_filename, len)) {
479 ehdr = (ElfW(Ehdr) *)file;
480 shdr = (ElfW(Shdr) *)(file + ehdr->e_shoff);
482 shstr_shdr = shdr + ehdr->e_shstrndx;
483 shstr = file + shstr_shdr->sh_offset;
485 for (i = 0; i < ehdr->e_shnum; i++) {
486 section_name = shstr + shdr[
i].sh_name;
487 if (!strcmp(section_name,
".debug_line")) {
488 debug_line_shdr = shdr +
i;
490 }
else if (!strcmp(section_name,
".gnu_debuglink")) {
491 gnu_debuglink_shdr = shdr +
i;
495 if (!debug_line_shdr) {
498 if (gnu_debuglink_shdr && check_debuglink) {
499 follow_debuglink(file + gnu_debuglink_shdr->sh_offset,
500 num_traces, traces, syms,
501 current_line, lines);
506 parse_debug_line(num_traces, traces,
507 file + debug_line_shdr->sh_offset,
508 debug_line_shdr->sh_size,
512 #ifdef HAVE_DL_ITERATE_PHDR
518 } fill_base_addr_state_t;
521 fill_base_addr(
struct dl_phdr_info *
info,
size_t size,
void *
data)
524 fill_base_addr_state_t *
st = (fill_base_addr_state_t *)data;
525 for (i = 0; i < st->num_traces; i++) {
528 size_t name_len =
strlen(info->dlpi_name);
530 if (get_path_from_symbol(st->syms[i], &path, &len) &&
531 (len == name_len || (len > name_len && path[len-name_len-1] ==
'/')) &&
532 !strncmp(path+len-name_len, info->dlpi_name, name_len)) {
533 st->lines[
i].base_addr = info->dlpi_addr;
542 rb_dump_backtrace_with_lines(
int num_traces,
void **trace,
char **syms)
546 line_info_t *lines = (line_info_t *)
calloc(num_traces,
547 sizeof(line_info_t));
551 #ifdef HAVE_DL_ITERATE_PHDR
552 fill_base_addr_state_t fill_base_addr_state;
554 fill_base_addr_state.num_traces = num_traces;
555 fill_base_addr_state.syms = syms;
556 fill_base_addr_state.lines = lines;
558 dl_iterate_phdr(fill_base_addr, &fill_base_addr_state);
561 for (i = 0; i < num_traces; i++) {
568 if (!get_path_from_symbol(syms[i], &path, &len)) {
572 strncpy(binary_filename, path, len);
573 binary_filename[
len] =
'\0';
575 fill_lines(num_traces, trace, syms, 1, &lines[i], lines);
579 for (i = 0; i < num_traces; i++) {
580 line_info_t *line = &lines[
i];
582 if (line->line > 0) {
583 fprintf(stderr,
"%s ", syms[i]);
584 if (line->filename) {
585 if (line->dirname && line->dirname[0]) {
586 fprintf(stderr,
"%s/", line->dirname);
588 fprintf(stderr,
"%s", line->filename);
590 fprintf(stderr,
"???");
592 fprintf(stderr,
":%d\n", line->line);
594 fprintf(stderr,
"%s\n", syms[i]);
598 for (i = 0; i < num_traces; i++) {
599 line_info_t *line = &lines[
i];
601 munmap(line->mapped, line->mapped_size);
size_t strlen(const char *)
static VALUE end_sequence(VALUE self)
char * strchr(char *, char)
register unsigned int len
RUBY_EXTERN char * strerror(int)
char * strrchr(const char *, const char)