|
Ruby
1.9.3p448(2013-06-27revision41675)
|
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 #ifndef PATH_MAX 00072 #define PATH_MAX 4096 00073 #endif 00074 00075 typedef struct { 00076 const char *dirname; 00077 const char *filename; 00078 int line; 00079 00080 int fd; 00081 void *mapped; 00082 size_t mapped_size; 00083 unsigned long base_addr; 00084 } line_info_t; 00085 00086 /* Avoid consuming stack as this module may be used from signal handler */ 00087 static char binary_filename[PATH_MAX]; 00088 00089 static unsigned long 00090 uleb128(char **p) { 00091 unsigned long r = 0; 00092 int s = 0; 00093 for (;;) { 00094 unsigned char b = *(unsigned char *)(*p)++; 00095 if (b < 0x80) { 00096 r += (unsigned long)b << s; 00097 break; 00098 } 00099 r += (b & 0x7f) << s; 00100 s += 7; 00101 } 00102 return r; 00103 } 00104 00105 static long 00106 sleb128(char **p) { 00107 long r = 0; 00108 int s = 0; 00109 for (;;) { 00110 unsigned char b = *(unsigned char *)(*p)++; 00111 if (b < 0x80) { 00112 if (b & 0x40) { 00113 r -= (0x80 - b) << s; 00114 } 00115 else { 00116 r += (b & 0x3f) << s; 00117 } 00118 break; 00119 } 00120 r += (b & 0x7f) << s; 00121 s += 7; 00122 } 00123 return r; 00124 } 00125 00126 static const char * 00127 get_nth_dirname(unsigned long dir, char *p) 00128 { 00129 if (!dir--) { 00130 return ""; 00131 } 00132 while (dir--) { 00133 while (*p) p++; 00134 p++; 00135 if (!*p) { 00136 fprintf(stderr, "Unexpected directory number %lu in %s\n", 00137 dir, binary_filename); 00138 return ""; 00139 } 00140 } 00141 return p; 00142 } 00143 00144 static void 00145 fill_filename(int file, char *include_directories, char *filenames, 00146 line_info_t *line) 00147 { 00148 int i; 00149 char *p = filenames; 00150 char *filename; 00151 unsigned long dir; 00152 for (i = 1; i <= file; i++) { 00153 filename = p; 00154 if (!*p) { 00155 /* Need to output binary file name? */ 00156 fprintf(stderr, "Unexpected file number %d in %s\n", 00157 file, binary_filename); 00158 return; 00159 } 00160 while (*p) p++; 00161 p++; 00162 dir = uleb128(&p); 00163 /* last modified. */ 00164 uleb128(&p); 00165 /* size of the file. */ 00166 uleb128(&p); 00167 00168 if (i == file) { 00169 line->filename = filename; 00170 line->dirname = get_nth_dirname(dir, include_directories); 00171 } 00172 } 00173 } 00174 00175 static int 00176 get_path_from_symbol(const char *symbol, const char **p, size_t *len) 00177 { 00178 if (symbol[0] == '0') { 00179 /* libexecinfo */ 00180 *p = strchr(symbol, '/'); 00181 if (*p == NULL) return 0; 00182 *len = strlen(*p); 00183 } 00184 else { 00185 /* glibc */ 00186 const char *q; 00187 *p = symbol; 00188 q = strchr(symbol, '('); 00189 if (q == NULL) return 0; 00190 *len = q - symbol; 00191 } 00192 return 1; 00193 } 00194 00195 static void 00196 fill_line(int num_traces, void **traces, 00197 unsigned long addr, int file, int line, 00198 char *include_directories, char *filenames, line_info_t *lines) 00199 { 00200 int i; 00201 for (i = 0; i < num_traces; i++) { 00202 unsigned long a = (unsigned long)traces[i] - lines[i].base_addr; 00203 /* We assume one line code doesn't result >100 bytes of native code. 00204 We may want more reliable way eventually... */ 00205 if (addr < a && a < addr + 100) { 00206 fill_filename(file, include_directories, filenames, &lines[i]); 00207 lines[i].line = line; 00208 } 00209 } 00210 } 00211 00212 static void 00213 parse_debug_line_cu(int num_traces, void **traces, 00214 char **debug_line, line_info_t *lines) 00215 { 00216 char *p, *cu_end, *cu_start, *include_directories, *filenames; 00217 unsigned long unit_length; 00218 int default_is_stmt, line_base; 00219 unsigned int header_length, minimum_instruction_length, line_range, 00220 opcode_base; 00221 unsigned char *standard_opcode_lengths; 00222 00223 /* The registers. */ 00224 unsigned long addr = 0; 00225 unsigned int file = 1; 00226 unsigned int line = 1; 00227 unsigned int column = 0; 00228 int is_stmt; 00229 int basic_block = 0; 00230 int end_sequence = 0; 00231 int prologue_end = 0; 00232 int epilogue_begin = 0; 00233 unsigned int isa = 0; 00234 00235 p = *debug_line; 00236 00237 unit_length = *(unsigned int *)p; 00238 p += sizeof(unsigned int); 00239 if (unit_length == 0xffffffff) { 00240 unit_length = *(unsigned long *)p; 00241 p += sizeof(unsigned long); 00242 } 00243 00244 cu_end = p + unit_length; 00245 00246 /*dwarf_version = *(unsigned short *)p;*/ 00247 p += 2; 00248 00249 header_length = *(unsigned int *)p; 00250 p += sizeof(unsigned int); 00251 00252 cu_start = p + header_length; 00253 00254 minimum_instruction_length = *(unsigned char *)p; 00255 p++; 00256 00257 is_stmt = default_is_stmt = *(unsigned char *)p; 00258 p++; 00259 00260 line_base = *(char *)p; 00261 p++; 00262 00263 line_range = *(unsigned char *)p; 00264 p++; 00265 00266 opcode_base = *(unsigned char *)p; 00267 p++; 00268 00269 standard_opcode_lengths = (unsigned char *)p - 1; 00270 p += opcode_base - 1; 00271 00272 include_directories = p; 00273 00274 /* skip include directories */ 00275 while (*p) { 00276 while (*p) p++; 00277 p++; 00278 } 00279 p++; 00280 00281 filenames = p; 00282 00283 p = cu_start; 00284 00285 #define FILL_LINE() \ 00286 do { \ 00287 fill_line(num_traces, traces, addr, file, line, \ 00288 include_directories, filenames, lines); \ 00289 basic_block = prologue_end = epilogue_begin = 0; \ 00290 } while (0) 00291 00292 while (p < cu_end) { 00293 unsigned long a; 00294 unsigned char op = *p++; 00295 switch (op) { 00296 case DW_LNS_copy: 00297 FILL_LINE(); 00298 break; 00299 case DW_LNS_advance_pc: 00300 a = uleb128(&p); 00301 addr += a; 00302 break; 00303 case DW_LNS_advance_line: { 00304 long a = sleb128(&p); 00305 line += a; 00306 break; 00307 } 00308 case DW_LNS_set_file: 00309 file = (unsigned int)uleb128(&p); 00310 break; 00311 case DW_LNS_set_column: 00312 column = (unsigned int)uleb128(&p); 00313 break; 00314 case DW_LNS_negate_stmt: 00315 is_stmt = !is_stmt; 00316 break; 00317 case DW_LNS_set_basic_block: 00318 basic_block = 1; 00319 break; 00320 case DW_LNS_const_add_pc: 00321 a = ((255 - opcode_base) / line_range) * 00322 minimum_instruction_length; 00323 addr += a; 00324 break; 00325 case DW_LNS_fixed_advance_pc: 00326 a = *(unsigned char *)p++; 00327 addr += a; 00328 break; 00329 case DW_LNS_set_prologue_end: 00330 prologue_end = 1; 00331 break; 00332 case DW_LNS_set_epilogue_begin: 00333 epilogue_begin = 1; 00334 break; 00335 case DW_LNS_set_isa: 00336 isa = (unsigned int)uleb128(&p); 00337 break; 00338 case 0: 00339 a = *(unsigned char *)p++; 00340 op = *p++; 00341 switch (op) { 00342 case DW_LNE_end_sequence: 00343 end_sequence = 1; 00344 FILL_LINE(); 00345 addr = 0; 00346 file = 1; 00347 line = 1; 00348 column = 0; 00349 is_stmt = default_is_stmt; 00350 end_sequence = 0; 00351 isa = 0; 00352 break; 00353 case DW_LNE_set_address: 00354 addr = *(unsigned long *)p; 00355 p += sizeof(unsigned long); 00356 break; 00357 case DW_LNE_define_file: 00358 fprintf(stderr, "Unsupported operation in %s\n", 00359 binary_filename); 00360 break; 00361 case DW_LNE_set_discriminator: 00362 /* TODO:currently ignore */ 00363 uleb128(&p); 00364 break; 00365 default: 00366 fprintf(stderr, "Unknown extended opcode: %d in %s\n", 00367 op, binary_filename); 00368 } 00369 break; 00370 default: { 00371 unsigned long addr_incr; 00372 unsigned long line_incr; 00373 a = op - opcode_base; 00374 addr_incr = (a / line_range) * minimum_instruction_length; 00375 line_incr = line_base + (a % line_range); 00376 addr += (unsigned int)addr_incr; 00377 line += (unsigned int)line_incr; 00378 FILL_LINE(); 00379 } 00380 } 00381 } 00382 *debug_line = p; 00383 } 00384 00385 static void 00386 parse_debug_line(int num_traces, void **traces, 00387 char *debug_line, unsigned long size, line_info_t *lines) 00388 { 00389 char *debug_line_end = debug_line + size; 00390 while (debug_line < debug_line_end) { 00391 parse_debug_line_cu(num_traces, traces, &debug_line, lines); 00392 } 00393 if (debug_line != debug_line_end) { 00394 fprintf(stderr, "Unexpected size of .debug_line in %s\n", 00395 binary_filename); 00396 } 00397 } 00398 00399 /* read file and fill lines */ 00400 static void 00401 fill_lines(int num_traces, void **traces, char **syms, int check_debuglink, 00402 line_info_t *current_line, line_info_t *lines); 00403 00404 static void 00405 follow_debuglink(char *debuglink, int num_traces, void **traces, char **syms, 00406 line_info_t *current_line, line_info_t *lines) 00407 { 00408 /* Ideally we should check 4 paths to follow gnu_debuglink, 00409 but we handle only one case for now as this format is used 00410 by some linux distributions. See GDB's info for detail. */ 00411 static const char global_debug_dir[] = "/usr/lib/debug"; 00412 char *p, *subdir; 00413 00414 p = strrchr(binary_filename, '/'); 00415 if (!p) { 00416 return; 00417 } 00418 p[1] = '\0'; 00419 00420 subdir = (char *)alloca(strlen(binary_filename) + 1); 00421 strcpy(subdir, binary_filename); 00422 strcpy(binary_filename, global_debug_dir); 00423 strncat(binary_filename, subdir, 00424 PATH_MAX - strlen(binary_filename) - 1); 00425 strncat(binary_filename, debuglink, 00426 PATH_MAX - strlen(binary_filename) - 1); 00427 00428 munmap(current_line->mapped, current_line->mapped_size); 00429 close(current_line->fd); 00430 fill_lines(num_traces, traces, syms, 0, current_line, lines); 00431 } 00432 00433 /* read file and fill lines */ 00434 static void 00435 fill_lines(int num_traces, void **traces, char **syms, int check_debuglink, 00436 line_info_t *current_line, line_info_t *lines) 00437 { 00438 int i; 00439 char *shstr; 00440 char *section_name; 00441 ElfW(Ehdr) *ehdr; 00442 ElfW(Shdr) *shdr, *shstr_shdr; 00443 ElfW(Shdr) *debug_line_shdr = NULL, *gnu_debuglink_shdr = NULL; 00444 int fd; 00445 off_t filesize; 00446 char *file; 00447 00448 fd = open(binary_filename, O_RDONLY); 00449 if (fd < 0) { 00450 return; 00451 } 00452 filesize = lseek(fd, 0, SEEK_END); 00453 if (filesize < 0) { 00454 int e = errno; 00455 close(fd); 00456 fprintf(stderr, "lseek: %s\n", strerror(e)); 00457 return; 00458 } 00459 lseek(fd, 0, SEEK_SET); 00460 /* async-signal unsafe */ 00461 file = (char *)mmap(NULL, filesize, PROT_READ, MAP_SHARED, fd, 0); 00462 if (file == MAP_FAILED) { 00463 int e = errno; 00464 close(fd); 00465 fprintf(stderr, "mmap: %s\n", strerror(e)); 00466 return; 00467 } 00468 00469 current_line->fd = fd; 00470 current_line->mapped = file; 00471 current_line->mapped_size = filesize; 00472 00473 for (i = 0; i < num_traces; i++) { 00474 const char *path; 00475 size_t len; 00476 if (get_path_from_symbol(syms[i], &path, &len) && 00477 !strncmp(path, binary_filename, len)) { 00478 lines[i].line = -1; 00479 } 00480 } 00481 00482 ehdr = (ElfW(Ehdr) *)file; 00483 shdr = (ElfW(Shdr) *)(file + ehdr->e_shoff); 00484 00485 shstr_shdr = shdr + ehdr->e_shstrndx; 00486 shstr = file + shstr_shdr->sh_offset; 00487 00488 for (i = 0; i < ehdr->e_shnum; i++) { 00489 section_name = shstr + shdr[i].sh_name; 00490 if (!strcmp(section_name, ".debug_line")) { 00491 debug_line_shdr = shdr + i; 00492 break; 00493 } else if (!strcmp(section_name, ".gnu_debuglink")) { 00494 gnu_debuglink_shdr = shdr + i; 00495 } 00496 } 00497 00498 if (!debug_line_shdr) { 00499 /* This file doesn't have .debug_line section, 00500 let's check .gnu_debuglink section instead. */ 00501 if (gnu_debuglink_shdr && check_debuglink) { 00502 follow_debuglink(file + gnu_debuglink_shdr->sh_offset, 00503 num_traces, traces, syms, 00504 current_line, lines); 00505 } 00506 return; 00507 } 00508 00509 parse_debug_line(num_traces, traces, 00510 file + debug_line_shdr->sh_offset, 00511 debug_line_shdr->sh_size, 00512 lines); 00513 } 00514 00515 #ifdef HAVE_DL_ITERATE_PHDR 00516 00517 typedef struct { 00518 int num_traces; 00519 char **syms; 00520 line_info_t *lines; 00521 } fill_base_addr_state_t; 00522 00523 static int 00524 fill_base_addr(struct dl_phdr_info *info, size_t size, void *data) 00525 { 00526 int i; 00527 fill_base_addr_state_t *st = (fill_base_addr_state_t *)data; 00528 for (i = 0; i < st->num_traces; i++) { 00529 const char *path; 00530 size_t len; 00531 size_t name_len = strlen(info->dlpi_name); 00532 00533 if (get_path_from_symbol(st->syms[i], &path, &len) && 00534 (len == name_len || (len > name_len && path[len-name_len-1] == '/')) && 00535 !strncmp(path+len-name_len, info->dlpi_name, name_len)) { 00536 st->lines[i].base_addr = info->dlpi_addr; 00537 } 00538 } 00539 return 0; 00540 } 00541 00542 #endif /* HAVE_DL_ITERATE_PHDR */ 00543 00544 void 00545 rb_dump_backtrace_with_lines(int num_traces, void **trace, char **syms) 00546 { 00547 int i; 00548 /* async-signal unsafe */ 00549 line_info_t *lines = (line_info_t *)calloc(num_traces, 00550 sizeof(line_info_t)); 00551 00552 /* Note that line info of shared objects might not be shown 00553 if we don't have dl_iterate_phdr */ 00554 #ifdef HAVE_DL_ITERATE_PHDR 00555 fill_base_addr_state_t fill_base_addr_state; 00556 00557 fill_base_addr_state.num_traces = num_traces; 00558 fill_base_addr_state.syms = syms; 00559 fill_base_addr_state.lines = lines; 00560 /* maybe async-signal unsafe */ 00561 dl_iterate_phdr(fill_base_addr, &fill_base_addr_state); 00562 #endif /* HAVE_DL_ITERATE_PHDR */ 00563 00564 for (i = 0; i < num_traces; i++) { 00565 const char *path; 00566 size_t len; 00567 if (lines[i].line) { 00568 continue; 00569 } 00570 00571 if (!get_path_from_symbol(syms[i], &path, &len)) { 00572 continue; 00573 } 00574 00575 strncpy(binary_filename, path, len); 00576 binary_filename[len] = '\0'; 00577 00578 fill_lines(num_traces, trace, syms, 1, &lines[i], lines); 00579 } 00580 00581 /* fprintf may not be async-signal safe */ 00582 for (i = 0; i < num_traces; i++) { 00583 line_info_t *line = &lines[i]; 00584 00585 if (line->line > 0) { 00586 fprintf(stderr, "%s ", syms[i]); 00587 if (line->filename) { 00588 if (line->dirname && line->dirname[0]) { 00589 fprintf(stderr, "%s/", line->dirname); 00590 } 00591 fprintf(stderr, "%s", line->filename); 00592 } else { 00593 fprintf(stderr, "???"); 00594 } 00595 fprintf(stderr, ":%d\n", line->line); 00596 } else { 00597 fprintf(stderr, "%s\n", syms[i]); 00598 } 00599 } 00600 00601 for (i = 0; i < num_traces; i++) { 00602 line_info_t *line = &lines[i]; 00603 if (line->fd) { 00604 munmap(line->mapped, line->mapped_size); 00605 close(line->fd); 00606 } 00607 } 00608 free(lines); 00609 } 00610 00611 #else /* defined(USE_ELF) */ 00612 #error not supported 00613 #endif 00614
1.7.6.1