Hi Mark, On Wed, Apr 1, 2026 at 8:55 AM Mark Wielaard <[email protected]> wrote: > > DWARF Package files (.dwp) might contain a .debug_cu_index and/or > .debug_tu_index section. Print the contents of these sections. Handles > version 2 (DWARF4 + GNU Debugfission extension), version 5 > (standardized) and version 6 (prelimenary, adds 64bit offsets). Use > the existing dwp4 and dwp5 testfiles. > > * src/readelf.c (options): Add cu_index to debug-dump docs. > (section_e): Add section_cu_index. > (section_all): Likewise. > (parse_opt): handle "cu_index" arg for debug-dump. > (dwarf_section_short_string): New static function. > (print_cu_index_section): Likewise. > (print_debug): Move up implicit_info and explicit_info. Only > search for skeleton file if .debug_info is needed. Handle both > .debug_cu_index and .debug_tu_index through > print_cu_index_section. > * tests/run-readelf-cu-index.sh: New test. > * tests/Makefile.am (TESTS): Add run-readelf-cu-index.sh. > (EXTRA_DIST): Likewise. > > Signed-off-by: Mark Wielaard <[email protected]> > --- > > v2 changes: > - The offset size field is only available in version 6. > - print_cu_index_section actually use off_bytes to print 32/64 bit offsets. > - Use fprint (out, ...) consistently. > - Remove Offset Size: headers in tests.
LGTM. Aaron > > src/readelf.c | 284 +++++++++++++++++++++++++++++++++- > tests/Makefile.am | 2 + > tests/run-readelf-cu-index.sh | 95 ++++++++++++ > 3 files changed, 373 insertions(+), 8 deletions(-) > create mode 100755 tests/run-readelf-cu-index.sh > > diff --git a/src/readelf.c b/src/readelf.c > index 6d4cde9f59c5..0258d41ae16d 100644 > --- a/src/readelf.c > +++ b/src/readelf.c > @@ -1,6 +1,6 @@ > /* Print information from ELF file in human-readable form. > Copyright (C) 1999-2018 Red Hat, Inc. > - Copyright (C) 2023, 2025 Mark J. Wielaard <[email protected]> > + Copyright (C) 2023, 2025, 2026 Mark J. Wielaard <[email protected]> > This file is part of elfutils. > > This file is free software; you can redistribute it and/or modify > @@ -141,8 +141,9 @@ 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, addr, " > - "aranges, decodedaranges, frame, gdb_index, info, info+, loc, line, " > - "decodedline, ranges, pubnames, str, macinfo, macro or exception"), 0 > }, > + "aranges, cu_index, decodedaranges, frame, gdb_index, info, info+, " > + "loc, line, decodedline, ranges, pubnames, str, macinfo, macro or " > + "exception"), 0 }, > { "hex-dump", 'x', "SECTION", 0, > N_("Dump the uninterpreted contents of SECTION, by number or name"), 0 }, > { "strings", 'p', "SECTION", OPTION_ARG_OPTIONAL, > @@ -288,11 +289,13 @@ static enum section_e > section_macro = 4096, /* .debug_macro */ > section_addr = 8192, /* .debug_addr */ > section_types = 16384, /* .debug_types (implied by .debug_info) */ > + section_cu_index = 32768, /* .debug_cu_index (include .debug_tu_index) > */ > 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_addr | section_types) > + | section_macro | section_addr | section_types > + | section_cu_index) > } print_debug_sections, implicit_debug_sections; > > /* Select hex dumping of sections. */ > @@ -789,6 +792,8 @@ parse_opt (int key, char *arg, > print_debug_sections |= section_exception; > else if (strcmp (arg, "gdb_index") == 0) > print_debug_sections |= section_gdb_index; > + else if (strcmp (arg, "cu_index") == 0) > + print_debug_sections |= section_cu_index; > else > { > fprintf (stderr, _("Unknown DWARF debug section `%s'.\n"), > @@ -4874,6 +4879,48 @@ dwarf_line_content_description_string (unsigned int > kind) > } > > > +/* Doesn't use dwarf_section_name because we want short (max 6 chars) > + lowercase strings and result depends on version. */ > +static const char * > +dwarf_section_short_string (unsigned int vers, unsigned kind) > +{ > + const char *sec_str = NULL; > + if (vers == 2 && (kind == 7 || kind == 8)) /* macinfo or macro */ > + sec_str = "macro"; > + else > + { > + switch (kind) > + { > + case DW_SECT_INFO: > + sec_str = "info"; > + break; > + case DW_SECT_TYPES: > + sec_str = "types"; /* Only really valid for version 2. */ > + break; > + case DW_SECT_ABBREV: > + sec_str = "abbrv"; > + break; > + case DW_SECT_LINE: > + sec_str = "line"; > + break; > + case DW_SECT_LOCLISTS: > + sec_str = "locs"; > + break; > + case DW_SECT_STR_OFFSETS: > + sec_str = "stroff"; > + break; > + case DW_SECT_MACRO: > + sec_str = "macro"; > + break; > + case DW_SECT_RNGLISTS: > + sec_str = "rngs"; > + break; > + } > + } > + > + return sec_str; > +} > + > /* Used by all dwarf_foo_name functions. */ > static const char * > string_or_unknown (const char *known, unsigned int code, > @@ -12176,6 +12223,222 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl > *ebl, > fprintf (out, "<unknown>\n"); > } > > +/* Print the content of the '.debug_cu_index' or '.debug_tu_index' > + sections. */ > +static void > +print_cu_index_section (Dwfl_Module *dwflmod __attribute__ ((unused)), > + Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), > + Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg, > + FILE *out) > +{ > + const char *sname = section_name (ebl, shdr); > + fprintf (out, _("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 > + " contains %" PRId64 " bytes :\n"), > + elf_ndxscn (scn), sname, > + (uint64_t) shdr->sh_offset, (uint64_t) shdr->sh_size); > + > + Elf_Data *data = elf_rawdata (scn, NULL); > + > + if (unlikely (data == NULL)) > + { > + error (0, 0, _("cannot get %s content: %s"), sname, elf_errmsg (-1)); > + return; > + } > + > + bool is_tu = sname != NULL && strcmp (sname, ".debug_tu_index") == 0; > + > + const unsigned char *readp = data->d_buf; > + const unsigned char *const dataend = readp + data->d_size; > + > + if (unlikely (readp > dataend - 4)) > + { > + invalid_data: > + error (0, 0, _("invalid data")); > + return; > + } > + > + /* If read as 4 bytes the version is 2, it is GNU DebugFission for > + DWARF 5, otherwise the version is in the first 2 bytes, with 2 > + bytes padding. */ > + int32_t vers = read_4ubyte_unaligned (dbg, readp); > + if (vers != 2) > + vers = read_2ubyte_unaligned (dbg, readp); > + fprintf (out, _(" Version: %8" PRId32 "\n"), vers); > + > + /* There used to be a version 1, which we don't support, but is > + described at https://gcc.gnu.org/wiki/DebugFissionDWP > + Version 2 is GNU DebugFission for DWARF4. > + Version 5 is the standardized DWARF5 index. > + Version 6 supports DWARF64 as described in > + https://dwarfstd.org/issues/220708.2.html */ > + if (vers != 2 && vers != 5 && vers != 6) > + { > + error (0, 0, _("unknown version, cannot parse section")); > + return; > + } > + > + /* The offset size field is only available in version 6. */ > + uint8_t offset_size_flag = 0; > + if (vers < 6) > + readp += 4; > + else > + { > + readp += 3; > + offset_size_flag = *readp; > + fprintf (out, _(" Offset Size: %4" PRIu8 "\n"), > + offset_size_flag == 0 ? 32 : 64); > + readp++; > + } > + > + const unsigned char off_bytes = offset_size_flag == 0 ? 4 : 8; > + > + if (unlikely (readp > dataend - 4)) > + goto invalid_data; > + uint32_t section_count = read_4ubyte_unaligned (dbg, readp); > + fprintf (out, _(" Columns: %8" PRId32 "\n"), section_count); > + > + readp += 4; > + if (unlikely (readp > dataend - 4)) > + goto invalid_data; > + uint32_t unit_count = read_4ubyte_unaligned (dbg, readp); > + fprintf (out, _(" Entries: %8" PRId32 "\n"), unit_count); > + > + readp += 4; > + if (unlikely (readp > dataend - 4)) > + goto invalid_data; > + uint32_t slot_count = read_4ubyte_unaligned (dbg, readp); > + fprintf (out, _(" Slots: %8" PRId32 "\n"), slot_count); > + > + /* Really should be slot_count > 3 * unit_count / 2, but accept as > + long as slot_count is at least unit_count. */ > + if (slot_count < unit_count) > + { > + error (0, 0, _("Must have at least as many slots as entries")); > + return; > + } > + > + readp += 4; > + > + /* Hash table starts directly after header (16 bytes). */ > + const unsigned char *hash_table = readp; > + /* Indices (which slot is used for each hash id entry) start after > + the hash table (ids of 8 bytes). */ > + const unsigned char *indices = hash_table + slot_count * 8; > + /* Sections used starts after the indices, indices and hash table > + have the same number of slots, indeces are 4 bytes each, */ > + const unsigned char *sections = indices + slot_count * 4; > + /* Offset slots for each section follow the one row of sections. */ > + const unsigned char *offsets = sections + section_count * 4; > + /* Size slots for each section follow the offsets (used rows). */ > + const unsigned char *lengths = (offsets + > + unit_count * section_count * off_bytes); > + const unsigned char *lengths_end = lengths + section_count * 4; > + > + /* Sanity check the above against dataend. */ > + if ((slot_count > UINT32_MAX / 8) > + || (section_count > SIZE_MAX / off_bytes) > + || (unit_count > SIZE_MAX / off_bytes) > + || ((unit_count != 0) && (section_count > SIZE_MAX / unit_count)) > + || ((section_count != 0) && (unit_count > SIZE_MAX / section_count)) > + || (indices > dataend) > + || (sections > dataend) > + || (offsets > dataend) > + || (lengths > dataend) > + || (lengths_end > dataend)) > + goto invalid_data; > + > + fprintf (out, _("\n Offset table\n")); > + fprintf (out, " slot %s", is_tu ? "tu sig" : "dwo id"); > + fprintf (out, " "); > + for (size_t i = 0; i < section_count; i++) > + { > + uint32_t section = read_4ubyte_unaligned (dbg, sections + i * 4); > + const char *sec_str = dwarf_section_short_string (vers, section); > + if (sec_str == NULL) > + fprintf (out, " ??? %2x", section); > + else > + fprintf (out, " %6s", sec_str); > + } > + fprintf (out, "\n"); > + > + for (size_t i = 0; i < slot_count; i++) > + { > + uint64_t id; > + uint32_t row; > + id = read_8ubyte_unaligned (dbg, hash_table + i * 8); > + row = read_4ubyte_unaligned (dbg, indices + i * 4); > + /* Only print used rows. */ > + if (id != 0 && row != 0) > + { > + fprintf (out, " [%3zd] %016" PRIx64 " ", i, id); > + if (row > unit_count) > + { > + error (0, 0, _("Row (%" PRIu32 ") larger than " > + "unit count (%" PRIu32 ")"), row, unit_count); > + continue; > + } > + /* Note row is one based, not zero based. */ > + const unsigned char *prow = (offsets > + + ((row - 1) * section_count > + * off_bytes)); > + if (off_bytes == 4) > + for (size_t j = 0; j < section_count; j++) > + { > + uint32_t off = read_4ubyte_unaligned (dbg, prow + j * 4); > + fprintf (out, " %6" PRIu32, off); > + } > + else > + for (size_t j = 0; j < section_count; j++) > + { > + uint64_t off = read_4ubyte_unaligned (dbg, prow + j * 8); > + fprintf (out, " %6" PRIu64, off); > + } > + fprintf (out, "\n"); > + } > + } > + > + fprintf (out, _("\n Size table\n")); > + fprintf (out, " slot %s", is_tu ? "tu sig" : "dwo id"); > + fprintf (out, " "); > + for (size_t i = 0; i < section_count; i++) > + { > + uint32_t section = read_4ubyte_unaligned (dbg, sections + i * 4); > + const char *sec_str = dwarf_section_short_string (vers, section); > + if (sec_str == NULL) > + fprintf (out, " ??? %2x", section); > + else > + fprintf (out, " %6s", sec_str); > + } > + fprintf (out, "\n"); > + > + for (size_t i = 0; i < slot_count; i++) > + { > + uint64_t id; > + uint32_t row; > + id = read_8ubyte_unaligned (dbg, hash_table + i * 8); > + row = read_4ubyte_unaligned (dbg, indices + i * 4); > + /* Only print used rows. */ > + if (id != 0 && row != 0) > + { > + fprintf (out, " [%3zd] %016" PRIx64 " ", i, id); > + if (row > unit_count) > + { > + error (0, 0, _("Row (%" PRIu32 ") larger than " > + "unit count (%" PRIu32 ")"), row, unit_count); > + continue; > + } > + /* Note row is one based, not zero based. */ > + const unsigned char *prow = lengths + (row - 1) * section_count * 4; > + for (size_t j = 0; j < section_count; j++) > + { > + uint32_t off = read_4ubyte_unaligned (dbg, prow + j * 4); > + fprintf (out, " %6" PRIu32, off); > + } > + fprintf (out, "\n"); > + } > + } > +} > + > /* Returns true and sets split DWARF CU id if there is a split compile > unit in the given Dwarf, and no non-split units are found (before it). */ > static bool > @@ -12293,6 +12556,11 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, > GElf_Ehdr *ehdr) > Dwarf *split_dbg = NULL; > Dwarf_CU *split_cu = NULL; > > + /* If we need to implicitly or explicitly scan the debug_info section > + we might need a bit more info, like a skeleton for split dwarf file). > */ > + bool implicit_info = (implicit_debug_sections & section_info) != 0; > + bool explicit_info = (print_debug_sections & section_info) != 0; > + > /* Before we start the real work get a debug context descriptor. */ > Dwarf_Addr dwbias; > Dwarf *dbg = dwfl_module_getdwarf (dwflmod, &dwbias); > @@ -12308,7 +12576,7 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, > GElf_Ehdr *ehdr) > dwfl_errmsg (-1)); > dbg = &dummy_dbg; > } > - else > + else if (implicit_info || explicit_info) > { > /* If we are asked about a split dwarf (.dwo) file, use the user > provided, or find the corresponding skeleton file. If we got > @@ -12461,8 +12729,6 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, > GElf_Ehdr *ehdr) > we must make sure to handle it before handling any other debug > section. Various other sections depend on the CU DIEs being > scanned (silently) first. */ > - bool implicit_info = (implicit_debug_sections & section_info) != 0; > - bool explicit_info = (print_debug_sections & section_info) != 0; > if (implicit_info) > { > Elf_Scn *scn = NULL; > @@ -12545,7 +12811,9 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, > GElf_Ehdr *ehdr) > print_debug_frame_hdr_section }, > { ".gcc_except_table", section_frame | section_exception, > print_debug_exception_table }, > - { ".gdb_index", section_gdb_index, print_gdb_index_section } > + { ".gdb_index", section_gdb_index, print_gdb_index_section }, > + { ".debug_cu_index", section_cu_index, print_cu_index_section }, > + { ".debug_tu_index", section_cu_index, print_cu_index_section }, > }; > const int ndebug_sections = (sizeof (debug_sections) > / sizeof (debug_sections[0])); > diff --git a/tests/Makefile.am b/tests/Makefile.am > index 3702444d820f..9a005416a398 100644 > --- a/tests/Makefile.am > +++ b/tests/Makefile.am > @@ -149,6 +149,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh > newfile test-nlist \ > run-nm-self.sh run-readelf-self.sh run-readelf-info-plus.sh \ > run-readelf-compressed.sh \ > run-readelf-const-values.sh \ > + run-readelf-cu-index.sh \ > 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 \ > @@ -368,6 +369,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ > run-readelf-compressed.sh \ > run-readelf-compressed-zstd.sh \ > run-readelf-const-values.sh testfile-const-values.debug.bz2 \ > + run-readelf-cu-index.sh \ > run-addrcfi.sh run-dwarfcfi.sh \ > testfile11-debugframe.bz2 testfile12-debugframe.bz2 \ > testfileaarch64-debugframe.bz2 testfilearm-debugframe.bz2 \ > diff --git a/tests/run-readelf-cu-index.sh b/tests/run-readelf-cu-index.sh > new file mode 100755 > index 000000000000..37642a11b0d7 > --- /dev/null > +++ b/tests/run-readelf-cu-index.sh > @@ -0,0 +1,95 @@ > +#! /bin/sh > +# Copyright (C) 2026 Mark J. Wielaard <[email protected]> > +# 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 <http://www.gnu.org/licenses/>. > + > +. $srcdir/test-subr.sh > + > +# See testfile-dwp.source. > +testfiles testfile-dwp-4.dwp testfile-dwp-5.dwp > + > +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=cu_index > testfile-dwp-4.dwp <<\EOF > + > +DWARF section [ 9] '.debug_cu_index' at offset 0x6c80 contains 376 bytes : > + Version: 2 > + Columns: 6 > + Entries: 3 > + Slots: 16 > + > + Offset table > + slot dwo id info abbrv line locs stroff macro > + [ 0] 947a8b559fb59920 404 585 164 283 3748 3469 > + [ 5] afdbe8f5b7425c95 286 370 82 0 1876 1735 > + [ 9] 0682b8eb2e720699 0 0 0 0 0 0 > + > + Size table > + slot dwo id info abbrv line locs stroff macro > + [ 0] 947a8b559fb59920 453 414 83 241 1908 1735 > + [ 5] afdbe8f5b7425c95 118 215 82 0 1872 1734 > + [ 9] 0682b8eb2e720699 286 370 82 283 1876 1735 > + > +DWARF section [10] '.debug_tu_index' at offset 0x6df8 contains 328 bytes : > + Version: 2 > + Columns: 6 > + Entries: 2 > + Slots: 16 > + > + Offset table > + slot tu sig types abbrv line locs stroff macro > + [ 12] 063b4ae40c9316fc 111 370 82 0 1876 1735 > + [ 14] f35612db645f377e 0 0 0 0 0 0 > + > + Size table > + slot tu sig types abbrv line locs stroff macro > + [ 12] 063b4ae40c9316fc 109 215 82 0 1872 1734 > + [ 14] f35612db645f377e 111 370 82 283 1876 1735 > +EOF > + > +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=cu_index > testfile-dwp-5.dwp<<\EOF > + > +DWARF section [10] '.debug_tu_index' at offset 0x6ca1 contains 204 bytes : > + Version: 5 > + Columns: 7 > + Entries: 2 > + Slots: 4 > + > + Offset table > + slot tu sig info abbrv line locs stroff macro rngs > + [ 0] 063b4ae40c9316fc 376 352 127 0 1884 1734 0 > + [ 2] f35612db645f377e 0 0 0 0 0 0 0 > + > + Size table > + slot tu sig info abbrv line locs stroff macro rngs > + [ 0] 063b4ae40c9316fc 110 202 127 0 1880 1733 0 > + [ 2] f35612db645f377e 112 352 127 219 1884 1734 34 > + > +DWARF section [11] '.debug_cu_index' at offset 0x6d6d contains 308 bytes : > + Version: 5 > + Columns: 7 > + Entries: 3 > + Slots: 8 > + > + Offset table > + slot dwo id info abbrv line locs stroff macro rngs > + [ 2] 2970268d42208082 606 554 254 219 3764 3467 34 > + [ 3] 225988b4ba73f4b3 112 0 0 0 0 0 0 > + [ 6] 1082731073ccdfbe 486 352 127 0 1884 1734 0 > + > + Size table > + slot dwo id info abbrv line locs stroff macro rngs > + [ 2] 2970268d42208082 403 394 129 201 1916 1734 67 > + [ 3] 225988b4ba73f4b3 264 352 127 219 1884 1734 34 > + [ 6] 1082731073ccdfbe 120 202 127 0 1880 1733 0 > +EOF > -- > 2.53.0 >
