Ruby  1.9.3p448(2013-06-27revision41675)
addr2line.c
Go to the documentation of this file.
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