Ruby 1.9.3p327(2012-11-10revision37606)
|
00001 /********************************************************************** 00002 00003 addr2line.h - 00004 00005 $Author$ 00006 00007 Copyright (C) 2010 Shinichiro Hamaji 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 /* DWARF3 */ 00055 #define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */ 00056 #define DW_LNS_set_isa 0x0c /* DWARF3 */ 00057 00058 /* Line number extended opcode name. */ 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 /* DWARF4 */ 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 /* Avoid consuming stack as this module may be used from signal handler */ 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 /* Need to output binary file name? */ 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 /* last modified. */ 00161 uleb128(&p); 00162 /* size of the file. */ 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 /* libexecinfo */ 00177 *p = strchr(symbol, '/'); 00178 if (*p == NULL) return 0; 00179 *len = strlen(*p); 00180 } 00181 else { 00182 /* glibc */ 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 /* We assume one line code doesn't result >100 bytes of native code. 00201 We may want more reliable way eventually... */ 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 /* The registers. */ 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 /*dwarf_version = *(unsigned short *)p;*/ 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 /* skip include directories */ 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 /* TODO:currently ignore */ 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 /* read file and fill lines */ 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 /* Ideally we should check 4 paths to follow gnu_debuglink, 00406 but we handle only one case for now as this format is used 00407 by some linux distributions. See GDB's info for detail. */ 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 /* read file and fill lines */ 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 /* async-signal unsafe */ 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 /* This file doesn't have .debug_line section, 00497 let's check .gnu_debuglink section instead. */ 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 /* HAVE_DL_ITERATE_PHDR */ 00540 00541 void 00542 rb_dump_backtrace_with_lines(int num_traces, void **trace, char **syms) 00543 { 00544 int i; 00545 /* async-signal unsafe */ 00546 line_info_t *lines = (line_info_t *)calloc(num_traces, 00547 sizeof(line_info_t)); 00548 00549 /* Note that line info of shared objects might not be shown 00550 if we don't have dl_iterate_phdr */ 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 /* maybe async-signal unsafe */ 00558 dl_iterate_phdr(fill_base_addr, &fill_base_addr_state); 00559 #endif /* HAVE_DL_ITERATE_PHDR */ 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 /* fprintf may not be async-signal safe */ 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 /* defined(USE_ELF) */ 00609 #error not supported 00610 #endif 00611