00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "ruby/config.h"
00012 #include "addr2line.h"
00013
00014 #include <stdio.h>
00015 #include <errno.h>
00016
00017 #ifdef USE_ELF
00018
00019 #ifdef __OpenBSD__
00020 #include <elf_abi.h>
00021 #else
00022 #include <elf.h>
00023 #endif
00024 #include <fcntl.h>
00025 #include <limits.h>
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <sys/mman.h>
00030 #include <sys/types.h>
00031 #include <sys/stat.h>
00032 #include <unistd.h>
00033
00034 #if defined(HAVE_ALLOCA_H)
00035 #include <alloca.h>
00036 #endif
00037
00038 #ifdef HAVE_DL_ITERATE_PHDR
00039 # ifndef _GNU_SOURCE
00040 # define _GNU_SOURCE
00041 # endif
00042 # include <link.h>
00043 #endif
00044
00045 #define DW_LNS_copy 0x01
00046 #define DW_LNS_advance_pc 0x02
00047 #define DW_LNS_advance_line 0x03
00048 #define DW_LNS_set_file 0x04
00049 #define DW_LNS_set_column 0x05
00050 #define DW_LNS_negate_stmt 0x06
00051 #define DW_LNS_set_basic_block 0x07
00052 #define DW_LNS_const_add_pc 0x08
00053 #define DW_LNS_fixed_advance_pc 0x09
00054 #define DW_LNS_set_prologue_end 0x0a
00055 #define DW_LNS_set_epilogue_begin 0x0b
00056 #define DW_LNS_set_isa 0x0c
00057
00058
00059 #define DW_LNE_end_sequence 0x01
00060 #define DW_LNE_set_address 0x02
00061 #define DW_LNE_define_file 0x03
00062 #define DW_LNE_set_discriminator 0x04
00063
00064 #ifndef ElfW
00065 # if SIZEOF_VOIDP == 8
00066 # define ElfW(x) Elf64##_##x
00067 # else
00068 # define ElfW(x) Elf32##_##x
00069 # endif
00070 #endif
00071
00072 typedef struct {
00073 const char *dirname;
00074 const char *filename;
00075 int line;
00076
00077 int fd;
00078 void *mapped;
00079 size_t mapped_size;
00080 unsigned long base_addr;
00081 } line_info_t;
00082
00083
00084 static char binary_filename[PATH_MAX];
00085
00086 static unsigned long
00087 uleb128(char **p) {
00088 unsigned long r = 0;
00089 int s = 0;
00090 for (;;) {
00091 unsigned char b = *(unsigned char *)(*p)++;
00092 if (b < 0x80) {
00093 r += (unsigned long)b << s;
00094 break;
00095 }
00096 r += (b & 0x7f) << s;
00097 s += 7;
00098 }
00099 return r;
00100 }
00101
00102 static long
00103 sleb128(char **p) {
00104 long r = 0;
00105 int s = 0;
00106 for (;;) {
00107 unsigned char b = *(unsigned char *)(*p)++;
00108 if (b < 0x80) {
00109 if (b & 0x40) {
00110 r -= (0x80 - b) << s;
00111 }
00112 else {
00113 r += (b & 0x3f) << s;
00114 }
00115 break;
00116 }
00117 r += (b & 0x7f) << s;
00118 s += 7;
00119 }
00120 return r;
00121 }
00122
00123 static const char *
00124 get_nth_dirname(unsigned long dir, char *p)
00125 {
00126 if (!dir--) {
00127 return "";
00128 }
00129 while (dir--) {
00130 while (*p) p++;
00131 p++;
00132 if (!*p) {
00133 fprintf(stderr, "Unexpected directory number %lu in %s\n",
00134 dir, binary_filename);
00135 return "";
00136 }
00137 }
00138 return p;
00139 }
00140
00141 static void
00142 fill_filename(int file, char *include_directories, char *filenames,
00143 line_info_t *line)
00144 {
00145 int i;
00146 char *p = filenames;
00147 char *filename;
00148 unsigned long dir;
00149 for (i = 1; i <= file; i++) {
00150 filename = p;
00151 if (!*p) {
00152
00153 fprintf(stderr, "Unexpected file number %d in %s\n",
00154 file, binary_filename);
00155 return;
00156 }
00157 while (*p) p++;
00158 p++;
00159 dir = uleb128(&p);
00160
00161 uleb128(&p);
00162
00163 uleb128(&p);
00164
00165 if (i == file) {
00166 line->filename = filename;
00167 line->dirname = get_nth_dirname(dir, include_directories);
00168 }
00169 }
00170 }
00171
00172 static int
00173 get_path_from_symbol(const char *symbol, const char **p, size_t *len)
00174 {
00175 if (symbol[0] == '0') {
00176
00177 *p = strchr(symbol, '/');
00178 if (*p == NULL) return 0;
00179 *len = strlen(*p);
00180 }
00181 else {
00182
00183 const char *q;
00184 *p = symbol;
00185 q = strchr(symbol, '(');
00186 if (q == NULL) return 0;
00187 *len = q - symbol;
00188 }
00189 return 1;
00190 }
00191
00192 static void
00193 fill_line(int num_traces, void **traces,
00194 unsigned long addr, int file, int line,
00195 char *include_directories, char *filenames, line_info_t *lines)
00196 {
00197 int i;
00198 for (i = 0; i < num_traces; i++) {
00199 unsigned long a = (unsigned long)traces[i] - lines[i].base_addr;
00200
00201
00202 if (addr < a && a < addr + 100) {
00203 fill_filename(file, include_directories, filenames, &lines[i]);
00204 lines[i].line = line;
00205 }
00206 }
00207 }
00208
00209 static void
00210 parse_debug_line_cu(int num_traces, void **traces,
00211 char **debug_line, line_info_t *lines)
00212 {
00213 char *p, *cu_end, *cu_start, *include_directories, *filenames;
00214 unsigned long unit_length;
00215 int default_is_stmt, line_base;
00216 unsigned int header_length, minimum_instruction_length, line_range,
00217 opcode_base;
00218 unsigned char *standard_opcode_lengths;
00219
00220
00221 unsigned long addr = 0;
00222 unsigned int file = 1;
00223 unsigned int line = 1;
00224 unsigned int column = 0;
00225 int is_stmt;
00226 int basic_block = 0;
00227 int end_sequence = 0;
00228 int prologue_end = 0;
00229 int epilogue_begin = 0;
00230 unsigned int isa = 0;
00231
00232 p = *debug_line;
00233
00234 unit_length = *(unsigned int *)p;
00235 p += sizeof(unsigned int);
00236 if (unit_length == 0xffffffff) {
00237 unit_length = *(unsigned long *)p;
00238 p += sizeof(unsigned long);
00239 }
00240
00241 cu_end = p + unit_length;
00242
00243
00244 p += 2;
00245
00246 header_length = *(unsigned int *)p;
00247 p += sizeof(unsigned int);
00248
00249 cu_start = p + header_length;
00250
00251 minimum_instruction_length = *(unsigned char *)p;
00252 p++;
00253
00254 is_stmt = default_is_stmt = *(unsigned char *)p;
00255 p++;
00256
00257 line_base = *(char *)p;
00258 p++;
00259
00260 line_range = *(unsigned char *)p;
00261 p++;
00262
00263 opcode_base = *(unsigned char *)p;
00264 p++;
00265
00266 standard_opcode_lengths = (unsigned char *)p - 1;
00267 p += opcode_base - 1;
00268
00269 include_directories = p;
00270
00271
00272 while (*p) {
00273 while (*p) p++;
00274 p++;
00275 }
00276 p++;
00277
00278 filenames = p;
00279
00280 p = cu_start;
00281
00282 #define FILL_LINE() \
00283 do { \
00284 fill_line(num_traces, traces, addr, file, line, \
00285 include_directories, filenames, lines); \
00286 basic_block = prologue_end = epilogue_begin = 0; \
00287 } while (0)
00288
00289 while (p < cu_end) {
00290 unsigned long a;
00291 unsigned char op = *p++;
00292 switch (op) {
00293 case DW_LNS_copy:
00294 FILL_LINE();
00295 break;
00296 case DW_LNS_advance_pc:
00297 a = uleb128(&p);
00298 addr += a;
00299 break;
00300 case DW_LNS_advance_line: {
00301 long a = sleb128(&p);
00302 line += a;
00303 break;
00304 }
00305 case DW_LNS_set_file:
00306 file = (unsigned int)uleb128(&p);
00307 break;
00308 case DW_LNS_set_column:
00309 column = (unsigned int)uleb128(&p);
00310 break;
00311 case DW_LNS_negate_stmt:
00312 is_stmt = !is_stmt;
00313 break;
00314 case DW_LNS_set_basic_block:
00315 basic_block = 1;
00316 break;
00317 case DW_LNS_const_add_pc:
00318 a = ((255 - opcode_base) / line_range) *
00319 minimum_instruction_length;
00320 addr += a;
00321 break;
00322 case DW_LNS_fixed_advance_pc:
00323 a = *(unsigned char *)p++;
00324 addr += a;
00325 break;
00326 case DW_LNS_set_prologue_end:
00327 prologue_end = 1;
00328 break;
00329 case DW_LNS_set_epilogue_begin:
00330 epilogue_begin = 1;
00331 break;
00332 case DW_LNS_set_isa:
00333 isa = (unsigned int)uleb128(&p);
00334 break;
00335 case 0:
00336 a = *(unsigned char *)p++;
00337 op = *p++;
00338 switch (op) {
00339 case DW_LNE_end_sequence:
00340 end_sequence = 1;
00341 FILL_LINE();
00342 addr = 0;
00343 file = 1;
00344 line = 1;
00345 column = 0;
00346 is_stmt = default_is_stmt;
00347 end_sequence = 0;
00348 isa = 0;
00349 break;
00350 case DW_LNE_set_address:
00351 addr = *(unsigned long *)p;
00352 p += sizeof(unsigned long);
00353 break;
00354 case DW_LNE_define_file:
00355 fprintf(stderr, "Unsupported operation in %s\n",
00356 binary_filename);
00357 break;
00358 case DW_LNE_set_discriminator:
00359
00360 uleb128(&p);
00361 break;
00362 default:
00363 fprintf(stderr, "Unknown extended opcode: %d in %s\n",
00364 op, binary_filename);
00365 }
00366 break;
00367 default: {
00368 unsigned long addr_incr;
00369 unsigned long line_incr;
00370 a = op - opcode_base;
00371 addr_incr = (a / line_range) * minimum_instruction_length;
00372 line_incr = line_base + (a % line_range);
00373 addr += (unsigned int)addr_incr;
00374 line += (unsigned int)line_incr;
00375 FILL_LINE();
00376 }
00377 }
00378 }
00379 *debug_line = p;
00380 }
00381
00382 static void
00383 parse_debug_line(int num_traces, void **traces,
00384 char *debug_line, unsigned long size, line_info_t *lines)
00385 {
00386 char *debug_line_end = debug_line + size;
00387 while (debug_line < debug_line_end) {
00388 parse_debug_line_cu(num_traces, traces, &debug_line, lines);
00389 }
00390 if (debug_line != debug_line_end) {
00391 fprintf(stderr, "Unexpected size of .debug_line in %s\n",
00392 binary_filename);
00393 }
00394 }
00395
00396
00397 static void
00398 fill_lines(int num_traces, void **traces, char **syms, int check_debuglink,
00399 line_info_t *current_line, line_info_t *lines);
00400
00401 static void
00402 follow_debuglink(char *debuglink, int num_traces, void **traces, char **syms,
00403 line_info_t *current_line, line_info_t *lines)
00404 {
00405
00406
00407
00408 static const char global_debug_dir[] = "/usr/lib/debug";
00409 char *p, *subdir;
00410
00411 p = strrchr(binary_filename, '/');
00412 if (!p) {
00413 return;
00414 }
00415 p[1] = '\0';
00416
00417 subdir = (char *)alloca(strlen(binary_filename) + 1);
00418 strcpy(subdir, binary_filename);
00419 strcpy(binary_filename, global_debug_dir);
00420 strncat(binary_filename, subdir,
00421 PATH_MAX - strlen(binary_filename) - 1);
00422 strncat(binary_filename, debuglink,
00423 PATH_MAX - strlen(binary_filename) - 1);
00424
00425 munmap(current_line->mapped, current_line->mapped_size);
00426 close(current_line->fd);
00427 fill_lines(num_traces, traces, syms, 0, current_line, lines);
00428 }
00429
00430
00431 static void
00432 fill_lines(int num_traces, void **traces, char **syms, int check_debuglink,
00433 line_info_t *current_line, line_info_t *lines)
00434 {
00435 int i;
00436 char *shstr;
00437 char *section_name;
00438 ElfW(Ehdr) *ehdr;
00439 ElfW(Shdr) *shdr, *shstr_shdr;
00440 ElfW(Shdr) *debug_line_shdr = NULL, *gnu_debuglink_shdr = NULL;
00441 int fd;
00442 off_t filesize;
00443 char *file;
00444
00445 fd = open(binary_filename, O_RDONLY);
00446 if (fd < 0) {
00447 return;
00448 }
00449 filesize = lseek(fd, 0, SEEK_END);
00450 if (filesize < 0) {
00451 int e = errno;
00452 close(fd);
00453 fprintf(stderr, "lseek: %s\n", strerror(e));
00454 return;
00455 }
00456 lseek(fd, 0, SEEK_SET);
00457
00458 file = (char *)mmap(NULL, filesize, PROT_READ, MAP_SHARED, fd, 0);
00459 if (file == MAP_FAILED) {
00460 int e = errno;
00461 close(fd);
00462 fprintf(stderr, "mmap: %s\n", strerror(e));
00463 return;
00464 }
00465
00466 current_line->fd = fd;
00467 current_line->mapped = file;
00468 current_line->mapped_size = filesize;
00469
00470 for (i = 0; i < num_traces; i++) {
00471 const char *path;
00472 size_t len;
00473 if (get_path_from_symbol(syms[i], &path, &len) &&
00474 !strncmp(path, binary_filename, len)) {
00475 lines[i].line = -1;
00476 }
00477 }
00478
00479 ehdr = (ElfW(Ehdr) *)file;
00480 shdr = (ElfW(Shdr) *)(file + ehdr->e_shoff);
00481
00482 shstr_shdr = shdr + ehdr->e_shstrndx;
00483 shstr = file + shstr_shdr->sh_offset;
00484
00485 for (i = 0; i < ehdr->e_shnum; i++) {
00486 section_name = shstr + shdr[i].sh_name;
00487 if (!strcmp(section_name, ".debug_line")) {
00488 debug_line_shdr = shdr + i;
00489 break;
00490 } else if (!strcmp(section_name, ".gnu_debuglink")) {
00491 gnu_debuglink_shdr = shdr + i;
00492 }
00493 }
00494
00495 if (!debug_line_shdr) {
00496
00497
00498 if (gnu_debuglink_shdr && check_debuglink) {
00499 follow_debuglink(file + gnu_debuglink_shdr->sh_offset,
00500 num_traces, traces, syms,
00501 current_line, lines);
00502 }
00503 return;
00504 }
00505
00506 parse_debug_line(num_traces, traces,
00507 file + debug_line_shdr->sh_offset,
00508 debug_line_shdr->sh_size,
00509 lines);
00510 }
00511
00512 #ifdef HAVE_DL_ITERATE_PHDR
00513
00514 typedef struct {
00515 int num_traces;
00516 char **syms;
00517 line_info_t *lines;
00518 } fill_base_addr_state_t;
00519
00520 static int
00521 fill_base_addr(struct dl_phdr_info *info, size_t size, void *data)
00522 {
00523 int i;
00524 fill_base_addr_state_t *st = (fill_base_addr_state_t *)data;
00525 for (i = 0; i < st->num_traces; i++) {
00526 const char *path;
00527 size_t len;
00528 size_t name_len = strlen(info->dlpi_name);
00529
00530 if (get_path_from_symbol(st->syms[i], &path, &len) &&
00531 (len == name_len || (len > name_len && path[len-name_len-1] == '/')) &&
00532 !strncmp(path+len-name_len, info->dlpi_name, name_len)) {
00533 st->lines[i].base_addr = info->dlpi_addr;
00534 }
00535 }
00536 return 0;
00537 }
00538
00539 #endif
00540
00541 void
00542 rb_dump_backtrace_with_lines(int num_traces, void **trace, char **syms)
00543 {
00544 int i;
00545
00546 line_info_t *lines = (line_info_t *)calloc(num_traces,
00547 sizeof(line_info_t));
00548
00549
00550
00551 #ifdef HAVE_DL_ITERATE_PHDR
00552 fill_base_addr_state_t fill_base_addr_state;
00553
00554 fill_base_addr_state.num_traces = num_traces;
00555 fill_base_addr_state.syms = syms;
00556 fill_base_addr_state.lines = lines;
00557
00558 dl_iterate_phdr(fill_base_addr, &fill_base_addr_state);
00559 #endif
00560
00561 for (i = 0; i < num_traces; i++) {
00562 const char *path;
00563 size_t len;
00564 if (lines[i].line) {
00565 continue;
00566 }
00567
00568 if (!get_path_from_symbol(syms[i], &path, &len)) {
00569 continue;
00570 }
00571
00572 strncpy(binary_filename, path, len);
00573 binary_filename[len] = '\0';
00574
00575 fill_lines(num_traces, trace, syms, 1, &lines[i], lines);
00576 }
00577
00578
00579 for (i = 0; i < num_traces; i++) {
00580 line_info_t *line = &lines[i];
00581
00582 if (line->line > 0) {
00583 fprintf(stderr, "%s ", syms[i]);
00584 if (line->filename) {
00585 if (line->dirname && line->dirname[0]) {
00586 fprintf(stderr, "%s/", line->dirname);
00587 }
00588 fprintf(stderr, "%s", line->filename);
00589 } else {
00590 fprintf(stderr, "???");
00591 }
00592 fprintf(stderr, ":%d\n", line->line);
00593 } else {
00594 fprintf(stderr, "%s\n", syms[i]);
00595 }
00596 }
00597
00598 for (i = 0; i < num_traces; i++) {
00599 line_info_t *line = &lines[i];
00600 if (line->fd) {
00601 munmap(line->mapped, line->mapped_size);
00602 close(line->fd);
00603 }
00604 }
00605 free(lines);
00606 }
00607
00608 #else
00609 #error not supported
00610 #endif
00611