[前][次][番号順一覧][スレッド一覧]

ruby-changes:33256

From: naruse <ko1@a...>
Date: Fri, 14 Mar 2014 15:29:44 +0900 (JST)
Subject: [ruby-changes:33256] naruse:r45335 (trunk): * addr2line.c (fill_lines): fetch symbol names from ELF binary's

naruse	2014-03-14 15:29:39 +0900 (Fri, 14 Mar 2014)

  New Revision: 45335

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=45335

  Log:
    * addr2line.c (fill_lines): fetch symbol names from ELF binary's
      symbol table if it is built with cc -g and not stripped.
      Now ruby can show static symbols on Linux though glibc's
      backtrace_symbols(3) don't show them.
    
    * addr2line.c (rb_dump_backtrace_with_lines): use dladdr(3) to
      detect what object file declares the symbol because
      dl_iterate_phdr can't detect the main executable file
      and codes on the stack.
      NOTE: signal trampolines sometimes on the user stack. (FreeBSD)
    
    * addr2line.c (rb_dump_backtrace_with_lines): stop showing
      backtrace if the function's name is main.
      NOTE: FreeBSD's backtrace (libexecinfo) shows _start and
      an additional address. Why it doesn't remove them on dladdr phase
      is, dladdr may fail to detect the main function but detect
      as _start function. Therefore it must be after scanning
      the symbol table and getting correct name.

  Modified files:
    trunk/ChangeLog
    trunk/addr2line.c
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 45334)
+++ ChangeLog	(revision 45335)
@@ -1,3 +1,25 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Fri Mar 14 14:58:38 2014  NARUSE, Yui  <naruse@r...>
+
+	* addr2line.c (fill_lines): fetch symbol names from ELF binary's
+	  symbol table if it is built with cc -g and not stripped.
+	  Now ruby can show static symbols on Linux though glibc's
+	  backtrace_symbols(3) don't show them.
+
+	* addr2line.c (rb_dump_backtrace_with_lines): use dladdr(3) to
+	  detect what object file declares the symbol because
+	  dl_iterate_phdr can't detect the main executable file
+	  and codes on the stack.
+	  NOTE: signal trampolines sometimes on the user stack. (FreeBSD)
+
+	* addr2line.c (rb_dump_backtrace_with_lines): stop showing
+	  backtrace if the function's name is main.
+	  NOTE: FreeBSD's backtrace (libexecinfo) shows _start and
+	  an additional address. Why it doesn't remove them on dladdr phase
+	  is, dladdr may fail to detect the main function but detect
+	  as _start function. Therefore it must be after scanning
+	  the symbol table and getting correct name.
+
+
 Fri Mar 14 12:07:46 2014  Zachary Scott  <e@z...>
 
 	* doc/syntax/literals.rdoc: [DOC] Single quote strings allows escape
Index: addr2line.c
===================================================================
--- addr2line.c	(revision 45334)
+++ addr2line.c	(revision 45335)
@@ -55,11 +55,9 @@ void *alloca(); https://github.com/ruby/ruby/blob/trunk/addr2line.c#L55
 # endif	/* HAVE_ALLOCA_H */
 #endif /* __GNUC__ */
 
-#ifdef HAVE_DL_ITERATE_PHDR
-# ifndef _GNU_SOURCE
-#  define _GNU_SOURCE
-# endif
+#ifdef HAVE_DLADDR
 # include <link.h>
+# include <dlfcn.h>
 #endif
 
 #define DW_LNS_copy                     0x01
@@ -88,6 +86,13 @@ void *alloca(); https://github.com/ruby/ruby/blob/trunk/addr2line.c#L86
 #  define ElfW(x) Elf32##_##x
 # endif
 #endif
+#ifndef ELF_ST_TYPE
+# if SIZEOF_VOIDP == 8
+#  define ELF_ST_TYPE ELF64_ST_TYPE
+# else
+#  define ELF_ST_TYPE ELF32_ST_TYPE
+# endif
+#endif
 #ifndef PATH_MAX
 #define PATH_MAX 4096
 #endif
@@ -97,16 +102,23 @@ int kprintf(const char *fmt, ...); https://github.com/ruby/ruby/blob/trunk/addr2line.c#L102
 typedef struct {
     const char *dirname;
     const char *filename;
+    const char *path; /* object path */
     int line;
 
     int fd;
     void *mapped;
     size_t mapped_size;
-    unsigned long base_addr;
+    int fd2;
+    void *mapped2;
+    size_t mapped_size2;
+    intptr_t base_addr;
+    intptr_t saddr;
+    const char *sname; /* function name */
 } line_info_t;
 
 /* Avoid consuming stack as this module may be used from signal handler */
 static char binary_filename[PATH_MAX];
+static intptr_t curobj_baseaddr;
 
 static unsigned long
 uleb128(char **p)
@@ -218,12 +230,13 @@ get_path_from_symbol(const char *symbol, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L230
 
 static void
 fill_line(int num_traces, void **traces,
-	  unsigned long addr, int file, int line,
+	  intptr_t addr, int file, int line,
 	  char *include_directories, char *filenames, line_info_t *lines)
 {
     int i;
+    addr += curobj_baseaddr;
     for (i = 0; i < num_traces; i++) {
-	unsigned long a = (unsigned long)traces[i] - lines[i].base_addr;
+	intptr_t a = (intptr_t)traces[i];
 	/* 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) {
@@ -447,8 +460,13 @@ follow_debuglink(char *debuglink, int nu https://github.com/ruby/ruby/blob/trunk/addr2line.c#L460
     strlcat(binary_filename, subdir, PATH_MAX);
     strlcat(binary_filename, debuglink, PATH_MAX);
 
-    munmap(current_line->mapped, current_line->mapped_size);
-    close(current_line->fd);
+    if (current_line->fd2) {
+	munmap(current_line->mapped2, current_line->mapped_size2);
+	close(current_line->fd2);
+    }
+    current_line->mapped2 = current_line->mapped;
+    current_line->mapped_size2 = current_line->mapped_size;
+    current_line->fd2 = current_line->fd;
     fill_lines(num_traces, traces, syms, 0, current_line, lines);
 }
 
@@ -457,7 +475,7 @@ static void https://github.com/ruby/ruby/blob/trunk/addr2line.c#L475
 fill_lines(int num_traces, void **traces, char **syms, int check_debuglink,
 	   line_info_t *current_line, line_info_t *lines)
 {
-    int i;
+    int i, j;
     char *shstr;
     char *section_name;
     ElfW(Ehdr) *ehdr;
@@ -466,6 +484,8 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L484
     int fd;
     off_t filesize;
     char *file;
+    ElfW(Shdr) *symtab_shdr = NULL;
+    ElfW(Shdr) *strtab_shdr = NULL;
 
     fd = open(binary_filename, O_RDONLY);
     if (fd < 0) {
@@ -509,36 +529,63 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L529
     current_line->mapped = file;
     current_line->mapped_size = (size_t)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)) {
-#if defined(HAVE_DLADDR) && defined(__pie__) && defined(__linux__)
-	    if (ehdr->e_type == ET_DYN && lines[i].base_addr == 0) {
-		Dl_info info;
-		if (dladdr(fill_lines, &info)) {
-		    lines[i].base_addr = (unsigned long)info.dli_fbase;
-		}
-	    }
-#endif
-	    lines[i].line = -1;
-	}
-    }
-
     shdr = (ElfW(Shdr) *)(file + ehdr->e_shoff);
 
     shstr_shdr = shdr + ehdr->e_shstrndx;
     shstr = file + shstr_shdr->sh_offset;
 
+    if (ehdr->e_type == ET_EXEC) {
+	/*
+	 * if object file type is ET_EXEC, base address must be 0
+	 */
+	intptr_t current_base_addr = current_line->base_addr;
+	for (i = 0; i < num_traces; i++) {
+	    if (current_base_addr == lines[i].base_addr) {
+		lines[i].base_addr = 0;
+	    }
+	}
+    }
+
+    /* j: ....xxx
+     * 1: debug_line
+     * 2: .symtab
+     * 4: .strtab
+     */
+    j = 0;
     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;
+	    j |= 1;
 	} else if (!strcmp(section_name, ".gnu_debuglink")) {
 	    gnu_debuglink_shdr = shdr + i;
+	} else if (!strcmp(section_name, ".symtab")) {
+	    symtab_shdr = shdr + i;
+	    j |= 2;
+	} else if (!strcmp(section_name, ".strtab")) {
+	    strtab_shdr = shdr + i;
+	    j |= 4;
+	}
+	if (j == 7) break;
+    }
+
+    if (symtab_shdr && strtab_shdr) {
+	char *strtab = file + strtab_shdr->sh_offset;
+	ElfW(Sym) *symtab = (ElfW(Sym) *)(file + symtab_shdr->sh_offset);
+	int symtab_count = (int)(symtab_shdr->sh_size / sizeof(ElfW(Sym)));
+	for (j = 0; j < symtab_count; j++) {
+	    int type = ELF_ST_TYPE(symtab[j].st_info);
+	    if (type != STT_FUNC) continue;
+	    for (i = 0; i < num_traces; i++) {
+		ElfW(Sym) *sym = &symtab[j];
+		intptr_t saddr = (intptr_t)sym->st_value + lines[i].base_addr;
+		ptrdiff_t d = (intptr_t)traces[i] - saddr;
+		if (d < 0 || d > (ptrdiff_t)sym->st_size) continue;
+		lines[i].sname = strtab + sym->st_name;
+		lines[i].saddr = saddr;
+	    }
 	}
+
     }
 
     if (!debug_line_shdr) {
@@ -558,91 +605,96 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L605
 		     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)
+rb_dump_backtrace_with_lines(int num_traces, void **traces, char **syms)
 {
     int i;
     /* async-signal unsafe */
-    line_info_t *lines = (line_info_t *)calloc(num_traces,
-					       sizeof(line_info_t));
+    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 */
+#ifdef HAVE_DLADDR
+    /* get object name in which the symbol is */
+    for (i = 0; i < num_traces; i++) {
+	Dl_info info;
+	if (dladdr(traces[i], &info)) {
+	    lines[i].path = info.dli_fname;
+	    /* this may set base addr even if executable is not shared object file */
+	    lines[i].base_addr = (intptr_t)info.dli_fbase;
+	    lines[i].line = 0;
+	    if (info.dli_saddr) {
+		lines[i].sname = info.dli_sname;
+		lines[i].saddr = (intptr_t)info.dli_saddr;
+	    }
+	}
+    }
+#endif
 
+    /* fill source lines by reading dwarf */
     for (i = 0; i < num_traces; i++) {
-	const char *path;
+	const char *path = NULL;
 	size_t len;
-	if (lines[i].line) {
-	    continue;
-	}
 
-	if (!get_path_from_symbol(syms[i], &path, &len)) {
+	if (lines[i].line > 0) continue;
+
+	if (lines[i].path) {
+	    path = lines[i].path;
+	    len = strlen(path);
+	}
+	else if (syms[i] && get_path_from_symbol(syms[i], &path, &len)) {
+	    lines[i].path = path;
+	}
+	else {
 	    continue;
 	}
 
 	strncpy(binary_filename, path, len);
 	binary_filename[len] = '\0';
 
-	fill_lines(num_traces, trace, syms, 1, &lines[i], lines);
+	curobj_baseaddr = lines[i].base_addr;
+	fill_lines(num_traces, traces, syms, 1, &lines[i], lines);
     }
 
+    /* output */
     for (i = 0; i < num_traces; i++) {
 	line_info_t *line = &lines[i];
 
-	if (line->line > 0) {
-	    if (line->filename) {
-		if (line->dirname && line->dirname[0]) {
-		    kprintf("%s %s/%s:%d\n", syms[i], line->dirname, line->filename, line->line);
-		}
-		else {
-		    kprintf("%s %s:%d\n", syms[i], line->filename, line->line);
-		}
-	    } else {
-		kprintf("%s ???:%d\n", syms[i], line->line);
+	if (line->sname) {
+	    intptr_t addr = (intptr_t)traces[i];
+	    intptr_t d = addr - line->saddr;
+	    if (line->line <= 0) {
+		kprintf("%s(%s+0x%lx) [0x%lx]\n", line->path, line->sname,
+			d, addr);
+	    }
+	    else if (!line->filename) {
+		kprintf("%s(%s+0x%lx) [0x%lx] ???:%d\n", line->path, line->sname,
+			d, addr, line->line);
+	    }
+	    else if (line->dirname && line->dirname[0]) {
+		kprintf("%s(%s+0x%lx) [0x%lx] %s/%s:%d\n", line->path, line->sname,
+			d, addr, line->dirname, line->filename, line->line);
 	    }
-	} else {
+	    else {
+		kprintf("%s(%s+0x%lx) [0x%lx] %s:%d\n", line->path, line->sname,
+			d, addr, line->filename, line->line);
+	    }
+	    /* FreeBSD's backtrace may show _start and so on */
+	    if (strcmp("main", line->sname) == 0)
+		break;
+	}
+	else if (line->line <= 0) {
 	    kprintf("%s\n", syms[i]);
 	}
+	else if (!line->filename) {
+	    kprintf("%s ???:%d\n", syms[i], line->line);
+	} else if (line->dirname && line->dirname[0]) {
+	    kprintf("%s %s/%s:%d\n", syms[i], line->dirname, line->filename, line->line);
+	}
+	else {
+	    kprintf("%s %s:%d\n", syms[i], line->filename, line->line);
+	}
     }
 
+    /* free */
     for (i = 0; i < num_traces; i++) {
 	line_info_t *line = &lines[i];
 	if (line->fd) {

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]