From 37ea0f19a7c31a7c8492f72aabdab8d493a2393c Mon Sep 17 00:00:00 2001 From: naruse Date: Thu, 11 Oct 2018 23:55:44 +0000 Subject: [PATCH] Show backtrace with .debug_info git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65007 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- addr2line.c | 965 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 630 insertions(+), 335 deletions(-) diff --git a/addr2line.c b/addr2line.c index cb367e3184..786da0abfc 100644 --- a/addr2line.c +++ b/addr2line.c @@ -113,7 +113,7 @@ void *alloca(); PRINTF_ARGS(static int kprintf(const char *fmt, ...), 1, 2); -typedef struct { +typedef struct line_info { const char *dirname; const char *filename; const char *path; /* object path */ @@ -122,6 +122,8 @@ typedef struct { uintptr_t base_addr; uintptr_t saddr; const char *sname; /* function name */ + + struct line_info *next; } line_info_t; typedef struct obj_info obj_info_t; struct obj_info { @@ -194,8 +196,7 @@ get_nth_dirname(unsigned long dir, char *p) } static void -fill_filename(int file, char *include_directories, char *filenames, - line_info_t *line) +fill_filename(int file, char *include_directories, char *filenames, line_info_t *line) { int i; char *p = filenames; @@ -522,11 +523,18 @@ fail: 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++){ - if ((i & 15) != 15) { - fprintf(stderr, "%02X ", p[i]); - } else { + 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) { @@ -535,200 +543,214 @@ void hexdump0(const unsigned char *p, size_t n) { } #define hexdump(p,n) hexdump0((const unsigned char *)p, n) +enum +{ + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_subprogram = 0x2e, +}; + /* Attributes encodings */ enum { - DW_AT_sibling = 0x01, - DW_AT_location = 0x02, - DW_AT_name = 0x03, - /* Reserved 0x04 */ - /* Reserved 0x05 */ - /* Reserved 0x06 */ - /* Reserved 0x07 */ - /* Reserved 0x08 */ - DW_AT_ordering = 0x09, - /* Reserved 0x0a */ - DW_AT_byte_size = 0x0b, - /* Reserved 0x0c */ - DW_AT_bit_size = 0x0d, - /* Reserved 0x0e */ - /* Reserved 0x0f */ - DW_AT_stmt_list = 0x10, - DW_AT_low_pc = 0x11, - DW_AT_high_pc = 0x12, - DW_AT_language = 0x13, - /* Reserved 0x14 */ - DW_AT_discr = 0x15, - DW_AT_discr_value = 0x16, - DW_AT_visibility = 0x17, - DW_AT_import = 0x18, - DW_AT_string_length = 0x19, - DW_AT_common_reference = 0x1a, - DW_AT_comp_dir = 0x1b, - DW_AT_const_value = 0x1c, - DW_AT_containing_type = 0x1d, - DW_AT_default_value = 0x1e, - /* Reserved 0x1f */ - DW_AT_inline = 0x20, - DW_AT_is_optional = 0x21, - DW_AT_lower_bound = 0x22, - /* Reserved 0x23 */ - /* Reserved 0x24 */ - DW_AT_producer = 0x25, - /* Reserved 0x26 */ - DW_AT_prototyped = 0x27, - /* Reserved 0x28 */ - /* Reserved 0x29 */ - DW_AT_return_addr = 0x2a, - /* Reserved 0x2b */ - DW_AT_start_scope = 0x2c, - /* Reserved 0x2d */ - DW_AT_bit_stride = 0x2e, - DW_AT_upper_bound = 0x2f, - /* Reserved 0x30 */ - DW_AT_abstract_origin = 0x31, - DW_AT_accessibility = 0x32, - DW_AT_address_class = 0x33, - DW_AT_artificial = 0x34, - DW_AT_base_types = 0x35, - DW_AT_calling_convention = 0x36, - DW_AT_count = 0x37, - DW_AT_data_member_location = 0x38, - DW_AT_decl_column = 0x39, - DW_AT_decl_file = 0x3a, - DW_AT_decl_line = 0x3b, - DW_AT_declaration = 0x3c, - DW_AT_discr_list = 0x3d, - DW_AT_encoding = 0x3e, - DW_AT_external = 0x3f, - DW_AT_frame_base = 0x40, - DW_AT_friend = 0x41, - DW_AT_identifier_case = 0x42, - /* Reserved 0x43 */ - DW_AT_namelist_item = 0x44, - DW_AT_priority = 0x45, - DW_AT_segment = 0x46, - DW_AT_specification = 0x47, - DW_AT_static_link = 0x48, - DW_AT_type = 0x49, - DW_AT_use_location = 0x4a, - DW_AT_variable_parameter = 0x4b, - DW_AT_virtuality = 0x4c, - DW_AT_vtable_elem_location = 0x4d, - DW_AT_allocated = 0x4e, - DW_AT_associated = 0x4f, - DW_AT_data_location = 0x50, - DW_AT_byte_stride = 0x51, - DW_AT_entry_pc = 0x52, - DW_AT_use_UTF8 = 0x53, - DW_AT_extension = 0x54, - DW_AT_ranges = 0x55, - DW_AT_trampoline = 0x56, - DW_AT_call_column = 0x57, - DW_AT_call_file = 0x58, - DW_AT_call_line = 0x59, - DW_AT_description = 0x5a, - DW_AT_binary_scale = 0x5b, - DW_AT_decimal_scale = 0x5c, - DW_AT_small = 0x5d, - DW_AT_decimal_sign = 0x5e, - DW_AT_digit_count = 0x5f, - DW_AT_picture_string = 0x60, - DW_AT_mutable = 0x61, - DW_AT_threads_scaled = 0x62, - DW_AT_explicit = 0x63, - DW_AT_object_pointer = 0x64, - DW_AT_endianity = 0x65, - DW_AT_elemental = 0x66, - DW_AT_pure = 0x67, - DW_AT_recursive = 0x68, - DW_AT_signature = 0x69, - DW_AT_main_subprogram = 0x6a, - DW_AT_data_bit_offset = 0x6b, - DW_AT_const_expr = 0x6c, - DW_AT_enum_class = 0x6d, - DW_AT_linkage_name = 0x6e, - DW_AT_string_length_bit_size = 0x6f, - DW_AT_string_length_byte_size = 0x70, - DW_AT_rank = 0x71, - DW_AT_str_offsets_base = 0x72, - DW_AT_addr_base = 0x73, - DW_AT_rnglists_base = 0x74, - /* Reserved 0x75 */ - DW_AT_dwo_name = 0x76, - DW_AT_reference = 0x77, - DW_AT_rvalue_reference = 0x78, - DW_AT_macros = 0x79, - DW_AT_call_all_calls = 0x7a, - DW_AT_call_all_source_calls = 0x7b, - DW_AT_call_all_tail_calls = 0x7c, - DW_AT_call_return_pc = 0x7d, - DW_AT_call_value = 0x7e, - DW_AT_call_origin = 0x7f, - DW_AT_call_parameter = 0x80, - DW_AT_call_pc = 0x81, - DW_AT_call_tail_call = 0x82, - DW_AT_call_target = 0x83, - DW_AT_call_target_clobbered = 0x84, - DW_AT_call_data_location = 0x85, - DW_AT_call_data_value = 0x86, - DW_AT_noreturn = 0x87, - DW_AT_alignment = 0x88, - DW_AT_export_symbols = 0x89, - DW_AT_deleted = 0x8a, - DW_AT_defaulted = 0x8b, - DW_AT_loclists_base = 0x8c, - DW_AT_lo_user = 0x2000, - DW_AT_hi_user = 0x3fff + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + /* Reserved 0x04 */ + /* Reserved 0x05 */ + /* Reserved 0x06 */ + /* Reserved 0x07 */ + /* Reserved 0x08 */ + DW_AT_ordering = 0x09, + /* Reserved 0x0a */ + DW_AT_byte_size = 0x0b, + /* Reserved 0x0c */ + DW_AT_bit_size = 0x0d, + /* Reserved 0x0e */ + /* Reserved 0x0f */ + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + /* Reserved 0x14 */ + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + /* Reserved 0x1f */ + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + /* Reserved 0x23 */ + /* Reserved 0x24 */ + DW_AT_producer = 0x25, + /* Reserved 0x26 */ + DW_AT_prototyped = 0x27, + /* Reserved 0x28 */ + /* Reserved 0x29 */ + DW_AT_return_addr = 0x2a, + /* Reserved 0x2b */ + DW_AT_start_scope = 0x2c, + /* Reserved 0x2d */ + DW_AT_bit_stride = 0x2e, + DW_AT_upper_bound = 0x2f, + /* Reserved 0x30 */ + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + /* Reserved 0x43 */ + DW_AT_namelist_item = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + DW_AT_allocated = 0x4e, + DW_AT_associated = 0x4f, + DW_AT_data_location = 0x50, + DW_AT_byte_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + DW_AT_description = 0x5a, + DW_AT_binary_scale = 0x5b, + DW_AT_decimal_scale = 0x5c, + DW_AT_small = 0x5d, + DW_AT_decimal_sign = 0x5e, + DW_AT_digit_count = 0x5f, + DW_AT_picture_string = 0x60, + DW_AT_mutable = 0x61, + DW_AT_threads_scaled = 0x62, + DW_AT_explicit = 0x63, + DW_AT_object_pointer = 0x64, + DW_AT_endianity = 0x65, + DW_AT_elemental = 0x66, + DW_AT_pure = 0x67, + DW_AT_recursive = 0x68, + DW_AT_signature = 0x69, + DW_AT_main_subprogram = 0x6a, + DW_AT_data_bit_offset = 0x6b, + DW_AT_const_expr = 0x6c, + DW_AT_enum_class = 0x6d, + DW_AT_linkage_name = 0x6e, + DW_AT_string_length_bit_size = 0x6f, + DW_AT_string_length_byte_size = 0x70, + DW_AT_rank = 0x71, + DW_AT_str_offsets_base = 0x72, + DW_AT_addr_base = 0x73, + DW_AT_rnglists_base = 0x74, + /* Reserved 0x75 */ + DW_AT_dwo_name = 0x76, + DW_AT_reference = 0x77, + DW_AT_rvalue_reference = 0x78, + DW_AT_macros = 0x79, + DW_AT_call_all_calls = 0x7a, + DW_AT_call_all_source_calls = 0x7b, + DW_AT_call_all_tail_calls = 0x7c, + DW_AT_call_return_pc = 0x7d, + DW_AT_call_value = 0x7e, + DW_AT_call_origin = 0x7f, + DW_AT_call_parameter = 0x80, + DW_AT_call_pc = 0x81, + DW_AT_call_tail_call = 0x82, + DW_AT_call_target = 0x83, + DW_AT_call_target_clobbered = 0x84, + DW_AT_call_data_location = 0x85, + DW_AT_call_data_value = 0x86, + DW_AT_noreturn = 0x87, + DW_AT_alignment = 0x88, + DW_AT_export_symbols = 0x89, + DW_AT_deleted = 0x8a, + DW_AT_defaulted = 0x8b, + DW_AT_loclists_base = 0x8c, + DW_AT_lo_user = 0x2000, + DW_AT_hi_user = 0x3fff }; /* Attribute form encodings */ enum { - DW_FORM_addr = 0x01, - /* Reserved 0x02 */ - DW_FORM_block2 = 0x03, - DW_FORM_block4 = 0x04, - DW_FORM_data2 = 0x05, - DW_FORM_data4 = 0x06, - DW_FORM_data8 = 0x07, - DW_FORM_string = 0x08, - DW_FORM_block = 0x09, - DW_FORM_block1 = 0x0a, - DW_FORM_data1 = 0x0b, - DW_FORM_flag = 0x0c, - DW_FORM_sdata = 0x0d, - DW_FORM_strp = 0x0e, - DW_FORM_udata = 0x0f, - DW_FORM_ref_addr = 0x10, - DW_FORM_ref1 = 0x11, - DW_FORM_ref2 = 0x12, - DW_FORM_ref4 = 0x13, - DW_FORM_ref8 = 0x14, - DW_FORM_ref_udata = 0x15, - DW_FORM_indirect = 0x16, - DW_FORM_sec_offset = 0x17, - DW_FORM_exprloc = 0x18, - DW_FORM_flag_present = 0x19, - DW_FORM_strx = 0x1a, - DW_FORM_addrx = 0x1b, - DW_FORM_ref_sup4 = 0x1c, - DW_FORM_strp_sup = 0x1d, - DW_FORM_data16 = 0x1e, - DW_FORM_line_strp = 0x1f, - DW_FORM_ref_sig8 = 0x20, - DW_FORM_implicit_const = 0x21, - DW_FORM_loclistx = 0x22, - DW_FORM_rnglistx = 0x23, - DW_FORM_ref_sup8 = 0x24, - DW_FORM_strx1 = 0x25, - DW_FORM_strx2 = 0x26, - DW_FORM_strx3 = 0x27, - DW_FORM_strx4 = 0x28, - DW_FORM_addrx1 = 0x29, - DW_FORM_addrx2 = 0x2a, - DW_FORM_addrx3 = 0x2b, - DW_FORM_addrx4 = 0x2c + DW_FORM_addr = 0x01, + /* Reserved 0x02 */ + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_strp = 0x1f, + DW_FORM_ref_sig8 = 0x20, + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c +}; + +enum { + VAL_none = 0, + VAL_cstr = 1, + VAL_data = 2, + VAL_uint = 3, + VAL_int = 4 }; typedef struct { @@ -747,25 +769,44 @@ typedef struct { } __attribute__((packed)) DW_CompilationUnitHeader64; typedef struct { - char *file; + 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; - ElfW(Shdr) *abbrev; char *q0; char *q; - ElfW(Shdr) *str; int format; /* 32 or 64 */; uint8_t address_size; int level; } DebugInfoReader; +typedef struct { + int tag; + int has_children; +} DIE; + typedef struct { union { char *ptr; uint64_t uint64; + int64_t int64; } as; + uint64_t at; + uint64_t form; size_t size; int type; } DebugInfoValue; @@ -842,64 +883,107 @@ read_sleb128(DebugInfoReader *reader) return sleb128(&reader->p); } -static DebugInfoReader * -debug_info_reader_new(char *file, ElfW(Shdr) *debug_info_shdr, ElfW(Shdr) *debug_abbrev_shdr, ElfW(Shdr) *debug_str_shdr) +static void +debug_info_reader_init(DebugInfoReader *reader, obj_info_t *obj, dwarf_args *dwarf) { - DebugInfoReader *p = malloc(sizeof(DebugInfoReader)); - p->file = file; - p->info = debug_info_shdr; - p->abbrev = debug_abbrev_shdr; - p->str = debug_str_shdr; - p->p0 = p->p = p->file + debug_info_shdr->sh_offset; - p->pend = p->file + debug_info_shdr->sh_offset + debug_info_shdr->sh_size; - return p; + 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; } static void -debug_info_reader_read_cu(DebugInfoReader *reader) +di_read_debug_line_cu(DebugInfoReader *reader) +{ + char *p; + unsigned long unit_length; + unsigned int opcode_base; + + p = reader->debug_line_cu_end; + + unit_length = *(unsigned int *)p; + p += sizeof(unsigned int); + if (unit_length == 0xffffffff) { + unit_length = *(unsigned long *)p; + p += sizeof(unsigned long); + } + + reader->debug_line_cu_end = p + unit_length; + p += 2; + p += sizeof(unsigned int); + p += 4; + opcode_base = *(unsigned char *)p++; + + /* standard_opcode_lengths = (unsigned char *)p - 1; */ + p += opcode_base - 1; + + reader->debug_line_directories = p; + + /* skip include directories */ + while (*p) { + 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)); + abort(); + } + p++; + } + p++; + reader->debug_line_files = p; +} + + +static void +di_read_cu(DebugInfoReader *reader) { DW_CompilationUnitHeader32 *hdr32 = (DW_CompilationUnitHeader32 *)reader->p; + reader->current_cu = reader->p; if (hdr32->unit_length == 0xffffffff) { DW_CompilationUnitHeader64 *hdr = (DW_CompilationUnitHeader64 *)hdr32; reader->p += 23; - reader->q0 = reader->file + reader->abbrev->sh_offset + hdr->debug_abbrev_offset; + reader->q0 = reader->file + reader->dwarf->abbrev->sh_offset + 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->abbrev->sh_offset + hdr->debug_abbrev_offset; + reader->q0 = reader->file + reader->dwarf->abbrev->sh_offset + hdr->debug_abbrev_offset; reader->address_size = hdr->address_size; reader->format = 32; } + reader->level = 0; + di_read_debug_line_cu(reader); } static void set_uint_value(DebugInfoValue *v, uint64_t n) { v->as.uint64 = n; - v->type = 1; + v->type = VAL_uint; } static void -set_sint_value(DebugInfoValue *v, int64_t n) +set_int_value(DebugInfoValue *v, int64_t n) { - v->as.uint64 = (uint64_t)n; - v->type = 2; + v->as.int64 = n; + v->type = VAL_int; } static void set_cstr_value(DebugInfoValue *v, char *s) { v->as.ptr = s; - v->type = 3; + v->type = VAL_cstr; } static void -set_bytes_value(DebugInfoValue *v, char *s) +set_data_value(DebugInfoValue *v, char *s) { v->as.ptr = s; - v->type = 4; + v->type = VAL_data; } static void @@ -907,7 +991,6 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa { switch (form) { case DW_FORM_addr: - fprintf(stderr, "%d: %d\n", __LINE__, reader->address_size); if (reader->address_size == 4) { set_uint_value(v, read_uint32(&reader->p)); } else if (reader->address_size == 8) { @@ -919,12 +1002,12 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa break; case DW_FORM_block2: v->size = read_uint16(&reader->p); - set_bytes_value(v, reader->p); + set_data_value(v, reader->p); reader->p += v->size; break; case DW_FORM_block4: v->size = read_uint32(&reader->p); - set_bytes_value(v, reader->p); + set_data_value(v, reader->p); reader->p += v->size; break; case DW_FORM_data2: @@ -943,12 +1026,12 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa break; case DW_FORM_block: v->size = uleb128(&reader->p); - set_bytes_value(v, reader->p); + set_data_value(v, reader->p); reader->p += v->size; break; case DW_FORM_block1: v->size = read_uint8(&reader->p); - set_bytes_value(v, reader->p); + set_data_value(v, reader->p); reader->p += v->size; break; case DW_FORM_data1: @@ -958,10 +1041,10 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa set_uint_value(v, read_uint8(&reader->p)); break; case DW_FORM_sdata: - set_sint_value(v, read_sleb128(reader)); + set_int_value(v, read_sleb128(reader)); break; case DW_FORM_strp: - set_cstr_value(v, reader->file + reader->str->sh_offset + read_uint(reader)); + set_cstr_value(v, reader->file + reader->dwarf->str->sh_offset + read_uint(reader)); break; case DW_FORM_udata: set_uint_value(v, read_uleb128(reader)); @@ -1000,8 +1083,7 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa break; case DW_FORM_exprloc: v->size = read_uleb128(reader); - set_bytes_value(v, reader->p); - hexdump(reader->p, v->size); + set_data_value(v, reader->p); reader->p += v->size; break; case DW_FORM_flag_present: @@ -1077,70 +1159,284 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa exit(1); } +static char * +find_abbrev(char *p, uint64_t abbrev_number) { + for (uint64_t n = uleb128(&p); abbrev_number != n; n = uleb128(&p)) { + if (n == 0) { + fprintf(stderr,"%d: Abbrev Number %ld not found\n",__LINE__, abbrev_number); + exit(1); + } + uleb128(&p); /* tag */ + p++; /* has_children */ + /* skip content */ + for (;;) { + uint64_t at = uleb128(&p); + uint64_t form = uleb128(&p); + if (!at && !form) break; + } + } + return p; +} + +#if 0 static void -debug_info_read(DebugInfoReader *reader) { - reader->level = 0; +div_inspect(DebugInfoValue *v) { + switch (v->type) { + case VAL_uint: + fprintf(stderr,"%d: type:%d size:%zx v:%lx\n",__LINE__,v->type,v->size,v->as.uint64); + break; + case VAL_int: + fprintf(stderr,"%d: type:%d size:%zx v:%ld\n",__LINE__,v->type,v->size,(int64_t)v->as.uint64); + break; + case VAL_cstr: + fprintf(stderr,"%d: type:%d size:%zx v:'%s'\n",__LINE__,v->type,v->size,v->as.ptr); + break; + case VAL_data: + fprintf(stderr,"%d: type:%d size:%zx v:\n",__LINE__,v->type,v->size); + hexdump(v->as.ptr, 16); + break; + } +} +#endif + +static DIE * +di_read_die(DebugInfoReader *reader, DIE *die) { + uint64_t abbrev_number = uleb128(&reader->p); + if (abbrev_number == 0) { + reader->level--; + return NULL; + } + + reader->q = find_abbrev(reader->q0, abbrev_number); + + die->tag = uleb128(&reader->q); /* tag */ + die->has_children = *reader->q++; /* has_children */ + if (die->has_children) { + reader->level++; + } + return die; +} + +static DebugInfoValue * +di_read_record(DebugInfoReader *reader, DebugInfoValue *vp) { + uint64_t at = uleb128(&reader->q); + uint64_t form = uleb128(&reader->q); + if (!at || !form) return NULL; + vp->at = at; + vp->form = form; + debug_info_reader_read_value(reader, form, vp); + return vp; +} + +static void +di_skip_records(DebugInfoReader *reader) { for (;;) { - uint64_t abbrev_number = uleb128(&reader->p); - char has_children; - fprintf(stderr,"\n\n%d: <%d> Abbrev Number: %lu\n",__LINE__,reader->level,abbrev_number); - if (abbrev_number == 0) { - if (reader->level == 1) { - return; + DebugInfoValue v = {0}; + uint64_t at = uleb128(&reader->q); + uint64_t form = uleb128(&reader->q); + if (!at || !form) return; + debug_info_reader_read_value(reader, form, &v); + } +} + +typedef struct { + char *file; + uintptr_t debug_ranges_offset; + uint64_t low_pc; + uint64_t high_pc; + uint64_t ranges; + bool low_pc_set; + bool high_pc_set; + bool ranges_set; +} ranges_t; + +static void +ranges_set_low_pc(ranges_t *ptr, uint64_t low_pc) { + ptr->low_pc = low_pc; + ptr->low_pc_set = true; +} + +static void +ranges_set_high_pc(ranges_t *ptr, uint64_t high_pc) { + ptr->high_pc = high_pc; + ptr->high_pc_set = true; +} + +static void +ranges_set_ranges(ranges_t *ptr, uint64_t ranges) { + ptr->ranges = ranges; + ptr->ranges_set = true; +} + +static int +ranges_include(ranges_t *ptr, uint64_t addr) { + if (ptr->high_pc_set) { + if (ptr->ranges_set || !ptr->low_pc_set) { + exit(1); + } + if (ptr->low_pc <= addr && addr <= ptr->low_pc + ptr->high_pc) { + return true; + } + } + else if (ptr->ranges_set) { + char *p = ptr->file + ptr->debug_ranges_offset + ptr->ranges; + for (;;) { + uint64_t from = read_uint64(&p); + uint64_t to = read_uint64(&p); + if (!from && !to) break; + if (from <= addr && addr <= to) { + return true; } - reader->level--; + } + } + else if (ptr->low_pc_set) { + if (ptr->low_pc == addr) { + return true; + } + } + return false; +} + +#if 0 +static void +ranges_inspect(ranges_t *ptr) { + if (ptr->high_pc_set) { + if (ptr->ranges_set || !ptr->low_pc_set) { + 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); + } + 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; + for (;;) { + uint64_t from = read_uint64(&p); + uint64_t to = read_uint64(&p); + if (!from && !to) break; + } + } + else if (ptr->low_pc_set) { + fprintf(stderr,"low_pc:%lx\n",ptr->low_pc); + } + else { + fprintf(stderr,"empty\n"); + } +} +#endif + +static void +read_abstract_origin(DebugInfoReader *reader, uint64_t abstract_origin, line_info_t *line) { + char *p = reader->p; + char *q = reader->q; + int level = reader->level; + DIE die; + + reader->p = reader->current_cu + abstract_origin; + if (!di_read_die(reader, &die)) goto finish; + + /* enumerate abbrev */ + for (;;) { + DebugInfoValue v = {0}; + if (!di_read_record(reader, &v)) break; + switch (v.at) { + case DW_AT_name: + line->sname = v.as.ptr; + break; + } + } + + finish: + reader->p = p; + reader->q = q; + reader->level = level; +} + +static void +debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, + 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; + line_info_t line = {0}; + + if (!di_read_die(reader, &die)) continue; + //fprintf(stderr,"%d:%tx: <%d> Abbrev Number: %lu\n",__LINE__,diepos,reader->level,die.tag); + + if (die.tag != DW_TAG_subprogram && die.tag != DW_TAG_inlined_subroutine) { + skip_die: + di_skip_records(reader); continue; } - reader->q = reader->q0; - - /* find abbrev */ - for (uint64_t n = uleb128(&reader->q); abbrev_number != n; n = uleb128(&reader->q)) { - if (n == 0) { - fprintf(stderr,"%d: Abbrev Number not found\n",__LINE__); - abort(); - } - /* fprintf(stderr,"%d: %lu != %lu\n",__LINE__, abbrev_number, n); */ - uleb128(&reader->q); /* tag */ - reader->q++; /* children */ - /* skip content */ - for (;;) { - uint64_t at = uleb128(&reader->q); - uint64_t form = uleb128(&reader->q); - if (!at && !form) break; - } - } - - uleb128(&reader->q); /* tag */ - has_children = *reader->q++; /* has_children */ - if (has_children) { - reader->level++; - } - /* enumerate abbrev */ for (;;) { - uint64_t at = uleb128(&reader->q); - uint64_t form = uleb128(&reader->q); DebugInfoValue v = {0}; - fprintf(stderr,"\n%d:%lx: AT:%lx FORM:%lx\n",__LINE__,reader->p-reader->p0,at,form); - if (!at && !form) break; - debug_info_reader_read_value(reader, form, &v); - switch (v.type) { - case 1: - fprintf(stderr,"%d: type:%d size:%zx v:%lx\n",__LINE__,v.type,v.size,v.as.uint64); + //ptrdiff_t pos = reader->p - reader->p0; + if (!di_read_record(reader, &v)) break; + //fprintf(stderr,"\n%d:%tx: AT:%lx FORM:%lx\n",__LINE__,pos,v.at,v.form); + //div_inspect(&v); + switch (v.at) { + case DW_AT_name: + line.sname = v.as.ptr; break; - case 2: - fprintf(stderr,"%d: type:%d size:%zx v:%ld\n",__LINE__,v.type,v.size,(int64_t)v.as.uint64); + case DW_AT_call_file: + fill_filename(v.as.uint64, reader->debug_line_directories, reader->debug_line_files, &line); break; - case 3: - fprintf(stderr,"%d: type:%d size:%zx v:'%s'\n",__LINE__,v.type,v.size,v.as.ptr); + case DW_AT_call_line: + line.line = v.as.uint64; break; - case 4: - fprintf(stderr,"%d: type:%d size:%zx v:\n",__LINE__,v.type,v.size); - hexdump(v.as.ptr, 16); + case DW_AT_low_pc: + ranges_set_low_pc(&ranges, v.as.uint64); + break; + case DW_AT_high_pc: + ranges_set_high_pc(&ranges, v.as.uint64); + break; + case DW_AT_declaration: + goto skip_die; + case DW_AT_inline: + /* 1 or 3 */ + break; // goto skip_die; + case DW_AT_abstract_origin: + read_abstract_origin(reader, v.as.uint64, &line); + break; //goto skip_die; + case DW_AT_ranges: + ranges_set_ranges(&ranges, v.as.uint64); break; } } + /* fprintf(stderr,"%d:%tx: %x ",__LINE__,diepos,die.tag); */ + 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) { + line_info_t *lp = malloc(sizeof(line_info_t)); + memcpy(lp, &lines[i], sizeof(line_info_t)); + lines[i].next = lp; + lp->dirname = line.dirname; + lp->filename = line.filename; + lp->line = line.line; + lp->saddr = 0; + } + 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 + ranges.low_pc; + } + } + } 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); } } @@ -1155,8 +1451,7 @@ fill_lines(int num_traces, void **traces, int check_debuglink, ElfW(Ehdr) *ehdr; ElfW(Shdr) *shdr, *shstr_shdr; ElfW(Shdr) *debug_line_shdr = NULL, *gnu_debuglink_shdr = NULL; - ElfW(Shdr) *debug_info_shdr = NULL, *debug_abbrev_shdr = NULL; - ElfW(Shdr) *debug_str_shdr = NULL; + dwarf_args dwarf = {0}; int fd; off_t filesize; char *file; @@ -1236,51 +1531,27 @@ fill_lines(int num_traces, void **traces, int check_debuglink, compressed_p = true; } debug_line_shdr = shdr + i; + dwarf.line = shdr + i; } else if (!strcmp(section_name, ".gnu_debuglink")) { gnu_debuglink_shdr = shdr + i; } - else if (!strcmp(section_name, ".debug_info")) { - debug_info_shdr = shdr + i; - } else if (!strcmp(section_name, ".debug_abbrev")) { - debug_abbrev_shdr = shdr + i; + dwarf.abbrev = shdr + i; } + 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")) { - debug_str_shdr = shdr + i; + dwarf.str = shdr + i; } break; } } - if (debug_info_shdr) { - unsigned char *info = (unsigned char *)(file + debug_info_shdr->sh_offset); - size_t info_count = debug_info_shdr->sh_size; - fprintf(stderr, "size: %zd\n", info_count); - hexdump(info, 11); - info += 11; - hexdump(info, 11); - info += 11; - hexdump(info, 32); - } - if (debug_abbrev_shdr) { - unsigned char *abbrev = (unsigned char *)(file + debug_abbrev_shdr->sh_offset); - size_t abbrev_count = debug_abbrev_shdr->sh_size; - fprintf(stderr, "size: %zd\n", abbrev_count); - hexdump(abbrev, 128); - } - { - int i = 0; - DebugInfoReader *reader = debug_info_reader_new(file, debug_info_shdr, debug_abbrev_shdr, debug_str_shdr); - - while (reader->p < reader->pend) { - fprintf(stderr, "CU[%d]\n", i++); - debug_info_reader_read_cu(reader); - debug_info_read(reader); - } - } - exit(0); - if (offset == -1) { /* main executable */ offset = 0; @@ -1312,6 +1583,16 @@ fill_lines(int num_traces, void **traces, int check_debuglink, } } + if (dwarf.str && dwarf.info && dwarf.line && dwarf.abbrev && dwarf.ranges) { + DebugInfoReader reader; + debug_info_reader_init(&reader, obj, &dwarf); + while (reader.p < reader.pend) { + //fprintf(stderr, "%d:%tx: CU[%d]\n", __LINE__, di_pos(&reader), i++); + di_read_cu(&reader); + debug_info_read(&reader, num_traces, traces, lines, offset); + } + } + if (!symtab_shdr) { symtab_shdr = dynsym_shdr; strtab_shdr = dynstr_shdr; @@ -1330,7 +1611,7 @@ fill_lines(int num_traces, void **traces, int check_debuglink, 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; + if (!lines[i].sname) lines[i].sname = strtab + sym->st_name; lines[i].saddr = saddr; lines[i].path = obj->path; lines[i].base_addr = obj->base_addr; @@ -1408,6 +1689,43 @@ main_exe_path(void) #undef HAVE_MAIN_EXE_PATH #endif +static void +print_line0(line_info_t *line, void *address) { + uintptr_t addr = (uintptr_t)address; + uintptr_t d = addr - line->saddr; + if (!address) { + /* inlined */ + if (line->dirname && line->dirname[0]) { + kprintf("%s(%s) %s/%s:%d\n", line->path, line->sname, line->dirname, line->filename, line->line); + } + else { + kprintf("%s(%s) %s:%d\n", line->path, line->sname, line->filename, line->line); + } + } + else if (!line->path) { + kprintf("[0x%lx]\n", addr); + } + else if (!line->saddr || !line->sname) { + kprintf("%s(0x%lx) [0x%lx]\n", line->path, addr-line->base_addr, addr); + } + else 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 { + kprintf("%s(%s+0x%lx) [0x%lx] %s:%d\n", line->path, line->sname, + d, addr, line->filename, line->line); + } +} + void rb_dump_backtrace_with_lines(int num_traces, void **traces) { @@ -1469,33 +1787,10 @@ next_line: /* output */ for (i = 0; i < num_traces; i++) { - line_info_t *line = &lines[i]; - uintptr_t addr = (uintptr_t)traces[i]; - uintptr_t d = addr - line->saddr; - if (!line->path) { - kprintf("[0x%lx]\n", addr); - } - else if (!line->saddr || !line->sname) { - kprintf("%s(0x%lx) [0x%lx]\n", line->path, addr-line->base_addr, addr); - } - else 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 { - kprintf("%s(%s+0x%lx) [0x%lx] %s:%d\n", line->path, line->sname, - d, addr, line->filename, line->line); - } + print_line(&lines[i], traces[i]); + /* FreeBSD's backtrace may show _start and so on */ - if (line->sname && strcmp("main", line->sname) == 0) + if (lines[i].sname && strcmp("main", lines[i].sname) == 0) break; }