From 595056135f2b57fc4c018e561d6ad8d59b06b26b Mon Sep 17 00:00:00 2001 From: naruse Date: Sat, 20 Oct 2018 06:35:25 +0000 Subject: Support Mach-O on backtrace with DWARF git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65202 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- addr2line.c | 268 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 212 insertions(+), 56 deletions(-) (limited to 'addr2line.c') diff --git a/addr2line.c b/addr2line.c index 89da6384aa..1bc3896e5a 100644 --- a/addr2line.c +++ b/addr2line.c @@ -28,7 +28,7 @@ #include "missing/stdbool.h" #endif -#ifdef USE_ELF +#if defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H) #include #include @@ -41,12 +41,6 @@ #include #include -#ifdef __OpenBSD__ -#include -#else -#include -#endif - /* Make alloca work the best possible way. */ #ifdef __GNUC__ # ifndef alloca @@ -70,24 +64,16 @@ void *alloca(); # include #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 +#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 +# else +# include +# endif #ifndef ElfW # if SIZEOF_VOIDP == 8 @@ -103,8 +89,6 @@ void *alloca(); # 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(); # 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 { } 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) *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, int num_traces, void **traces, o2->path = o1->path; fill_lines(num_traces, traces, 0, objp, lines, offset); } +#endif enum { @@ -1371,7 +1380,7 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr) /* 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, ranges_t *ptr) 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, int num_traces, void **traces, 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, int check_debuglink, { 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, int check_debuglink, 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, int check_debuglink, ".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, int check_debuglink, 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, int check_debuglink, } } - 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: 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: 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); } } -- cgit v1.2.3