From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 68834 invoked by alias); 28 May 2018 14:32:41 -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 68815 invoked by uid 89); 28 May 2018 14:32:40 -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=Display 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; Mon, 28 May 2018 14:32:37 +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 03E9A30008B6; Mon, 28 May 2018 16:32:33 +0200 (CEST) Received: by tarox.wildebeest.org (Postfix, from userid 1000) id B8E53413CB92; Mon, 28 May 2018 16:32:33 +0200 (CEST) From: Mark Wielaard To: elfutils-devel@sourceware.org Cc: Mark Wielaard Subject: [PATCH] readelf handle .debug_addr section. Date: Mon, 28 May 2018 14:32:00 -0000 Message-Id: <1527517947-17573-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/msg00093.txt.bz2 Add debug-dump=addr which will show the .debug_addr section tables. The only tricky bit is the fact that GNU DebugFission, a DWARF4 extension, didn't produce unit table headers. So if we see a mixed DWARF4/5 .debug_addr table we have to reconstruct the table length from the CU DIE DW_AT_[GNU_]_addr_base offsets. Signed-off-by: Mark Wielaard --- src/ChangeLog | 12 ++ src/readelf.c | 267 +++++++++++++++++++++++++++++++++++++++++++- tests/ChangeLog | 7 ++ tests/Makefile.am | 2 + tests/run-readelf-addr.sh | 143 ++++++++++++++++++++++++ tests/run-readelf-ranges.sh | 4 +- 6 files changed, 431 insertions(+), 4 deletions(-) create mode 100755 tests/run-readelf-addr.sh diff --git a/src/ChangeLog b/src/ChangeLog index b6c2743..01ecc61 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,15 @@ +2018-04-27 Mark Wielaard + + * readelf.c (options): Add addr. + (enum section_e): Add section_addr. + (section_all): Add section_addr. + (parse_opt): Parse "addr". + (known_addrbases): New static variable. + (get_listptr): New function. + (print_debug_addr_section): Likewise. + (attr_callback): Handle DW_AT_addr_base and DW_AT_GNU_addr_base. + (print_debug): Add NEW_SECTION (addr). Reset known_addrbases. + 2018-04-07 Mark Wielaard * readelf.c (attr_callback): Handle DW_FORM_loclistx and diff --git a/src/readelf.c b/src/readelf.c index 0171673..de38ac6 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -121,7 +121,7 @@ static const struct argp_option options[] = { NULL, 0, NULL, 0, N_("Additional output selection:"), 0 }, { "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL, - N_("Display DWARF section content. SECTION can be one of abbrev, " + N_("Display DWARF section content. SECTION can be one of abbrev, addr, " "aranges, decodedaranges, frame, gdb_index, info, info+, loc, line, " "decodedline, ranges, pubnames, str, macinfo, macro or exception"), 0 }, { "hex-dump", 'x', "SECTION", 0, @@ -248,11 +248,12 @@ static enum section_e section_exception = 1024, /* .eh_frame & al. */ section_gdb_index = 2048, /* .gdb_index */ section_macro = 4096, /* .debug_macro */ + section_addr = 8192, section_all = (section_abbrev | section_aranges | section_frame | section_info | section_line | section_loc | section_pubnames | section_str | section_macinfo | section_ranges | section_exception | section_gdb_index - | section_macro) + | section_macro | section_addr) } print_debug_sections, implicit_debug_sections; /* Select hex dumping of sections. */ @@ -442,6 +443,11 @@ parse_opt (int key, char *arg, } else if (strcmp (arg, "abbrev") == 0) print_debug_sections |= section_abbrev; + else if (strcmp (arg, "addr") == 0) + { + print_debug_sections |= section_addr; + implicit_debug_sections |= section_info; + } else if (strcmp (arg, "aranges") == 0) print_debug_sections |= section_aranges; else if (strcmp (arg, "decodedaranges") == 0) @@ -4817,6 +4823,7 @@ static struct listptr_table known_locsptr; static struct listptr_table known_loclistsptr; static struct listptr_table known_rangelistptr; static struct listptr_table known_rnglistptr; +static struct listptr_table known_addrbases; static void reset_listptr (struct listptr_table *table) @@ -4934,6 +4941,15 @@ next_listptr_offset (struct listptr_table *table, size_t idx) return 0; } +/* Returns the listptr associated with the given index, or NULL. */ +static struct listptr * +get_listptr (struct listptr_table *table, size_t idx) +{ + if (idx >= table->n) + return NULL; + return &table->table[idx]; +} + /* 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. */ @@ -5033,6 +5049,235 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), } +static void +print_debug_addr_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + Ebl *ebl, GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) +{ + printf (gettext ("\ +\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"), + elf_ndxscn (scn), section_name (ebl, ehdr, shdr), + (uint64_t) shdr->sh_offset); + + if (shdr->sh_size == 0) + return; + + /* We like to get the section from libdw to make sure they are relocated. */ + Elf_Data *data = (dbg->sectiondata[IDX_debug_addr] + ?: elf_rawdata (scn, NULL)); + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get .debug_addr section data: %s"), + elf_errmsg (-1)); + return; + } + + size_t idx = 0; + sort_listptr (&known_addrbases, "addr_base"); + + const unsigned char *start = (const unsigned char *) data->d_buf; + const unsigned char *readp = start; + const unsigned char *readendp = ((const unsigned char *) data->d_buf + + data->d_size); + + while (readp < readendp) + { + /* We cannot really know whether or not there is an header. The + DebugFission extension to DWARF4 doesn't add one. The DWARF5 + .debug_addr variant does. Whether or not we have an header, + DW_AT_[GNU_]addr_base points at "index 0". So if the current + offset equals the CU addr_base then we can just start + printing addresses. If there is no CU with an exact match + then we'll try to parse the header first. */ + Dwarf_Off off = (Dwarf_Off) (readp + - (const unsigned char *) data->d_buf); + + printf ("Table at offset %" PRIx64 " ", off); + + struct listptr *listptr = get_listptr (&known_addrbases, idx++); + const unsigned char *next_unitp; + + uint64_t unit_length; + uint16_t version; + uint8_t address_size; + uint8_t segment_size; + if (listptr == NULL) + { + error (0, 0, "Warning: No CU references .debug_addr after %" PRIx64, + off); + + /* We will have to assume it is just addresses to the end... */ + address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + next_unitp = readendp; + printf ("Unknown CU:\n"); + } + else + { + Dwarf_Die cudie; + if (dwarf_cu_die (listptr->cu, &cudie, + NULL, NULL, NULL, NULL, + NULL, NULL) == NULL) + printf ("Unknown CU (%s):\n", dwarf_errmsg (-1)); + else + printf ("for CU [%6" PRIx64 "]:\n", dwarf_dieoffset (&cudie)); + + if (listptr->offset == off) + { + address_size = listptr_address_size (listptr); + segment_size = 0; + version = 4; + + /* The addresses start here, but where do they end? */ + listptr = get_listptr (&known_addrbases, idx); + if (listptr == NULL) + { + next_unitp = readendp; + unit_length = (uint64_t) (next_unitp - readp); + } + else if (listptr->cu->version < 5) + { + next_unitp = start + listptr->offset; + if (listptr->offset < off || listptr->offset > data->d_size) + { + error (0, 0, + "Warning: Bad address base for next unit at %" + PRIx64, off); + next_unitp = readendp; + } + unit_length = (uint64_t) (next_unitp - readp); + } + else + { + /* Tricky, we don't have a header for this unit, but + there is one for the next. We will have to + "guess" how big it is and subtract it from the + offset (because that points after the header). */ + unsigned int offset_size = listptr_offset_size (listptr); + Dwarf_Off next_off = (listptr->offset + - (offset_size == 4 ? 4 : 12) /* len */ + - 2 /* version */ + - 1 /* address size */ + - 1); /* segment selector size */ + next_unitp = start + next_off; + if (next_off < off || next_off > data->d_size) + { + error (0, 0, + "Warning: Couldn't calculate .debug_addr " + " unit lenght at %" PRIx64, off); + next_unitp = readendp; + } + } + + /* Pretend we have a header. */ + printf ("\n"); + printf (gettext (" Length: %8" PRIu64 "\n"), + unit_length); + printf (gettext (" DWARF version: %8" PRIu16 "\n"), version); + printf (gettext (" Address size: %8" PRIu64 "\n"), + (uint64_t) address_size); + printf (gettext (" Segment size: %8" PRIu64 "\n"), + (uint64_t) segment_size); + printf ("\n"); + } + else + { + /* OK, we have to parse an header first. */ + unit_length = read_4ubyte_unaligned_inc (dbg, readp); + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readp > readendp - 8)) + { + invalid_data: + error (0, 0, "Invalid data"); + return; + } + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + } + printf ("\n"); + printf (gettext (" Length: %8" PRIu64 "\n"), + unit_length); + + /* We need at least 2-bytes (version) + 1-byte + (addr_size) + 1-byte (segment_size) = 4 bytes to + complete the header. And this unit cannot go beyond + the section data. */ + if (readp > readendp - 4 + || unit_length < 4 + || unit_length > (uint64_t) (readendp - readp)) + goto invalid_data; + + next_unitp = readp + unit_length; + + 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_unit; + } + + 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_unit; + } + + segment_size = *readp++; + printf (gettext (" Segment size: %8" PRIu64 "\n"), + (uint64_t) segment_size); + printf ("\n"); + + if (segment_size != 0) + { + error (0, 0, gettext ("unsupported segment size")); + goto next_unit; + } + + if (listptr->offset != (Dwarf_Off) (readp - start)) + { + error (0, 0, "Address index doesn't start after header"); + goto next_unit; + } + } + } + + int digits = 1; + size_t addresses = (next_unitp - readp) / address_size; + while (addresses >= 10) + { + ++digits; + addresses /= 10; + } + + unsigned int index = 0; + size_t index_offset = readp - (const unsigned char *) data->d_buf; + printf (" Addresses start at offset 0x%zx:\n", index_offset); + while (readp <= next_unitp - address_size) + { + Dwarf_Addr addr = read_addr_unaligned_inc (address_size, dbg, + readp); + printf (" [%*u] ", digits, index++); + char *a = format_dwarf_addr (dwflmod, address_size, + addr, addr); + printf ("%s\n", a); + free (a); + } + printf ("\n"); + + if (readp != next_unitp) + error (0, 0, "extra %zd bytes at end of unit", + (size_t) (next_unitp - readp)); + + next_unit: + readp = next_unitp; + } +} + /* Print content of DWARF .debug_aranges section. We fortunately do not have to know a bit about the structure of the section, libdwarf takes care of it. */ @@ -6931,6 +7176,22 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) } return DWARF_CB_OK; + case DW_AT_addr_base: + case DW_AT_GNU_addr_base: + { + bool addrbase = notice_listptr (section_addr, &known_addrbases, + cbargs->addrsize, + cbargs->offset_size, + cbargs->cu, num, attr); + if (!cbargs->silent) + printf (" %*s%-20s (%s) address base [%6" + PRIxMAX "]%s\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num, + addrbase ? "" : " "); + } + return DWARF_CB_OK; + case DW_AT_language: valuestr = dwarf_lang_name (num); break; @@ -10473,6 +10734,7 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) #define NEW_SECTION(name) \ { ".debug_" #name, section_##name, print_debug_##name##_section } NEW_SECTION (abbrev), + NEW_SECTION (addr), NEW_SECTION (aranges), NEW_SECTION (frame), NEW_SECTION (info), @@ -10541,6 +10803,7 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) reset_listptr (&known_loclistsptr); reset_listptr (&known_rangelistptr); reset_listptr (&known_rnglistptr); + reset_listptr (&known_addrbases); } diff --git a/tests/ChangeLog b/tests/ChangeLog index 3ed88f6..6991551 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,10 @@ +2018-04-27 Mark Wielaard + + * run-readelf-ranges.sh: Adjust expected output for address base. + * run-readelf-addr.sh: New test. + * Makefile.am (TESTS): Add run-readelf-addr.sh. + (EXTRA_DIST): Likewise. + 2018-04-07 Mark Wielaard * run-varlocs.sh: Run on testfileranges5.debug and diff --git a/tests/Makefile.am b/tests/Makefile.am index 63f077c..e15fe51 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -130,6 +130,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-backtrace-core-aarch64.sh run-backtrace-core-sparc.sh \ run-backtrace-demangle.sh run-stack-d-test.sh run-stack-i-test.sh \ run-stack-demangled-test.sh run-readelf-zx.sh run-readelf-zp.sh \ + run-readelf-addr.sh \ run-readelf-dwz-multi.sh run-allfcts-multi.sh run-deleted.sh \ run-linkmap-cut.sh run-aggregate-size.sh run-peel-type.sh \ vdsosyms run-readelf-A.sh \ @@ -245,6 +246,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ run-readelf-dwz-multi.sh libtestfile_multi_shared.so.bz2 \ testfile_multi.dwz.bz2 testfile_multi_main.bz2 \ testfile-dwzstr.bz2 testfile-dwzstr.multi.bz2 \ + run-readelf-addr.sh \ run-allfcts-multi.sh \ test-offset-loop.bz2 test-offset-loop.alt.bz2 \ run-prelink-addr-test.sh \ diff --git a/tests/run-readelf-addr.sh b/tests/run-readelf-addr.sh new file mode 100755 index 0000000..ea723c2 --- /dev/null +++ b/tests/run-readelf-addr.sh @@ -0,0 +1,143 @@ +#! /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 tests/testfile-dwarf-45.source +testfiles testfile-splitdwarf-4 testfile-splitdwarf-5 + +# DWARF4 GNU DebugFission No real table header. +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=addr testfile-splitdwarf-4<<\EOF + +DWARF section [33] '.debug_addr' at offset 0x3671: +Table at offset 0 for CU [ b]: + + Length: 152 + DWARF version: 4 + Address size: 8 + Segment size: 0 + + Addresses start at offset 0x0: + [ 0] 0x000000000040116a + [ 1] 0x0000000000401189 + [ 2] 0x000000000040118d + [ 3] 0x0000000000401194 + [ 4] 0x0000000000401181 + [ 5] 0x00000000004011af + [ 6] 0x00000000004011b1 + [ 7] 0x00000000004011a0 + [ 8] 0x0000000000401160 + [ 9] 0x00000000004011a0 + [10] 0x000000000040117b + [11] 0x000000000040117b + [12] 0x0000000000401181 + [13] 0x0000000000401181 + [14] 0x000000000040118d + [15] 0x0000000000401160 + [16] 0x0000000000401060
+ [17] 0x000000000040117b + [18] 0x0000000000404038 + +Table at offset 98 for CU [ 3f]: + + Length: 136 + DWARF version: 4 + Address size: 8 + Segment size: 0 + + Addresses start at offset 0x98: + [ 0] 0x00000000004011df + [ 1] 0x00000000004011e4 + [ 2] 0x0000000000401060
+ [ 3] 0x0000000000401071 + [ 4] 0x0000000000401074 + [ 5] 0x0000000000401079 + [ 6] 0x00000000004011d3 + [ 7] 0x0000000000401078 + [ 8] 0x00000000004011a0 + [ 9] 0x0000000000401040 + [10] 0x0000000000401080 <_start> + [11] 0x00000000004011c0 + [12] 0x0000000000401060
+ [13] 0x00000000004011c0 + [14] 0x00000000004011c8 + [15] 0x00000000004011d8 + [16] 0x00000000004011da + +EOF + +# DWARF5 Real table header. +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=addr testfile-splitdwarf-5<<\EOF + +DWARF section [32] '.debug_addr' at offset 0x365e: +Table at offset 0 for CU [ 14]: + + Length: 156 + DWARF version: 5 + Address size: 8 + Segment size: 0 + + Addresses start at offset 0x8: + [ 0] 0x000000000040116a + [ 1] 0x0000000000401189 + [ 2] 0x000000000040118d + [ 3] 0x0000000000401194 + [ 4] 0x0000000000401181 + [ 5] 0x00000000004011af + [ 6] 0x00000000004011b1 + [ 7] 0x00000000004011a0 + [ 8] 0x0000000000401160 + [ 9] 0x00000000004011a0 + [10] 0x000000000040117b + [11] 0x000000000040117b + [12] 0x0000000000401181 + [13] 0x0000000000401181 + [14] 0x000000000040118d + [15] 0x0000000000401160 + [16] 0x0000000000401060
+ [17] 0x000000000040117b + [18] 0x0000000000404038 + +Table at offset a0 for CU [ 49]: + + Length: 140 + DWARF version: 5 + Address size: 8 + Segment size: 0 + + Addresses start at offset 0xa8: + [ 0] 0x00000000004011df + [ 1] 0x00000000004011e4 + [ 2] 0x0000000000401060
+ [ 3] 0x0000000000401071 + [ 4] 0x0000000000401074 + [ 5] 0x0000000000401079 + [ 6] 0x00000000004011d3 + [ 7] 0x0000000000401078 + [ 8] 0x00000000004011a0 + [ 9] 0x0000000000401040 + [10] 0x0000000000401080 <_start> + [11] 0x00000000004011c0 + [12] 0x0000000000401060
+ [13] 0x00000000004011c0 + [14] 0x00000000004011c8 + [15] 0x00000000004011d8 + [16] 0x00000000004011da + +EOF + +exit 0 diff --git a/tests/run-readelf-ranges.sh b/tests/run-readelf-ranges.sh index 160ef58..ca3e302 100755 --- a/tests/run-readelf-ranges.sh +++ b/tests/run-readelf-ranges.sh @@ -194,7 +194,7 @@ DWARF section [28] '.debug_info' at offset 0x3102: dwo_name (strp) "testfile-hello5.dwo" comp_dir (strp) "/home/mark/src/elfutils/tests" GNU_pubnames (flag_present) yes - addr_base (sec_offset) 8 + addr_base (sec_offset) address base [ 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 @@ -205,7 +205,7 @@ DWARF section [28] '.debug_info' at offset 0x3102: dwo_name (strp) "testfile-world5.dwo" comp_dir (strp) "/home/mark/src/elfutils/tests" GNU_pubnames (flag_present) yes - addr_base (sec_offset) 168 + addr_base (sec_offset) address base [ a8] rnglists_base (sec_offset) range list [ c] EOF -- 1.8.3.1