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

ruby-changes:52988

From: naruse <ko1@a...>
Date: Sat, 20 Oct 2018 15:35:31 +0900 (JST)
Subject: [ruby-changes:52988] naruse:r65202 (trunk): Support Mach-O on backtrace with DWARF

naruse	2018-10-20 15:35:25 +0900 (Sat, 20 Oct 2018)

  New Revision: 65202

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=65202

  Log:
    Support Mach-O on backtrace with DWARF

  Modified files:
    trunk/addr2line.c
    trunk/addr2line.h
    trunk/configure.ac
    trunk/vm_dump.c
Index: addr2line.c
===================================================================
--- addr2line.c	(revision 65201)
+++ addr2line.c	(revision 65202)
@@ -28,7 +28,7 @@ https://github.com/ruby/ruby/blob/trunk/addr2line.c#L28
 #include "missing/stdbool.h"
 #endif
 
-#ifdef USE_ELF
+#if defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)
 
 #include <fcntl.h>
 #include <limits.h>
@@ -41,12 +41,6 @@ https://github.com/ruby/ruby/blob/trunk/addr2line.c#L41
 #include <sys/stat.h>
 #include <unistd.h>
 
-#ifdef __OpenBSD__
-#include <elf_abi.h>
-#else
-#include <elf.h>
-#endif
-
 /* Make alloca work the best possible way.  */
 #ifdef __GNUC__
 # ifndef alloca
@@ -70,24 +64,16 @@ void *alloca(); https://github.com/ruby/ruby/blob/trunk/addr2line.c#L64
 # include <dlfcn.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 */
+#ifdef HAVE_MACH_O_LOADER_H
+# include <mach-o/loader.h>
+#endif
 
-/* 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 */
+#ifdef USE_ELF
+# ifdef __OpenBSD__
+#  include <elf_abi.h>
+# else
+#  include <elf.h>
+# endif
 
 #ifndef ElfW
 # if SIZEOF_VOIDP == 8
@@ -103,8 +89,6 @@ void *alloca(); https://github.com/ruby/ruby/blob/trunk/addr2line.c#L89
 #  define ELF_ST_TYPE ELF32_ST_TYPE
 # endif
 #endif
-#ifndef PATH_MAX
-#define PATH_MAX 4096
 #endif
 
 #ifdef SHF_COMPRESSED
@@ -117,6 +101,29 @@ void *alloca(); https://github.com/ruby/ruby/blob/trunk/addr2line.c#L101
 # define SHF_COMPRESSED 0
 #endif
 
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#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 */
+
 PRINTF_ARGS(static int kprintf(const char *fmt, ...), 1, 2);
 
 typedef struct line_info {
@@ -133,9 +140,9 @@ typedef struct line_info { https://github.com/ruby/ruby/blob/trunk/addr2line.c#L140
 } line_info_t;
 
 struct dwarf_section {
-    ElfW(Shdr) *shdr;
     char *ptr;
     size_t size;
+    uint64_t flags;
 };
 
 typedef struct obj_info {
@@ -485,6 +492,7 @@ append_obj(obj_info_t **objp) https://github.com/ruby/ruby/blob/trunk/addr2line.c#L492
     *objp = newobj;
 }
 
+#ifdef USE_ELF
 static void
 follow_debuglink(const char *debuglink, int num_traces, void **traces,
 		 obj_info_t **objp, line_info_t *lines, int offset)
@@ -518,6 +526,7 @@ follow_debuglink(const char *debuglink, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L526
     o2->path = o1->path;
     fill_lines(num_traces, traces, 0, objp, lines, offset);
 }
+#endif
 
 enum
 {
@@ -1371,7 +1380,7 @@ ranges_include(DebugInfoReader *reader, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1380
                 /* base address selection entry */
                 base = to;
             }
-            else if (base + from <= addr && addr <= base + to) {
+            else if (base + from <= addr && addr < base + to) {
                 return from;
             }
         }
@@ -1393,22 +1402,21 @@ ranges_inspect(DebugInfoReader *reader, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1402
             fprintf(stderr,"low_pc_set:%d high_pc_set:%d ranges_set:%d\n",ptr->low_pc_set,ptr->high_pc_set,ptr->ranges_set);
             exit(1);
         }
-        fprintf(stderr,"low_pc:%lx high_pc:%lx\n",ptr->low_pc,ptr->high_pc);
+        fprintf(stderr,"low_pc:%"PRIx64" high_pc:%"PRIx64"\n",ptr->low_pc,ptr->high_pc);
     }
     else if (ptr->ranges_set) {
-        char *p;
-        fprintf(stderr,"low_pc:%lx ranges:%lx ",ptr->low_pc,ptr->ranges);
-        p = reader->obj->debug_ranges.ptr + ptr->ranges;
+        char *p = reader->obj->debug_ranges.ptr + ptr->ranges;
+        fprintf(stderr,"low_pc:%"PRIx64" ranges:%"PRIx64" %lx ",ptr->low_pc,ptr->ranges, p-reader->obj->mapped);
         for (;;) {
             uintptr_t from = read_uintptr(&p);
             uintptr_t to = read_uintptr(&p);
             if (!from && !to) break;
-            fprintf(stderr,"%lx-%lx ",ptr->low_pc+from,ptr->low_pc+to);
+            fprintf(stderr,"%"PRIx64"-%"PRIx64" ",ptr->low_pc+from,ptr->low_pc+to);
         }
         fprintf(stderr,"\n");
     }
     else if (ptr->low_pc_set) {
-        fprintf(stderr,"low_pc:%lx\n",ptr->low_pc);
+        fprintf(stderr,"low_pc:%"PRIx64"\n",ptr->low_pc);
     }
     else {
         fprintf(stderr,"empty\n");
@@ -1565,12 +1573,13 @@ debug_info_read(DebugInfoReader *reader, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1573
                 lines[i].path = reader->obj->path;
                 lines[i].base_addr = line.base_addr;
                 lines[i].sname = line.sname;
-                lines[i].saddr = reader->obj->base_addr + saddr;
+                lines[i].saddr = saddr + reader->obj->base_addr - reader->obj->vmaddr;
             }
         }
     }
 }
 
+#ifdef USE_ELF
 static unsigned long
 uncompress_debug_section(ElfW(Shdr) *shdr, char *file, char **ptr)
 {
@@ -1605,7 +1614,6 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1614
 {
     int i, j;
     char *shstr;
-    char *section_name;
     ElfW(Ehdr) *ehdr;
     ElfW(Shdr) *shdr, *shstr_shdr;
     ElfW(Shdr) *gnu_debuglink_shdr = NULL;
@@ -1663,7 +1671,7 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1671
     shstr = file + shstr_shdr->sh_offset;
 
     for (i = 0; i < ehdr->e_shnum; i++) {
-	section_name = shstr + shdr[i].sh_name;
+        char *section_name = shstr + shdr[i].sh_name;
 	switch (shdr[i].sh_type) {
 	  case SHT_STRTAB:
 	    if (!strcmp(section_name, ".strtab")) {
@@ -1693,11 +1701,21 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1701
                     ".debug_ranges",
                     ".debug_str"
                 };
+
                 for (j=0; j < DWARF_SECTION_COUNT; j++) {
-                    if (strcmp(section_name, debug_section_names[j]) == 0) {
-                        obj_dwarf_section_at(obj, j)->shdr = &shdr[i];
-                        break;
+                    struct dwarf_section *s = obj_dwarf_section_at(obj, j);
+
+                    if (strcmp(section_name, debug_section_names[j]) != 0)
+                        continue;
+
+                    s->ptr = file + shdr[i].sh_offset;
+                    s->size = shdr[i].sh_size;
+                    s->flags = shdr[i].sh_flags;
+                    if (s->flags & SHF_COMPRESSED) {
+                        s->size = uncompress_debug_section(&shdr[i], file, &s->ptr);
+                        if (!s->size) goto fail;
                     }
+                    break;
                 }
             }
 	    break;
@@ -1720,6 +1738,7 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1738
                     if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_size == 0) continue;
                     s = dlsym(handle, strtab + sym->st_name);
                     if (s && dladdr(s, &info)) {
+                        obj->base_addr = dladdr_fbase;
                         dladdr_fbase = (uintptr_t)info.dli_fbase;
                         break;
                     }
@@ -1736,21 +1755,6 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1755
 	}
     }
 
-    if (obj->debug_info.shdr) {
-        size_t j;
-        for (j=0; j < DWARF_SECTION_COUNT; j++) {
-            struct dwarf_section *s = obj_dwarf_section_at(obj, j);
-            ElfW(Shdr) *shdr = s->shdr;
-            if (!shdr) break;
-            s->ptr = file + shdr->sh_offset;
-            s->size = shdr->sh_size;
-            if (shdr->sh_flags & SHF_COMPRESSED) {
-                s->size = uncompress_debug_section(shdr, file, &s->ptr);
-                if (!s->size) goto fail;
-            }
-        }
-    }
-
     if (obj->debug_info.ptr && obj->debug_abbrev.ptr) {
         DebugInfoReader reader;
         debug_info_reader_init(&reader, obj);
@@ -1813,6 +1817,158 @@ finish: https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1817
 fail:
     return (uintptr_t)-1;
 }
+#else /* Mach-O */
+/* read file and fill lines */
+static uintptr_t
+fill_lines(int num_traces, void **traces, int check_debuglink,
+        obj_info_t **objp, line_info_t *lines, int offset)
+{
+    int fd;
+    off_t filesize;
+    char *file, *p;
+    obj_info_t *obj = *objp;
+    struct mach_header_64 *header;
+    uintptr_t dladdr_fbase = 0;
+
+    {
+        char *s = binary_filename;
+        char *base = strrchr(binary_filename, '/')+1;
+        size_t max = PATH_MAX;
+        size_t size = strlen(binary_filename);
+        size_t basesize = size - (base - binary_filename);
+        s += size;
+        max -= size;
+        size = strlcpy(s, ".dSYM/Contents/Resources/DWARF/", max);
+        if (size == 0) goto fail;
+        s += size;
+        max -= size;
+        if (max <= basesize) goto fail;
+        memcpy(s, base, basesize);
+        s[basesize] = 0;
+    }
+
+    fd = open(binary_filename, O_RDONLY);
+    if (fd < 0) {
+        goto fail;
+    }
+    filesize = lseek(fd, 0, SEEK_END);
+    if (filesize < 0) {
+        int e = errno;
+        close(fd);
+        kprintf("lseek: %s\n", strerror(e));
+        goto fail;
+    }
+#if SIZEOF_OFF_T > SIZEOF_SIZE_T
+    if (filesize > (off_t)SIZE_MAX) {
+        close(fd);
+        kprintf("Too large file %s\n", binary_filename);
+        goto fail;
+    }
+#endif
+    lseek(fd, 0, SEEK_SET);
+    /* async-signal unsafe */
+    file = (char *)mmap(NULL, (size_t)filesize, PROT_READ, MAP_SHARED, fd, 0);
+    if (file == MAP_FAILED) {
+        int e = errno;
+        close(fd);
+        kprintf("mmap: %s\n", strerror(e));
+        goto fail;
+    }
+    close(fd);
+
+    obj->mapped = file;
+    obj->mapped_size = (size_t)filesize;
+
+    header = (struct mach_header_64 *)file;
+    if (header->magic != MH_MAGIC_64) {
+        /* TODO: universal binaries */
+        kprintf("'%s' is not a 64-bit Mach-O file!\n",binary_filename);
+        close(fd);
+        goto fail;
+    }
+
+    p = file + sizeof(struct mach_header_64);
+    for (uint32_t i = 0; i < (uint32_t)header->ncmds; i++) {
+        struct load_command *lcmd = (struct load_command *)p;
+        switch (lcmd->cmd) {
+          case LC_SEGMENT_64:
+            {
+                static const char *debug_section_names[] = {
+                    "__debug_abbrev",
+                    "__debug_info",
+                    "__debug_line",
+                    "__debug_ranges",
+                    "__debug_str"
+                };
+                struct segment_command_64 *scmd = (struct segment_command_64 *)lcmd;
+                if (strcmp(scmd->segname, "__TEXT") == 0) {
+                    obj->vmaddr = scmd->vmaddr;
+                }
+                else if (strcmp(scmd->segname, "__DWARF") == 0) {
+                    p += sizeof(struct segment_command_64);
+                    for (uint64_t i = 0; i < scmd->nsects; i++) {
+                        struct section_64 *sect = (struct section_64 *)p;
+                        p += sizeof(struct section_64);
+                        for (int j=0; j < DWARF_SECTION_COUNT; j++) {
+                            struct dwarf_section *s = obj_dwarf_section_at(obj, j);
+
+                            if (strcmp(sect->sectname, debug_section_names[j]) != 0)
+                                continue;
+
+                            s->ptr = file + sect->offset;
+                            s->size = sect->size;
+                            s->flags = sect->flags;
+                            if (s->flags & SHF_COMPRESSED) {
+                                goto fail;
+                            }
+                            break;
+                        }
+                    }
+                }
+            }
+            break;
+
+#if 0
+          case LC_SYMTAB:
+            {
+                struct symtab_command *c = (struct symtab_command *)lcmd;
+                struct nlist_64 *nl = (struct nlist *)(file + c->symoff);
+                char *strtab = file + c->stroff;
+                uint32_t j;
+                kprintf("[%2d]: %x/symtab %lx\n", i, c->cmd, p);
+                for (j = 0; j < c->nsyms; j++) {
+                    struct nlist_64 *e = &nl[j];
+                    if (!(e->n_type & N_STAB)) continue;
+                    /* if (e->n_type != N_FUN) continue; */
+                    kprintf("[%2d][%4d]: %02x/%x/%x: %s %lx\n", i, j,
+                            e->n_type,e->n_sect,e->n_desc,strtab+e->n_un.n_strx,e->n_value);
+                }
+            }
+#endif
+        }
+        p += lcmd->cmdsize;
+    }
+
+    if (obj->debug_info.ptr && obj->debug_abbrev.ptr) {
+        DebugInfoReader reader;
+        debug_info_reader_init(&reader, obj);
+        while (reader.p < reader.pend) {
+            di_read_cu(&reader);
+            debug_info_read(&reader, num_traces, traces, lines, offset);
+        }
+    }
+
+    if (parse_debug_line(num_traces, traces,
+            obj->debug_line.ptr,
+            obj->debug_line.size,
+            obj, lines, offset) == -1)
+        goto fail;
+
+    return dladdr_fbase;
+fail:
+    return (uintptr_t)-1;
+}
+#endif
 
 #define HAVE_MAIN_EXE_PATH
 #if defined(__FreeBSD__)
@@ -1971,7 +2127,7 @@ next_line: https://github.com/ruby/ruby/blob/trunk/addr2line.c#L2127
 	obj_info_t *o = obj;
         for (i=0; i < DWARF_SECTION_COUNT; i++) {
             struct dwarf_section *s = obj_dwarf_section_at(obj, i);
-            if (s->shdr && (s->shdr->sh_flags & SHF_COMPRESSED)) {
+            if (s->flags & SHF_COMPRESSED) {
                 free(s->ptr);
             }
         }
Index: vm_dump.c
===================================================================
--- vm_dump.c	(revision 65201)
+++ vm_dump.c	(revision 65202)
@@ -713,7 +713,7 @@ rb_print_backtrace(void) https://github.com/ruby/ruby/blob/trunk/vm_dump.c#L713
 #define MAX_NATIVE_TRACE 1024
     static void *trace[MAX_NATIVE_TRACE];
     int n = (int)backtrace(trace, MAX_NATIVE_TRACE);
-#if defined(USE_ELF) && defined(HAVE_DLADDR) && !defined(__sparc)
+#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)) && defined(HAVE_DLADDR) && !defined(__sparc)
     rb_dump_backtrace_with_lines(n, trace);
 #else
     char **syms = backtrace_symbols(trace, n);
Index: addr2line.h
===================================================================
--- addr2line.h	(revision 65201)
+++ addr2line.h	(revision 65202)
@@ -11,7 +11,7 @@ https://github.com/ruby/ruby/blob/trunk/addr2line.h#L11
 #ifndef RUBY_ADDR2LINE_H
 #define RUBY_ADDR2LINE_H
 
-#ifdef USE_ELF
+#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H))
 
 void
 rb_dump_backtrace_with_lines(int num_traces, void **traces);
Index: configure.ac
===================================================================
--- configure.ac	(revision 65201)
+++ configure.ac	(revision 65202)
@@ -2515,6 +2515,11 @@ AS_IF([test "$rb_cv_binary_elf" = yes], https://github.com/ruby/ruby/blob/trunk/configure.ac#L2515
   ])
 ])
 
+AC_CHECK_HEADERS([mach-o/loader.h])
+AS_IF([test "$ac_cv_header_mach_o_loader_h" = yes], [
+  AC_LIBOBJ([addr2line])
+])
+
 AS_CASE(["$target_os"],
 [linux* | gnu* | k*bsd*-gnu | bsdi* | kopensolaris*-gnu], [
     AS_IF([test "$rb_cv_binary_elf" = no], [

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

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