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

ruby-changes:33414

From: naruse <ko1@a...>
Date: Tue, 1 Apr 2014 12:38:09 +0900 (JST)
Subject: [ruby-changes:33414] naruse:r45493 (trunk): * addr2line.c (rb_dump_backtrace_with_lines): don't depend hard coded

naruse	2014-04-01 12:38:05 +0900 (Tue, 01 Apr 2014)

  New Revision: 45493

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

  Log:
    * addr2line.c (rb_dump_backtrace_with_lines): don't depend hard coded
      symbol '_start'.
    
    * addr2line.c (fill_lines): instead of above, get a dynamic symbol
      in the main executable and use it to know the base address.
    
    * addr2line.c (follow_debuglink0): use obj_info_t instead of
      line_info_t to handle object related data.
    
    * addr2line.c (main_exe_path): defined for Linux.

  Modified files:
    trunk/ChangeLog
    trunk/addr2line.c
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 45492)
+++ ChangeLog	(revision 45493)
@@ -1,3 +1,16 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Tue Apr  1 12:06:49 2014  NARUSE, Yui  <naruse@r...>
+
+	* addr2line.c (rb_dump_backtrace_with_lines): don't depend hard coded
+	  symbol '_start'.
+
+	* addr2line.c (fill_lines): instead of above, get a dynamic symbol
+	  in the main executable and use it to know the base address.
+
+	* addr2line.c (follow_debuglink0): use obj_info_t instead of
+	  line_info_t to handle object related data.
+
+	* addr2line.c (main_exe_path): defined for Linux.
+
 Tue Apr  1 08:58:39 2014  Kazuki Tsujimoto  <kazuki@c...>
 
 	* parse.y (rb_str_dynamic_intern): set mark bit if dynamic symbol
Index: addr2line.c
===================================================================
--- addr2line.c	(revision 45492)
+++ addr2line.c	(revision 45493)
@@ -56,7 +56,6 @@ void *alloca(); https://github.com/ruby/ruby/blob/trunk/addr2line.c#L56
 #endif /* __GNUC__ */
 
 #ifdef HAVE_DLADDR
-# include <link.h>
 # include <dlfcn.h>
 #endif
 
@@ -105,16 +104,19 @@ typedef struct { https://github.com/ruby/ruby/blob/trunk/addr2line.c#L104
     const char *path; /* object path */
     int line;
 
-    int fd;
-    void *mapped;
-    size_t mapped_size;
-    int fd2;
-    void *mapped2;
-    size_t mapped_size2;
     uintptr_t base_addr;
     uintptr_t saddr;
     const char *sname; /* function name */
 } line_info_t;
+typedef struct obj_info obj_info_t;
+struct obj_info {
+    const char *path; /* object path */
+    int fd;
+    void *mapped;
+    size_t mapped_size;
+    uintptr_t base_addr;
+    obj_info_t *next;
+};
 
 /* Avoid consuming stack as this module may be used from signal handler */
 static char binary_filename[PATH_MAX];
@@ -208,12 +210,12 @@ fill_filename(int file, char *include_di https://github.com/ruby/ruby/blob/trunk/addr2line.c#L210
 }
 
 static void
-fill_line(int num_traces, void **traces,
-	  uintptr_t addr, int file, int line,
-	  char *include_directories, char *filenames, line_info_t *lines, int offset)
+fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
+	  char *include_directories, char *filenames,
+	  obj_info_t *obj, line_info_t *lines, int offset)
 {
     int i;
-    addr += lines[offset].base_addr;
+    addr += obj->base_addr;
     for (i = offset; i < num_traces; i++) {
 	uintptr_t a = (uintptr_t)traces[i];
 	/* We assume one line code doesn't result >100 bytes of native code.
@@ -226,8 +228,8 @@ fill_line(int num_traces, void **traces, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L228
 }
 
 static void
-parse_debug_line_cu(int num_traces, void **traces,
-		    char **debug_line, line_info_t *lines, int offset)
+parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
+		obj_info_t *obj, line_info_t *lines, int offset)
 {
     char *p, *cu_end, *cu_start, *include_directories, *filenames;
     unsigned long unit_length;
@@ -301,7 +303,8 @@ parse_debug_line_cu(int num_traces, void https://github.com/ruby/ruby/blob/trunk/addr2line.c#L303
 #define FILL_LINE()						    \
     do {							    \
 	fill_line(num_traces, traces, addr, file, line,		    \
-		  include_directories, filenames, lines, offset);	    \
+		  include_directories, filenames,		    \
+		  obj, lines, offset);				    \
 	/*basic_block = prologue_end = epilogue_begin = 0;*/	    \
     } while (0)
 
@@ -400,11 +403,12 @@ parse_debug_line_cu(int num_traces, void https://github.com/ruby/ruby/blob/trunk/addr2line.c#L403
 
 static void
 parse_debug_line(int num_traces, void **traces,
-		 char *debug_line, unsigned long size, line_info_t *lines, int offset)
+		 char *debug_line, unsigned long size,
+		 obj_info_t *obj, line_info_t *lines, int offset)
 {
     char *debug_line_end = debug_line + size;
     while (debug_line < debug_line_end) {
-	parse_debug_line_cu(num_traces, traces, &debug_line, lines, offset);
+	parse_debug_line_cu(num_traces, traces, &debug_line, obj, lines, offset);
     }
     if (debug_line != debug_line_end) {
 	kprintf("Unexpected size of .debug_line in %s\n",
@@ -415,17 +419,25 @@ parse_debug_line(int num_traces, void ** https://github.com/ruby/ruby/blob/trunk/addr2line.c#L419
 /* read file and fill lines */
 static void
 fill_lines(int num_traces, void **traces, int check_debuglink,
-	   line_info_t *current_line, line_info_t *lines, int offset);
+	   obj_info_t **objp, line_info_t *lines, int offset);
+
+static void
+append_obj(obj_info_t **objp) {
+    obj_info_t *newobj = malloc(sizeof(obj_info_t));
+    if (*objp) (*objp)->next = newobj;
+    *objp = newobj;
+}
 
 static void
 follow_debuglink(char *debuglink, int num_traces, void **traces,
-		 line_info_t *current_line, line_info_t *lines, int offset)
+		 obj_info_t **objp, line_info_t *lines, int offset)
 {
     /* 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;
+    obj_info_t *o1 = *objp, *o2;
 
     p = strrchr(binary_filename, '/');
     if (!p) {
@@ -439,21 +451,17 @@ follow_debuglink(char *debuglink, int nu https://github.com/ruby/ruby/blob/trunk/addr2line.c#L451
     strlcat(binary_filename, subdir, PATH_MAX);
     strlcat(binary_filename, debuglink, PATH_MAX);
 
-    if (current_line->fd2) {
-	kprintf("follow_debuglink twice %s\n", binary_filename);
-	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, 0, current_line, lines, offset);
+    append_obj(objp);
+    o2 = *objp;
+    o2->base_addr = o1->base_addr;
+    o2->path = o1->path;
+    fill_lines(num_traces, traces, 0, objp, lines, offset);
 }
 
 /* read file and fill lines */
 static void
 fill_lines(int num_traces, void **traces, int check_debuglink,
-	   line_info_t *current_line, line_info_t *lines, int offset)
+	   obj_info_t **objp, line_info_t *lines, int offset)
 {
     int i, j;
     char *shstr;
@@ -466,6 +474,7 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L474
     char *file;
     ElfW(Shdr) *symtab_shdr = NULL, *strtab_shdr = NULL;
     ElfW(Shdr) *dynsym_shdr = NULL, *dynstr_shdr = NULL;
+    obj_info_t *obj = *objp;
 
     fd = open(binary_filename, O_RDONLY);
     if (fd < 0) {
@@ -505,26 +514,16 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L514
 	goto fail;
     }
 
-    current_line->fd = fd;
-    current_line->mapped = file;
-    current_line->mapped_size = (size_t)filesize;
+    if (ehdr->e_type == ET_EXEC) obj->base_addr = 0;
+    obj->fd = fd;
+    obj->mapped = file;
+    obj->mapped_size = (size_t)filesize;
 
     shdr = (ElfW(Shdr) *)(file + ehdr->e_shoff);
 
     shstr_shdr = shdr + ehdr->e_shstrndx;
     shstr = file + shstr_shdr->sh_offset;
 
-    /* reverse order not to overwrite current_line->base_addr */
-    for (i = num_traces-1; i >= offset; i--) {
-	if (current_line->base_addr == lines[i].base_addr) {
-	    lines[i].line = -1;
-	    if (ehdr->e_type == ET_EXEC) {
-		/* if object type is ET_EXEC, base address must be 0 */
-		lines[i].base_addr = 0;
-	    }
-	}
-    }
-
     for (i = 0; i < ehdr->e_shnum; i++) {
 	section_name = shstr + shdr[i].sh_name;
 	switch (shdr[i].sh_type) {
@@ -555,6 +554,29 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L554
 	}
     }
 
+    if (offset == -1) {
+	offset = 0;
+	if (ehdr->e_type != ET_EXEC && dynsym_shdr && dynstr_shdr) {
+	    char *strtab = file + dynstr_shdr->sh_offset;
+	    ElfW(Sym) *symtab = (ElfW(Sym) *)(file + dynsym_shdr->sh_offset);
+	    int symtab_count = (int)(dynsym_shdr->sh_size / sizeof(ElfW(Sym)));
+	    for (j = 0; j < symtab_count; j++) {
+		ElfW(Sym) *sym = &symtab[j];
+		Dl_info info;
+		void *h, *s;
+		if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_size <= 0) continue;
+		h = dlopen(NULL, RTLD_NOW|RTLD_LOCAL);
+		if (!h) continue;
+		s = dlsym(h, strtab + sym->st_name);
+		if (!s) continue;
+		if (dladdr(s, &info)) {
+		    obj->base_addr = (uintptr_t)info.dli_fbase;
+		    break;
+		}
+	    }
+	}
+    }
+
     if (!symtab_shdr) {
 	symtab_shdr = dynsym_shdr;
 	strtab_shdr = dynstr_shdr;
@@ -566,19 +588,19 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L588
 	int symtab_count = (int)(symtab_shdr->sh_size / sizeof(ElfW(Sym)));
 	for (j = 0; j < symtab_count; j++) {
 	    ElfW(Sym) *sym = &symtab[j];
-	    int type = ELF_ST_TYPE(sym->st_info);
-	    uintptr_t saddr = (uintptr_t)sym->st_value + current_line->base_addr;
-	    if (type != STT_FUNC) continue;
+	    uintptr_t saddr = (uintptr_t)sym->st_value + obj->base_addr;
+	    if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_size <= 0) continue;
 	    for (i = offset; i < num_traces; i++) {
 		uintptr_t d = (uintptr_t)traces[i] - saddr;
-		if (lines[i].line != -1 || d <= 0 || d > (uintptr_t)sym->st_size)
+		if (lines[i].line > 0 || d <= 0 || d > (uintptr_t)sym->st_size)
 		    continue;
 		/* fill symbol name and addr from .symtab */
 		lines[i].sname = strtab + sym->st_name;
 		lines[i].saddr = saddr;
+		lines[i].path  = obj->path;
+		lines[i].base_addr = obj->base_addr;
 	    }
 	}
-
     }
 
     if (!debug_line_shdr) {
@@ -587,7 +609,7 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L609
 	if (gnu_debuglink_shdr && check_debuglink) {
 	    follow_debuglink(file + gnu_debuglink_shdr->sh_offset,
 			     num_traces, traces,
-			     current_line, lines, offset);
+			     objp, lines, offset);
 	}
 	goto finish;
     }
@@ -595,7 +617,7 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L617
     parse_debug_line(num_traces, traces,
 		     file + debug_line_shdr->sh_offset,
 		     debug_line_shdr->sh_size,
-		     lines, offset);
+		     obj, lines, offset);
 finish:
     for (i = offset; i < num_traces; i++) {
 	if (lines[i].line == -1) {
@@ -605,60 +627,49 @@ finish: https://github.com/ruby/ruby/blob/trunk/addr2line.c#L627
     return;
 fail:
     for (i = offset; i < num_traces; i++) {
-	if (current_line->base_addr == lines[i].base_addr) {
+	if (obj->base_addr == lines[i].base_addr) {
 	    lines[i].line = -2;
 	}
     }
     return;
 }
 
+#define HAVE_MAIN_EXE_PATH
+#if defined(__linux__)
+ssize_t
+main_exe_path(void)
+{
+# define PROC_SELF_EXE "/proc/self/exe"
+    ssize_t len = readlink(PROC_SELF_EXE, binary_filename, PATH_MAX);
+    binary_filename[len] = 0;
+    return len;
+}
+#else
+#undef HAVE_MAIN_EXE_PATH
+#endif
+
 void
 rb_dump_backtrace_with_lines(int num_traces, void **traces)
 {
     int i;
     /* async-signal unsafe */
     line_info_t *lines = (line_info_t *)calloc(num_traces, sizeof(line_info_t));
-
-#ifdef HAVE_DLADDR
-# ifdef __linux__
-#  define PROC_SELF_EXE "/proc/self/exe"
-    uintptr_t main_fbase;
-    char *main_path;
-    {
-	Dl_info info;
-	void *handle = dlopen(NULL, RTLD_LAZY);
-	void *sym = dlsym(handle, "_start");
-	dladdr(sym, &info);
-	main_fbase = (uintptr_t)info.dli_fbase;
-    }
-    {
-	ssize_t len = readlink(PROC_SELF_EXE, binary_filename, PATH_MAX);
+    obj_info_t *obj = NULL;
+    void **base_addrs = (void **)calloc(num_traces+1, sizeof(void *));
+#ifdef HAVE_MAIN_EXE_PATH
+    char *main_path = NULL; /* keep while this func */
+    ssize_t len;
+    if ((len = main_exe_path()) > 0) {
 	main_path = (char *)alloca(len + 1);
-	if (!main_path) return;
-	strncpy(main_path, binary_filename, len);
-	main_path[len] = 0;
-    }
-# endif
-    /* get object name in which the symbol is */
-    for (i = 0; i < num_traces; i++) {
-	Dl_info info;
-	if (dladdr(traces[i], &info)) {
-	    /* this may set base addr even if executable is not shared object file */
-	    lines[i].base_addr = (uintptr_t)info.dli_fbase;
-# ifdef __linux__
-	    if (lines[i].base_addr == main_fbase) {
-		lines[i].path = main_path;
-	    }
-	    else {
-		lines[i].path = info.dli_fname;
-	    }
-# else
-	    lines[i].path = info.dli_fname;
-# endif
-	    lines[i].line = 0;
-	    if (info.dli_saddr) {
-		lines[i].sname = info.dli_sname;
-		lines[i].saddr = (uintptr_t)info.dli_saddr;
+	if (main_path) {
+	    obj_info_t *o;
+	    memcpy(main_path, binary_filename, len+1);
+	    append_obj(&obj);
+	    o = obj;
+	    obj->path = main_path;
+	    fill_lines(num_traces, traces, 1, &obj, lines, -1);
+	    for (i=0; o=o->next; i++) {
+		base_addrs[i] = (void *)o->base_addr;
 	    }
 	}
     }
@@ -666,23 +677,25 @@ rb_dump_backtrace_with_lines(int num_tra https://github.com/ruby/ruby/blob/trunk/addr2line.c#L677
 
     /* fill source lines by reading dwarf */
     for (i = 0; i < num_traces; i++) {
-	const char *path = NULL;
-	size_t len;
-
+	Dl_info info;
 	if (lines[i].line) continue;
-
-	if (lines[i].path) {
-	    path = lines[i].path;
-	    len = strlen(path);
-	}
-	else {
-	    continue;
+	if (dladdr(traces[i], &info)) {
+	    const char *path;
+	    void **p;
+	    for (p=base_addrs; *p; p++) {
+		if (*p == info.dli_fbase)
+		    goto next_line;
+	    }
+	    append_obj(&obj);
+	    obj->base_addr = (uintptr_t)info.dli_fbase;
+	    path = info.dli_fname;
+	    obj->path = path;
+	    lines[i].path = path;
+	    strcpy(binary_filename, path);
+	    fill_lines(num_traces, traces, 1, &obj, lines, i);
 	}
-
-	strncpy(binary_filename, path, len);
-	binary_filename[len] = '\0';
-
-	fill_lines(num_traces, traces, 1, &lines[i], lines, i);
+next_line:
+	continue;
     }
 
     /* output */
@@ -718,12 +731,14 @@ rb_dump_backtrace_with_lines(int num_tra https://github.com/ruby/ruby/blob/trunk/addr2line.c#L731
     }
 
     /* free */
-    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);
+    while (obj) {
+	obj_info_t *o = obj;
+	obj = o->next;
+	if (o->fd) {
+	    munmap(o->mapped, o->mapped_size);
+	    close(o->fd);
 	}
+	free(o);
     }
     free(lines);
 }

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

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