ruby-changes:52804
From: naruse <ko1@a...>
Date: Fri, 12 Oct 2018 09:12:00 +0900 (JST)
Subject: [ruby-changes:52804] naruse:r65012 (trunk): Support compressed debug_info
naruse 2018-10-12 08:55:47 +0900 (Fri, 12 Oct 2018) New Revision: 65012 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=65012 Log: Support compressed debug_info Modified files: trunk/addr2line.c Index: addr2line.c =================================================================== --- addr2line.c (revision 65011) +++ addr2line.c (revision 65012) @@ -125,14 +125,32 @@ typedef struct line_info { https://github.com/ruby/ruby/blob/trunk/addr2line.c#L125 struct line_info *next; } line_info_t; -typedef struct obj_info obj_info_t; -struct obj_info { + +struct dwarf_section { + ElfW(Shdr) *shdr; + char *ptr; + size_t size; +}; + +typedef struct obj_info { const char *path; /* object path */ - void *mapped; + char *mapped; size_t mapped_size; - void *uncompressed_debug_line; + void *uncompressed; uintptr_t base_addr; - obj_info_t *next; + struct dwarf_section debug_abbrev; + struct dwarf_section debug_info; + struct dwarf_section debug_line; + struct dwarf_section debug_ranges; + struct dwarf_section debug_str; + struct obj_info *next; +} obj_info_t; +#define obj_dwarf_section_at(obj,n) (&obj->debug_abbrev + n) +#define DWARF_SECTION_COUNT 5 + +struct debug_section_definition { + const char *name; + struct dwarf_section *dwarf; }; /* Avoid consuming stack as this module may be used from signal handler */ @@ -486,63 +504,6 @@ follow_debuglink(const char *debuglink, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L504 fill_lines(num_traces, traces, 0, objp, lines, offset); } -#ifdef SUPPORT_COMPRESSED_DEBUG_LINE -static int -parse_compressed_debug_line(int num_traces, void **traces, - char *debug_line, unsigned long size, - obj_info_t *obj, line_info_t *lines, int offset) -{ - void *uncompressed_debug_line; - ElfW(Chdr) *chdr = (ElfW(Chdr) *)debug_line; - unsigned long destsize = chdr->ch_size; - int ret = 0; - - if (chdr->ch_type != ELFCOMPRESS_ZLIB) { - /* unsupported compression type */ - return -1; - } - - uncompressed_debug_line = malloc(destsize); - if (!uncompressed_debug_line) return -1; - ret = uncompress(uncompressed_debug_line, &destsize, - (const Bytef *)debug_line + sizeof(ElfW(Chdr)), size-sizeof(ElfW(Chdr))); - if (ret != Z_OK) goto fail; - ret = parse_debug_line(num_traces, traces, - uncompressed_debug_line, - destsize, - obj, lines, offset); - if (ret) goto fail; - obj->uncompressed_debug_line = uncompressed_debug_line; - return 0; - -fail: - free(uncompressed_debug_line); - return -1; -} -#endif - -void hexdump0(const unsigned char *p, size_t n) { - size_t i; - fprintf(stderr, " 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); - for (i=0; i < n; i++){ - switch (i & 15) { - case 0: - fprintf(stderr, "%02zd: %02X ", i/16, p[i]); - break; - case 15: - fprintf(stderr, "%02X\n", p[i]); - break; - default: - fprintf(stderr, "%02X ", p[i]); - break; - } - } - if ((i & 15) != 15) { - fprintf(stderr, "\n"); - } -} -#define hexdump(p,n) hexdump0((const unsigned char *)p, n) - enum { DW_TAG_inlined_subroutine = 0x1d, @@ -769,22 +730,12 @@ typedef struct { https://github.com/ruby/ruby/blob/trunk/addr2line.c#L730 } __attribute__((packed)) DW_CompilationUnitHeader64; typedef struct { - ElfW(Shdr) *abbrev; - ElfW(Shdr) *info; - ElfW(Shdr) *ranges; - ElfW(Shdr) *str; - ElfW(Shdr) *line; -} dwarf_args; - -typedef struct { - dwarf_args *dwarf; obj_info_t *obj; char *file; char *current_cu; char *debug_line_cu_end; char *debug_line_files; char *debug_line_directories; - char *p0; char *p; char *pend; char *q0; @@ -795,6 +746,7 @@ typedef struct { https://github.com/ruby/ruby/blob/trunk/addr2line.c#L746 } DebugInfoReader; typedef struct { + ptrdiff_t pos; int tag; int has_children; } DIE; @@ -805,6 +757,7 @@ typedef struct { https://github.com/ruby/ruby/blob/trunk/addr2line.c#L757 uint64_t uint64; int64_t int64; } as; + uint64_t off; uint64_t at; uint64_t form; size_t size; @@ -891,14 +844,13 @@ read_sleb128(DebugInfoReader *reader) https://github.com/ruby/ruby/blob/trunk/addr2line.c#L844 } static void -debug_info_reader_init(DebugInfoReader *reader, obj_info_t *obj, dwarf_args *dwarf) +debug_info_reader_init(DebugInfoReader *reader, obj_info_t *obj) { reader->file = obj->mapped; reader->obj = obj; - reader->dwarf = dwarf; - reader->p0 = reader->p = reader->file + dwarf->info->sh_offset; - reader->pend = reader->file + dwarf->info->sh_offset + dwarf->info->sh_size; - reader->debug_line_cu_end = reader->file + dwarf->line->sh_offset; + reader->p = obj->debug_info.ptr; + reader->pend = obj->debug_info.ptr + obj->debug_info.size; + reader->debug_line_cu_end = obj->debug_line.ptr; } static void @@ -933,7 +885,7 @@ di_read_debug_line_cu(DebugInfoReader *r https://github.com/ruby/ruby/blob/trunk/addr2line.c#L885 p = memchr(p, '\0', reader->debug_line_cu_end - p); if (!p) { fprintf(stderr, "Wrongly reached the end of Directory Table at %tx", - reader->debug_line_directories - (reader->file + reader->dwarf->line->sh_offset)); + reader->debug_line_directories - reader->obj->debug_line.ptr); abort(); } p++; @@ -951,13 +903,13 @@ di_read_cu(DebugInfoReader *reader) https://github.com/ruby/ruby/blob/trunk/addr2line.c#L903 if (hdr32->unit_length == 0xffffffff) { DW_CompilationUnitHeader64 *hdr = (DW_CompilationUnitHeader64 *)hdr32; reader->p += 23; - reader->q0 = reader->file + reader->dwarf->abbrev->sh_offset + hdr->debug_abbrev_offset; + reader->q0 = reader->obj->debug_abbrev.ptr + hdr->debug_abbrev_offset; reader->address_size = hdr->address_size; reader->format = 64; } else { DW_CompilationUnitHeader32 *hdr = hdr32; reader->p += 11; - reader->q0 = reader->file + reader->dwarf->abbrev->sh_offset + hdr->debug_abbrev_offset; + reader->q0 = reader->obj->debug_abbrev.ptr + hdr->debug_abbrev_offset; reader->address_size = hdr->address_size; reader->format = 32; } @@ -983,6 +935,15 @@ static void https://github.com/ruby/ruby/blob/trunk/addr2line.c#L935 set_cstr_value(DebugInfoValue *v, char *s) { v->as.ptr = s; + v->off = 0; + v->type = VAL_cstr; +} + +static void +set_cstrp_value(DebugInfoValue *v, char *s, uint64_t off) +{ + v->as.ptr = s; + v->off = off; v->type = VAL_cstr; } @@ -993,6 +954,16 @@ set_data_value(DebugInfoValue *v, char * https://github.com/ruby/ruby/blob/trunk/addr2line.c#L954 v->type = VAL_data; } +static const char * +get_cstr_value(DebugInfoValue *v) +{ + if (v->as.ptr) { + return v->as.ptr + v->off; + } else { + return NULL; + } +} + static void debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v) { @@ -1051,7 +1022,7 @@ debug_info_reader_read_value(DebugInfoRe https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1022 set_int_value(v, read_sleb128(reader)); break; case DW_FORM_strp: - set_cstr_value(v, reader->file + reader->dwarf->str->sh_offset + read_uint(reader)); + set_cstrp_value(v, reader->obj->debug_str.ptr, read_uint(reader)); break; case DW_FORM_udata: set_uint_value(v, read_uleb128(reader)); @@ -1227,6 +1198,7 @@ di_read_die(DebugInfoReader *reader, DIE https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1198 reader->q = find_abbrev(reader->q0, abbrev_number); + die->pos = reader->p - reader->obj->debug_info.ptr - 1; die->tag = uleb128(&reader->q); /* tag */ die->has_children = *reader->q++; /* has_children */ if (die->has_children) { @@ -1258,8 +1230,7 @@ di_skip_records(DebugInfoReader *reader) https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1230 } typedef struct { - char *file; - uintptr_t debug_ranges_offset; + char *debug_ranges; uint64_t low_pc; uint64_t high_pc; uint64_t ranges; @@ -1286,8 +1257,8 @@ ranges_set_ranges(ranges_t *ptr, uint64_ https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1257 ptr->ranges_set = true; } -static int -ranges_include(ranges_t *ptr, uint64_t addr) { +static bool +ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr) { if (ptr->high_pc_set) { if (ptr->ranges_set || !ptr->low_pc_set) { exit(1); @@ -1297,7 +1268,7 @@ ranges_include(ranges_t *ptr, uint64_t a https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1268 } } else if (ptr->ranges_set) { - char *p = ptr->file + ptr->debug_ranges_offset + ptr->ranges; + char *p = reader->obj->debug_ranges.ptr + ptr->ranges; for (;;) { uint64_t from = read_uint64(&p); uint64_t to = read_uint64(&p); @@ -1328,7 +1299,7 @@ ranges_inspect(ranges_t *ptr) { https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1299 else if (ptr->ranges_set) { char *p; fprintf(stderr,"low_pc:%lx ranges:%lx\n",ptr->low_pc,ptr->ranges); - p = ptr->file + ptr->debug_ranges_offset + ptr->ranges; + p = ptr->debug_ranges + ptr->ranges; for (;;) { uint64_t from = read_uint64(&p); uint64_t to = read_uint64(&p); @@ -1360,7 +1331,7 @@ read_abstract_origin(DebugInfoReader *re https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1331 if (!di_read_record(reader, &v)) break; switch (v.at) { case DW_AT_name: - line->sname = v.as.ptr; + line->sname = get_cstr_value(&v); break; } } @@ -1376,8 +1347,7 @@ debug_info_read(DebugInfoReader *reader, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1347 line_info_t *lines, int offset) { do { DIE die; - ranges_t ranges = {reader->file, reader->dwarf->ranges->sh_offset}; - //ptrdiff_t diepos = reader->p - reader->p0; + ranges_t ranges = {0}; line_info_t line = {0}; if (!di_read_die(reader, &die)) continue; @@ -1398,7 +1368,7 @@ debug_info_read(DebugInfoReader *reader, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1368 //div_inspect(&v); switch (v.at) { case DW_AT_name: - line.sname = v.as.ptr; + line.sname = get_cstr_value(&v); break; case DW_AT_call_file: fill_filename(v.as.uint64, reader->debug_line_directories, reader->debug_line_files, &line); @@ -1429,9 +1399,9 @@ debug_info_read(DebugInfoReader *reader, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1399 for (int i=offset; i < num_traces; i++) { uintptr_t addr = (uintptr_t)traces[i]; uintptr_t offset = addr - reader->obj->base_addr; - if (ranges_include(&ranges, offset)) { - //fprintf(stderr, "%d:%tx: %lx %x %s: %s/%s %d\n",__LINE__,diepos,addr, die.tag,line.sname,line.dirname,line.filename,line.line); - if (lines[i].path) { + if (ranges_include(reader, &ranges, offset)) { + //fprintf(stderr, "%d:%tx: %lx->%lx %x %s: %s/%s %d %s\n",__LINE__,die.pos, addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path); + if (lines[i].sname) { line_info_t *lp = malloc(sizeof(line_info_t)); memcpy(lp, &lines[i], sizeof(line_info_t)); lines[i].next = lp; @@ -1449,14 +1419,54 @@ debug_info_read(DebugInfoReader *reader, https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1419 } while (reader->level > 0); } -static void print_line0(line_info_t *line, void *address); -static void -print_line(line_info_t *line, void *address) { - print_line0(line, address); - if (line->next) { - print_line(line->next, NULL); +static unsigned long +uncompress_debug_section(ElfW(Shdr) *shdr, char *file, char **ptr) +{ +#ifdef SUPPORT_COMPRESSED_DEBUG_LINE + ElfW(Chdr) *chdr = (ElfW(Chdr) *)(file + shdr->sh_offset); + unsigned long destsize = chdr->ch_size; + int ret = 0; + + if (chdr->ch_type != ELFCOMPRESS_ZLIB) { + /* unsupported compression type */ + return 0; + } + + *ptr = malloc(destsize); + if (!*ptr) return 0; + ret = uncompress((Bytef *)*ptr, &destsize, + (const Bytef*)chdr + sizeof(ElfW(Chdr)), + shdr->sh_size - sizeof(ElfW(Chdr))); + if (ret != Z_OK) goto fail; + return destsize; + +fail: + free(*ptr); +#endif + return 0; +} + +void hexdump0(const unsigned char *p, size_t n) { + size_t i; + fprintf(stderr, " 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); + for (i=0; i < n; i++){ + switch (i & 15) { + case 0: + fprintf(stderr, "%02zd: %02X ", i/16, p[i]); + break; + case 15: + fprintf(stderr, "%02X\n", p[i]); + break; + default: + fprintf(stderr, "%02X ", p[i]); + break; + } + } + if ((i & 15) != 15) { + fprintf(stderr, "\n"); } } +#define hexdump(p,n) hexdump0((const unsigned char *)p, n) /* read file and fill lines */ static uintptr_t @@ -1468,8 +1478,7 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1478 char *section_name; ElfW(Ehdr) *ehdr; ElfW(Shdr) *shdr, *shstr_shdr; - ElfW(Shdr) *debug_line_shdr = NULL, *gnu_debuglink_shdr = NULL; - dwarf_args dwarf = {0}; + ElfW(Shdr) *gnu_debuglink_shdr = NULL; int fd; off_t filesize; char *file; @@ -1477,7 +1486,13 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1486 ElfW(Shdr) *dynsym_shdr = NULL, *dynstr_shdr = NULL; obj_info_t *obj = *objp; uintptr_t dladdr_fbase = 0; - bool compressed_p = false; + const char *debug_section_names[] = { + ".debug_abbrev", + ".debug_info", + ".debug_line", + ".debug_ranges", + ".debug_str" + }; fd = open(binary_filename, O_RDONLY); if (fd < 0) { @@ -1544,28 +1559,18 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1559 dynsym_shdr = shdr + i; break; case SHT_PROGBITS: - if (!strcmp(section_name, ".debug_line")) { - if (shdr[i].sh_flags & SHF_COMPRESSED) { - compressed_p = true; - } - debug_line_shdr = shdr + i; - dwarf.line = shdr + i; - } - else if (!strcmp(section_name, ".gnu_debuglink")) { + if (!strcmp(section_name, ".gnu_debuglink")) { gnu_debuglink_shdr = shdr + i; } - else if (!strcmp(section_name, ".debug_abbrev")) { - dwarf.abbrev = shdr + i; + else { + int j; + 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; + } + } } - else if (!strcmp(section_name, ".debug_info")) { - dwarf.info = shdr + i; - } - else if (!strcmp(section_name, ".debug_ranges")) { - dwarf.ranges = shdr + i; - } - else if (!strcmp(section_name, ".debug_str")) { - dwarf.str = shdr + i; - } break; } } @@ -1583,7 +1588,7 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1588 ElfW(Sym) *sym = &symtab[j]; Dl_info info; void *s; - if (ELF_ST_TYPE(sym->st_info) != STT_FUNC) continue; + 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)) { dladdr_fbase = (uintptr_t)info.dli_fbase; @@ -1602,21 +1607,56 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1607 } } - if (dwarf.str && dwarf.info && dwarf.line && dwarf.abbrev && dwarf.ranges) { + 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_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, + objp, lines, offset); + } + goto finish; + } + + if (obj->debug_info.ptr && obj->debug_abbrev.ptr) { DebugInfoReader reader; - debug_info_reader_init(&reader, obj, &dwarf); + debug_info_reader_init(&reader, obj); + i = 0; while (reader.p < reader.pend) { - //fprintf(stderr, "%d:%tx: CU[%d]\n", __LINE__, di_pos(&reader), i++); + //fprintf(stderr, "%d:%tx: CU[%d]\n", __LINE__, reader.p - reader.obj->debug_info, i++); 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; + + /* This file doesn't have symtab, use dynsym instead */ if (!symtab_shdr) { symtab_shdr = dynsym_shdr; strtab_shdr = dynstr_shdr; } + /* This file doesn't have dwarf, use symtab */ if (symtab_shdr && strtab_shdr) { char *strtab = file + strtab_shdr->sh_offset; ElfW(Sym) *symtab = (ElfW(Sym) *)(file + symtab_shdr->sh_offset); @@ -1638,33 +1678,6 @@ fill_lines(int num_traces, void **traces https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1678 } } - 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, - objp, lines, offset); - } - goto finish; - } - - if (compressed_p) { -#ifdef SUPPORT_COMPRESSED_DEBUG_LINE - int r = parse_compressed_debug_line(num_traces, traces, - file + debug_line_shdr->sh_offset, - debug_line_shdr->sh_size, - obj, lines, offset); - if (r) goto fail; -#endif - } - else { - int r = parse_debug_line(num_traces, traces, - file + debug_line_shdr->sh_offset, - debug_line_shdr->sh_size, - obj, lines, offset); - if (r) goto fail; - } finish: return dladdr_fbase; fail: @@ -1745,6 +1758,14 @@ print_line0(line_info_t *line, void *add https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1758 } } +static void +print_line(line_info_t *line, void *address) { + print_line0(line, address); + if (line->next) { + print_line(line->next, NULL); + } +} + void rb_dump_backtrace_with_lines(int num_traces, void **traces) { @@ -1816,13 +1837,16 @@ next_line: https://github.com/ruby/ruby/blob/trunk/addr2line.c#L1837 /* free */ while (obj) { obj_info_t *o = obj; - obj = o->next; - if (o->mapped_size) { - munmap(o->mapped, o->mapped_size); - } - if (o->uncompressed_debug_line) { - free(o->uncompressed_debug_line (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/