Ruby 1.9.3p327(2012-11-10revision37606)
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 
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