From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 46729 invoked by alias); 23 May 2018 14:30:23 -0000 Mailing-List: contact elfutils-devel-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Post: List-Help: List-Subscribe: Sender: elfutils-devel-owner@sourceware.org Received: (qmail 45302 invoked by uid 89); 23 May 2018 14:30:09 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Checked: by ClamAV 0.99.4 on sourceware.org X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_PASS autolearn=ham version=3.3.2 spammy=UD:bz2 X-Spam-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_PASS autolearn=ham version=3.3.2 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on sourceware.org X-Spam-Level: X-HELO: gnu.wildebeest.org Received: from wildebeest.demon.nl (HELO gnu.wildebeest.org) (212.238.236.112) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 23 May 2018 14:30:03 +0000 Received: from tarox.wildebeest.org (tarox.wildebeest.org [172.31.17.39]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by gnu.wildebeest.org (Postfix) with ESMTPSA id 9EC96302BB43; Wed, 23 May 2018 16:29:59 +0200 (CEST) Received: by tarox.wildebeest.org (Postfix, from userid 1000) id 46C4040006DA; Wed, 23 May 2018 16:29:59 +0200 (CEST) From: Mark Wielaard To: elfutils-devel@sourceware.org Cc: Mark Wielaard Subject: [PATCH] readelf: Add .debug_rnglists support. Date: Wed, 23 May 2018 14:30:00 -0000 Message-Id: <1527085796-18259-1-git-send-email-mark@klomp.org> X-Mailer: git-send-email 1.8.3.1 X-Spam-Flag: NO X-IsSubscribed: yes X-SW-Source: 2018-q2/txt/msg00077.txt.bz2 Parse the .debug_rnglists section for DWARF5 --debug-dump=ranges. Add testcase to show both "normal" and "split" DWARF variants are handled for DWARF4 and DWARF5. Signed-off-by: Mark Wielaard --- libdw/ChangeLog | 11 + libdw/dwarf.h | 13 ++ libdw/dwarf_begin_elf.c | 1 + libdw/dwarf_error.c | 1 + libdw/dwarf_formudata.c | 23 +- libdw/dwarf_getscopes.c | 4 +- libdw/libdwP.h | 2 + src/ChangeLog | 13 ++ src/readelf.c | 529 ++++++++++++++++++++++++++++++++++++++++++-- tests/ChangeLog | 6 + tests/Makefile.am | 3 +- tests/run-readelf-ranges.sh | 236 ++++++++++++++++++++ 12 files changed, 820 insertions(+), 22 deletions(-) create mode 100755 tests/run-readelf-ranges.sh diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 3045359..b47fac7 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,14 @@ +2018-04-11 Mark Wielaard + + * dwarf.h: Add DWARF5 range list entry DW_RLE encodings. + * begin_elf.c (dwarf_scnnames): Add IDX_debug_rnglists. + * dwarf_error.c (errmsgs): Add DWARF_E_NO_DEBUG_RNGLISTS. + * dwarf_formudata.c (dwarf_formudata): Handle DW_AT_rnglists_base + and DW_FORM_rnglistx. + * dwarf_getscopes.c (pc_match): Also check for + DWARF_E_NO_DEBUG_RNGLISTS. + * libdwP.h: Add IDX_debug_rnglists. + 2018-05-22 Mark Wielaard * dwarf_getlocation.c (__libdw_cu_base_address): Treat errors of diff --git a/libdw/dwarf.h b/libdw/dwarf.h index c438399..9c2495e 100644 --- a/libdw/dwarf.h +++ b/libdw/dwarf.h @@ -886,6 +886,19 @@ enum #define DW_MACRO_GNU_hi_user DW_MACRO_hi_user +/* Range list entry encoding. */ +enum + { + DW_RLE_end_of_list = 0x0, + DW_RLE_base_addressx = 0x1, + DW_RLE_startx_endx = 0x2, + DW_RLE_startx_length = 0x3, + DW_RLE_offset_pair = 0x4, + DW_RLE_base_address = 0x5, + DW_RLE_start_end = 0x6, + DW_RLE_start_length = 0x7 + }; + /* DWARF call frame instruction encodings. */ enum { diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index 5d8e79e..2e8c5f3 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -64,6 +64,7 @@ static const char dwarf_scnnames[IDX_last][19] = [IDX_debug_macinfo] = ".debug_macinfo", [IDX_debug_macro] = ".debug_macro", [IDX_debug_ranges] = ".debug_ranges", + [IDX_debug_rnglists] = ".debug_rnglists", [IDX_gnu_debugaltlink] = ".gnu_debugaltlink" }; #define ndwarf_scnnames (sizeof (dwarf_scnnames) / sizeof (dwarf_scnnames[0])) diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c index 63c8bbe..2e8cd77 100644 --- a/libdw/dwarf_error.c +++ b/libdw/dwarf_error.c @@ -93,6 +93,7 @@ static const char *errmsgs[] = [DWARF_E_NO_FLAG] = N_("no flag value"), [DWARF_E_INVALID_OFFSET] = N_("invalid offset"), [DWARF_E_NO_DEBUG_RANGES] = N_(".debug_ranges section missing"), + [DWARF_E_NO_DEBUG_RNGLISTS] = N_(".debug_rnglists section missing"), [DWARF_E_INVALID_CFI] = N_("invalid CFI section"), [DWARF_E_NO_ALT_DEBUGLINK] = N_("no alternative debug link found"), [DWARF_E_INVALID_OPCODE] = N_("invalid opcode"), diff --git a/libdw/dwarf_formudata.c b/libdw/dwarf_formudata.c index d56e7dc..280fef2 100644 --- a/libdw/dwarf_formudata.c +++ b/libdw/dwarf_formudata.c @@ -211,11 +211,23 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval) case DW_AT_ranges: case DW_AT_start_scope: case DW_AT_GNU_ranges_base: - /* rangelistptr */ - if (__libdw_formptr (attr, IDX_debug_ranges, - DWARF_E_NO_DEBUG_RANGES, NULL, - return_uval) == NULL) - return -1; + case DW_AT_rnglists_base: + if (attr->cu->version < 5) + { + /* rangelistptr */ + if (__libdw_formptr (attr, IDX_debug_ranges, + DWARF_E_NO_DEBUG_RANGES, NULL, + return_uval) == NULL) + return -1; + } + else + { + /* rnglistsptr */ + if (__libdw_formptr (attr, IDX_debug_rnglists, + DWARF_E_NO_DEBUG_RNGLISTS, NULL, + return_uval) == NULL) + return -1; + } break; case DW_AT_stmt_list: @@ -278,6 +290,7 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval) break; case DW_FORM_udata: + case DW_FORM_rnglistx: if (datap + 1 > endp) goto invalid; get_uleb128 (*return_uval, datap, endp); diff --git a/libdw/dwarf_getscopes.c b/libdw/dwarf_getscopes.c index df480d3..5662eec 100644 --- a/libdw/dwarf_getscopes.c +++ b/libdw/dwarf_getscopes.c @@ -62,7 +62,9 @@ pc_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg) if (result < 0) { int error = INTUSE(dwarf_errno) (); - if (error != DWARF_E_NOERROR && error != DWARF_E_NO_DEBUG_RANGES) + if (error != DWARF_E_NOERROR + && error != DWARF_E_NO_DEBUG_RANGES + && error != DWARF_E_NO_DEBUG_RNGLISTS) { __libdw_seterrno (error); return -1; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 82ee5d0..3cfcc55 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -84,6 +84,7 @@ enum IDX_debug_macinfo, IDX_debug_macro, IDX_debug_ranges, + IDX_debug_rnglists, IDX_gnu_debugaltlink, IDX_last }; @@ -131,6 +132,7 @@ enum DWARF_E_NO_FLAG, DWARF_E_INVALID_OFFSET, DWARF_E_NO_DEBUG_RANGES, + DWARF_E_NO_DEBUG_RNGLISTS, DWARF_E_INVALID_CFI, DWARF_E_NO_ALT_DEBUGLINK, DWARF_E_INVALID_OPCODE, diff --git a/src/ChangeLog b/src/ChangeLog index 1a9f4a3..d28d89a 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,16 @@ +2018-04-11 Mark Wielaard + + * readelf.c (dwarf_range_list_encoding_string): New function. + (dwarf_range_list_encoding_name): Likewise. + (known_rnglistptr): New static variable. + (listptr_cu): New function. + (print_debug_rnglists_section): Likewise. + (attr_callback): Call notice_listptr for DW_AT_ranges. Handle + DW_AT_rnglists_base. + (print_debug_units): Do (silently) scan split DWARF also for + debug_ranges before DWARF5 to catch all rangelistptrs. + (print_debug): Recognize .debug_rnglists. Reset known_rnglistptr. + 2018-01-21 Mark Wielaard * readelf.c (get_indexed_addr): New function. diff --git a/src/readelf.c b/src/readelf.c index 1858802..071906f 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -3984,6 +3984,20 @@ dwarf_unit_string (unsigned int type) static const char * +dwarf_range_list_encoding_string (unsigned int kind) +{ + switch (kind) + { +#define DWARF_ONE_KNOWN_DW_RLE(NAME, CODE) case CODE: return #NAME; + DWARF_ALL_KNOWN_DW_RLE +#undef DWARF_ONE_KNOWN_DW_RLE + default: + return NULL; + } +} + + +static const char * dwarf_line_content_description_string (unsigned int kind) { switch (kind) @@ -4145,6 +4159,14 @@ dwarf_unit_name (unsigned int type) static const char * +dwarf_range_list_encoding_name (unsigned int kind) +{ + const char *ret = dwarf_range_list_encoding_string (kind); + return string_or_unknown (ret, kind, 0, 0, false); +} + + +static const char * dwarf_line_content_description_name (unsigned int kind) { const char *ret = dwarf_line_content_description_string (kind); @@ -4183,6 +4205,9 @@ print_bytes (size_t n, const unsigned char *bytes) static int get_indexed_addr (Dwarf_CU *cu, Dwarf_Word idx, Dwarf_Addr *addr) { + if (cu == NULL) + return -1; + Elf_Data *debug_addr = cu->dbg->sectiondata[IDX_debug_addr]; if (debug_addr == NULL) return -1; @@ -4734,6 +4759,7 @@ struct listptr_table static struct listptr_table known_loclistptr; static struct listptr_table known_rangelistptr; +static struct listptr_table known_rnglistptr; static void reset_listptr (struct listptr_table *table) @@ -4851,6 +4877,32 @@ next_listptr_offset (struct listptr_table *table, size_t idx) return 0; } +/* Returns the next index, base address and CU associated with the + list unit offsets. If there is none false is returned, otherwise + true. Assumes the table has been sorted. */ +static bool +listptr_cu (struct listptr_table *table, size_t *idxp, + Dwarf_Off start, Dwarf_Off end, + Dwarf_Addr *base, struct Dwarf_CU **cu) +{ + while (*idxp < table->n + && table->table[*idxp].offset < start) + ++*idxp; + + if (*idxp < table->n + && table->table[*idxp].offset >= start + && table->table[*idxp].offset < end) + { + struct listptr *p = &table->table[*idxp]; + *base = listptr_base (p); + *cu = p->cu; + ++*idxp; + return true; + } + + return false; +} + static void print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), Ebl *ebl, GElf_Ehdr *ehdr, @@ -5151,6 +5203,396 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), } } +/* Print content of DWARF .debug_rnglists section. */ +static void +print_debug_rnglists_section (Dwfl_Module *dwflmod, + Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, + Dwarf *dbg __attribute__((unused))) +{ + printf (gettext ("\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset); + + Elf_Data *data =(dbg->sectiondata[IDX_debug_rnglists] + ?: elf_rawdata (scn, NULL)); + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get .debug_rnglists content: %s"), + elf_errmsg (-1)); + return; + } + + /* For the listptr to get the base address/CU. */ + sort_listptr (&known_rnglistptr, "rnglistptr"); + size_t listptr_idx = 0; + + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend = ((unsigned char *) data->d_buf + + data->d_size); + while (readp < dataend) + { + if (unlikely (readp > dataend - 4)) + { + invalid_data: + error (0, 0, gettext ("invalid data in section [%zu] '%s'"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr)); + return; + } + + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + printf (gettext ("Table at Offset 0x%" PRIx64 ":\n\n"), + (uint64_t) offset); + + uint64_t unit_length = read_4ubyte_unaligned_inc (dbg, readp); + unsigned int offset_size = 4; + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readp > dataend - 8)) + goto invalid_data; + + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + offset_size = 8; + } + printf (gettext (" Length: %8" PRIu64 "\n"), unit_length); + + /* We need at least 2-bytes + 1-byte + 1-byte + 4-bytes = 8 + bytes to complete the header. And this unit cannot go beyond + the section data. */ + if (readp > dataend - 8 + || unit_length < 8 + || unit_length > (uint64_t) (dataend - readp)) + goto invalid_data; + + const unsigned char *nexthdr = readp + unit_length; + + uint16_t version = read_2ubyte_unaligned_inc (dbg, readp); + printf (gettext (" DWARF version: %8" PRIu16 "\n"), version); + + if (version != 5) + { + error (0, 0, gettext ("Unknown version")); + goto next_table; + } + + uint8_t address_size = *readp++; + printf (gettext (" Address size: %8" PRIu64 "\n"), + (uint64_t) address_size); + + if (address_size != 4 && address_size != 8) + { + error (0, 0, gettext ("unsupported address size")); + goto next_table; + } + + uint8_t segment_size = *readp++; + printf (gettext (" Segment size: %8" PRIu64 "\n"), + (uint64_t) segment_size); + + if (segment_size != 0 && segment_size != 4 && segment_size != 8) + { + error (0, 0, gettext ("unsupported segment size")); + goto next_table; + } + + uint32_t offset_entry_count = read_4ubyte_unaligned_inc (dbg, readp); + printf (gettext (" Offset entries: %8" PRIu64 "\n"), + (uint64_t) offset_entry_count); + + /* We need the CU that uses this unit to get the initial base address. */ + Dwarf_Addr cu_base = 0; + struct Dwarf_CU *cu = NULL; + if (listptr_cu (&known_rnglistptr, &listptr_idx, + (Dwarf_Off) offset, + (Dwarf_Off) (nexthdr - (unsigned char *) data->d_buf), + &cu_base, &cu)) + { + char *basestr = format_dwarf_addr (dwflmod, address_size, + cu_base, cu_base); + Dwarf_Die cudie; + if (dwarf_cu_die (cu, &cudie, + NULL, NULL, NULL, NULL, + NULL, NULL) == NULL) + printf (gettext (" Unknown CU base: %s\n"), basestr); + else + printf (gettext (" CU [%6" PRIx64 "] base: %s\n"), + dwarf_dieoffset (&cudie), basestr); + free (basestr); + } + else + printf (gettext (" Not associated with a CU.\n")); + + printf ("\n"); + + const unsigned char *offset_array_start = readp; + if (offset_entry_count > 0) + { + uint64_t needed = offset_entry_count * offset_size; + if (unit_length - 8 < needed) + { + error (0, 0, + gettext ("too many offset entries for unit length")); + goto next_table; + } + + printf (gettext (" Offsets starting at 0x%" PRIx64 ":\n"), + (uint64_t) (offset_array_start + - (unsigned char *) data->d_buf)); + for (uint32_t idx = 0; idx < offset_entry_count; idx++) + { + printf (" [%6" PRIu32 "] ", idx); + if (offset_size == 4) + { + uint32_t off = read_4ubyte_unaligned_inc (dbg, readp); + printf ("0x%" PRIx32 "\n", off); + } + else + { + uint64_t off = read_8ubyte_unaligned_inc (dbg, readp); + printf ("0x%" PRIx64 "\n", off); + } + } + printf ("\n"); + } + + Dwarf_Addr base = cu_base; + bool start_of_list = true; + while (readp < nexthdr) + { + uint8_t kind = *readp++; + uint64_t op1, op2; + char *a1, *a2; + + /* Skip padding. */ + if (start_of_list && kind == DW_RLE_end_of_list) + continue; + + if (start_of_list) + { + base = cu_base; + printf (" Offset: %" PRIx64 ", Index: %" PRIx64 "\n", + (uint64_t) (readp - (unsigned char *) data->d_buf - 1), + (uint64_t) (readp - offset_array_start - 1)); + start_of_list = false; + } + + printf (" %s", dwarf_range_list_encoding_name (kind)); + switch (kind) + { + case DW_RLE_end_of_list: + start_of_list = true; + printf ("\n\n"); + break; + + case DW_RLE_base_addressx: + if ((uint64_t) (nexthdr - readp) < 1) + { + invalid_range: + error (0, 0, gettext ("invalid range list data")); + goto next_table; + } + get_uleb128 (op1, readp, nexthdr); + printf (" %" PRIx64 "\n", op1); + if (! print_unresolved_addresses) + { + Dwarf_Addr addr; + if (get_indexed_addr (cu, op1, &addr) != 0) + printf (" ???\n"); + else + { + a1 = format_dwarf_addr (dwflmod, address_size, + addr, addr); + printf (" %s\n", a1); + free (a1); + } + } + break; + + case DW_RLE_startx_endx: + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op1, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op2, readp, nexthdr); + printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + Dwarf_Addr addr1; + Dwarf_Addr addr2; + if (get_indexed_addr (cu, op1, &addr1) != 0 + || get_indexed_addr (cu, op2, &addr2) != 0) + { + printf (" ???..\n"); + printf (" ???\n"); + } + else + { + a1 = format_dwarf_addr (dwflmod, address_size, op1, op1); + a2 = format_dwarf_addr (dwflmod, address_size, + op2 - 1, op2); + printf (" %s..\n", a1); + printf (" %s\n", a2); + free (a1); + free (a2); + } + } + break; + + case DW_RLE_startx_length: + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op1, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op2, readp, nexthdr); + printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + Dwarf_Addr addr1; + Dwarf_Addr addr2; + bool resolved = get_indexed_addr (cu, op1, &addr1) == 0; + if (resolved) + { + op2 = addr1 + op2; + resolved = get_indexed_addr (cu, op2, &addr2) == 0; + } + if (resolved) + { + printf (" ???..\n"); + printf (" ???\n"); + } + else + { + a1 = format_dwarf_addr (dwflmod, address_size, op1, op1); + a2 = format_dwarf_addr (dwflmod, address_size, + op2 - 1, op2); + printf (" %s..\n", a1); + printf (" %s\n", a2); + free (a1); + free (a2); + } + } + break; + + case DW_RLE_offset_pair: + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op1, readp, nexthdr); + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op2, readp, nexthdr); + printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + op1 += base; + op2 += base; + a1 = format_dwarf_addr (dwflmod, address_size, op1, op1); + a2 = format_dwarf_addr (dwflmod, address_size, + op2 - 1, op2); + printf (" %s..\n", a1); + printf (" %s\n", a2); + free (a1); + free (a2); + } + break; + + case DW_RLE_base_address: + if (address_size == 4) + { + if ((uint64_t) (nexthdr - readp) < 4) + goto invalid_range; + op1 = read_4ubyte_unaligned_inc (dbg, readp); + } + else + { + if ((uint64_t) (nexthdr - readp) < 8) + goto invalid_range; + op1 = read_8ubyte_unaligned_inc (dbg, readp); + } + base = op1; + printf (" 0x%" PRIx64 "\n", base); + if (! print_unresolved_addresses) + { + a1 = format_dwarf_addr (dwflmod, address_size, base, base); + printf (" %s\n", a1); + free (a1); + } + break; + + case DW_RLE_start_end: + if (address_size == 4) + { + if ((uint64_t) (nexthdr - readp) < 8) + goto invalid_range; + op1 = read_4ubyte_unaligned_inc (dbg, readp); + op2 = read_4ubyte_unaligned_inc (dbg, readp); + } + else + { + if ((uint64_t) (nexthdr - readp) < 16) + goto invalid_range; + op1 = read_8ubyte_unaligned_inc (dbg, readp); + op2 = read_8ubyte_unaligned_inc (dbg, readp); + } + printf (" 0x%" PRIx64 "..0x%" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + a1 = format_dwarf_addr (dwflmod, address_size, op1, op1); + a2 = format_dwarf_addr (dwflmod, address_size, + op2 - 1, op2); + printf (" %s..\n", a1); + printf (" %s\n", a2); + free (a1); + free (a2); + } + break; + + case DW_RLE_start_length: + if (address_size == 4) + { + if ((uint64_t) (nexthdr - readp) < 4) + goto invalid_range; + op1 = read_4ubyte_unaligned_inc (dbg, readp); + } + else + { + if ((uint64_t) (nexthdr - readp) < 8) + goto invalid_range; + op1 = read_8ubyte_unaligned_inc (dbg, readp); + } + if ((uint64_t) (nexthdr - readp) < 1) + goto invalid_range; + get_uleb128 (op2, readp, nexthdr); + printf (" 0x%" PRIx64 ", %" PRIx64 "\n", op1, op2); + if (! print_unresolved_addresses) + { + a1 = format_dwarf_addr (dwflmod, address_size, op1, op1); + op2 = op1 + op2; + a2 = format_dwarf_addr (dwflmod, address_size, + op2 - 1, op2); + printf (" %s..\n", a1); + printf (" %s\n", a2); + free (a1); + free (a2); + } + break; + + default: + goto invalid_range; + } + } + + next_table: + if (readp != nexthdr) + { + size_t padding = nexthdr - readp; + printf (gettext (" %zu padding bytes\n\n"), padding); + readp = nexthdr; + } + } +} /* Print content of DWARF .debug_ranges section. */ static void @@ -6317,11 +6759,51 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_AT_ranges: { - bool nlpt = notice_listptr (section_ranges, &known_rangelistptr, + bool nlpt; + if (cbargs->cu->version < 5) + nlpt = notice_listptr (section_ranges, &known_rangelistptr, + cbargs->addrsize, cbargs->offset_size, + cbargs->cu, num, attr); + else + { + /* Only register for a real section offset. Otherwise + it is a DW_FORM_rangelistx which is just an index + number and we should already have registered the + section offset for the index when we saw the + DW_AT_rnglists_base CU attribute. */ + if (form == DW_FORM_sec_offset) + nlpt = notice_listptr (section_ranges, &known_rnglistptr, + cbargs->addrsize, cbargs->offset_size, + cbargs->cu, num, attr); + else + nlpt = true; + } + + if (!cbargs->silent) + { + if (cbargs->cu->version < 5 || form == DW_FORM_sec_offset) + printf (" %*s%-20s (%s) range list [%6" + PRIxMAX "]%s\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num, + nlpt ? "" : " "); + else + printf (" %*s%-20s (%s) range index [%6" + PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num); + } + } + return DWARF_CB_OK; + + case DW_AT_rnglists_base: + { + bool nlpt = notice_listptr (section_ranges, &known_rnglistptr, cbargs->addrsize, cbargs->offset_size, cbargs->cu, num, attr); if (!cbargs->silent) - printf (" %*s%-20s (%s) range list [%6" PRIxMAX "]%s\n", + printf (" %*s%-20s (%s) range list [%6" + PRIxMAX "]%s\n", (int) (level * 2), "", dwarf_attr_name (attr), dwarf_form_name (form), (uintmax_t) num, nlpt ? "" : " "); @@ -6771,13 +7253,21 @@ print_debug_units (Dwfl_Module *dwflmod, } while (level >= 0); - /* We might want to show the split compile unit if this was a skeleton. */ - if (!silent && show_split_units && unit_type == DW_UT_skeleton) + /* We might want to show the split compile unit if this was a skeleton. + We need to scan it if we are requesting printing .debug_ranges for + DWARF4 since GNU DebugFission uses "offsets" into the main ranges + section. */ + if (unit_type == DW_UT_skeleton + && ((!silent && show_split_units) + || (version < 5 && (print_debug_sections & section_ranges) != 0))) { Dwarf_Die subdie; if (dwarf_cu_info (cu, NULL, NULL, NULL, &subdie, NULL, NULL, NULL) != 0 || dwarf_tag (&subdie) == DW_TAG_invalid) - error (0, 0, gettext ("Could not find split compile unit")); + { + if (!silent) + error (0, 0, gettext ("Could not find split compile unit")); + } else { Dwarf_CU *split_cu = subdie.cu; @@ -6785,16 +7275,21 @@ print_debug_units (Dwfl_Module *dwflmod, &addrsize, &offsize, &unit_id, &subdie_off); Dwarf_Off offset = cu->start; - printf (gettext (" Split compilation unit at offset %" PRIu64 ":\n" - " Version: %" PRIu16 - ", Abbreviation section offset: %" PRIu64 - ", Address size: %" PRIu8 - ", Offset size: %" PRIu8 "\n"), - (uint64_t) offset, version, abbroffset, addrsize, offsize); - printf (gettext (" Unit type: %s (%" PRIu8 ")"), - dwarf_unit_name (unit_type), unit_type); - printf (", Unit id: 0x%.16" PRIx64 "", unit_id); - printf ("\n"); + if (!silent) + { + printf (gettext (" Split compilation unit at offset %" + PRIu64 ":\n" + " Version: %" PRIu16 + ", Abbreviation section offset: %" PRIu64 + ", Address size: %" PRIu8 + ", Offset size: %" PRIu8 "\n"), + (uint64_t) offset, version, abbroffset, + addrsize, offsize); + printf (gettext (" Unit type: %s (%" PRIu8 ")"), + dwarf_unit_name (unit_type), unit_type); + printf (", Unit id: 0x%.16" PRIx64 "", unit_id); + printf ("\n"); + } unit_type = DW_UT_split_compile; is_split = true; @@ -9248,6 +9743,9 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) NEW_SECTION (macinfo), NEW_SECTION (macro), NEW_SECTION (ranges), + /* rnglists is ranges for DWARF5. */ + { ".debug_rnglists", section_ranges, + print_debug_rnglists_section }, { ".eh_frame", section_frame | section_exception, print_debug_frame_section }, { ".eh_frame_hdr", section_frame | section_exception, @@ -9291,6 +9789,7 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) reset_listptr (&known_loclistptr); reset_listptr (&known_rangelistptr); + reset_listptr (&known_rnglistptr); } diff --git a/tests/ChangeLog b/tests/ChangeLog index e6f9959..7a4ccf0 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +2018-04-11 Mark Wielaard + + * run-readelf-ranges.sh: New test. + * Makefile.am (TESTS): Add run-readelf-ranges.sh. + (EXTRA_DIST): Likewise. + 2018-05-21 Mark Wielaard * addrx_constx-4.dwo.bz2: New testfile. diff --git a/tests/Makefile.am b/tests/Makefile.am index 4b13be2..54a3d1d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -97,7 +97,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-varlocs-self.sh run-exprlocs-self.sh \ run-readelf-test1.sh run-readelf-test2.sh run-readelf-test3.sh \ run-readelf-test4.sh run-readelf-twofiles.sh \ - run-readelf-macro.sh run-readelf-loc.sh \ + run-readelf-macro.sh run-readelf-loc.sh run-readelf-ranges.sh \ run-readelf-aranges.sh run-readelf-line.sh run-readelf-z.sh \ run-native-test.sh run-bug1-test.sh \ run-debuglink.sh run-debugaltlink.sh run-buildid.sh \ @@ -234,6 +234,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ testfile-macros-0xff.bz2 \ run-readelf-macro.sh testfilemacro.bz2 \ run-readelf-loc.sh testfileloc.bz2 \ + run-readelf-ranges.sh \ run-readelf-aranges.sh run-readelf-line.sh testfilefoobarbaz.bz2 \ testfile-ppc64-min-instr.bz2 \ testfile-dwarf-45.source \ diff --git a/tests/run-readelf-ranges.sh b/tests/run-readelf-ranges.sh new file mode 100755 index 0000000..160ef58 --- /dev/null +++ b/tests/run-readelf-ranges.sh @@ -0,0 +1,236 @@ +#! /bin/sh +# Copyright (C) 2018 Red Hat, Inc. +# This file is part of elfutils. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# elfutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +. $srcdir/test-subr.sh + +# See run-readelf-loc.sh + +testfiles testfileloc + +# Process values as offsets from base addresses and resolve to symbols. +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=ranges testfileloc<<\EOF + +DWARF section [34] '.debug_ranges' at offset 0xd94: + + CU [ b] base: 0x0000000000400480
+ [ 0] range 0, 2 + 0x0000000000400480
.. + 0x0000000000400481 + range 5, d + 0x0000000000400485 .. + 0x000000000040048c + + CU [ e0] base: 0x00000000004004a0 + [ 30] range d, f + 0x00000000004004ad .. + 0x00000000004004ae + range 12, 1a + 0x00000000004004b2 .. + 0x00000000004004b9 +EOF + +# Don't resolve addresses to symbols. +testrun_compare ${abs_top_builddir}/src/readelf -N --debug-dump=ranges testfileloc<<\EOF + +DWARF section [34] '.debug_ranges' at offset 0xd94: + + CU [ b] base: 0x0000000000400480 + [ 0] range 0, 2 + 0x0000000000400480.. + 0x0000000000400481 + range 5, d + 0x0000000000400485.. + 0x000000000040048c + + CU [ e0] base: 0x00000000004004a0 + [ 30] range d, f + 0x00000000004004ad.. + 0x00000000004004ae + range 12, 1a + 0x00000000004004b2.. + 0x00000000004004b9 +EOF + +# Produce "raw" unprocessed content. +testrun_compare ${abs_top_builddir}/src/readelf -U --debug-dump=ranges testfileloc<<\EOF + +DWARF section [34] '.debug_ranges' at offset 0xd94: + + CU [ b] base: 0x0000000000400480 + [ 0] range 0, 2 + range 5, d + + CU [ e0] base: 0x00000000004004a0 + [ 30] range d, f + range 12, 1a +EOF + +# .debug_rnglists (DWARF5), see tests/testfile-dwarf-45.source +testfiles testfile-dwarf-5 +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=ranges testfile-dwarf-5<<\EOF + +DWARF section [33] '.debug_rnglists' at offset 0x1d9a: +Table at Offset 0x0: + + Length: 45 + DWARF version: 5 + Address size: 8 + Segment size: 0 + Offset entries: 0 + CU [ 218] base: 000000000000000000 + + Offset: c, Index: 0 + base_address 0x400583 + 0x0000000000400583 + offset_pair 0, 2 + 0x0000000000400583 .. + 0x0000000000400584 + offset_pair 5, 15 + 0x0000000000400588 .. + 0x0000000000400597 + end_of_list + + Offset: 1c, Index: 10 + start_length 0x400570, 2b + 0x0000000000400570 .. + 0x000000000040059a + start_length 0x400410, 20 + 0x0000000000400410
.. + 0x000000000040042f + end_of_list + +EOF + +# Same as above, but for DWARF4, note no header, and base address is not +# given, but ranges are the same. +testfiles testfile-dwarf-4 +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=ranges testfile-dwarf-4<<\EOF + +DWARF section [32] '.debug_ranges' at offset 0x1f96: + + CU [ 21c] base: 000000000000000000 + [ 0] range 400583, 400585 + 0x0000000000400583 .. + 0x0000000000400584 + range 400588, 400598 + 0x0000000000400588 .. + 0x0000000000400597 + [ 30] range 400570, 40059b + 0x0000000000400570 .. + 0x000000000040059a + range 400410, 400430 + 0x0000000000400410
.. + 0x000000000040042f +EOF + +# Now with split dwarf. See tests/testfile-dwarf-45.source. +# Note that this will have an offsets table that the .dwo can refer to. +testfiles testfile-splitdwarf-5 testfile-hello5.dwo testfile-world5.dwo +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=ranges testfile-splitdwarf-5<<\EOF + +DWARF section [35] '.debug_rnglists' at offset 0x393a: +Table at Offset 0x0: + + Length: 53 + DWARF version: 5 + Address size: 8 + Segment size: 0 + Offset entries: 2 + CU [ 49] base: 000000000000000000 + + Offsets starting at 0xc: + [ 0] 0x8 + [ 1] 0x18 + + Offset: 14, Index: 8 + base_address 0x4011d3 + 0x00000000004011d3 + offset_pair 0, 2 + 0x00000000004011d3 .. + 0x00000000004011d4 + offset_pair 5, 15 + 0x00000000004011d8 .. + 0x00000000004011e7 + end_of_list + + Offset: 24, Index: 18 + start_length 0x4011c0, 2b + 0x00000000004011c0 .. + 0x00000000004011ea + start_length 0x401060, 20 + 0x0000000000401060
.. + 0x000000000040107f + end_of_list + +EOF + +# Note that the rnglist_base attribute of the second CU points to the offsets +# above 0xc [c]. +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=info testfile-splitdwarf-5<<\EOF + +DWARF section [28] '.debug_info' at offset 0x3102: + [Offset] + Compilation unit at offset 0: + Version: 5, Abbreviation section offset: 0, Address size: 8, Offset size: 4 + Unit type: skeleton (4), Unit id: 0xc422aa5c31fec205 + [ 14] skeleton_unit abbrev: 1 + low_pc (addr) 0x0000000000401160 + high_pc (data8) 81 (0x00000000004011b1) + stmt_list (sec_offset) 0 + dwo_name (strp) "testfile-hello5.dwo" + comp_dir (strp) "/home/mark/src/elfutils/tests" + GNU_pubnames (flag_present) yes + addr_base (sec_offset) 8 + Compilation unit at offset 53: + Version: 5, Abbreviation section offset: 21, Address size: 8, Offset size: 4 + Unit type: skeleton (4), Unit id: 0xb6c8b9d97e6dfdfe + [ 49] skeleton_unit abbrev: 1 + ranges (sec_offset) range list [ 24] + low_pc (addr) 000000000000000000 + stmt_list (sec_offset) 655 + dwo_name (strp) "testfile-world5.dwo" + comp_dir (strp) "/home/mark/src/elfutils/tests" + GNU_pubnames (flag_present) yes + addr_base (sec_offset) 168 + rnglists_base (sec_offset) range list [ c] +EOF + +# Same for DWARF4 GNU DebugFission. But now we need to scan the .dwo +# explicitly to know it will use the first ranges. +testfiles testfile-splitdwarf-4 testfile-hello4.dwo testfile-world4.dwo +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=ranges testfile-splitdwarf-4<<\EOF + +DWARF section [32] '.debug_ranges' at offset 0x3611: + + CU [ b] base: 000000000000000000 + [ 0] range 4011d3, 4011d5 + 0x00000000004011d3 .. + 0x00000000004011d4 + range 4011d8, 4011e8 + 0x00000000004011d8 .. + 0x00000000004011e7 + + CU [ 3f] base: 000000000000000000 + [ 30] range 4011c0, 4011eb + 0x00000000004011c0 .. + 0x00000000004011ea + range 401060, 401080 + 0x0000000000401060
.. + 0x000000000040107f +EOF + +exit 0 -- 1.8.3.1