diff options
author | usa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-05-14 11:39:21 +0000 |
---|---|---|
committer | usa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-05-14 11:39:21 +0000 |
commit | e76eb06feb024828034379797129d92522e28516 (patch) | |
tree | 1326c625c233ba2554043203a7697f5e3d4c49e5 /ruby_1_9_3/addr2line.c | |
parent | aa6e98139c8e1ea442fb2182341aaa08ff55b529 (diff) | |
parent | bede15ac5e701ed08f3fc64c2dba03d3f393c652 (diff) |
add tag v1_9_3_426v1_9_3_426
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_9_3_426@40737 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ruby_1_9_3/addr2line.c')
-rw-r--r-- | ruby_1_9_3/addr2line.c | 610 |
1 files changed, 0 insertions, 610 deletions
diff --git a/ruby_1_9_3/addr2line.c b/ruby_1_9_3/addr2line.c deleted file mode 100644 index 219d1d4b47..0000000000 --- a/ruby_1_9_3/addr2line.c +++ /dev/null @@ -1,610 +0,0 @@ -/********************************************************************** - - addr2line.h - - - $Author$ - - Copyright (C) 2010 Shinichiro Hamaji - -**********************************************************************/ - -#include "ruby/config.h" -#include "addr2line.h" - -#include <stdio.h> -#include <errno.h> - -#ifdef USE_ELF - -#ifdef __OpenBSD__ -#include <elf_abi.h> -#else -#include <elf.h> -#endif -#include <fcntl.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> - -#if defined(HAVE_ALLOCA_H) -#include <alloca.h> -#endif - -#ifdef HAVE_DL_ITERATE_PHDR -# ifndef _GNU_SOURCE -# define _GNU_SOURCE -# endif -# include <link.h> -#endif - -#define DW_LNS_copy 0x01 -#define DW_LNS_advance_pc 0x02 -#define DW_LNS_advance_line 0x03 -#define DW_LNS_set_file 0x04 -#define DW_LNS_set_column 0x05 -#define DW_LNS_negate_stmt 0x06 -#define DW_LNS_set_basic_block 0x07 -#define DW_LNS_const_add_pc 0x08 -#define DW_LNS_fixed_advance_pc 0x09 -#define DW_LNS_set_prologue_end 0x0a /* DWARF3 */ -#define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */ -#define DW_LNS_set_isa 0x0c /* DWARF3 */ - -/* Line number extended opcode name. */ -#define DW_LNE_end_sequence 0x01 -#define DW_LNE_set_address 0x02 -#define DW_LNE_define_file 0x03 -#define DW_LNE_set_discriminator 0x04 /* DWARF4 */ - -#ifndef ElfW -# if SIZEOF_VOIDP == 8 -# define ElfW(x) Elf64##_##x -# else -# define ElfW(x) Elf32##_##x -# endif -#endif - -typedef struct { - const char *dirname; - const char *filename; - int line; - - int fd; - void *mapped; - size_t mapped_size; - unsigned long base_addr; -} line_info_t; - -/* Avoid consuming stack as this module may be used from signal handler */ -static char binary_filename[PATH_MAX]; - -static unsigned long -uleb128(char **p) { - unsigned long r = 0; - int s = 0; - for (;;) { - unsigned char b = *(unsigned char *)(*p)++; - if (b < 0x80) { - r += (unsigned long)b << s; - break; - } - r += (b & 0x7f) << s; - s += 7; - } - return r; -} - -static long -sleb128(char **p) { - long r = 0; - int s = 0; - for (;;) { - unsigned char b = *(unsigned char *)(*p)++; - if (b < 0x80) { - if (b & 0x40) { - r -= (0x80 - b) << s; - } - else { - r += (b & 0x3f) << s; - } - break; - } - r += (b & 0x7f) << s; - s += 7; - } - return r; -} - -static const char * -get_nth_dirname(unsigned long dir, char *p) -{ - if (!dir--) { - return ""; - } - while (dir--) { - while (*p) p++; - p++; - if (!*p) { - fprintf(stderr, "Unexpected directory number %lu in %s\n", - dir, binary_filename); - return ""; - } - } - return p; -} - -static void -fill_filename(int file, char *include_directories, char *filenames, - line_info_t *line) -{ - int i; - char *p = filenames; - char *filename; - unsigned long dir; - for (i = 1; i <= file; i++) { - filename = p; - if (!*p) { - /* Need to output binary file name? */ - fprintf(stderr, "Unexpected file number %d in %s\n", - file, binary_filename); - return; - } - while (*p) p++; - p++; - dir = uleb128(&p); - /* last modified. */ - uleb128(&p); - /* size of the file. */ - uleb128(&p); - - if (i == file) { - line->filename = filename; - line->dirname = get_nth_dirname(dir, include_directories); - } - } -} - -static int -get_path_from_symbol(const char *symbol, const char **p, size_t *len) -{ - if (symbol[0] == '0') { - /* libexecinfo */ - *p = strchr(symbol, '/'); - if (*p == NULL) return 0; - *len = strlen(*p); - } - else { - /* glibc */ - const char *q; - *p = symbol; - q = strchr(symbol, '('); - if (q == NULL) return 0; - *len = q - symbol; - } - return 1; -} - -static void -fill_line(int num_traces, void **traces, - unsigned long addr, int file, int line, - char *include_directories, char *filenames, line_info_t *lines) -{ - int i; - for (i = 0; i < num_traces; i++) { - unsigned long a = (unsigned long)traces[i] - lines[i].base_addr; - /* We assume one line code doesn't result >100 bytes of native code. - We may want more reliable way eventually... */ - if (addr < a && a < addr + 100) { - fill_filename(file, include_directories, filenames, &lines[i]); - lines[i].line = line; - } - } -} - -static void -parse_debug_line_cu(int num_traces, void **traces, - char **debug_line, line_info_t *lines) -{ - char *p, *cu_end, *cu_start, *include_directories, *filenames; - unsigned long unit_length; - int default_is_stmt, line_base; - unsigned int header_length, minimum_instruction_length, line_range, - opcode_base; - unsigned char *standard_opcode_lengths; - - /* The registers. */ - unsigned long addr = 0; - unsigned int file = 1; - unsigned int line = 1; - unsigned int column = 0; - int is_stmt; - int basic_block = 0; - int end_sequence = 0; - int prologue_end = 0; - int epilogue_begin = 0; - unsigned int isa = 0; - - p = *debug_line; - - unit_length = *(unsigned int *)p; - p += sizeof(unsigned int); - if (unit_length == 0xffffffff) { - unit_length = *(unsigned long *)p; - p += sizeof(unsigned long); - } - - cu_end = p + unit_length; - - /*dwarf_version = *(unsigned short *)p;*/ - p += 2; - - header_length = *(unsigned int *)p; - p += sizeof(unsigned int); - - cu_start = p + header_length; - - minimum_instruction_length = *(unsigned char *)p; - p++; - - is_stmt = default_is_stmt = *(unsigned char *)p; - p++; - - line_base = *(char *)p; - p++; - - line_range = *(unsigned char *)p; - p++; - - opcode_base = *(unsigned char *)p; - p++; - - standard_opcode_lengths = (unsigned char *)p - 1; - p += opcode_base - 1; - - include_directories = p; - - /* skip include directories */ - while (*p) { - while (*p) p++; - p++; - } - p++; - - filenames = p; - - p = cu_start; - -#define FILL_LINE() \ - do { \ - fill_line(num_traces, traces, addr, file, line, \ - include_directories, filenames, lines); \ - basic_block = prologue_end = epilogue_begin = 0; \ - } while (0) - - while (p < cu_end) { - unsigned long a; - unsigned char op = *p++; - switch (op) { - case DW_LNS_copy: - FILL_LINE(); - break; - case DW_LNS_advance_pc: - a = uleb128(&p); - addr += a; - break; - case DW_LNS_advance_line: { - long a = sleb128(&p); - line += a; - break; - } - case DW_LNS_set_file: - file = (unsigned int)uleb128(&p); - break; - case DW_LNS_set_column: - column = (unsigned int)uleb128(&p); - break; - case DW_LNS_negate_stmt: - is_stmt = !is_stmt; - break; - case DW_LNS_set_basic_block: - basic_block = 1; - break; - case DW_LNS_const_add_pc: - a = ((255 - opcode_base) / line_range) * - minimum_instruction_length; - addr += a; - break; - case DW_LNS_fixed_advance_pc: - a = *(unsigned char *)p++; - addr += a; - break; - case DW_LNS_set_prologue_end: - prologue_end = 1; - break; - case DW_LNS_set_epilogue_begin: - epilogue_begin = 1; - break; - case DW_LNS_set_isa: - isa = (unsigned int)uleb128(&p); - break; - case 0: - a = *(unsigned char *)p++; - op = *p++; - switch (op) { - case DW_LNE_end_sequence: - end_sequence = 1; - FILL_LINE(); - addr = 0; - file = 1; - line = 1; - column = 0; - is_stmt = default_is_stmt; - end_sequence = 0; - isa = 0; - break; - case DW_LNE_set_address: - addr = *(unsigned long *)p; - p += sizeof(unsigned long); - break; - case DW_LNE_define_file: - fprintf(stderr, "Unsupported operation in %s\n", - binary_filename); - break; - case DW_LNE_set_discriminator: - /* TODO:currently ignore */ - uleb128(&p); - break; - default: - fprintf(stderr, "Unknown extended opcode: %d in %s\n", - op, binary_filename); - } - break; - default: { - unsigned long addr_incr; - unsigned long line_incr; - a = op - opcode_base; - addr_incr = (a / line_range) * minimum_instruction_length; - line_incr = line_base + (a % line_range); - addr += (unsigned int)addr_incr; - line += (unsigned int)line_incr; - FILL_LINE(); - } - } - } - *debug_line = p; -} - -static void -parse_debug_line(int num_traces, void **traces, - char *debug_line, unsigned long size, line_info_t *lines) -{ - char *debug_line_end = debug_line + size; - while (debug_line < debug_line_end) { - parse_debug_line_cu(num_traces, traces, &debug_line, lines); - } - if (debug_line != debug_line_end) { - fprintf(stderr, "Unexpected size of .debug_line in %s\n", - binary_filename); - } -} - -/* read file and fill lines */ -static void -fill_lines(int num_traces, void **traces, char **syms, int check_debuglink, - line_info_t *current_line, line_info_t *lines); - -static void -follow_debuglink(char *debuglink, int num_traces, void **traces, char **syms, - line_info_t *current_line, line_info_t *lines) -{ - /* Ideally we should check 4 paths to follow gnu_debuglink, - but we handle only one case for now as this format is used - by some linux distributions. See GDB's info for detail. */ - static const char global_debug_dir[] = "/usr/lib/debug"; - char *p, *subdir; - - p = strrchr(binary_filename, '/'); - if (!p) { - return; - } - p[1] = '\0'; - - subdir = (char *)alloca(strlen(binary_filename) + 1); - strcpy(subdir, binary_filename); - strcpy(binary_filename, global_debug_dir); - strncat(binary_filename, subdir, - PATH_MAX - strlen(binary_filename) - 1); - strncat(binary_filename, debuglink, - PATH_MAX - strlen(binary_filename) - 1); - - munmap(current_line->mapped, current_line->mapped_size); - close(current_line->fd); - fill_lines(num_traces, traces, syms, 0, current_line, lines); -} - -/* read file and fill lines */ -static void -fill_lines(int num_traces, void **traces, char **syms, int check_debuglink, - line_info_t *current_line, line_info_t *lines) -{ - int i; - char *shstr; - char *section_name; - ElfW(Ehdr) *ehdr; - ElfW(Shdr) *shdr, *shstr_shdr; - ElfW(Shdr) *debug_line_shdr = NULL, *gnu_debuglink_shdr = NULL; - int fd; - off_t filesize; - char *file; - - fd = open(binary_filename, O_RDONLY); - if (fd < 0) { - return; - } - filesize = lseek(fd, 0, SEEK_END); - if (filesize < 0) { - int e = errno; - close(fd); - fprintf(stderr, "lseek: %s\n", strerror(e)); - return; - } - lseek(fd, 0, SEEK_SET); - /* async-signal unsafe */ - file = (char *)mmap(NULL, filesize, PROT_READ, MAP_SHARED, fd, 0); - if (file == MAP_FAILED) { - int e = errno; - close(fd); - fprintf(stderr, "mmap: %s\n", strerror(e)); - return; - } - - current_line->fd = fd; - current_line->mapped = file; - current_line->mapped_size = filesize; - - for (i = 0; i < num_traces; i++) { - const char *path; - size_t len; - if (get_path_from_symbol(syms[i], &path, &len) && - !strncmp(path, binary_filename, len)) { - lines[i].line = -1; - } - } - - ehdr = (ElfW(Ehdr) *)file; - shdr = (ElfW(Shdr) *)(file + ehdr->e_shoff); - - shstr_shdr = shdr + ehdr->e_shstrndx; - shstr = file + shstr_shdr->sh_offset; - - for (i = 0; i < ehdr->e_shnum; i++) { - section_name = shstr + shdr[i].sh_name; - if (!strcmp(section_name, ".debug_line")) { - debug_line_shdr = shdr + i; - break; - } else if (!strcmp(section_name, ".gnu_debuglink")) { - gnu_debuglink_shdr = shdr + i; - } - } - - if (!debug_line_shdr) { - /* This file doesn't have .debug_line section, - let's check .gnu_debuglink section instead. */ - if (gnu_debuglink_shdr && check_debuglink) { - follow_debuglink(file + gnu_debuglink_shdr->sh_offset, - num_traces, traces, syms, - current_line, lines); - } - return; - } - - parse_debug_line(num_traces, traces, - file + debug_line_shdr->sh_offset, - debug_line_shdr->sh_size, - lines); -} - -#ifdef HAVE_DL_ITERATE_PHDR - -typedef struct { - int num_traces; - char **syms; - line_info_t *lines; -} fill_base_addr_state_t; - -static int -fill_base_addr(struct dl_phdr_info *info, size_t size, void *data) -{ - int i; - fill_base_addr_state_t *st = (fill_base_addr_state_t *)data; - for (i = 0; i < st->num_traces; i++) { - const char *path; - size_t len; - size_t name_len = strlen(info->dlpi_name); - - if (get_path_from_symbol(st->syms[i], &path, &len) && - (len == name_len || (len > name_len && path[len-name_len-1] == '/')) && - !strncmp(path+len-name_len, info->dlpi_name, name_len)) { - st->lines[i].base_addr = info->dlpi_addr; - } - } - return 0; -} - -#endif /* HAVE_DL_ITERATE_PHDR */ - -void -rb_dump_backtrace_with_lines(int num_traces, void **trace, char **syms) -{ - int i; - /* async-signal unsafe */ - line_info_t *lines = (line_info_t *)calloc(num_traces, - sizeof(line_info_t)); - - /* Note that line info of shared objects might not be shown - if we don't have dl_iterate_phdr */ -#ifdef HAVE_DL_ITERATE_PHDR - fill_base_addr_state_t fill_base_addr_state; - - fill_base_addr_state.num_traces = num_traces; - fill_base_addr_state.syms = syms; - fill_base_addr_state.lines = lines; - /* maybe async-signal unsafe */ - dl_iterate_phdr(fill_base_addr, &fill_base_addr_state); -#endif /* HAVE_DL_ITERATE_PHDR */ - - for (i = 0; i < num_traces; i++) { - const char *path; - size_t len; - if (lines[i].line) { - continue; - } - - if (!get_path_from_symbol(syms[i], &path, &len)) { - continue; - } - - strncpy(binary_filename, path, len); - binary_filename[len] = '\0'; - - fill_lines(num_traces, trace, syms, 1, &lines[i], lines); - } - - /* fprintf may not be async-signal safe */ - for (i = 0; i < num_traces; i++) { - line_info_t *line = &lines[i]; - - if (line->line > 0) { - fprintf(stderr, "%s ", syms[i]); - if (line->filename) { - if (line->dirname && line->dirname[0]) { - fprintf(stderr, "%s/", line->dirname); - } - fprintf(stderr, "%s", line->filename); - } else { - fprintf(stderr, "???"); - } - fprintf(stderr, ":%d\n", line->line); - } else { - fprintf(stderr, "%s\n", syms[i]); - } - } - - for (i = 0; i < num_traces; i++) { - line_info_t *line = &lines[i]; - if (line->fd) { - munmap(line->mapped, line->mapped_size); - close(line->fd); - } - } - free(lines); -} - -#else /* defined(USE_ELF) */ -#error not supported -#endif |