From: Omar Sandoval Date: Wed, 6 Dec 2023 09:22:16 +0000 (-0800) Subject: libdw: Parse DWARF package file index sections X-Git-Tag: elfutils-0.191~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=985112bc7d0f4eabbaa8a33fdcb241832db54642;p=thirdparty%2Felfutils.git libdw: Parse DWARF package file index sections The .debug_cu_index and .debug_tu_index sections in DWARF package files are basically hash tables mapping a unit's 8 byte signature to an offset and size in each section used by that unit [1]. Add support for parsing and doing lookups in the index sections. We look up a unit in the index when we intern it and cache its hash table row in Dwarf_CU. Then, a new function, dwarf_cu_dwp_section_info, can be used to look up the section offsets and sizes for a unit. This will mostly be used internally in libdw, but it will also be needed in static inline functions shared with eu-readelf. Additionally, making it public it makes dwp support much easier for external tools that do their own low-level parsing of DWARF information, like drgn [2]. 1: https://gcc.gnu.org/wiki/DebugFissionDWP#Format_of_the_CU_and_TU_Index_Sections 2: https://github.com/osandov/drgn * libdw/dwarf.h: Add DW_SECT_TYPES. * libdw/libdwP.h (Dwarf): Add cu_index and tu_index. (Dwarf_CU): Add dwp_row. (Dwarf_Package_Index): New type. (__libdw_dwp_find_unit): New declaration. (dwarf_cu_dwp_section_info): New INTDECL. Add DWARF_E_UNKNOWN_SECTION. * libdw/Makefile.am (libdw_a_SOURCES): Add dwarf_cu_dwp_section_info.c. * libdw/dwarf_end.c (dwarf_end): Free dwarf->cu_index and dwarf->tu_index. * libdw/dwarf_error.c (errmsgs): Add DWARF_E_UNKNOWN_SECTION. * libdw/libdw.h (dwarf_cu_dwp_section_info): New declaration. * libdw/libdw.map (ELFUTILS_0.190): Add dwarf_cu_dwp_section_info. * libdw/libdw_findcu.c (__libdw_intern_next_unit): Call __libdw_dwp_find_unit, and use it to adjust abbrev_offset and assign newp->dwp_row. * libdw/dwarf_cu_dwp_section_info.c: New file. * tests/Makefile.am (check_PROGRAMS): Add cu-dwp-section-info. (TESTS): Add run-cu-dwp-section-info.sh (EXTRA_DIST): Add run-cu-dwp-section-info.sh and new test files. (cu_dwp_section_info_LDADD): New variable. * tests/cu-dwp-section-info.c: New test. * tests/run-cu-dwp-section-info.sh: New test. * tests/testfile-dwp-4-strict.bz2: New test file. * tests/testfile-dwp-4-strict.dwp.bz2: New test file. * tests/testfile-dwp-4.bz2: New test file. * tests/testfile-dwp-4.dwp.bz2: New test file. * tests/testfile-dwp-5.bz2: New test file. * tests/testfile-dwp-5.dwp.bz2: New test file. * tests/testfile-dwp.source: New file. Signed-off-by: Omar Sandoval --- diff --git a/libdw/Makefile.am b/libdw/Makefile.am index e548f38c..5363c02a 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -93,7 +93,7 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_cu_die.c dwarf_peel_type.c dwarf_default_lower_bound.c \ dwarf_die_addr_die.c dwarf_get_units.c \ libdw_find_split_unit.c dwarf_cu_info.c \ - dwarf_next_lines.c + dwarf_next_lines.c dwarf_cu_dwp_section_info.c if MAINTAINER_MODE BUILT_SOURCES = $(srcdir)/known-dwarf.h diff --git a/libdw/dwarf.h b/libdw/dwarf.h index b2e49db2..4be32de5 100644 --- a/libdw/dwarf.h +++ b/libdw/dwarf.h @@ -942,7 +942,7 @@ enum enum { DW_SECT_INFO = 1, - /* Reserved = 2, */ + DW_SECT_TYPES = 2, /* Only DWARF4 GNU DebugFission. Reserved in DWARF5. */ DW_SECT_ABBREV = 3, DW_SECT_LINE = 4, DW_SECT_LOCLISTS = 5, diff --git a/libdw/dwarf_cu_dwp_section_info.c b/libdw/dwarf_cu_dwp_section_info.c new file mode 100644 index 00000000..4a4eac8c --- /dev/null +++ b/libdw/dwarf_cu_dwp_section_info.c @@ -0,0 +1,371 @@ +/* Read DWARF package file index sections. + Copyright (c) 2023 Meta Platforms, Inc. and affiliates. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "libdwP.h" + +static Dwarf_Package_Index * +__libdw_read_package_index (Dwarf *dbg, bool tu) +{ + Elf_Data *data; + if (tu) + data = dbg->sectiondata[IDX_debug_tu_index]; + else + data = dbg->sectiondata[IDX_debug_cu_index]; + + /* We need at least 16 bytes for the header. */ + if (data == NULL || data->d_size < 16) + { + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return NULL; + } + + const unsigned char *datap = data->d_buf; + const unsigned char *endp = datap + data->d_size; + uint16_t version; + /* In GNU DebugFission for DWARF 4, the version is 2 as a uword. In the + standardized DWARF 5 format, it is a uhalf followed by a padding uhalf. + Check for both. */ + if (read_4ubyte_unaligned (dbg, datap) == 2) + version = 2; + else + { + version = read_2ubyte_unaligned (dbg, datap); + if (version != 5) + { + __libdw_seterrno (DWARF_E_VERSION); + return NULL; + } + } + datap += 4; + uint32_t section_count = read_4ubyte_unaligned_inc (dbg, datap); + uint32_t unit_count = read_4ubyte_unaligned_inc (dbg, datap); + uint32_t slot_count = read_4ubyte_unaligned_inc (dbg, datap); + + /* The specification has a stricter requirement that + slot_count > 3 * unit_count / 2, but this is enough for us. */ + if (slot_count < unit_count) + goto invalid; + + /* After the header, the section must contain: + + 8 byte signature per hash table slot + + 4 byte index per hash table slot + + Section offset table with 1 header row, 1 row per unit, 1 column per + section, 4 bytes per field + + Section size table with 1 row per unit, 1 column per section, 4 bytes + per field + + We have to be careful about overflow when checking this. */ + const unsigned char *hash_table = datap; + if ((size_t) (endp - hash_table) < (uint64_t) slot_count * 12) + goto invalid; + const unsigned char *indices = hash_table + (size_t) slot_count * 8; + const unsigned char *sections = indices + (size_t) slot_count * 4; + if ((size_t) (endp - sections) < (uint64_t) section_count * 4) + goto invalid; + const unsigned char *section_offsets = sections + (size_t) section_count * 4; + if ((uint64_t) unit_count * section_count > UINT64_MAX / 8 + || ((size_t) (endp - section_offsets) + < (uint64_t) unit_count * section_count * 8)) + goto invalid; + const unsigned char *section_sizes + = section_offsets + (uint64_t) unit_count * section_count * 4; + + Dwarf_Package_Index *index = malloc (sizeof (*index)); + if (index == NULL) + { + __libdw_seterrno (DWARF_E_NOMEM); + return NULL; + } + + index->dbg = dbg; + /* Set absent sections to UINT32_MAX. */ + memset (index->sections, 0xff, sizeof (index->sections)); + for (size_t i = 0; i < section_count; i++) + { + uint32_t section = read_4ubyte_unaligned (dbg, sections + i * 4); + /* 2 is DW_SECT_TYPES in version 2 and reserved in version 5. We ignore + it for version 5. + 5 is DW_SECT_LOC in version 2 and DW_SECT_LOCLISTS in version 5. We + use the same index for both. + 7 is DW_SECT_MACINFO in version 2 and DW_SECT_MACRO in version 5. We + use the same index for both. + 8 is DW_SECT_MACRO in version 2 and DW_SECT_RNGLISTS in version 5. We + use the same index for version 2's DW_SECT_MACRO as version 2's + DW_SECT_MACINFO/version 5's DW_SECT_MACRO. + We ignore unknown sections. */ + if (section == 0) + continue; + if (version == 2) + { + if (section > 8) + continue; + else if (section == 8) + section = DW_SECT_MACRO; + } + else if (section == 2 + || (section + > sizeof (index->sections) / sizeof (index->sections[0]))) + continue; + index->sections[section - 1] = i; + } + + /* DW_SECT_INFO (or DW_SECT_TYPES for DWARF 4 type units) and DW_SECT_ABBREV + are required. */ + if (((!tu || dbg->sectiondata[IDX_debug_types] == NULL) + && index->sections[DW_SECT_INFO - 1] == UINT32_MAX) + || (tu && dbg->sectiondata[IDX_debug_types] != NULL + && index->sections[DW_SECT_TYPES - 1] == UINT32_MAX) + || index->sections[DW_SECT_ABBREV - 1] == UINT32_MAX) + { + free (index); + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return NULL; + } + + index->section_count = section_count; + index->unit_count = unit_count; + index->slot_count = slot_count; + index->last_unit_found = 0; + index->hash_table = hash_table; + index->indices = indices; + index->section_offsets = section_offsets; + index->section_sizes = section_sizes; + + return index; +} + +static Dwarf_Package_Index * +__libdw_package_index (Dwarf *dbg, bool tu) +{ + if (tu && dbg->tu_index != NULL) + return dbg->tu_index; + else if (!tu && dbg->cu_index != NULL) + return dbg->cu_index; + + Dwarf_Package_Index *index = __libdw_read_package_index (dbg, tu); + if (index == NULL) + return NULL; + + if (tu) + dbg->tu_index = index; + else + dbg->cu_index = index; + return index; +} + +static int +__libdw_dwp_unit_row (Dwarf_Package_Index *index, uint64_t unit_id, + uint32_t *unit_rowp) +{ + if (index == NULL) + return -1; + + uint32_t hash = unit_id; + uint32_t hash2 = (unit_id >> 32) | 1; + /* Only check each slot once. */ + for (uint32_t n = index->slot_count; n-- > 0; ) + { + size_t slot = hash & (index->slot_count - 1); + uint64_t sig = read_8ubyte_unaligned (index->dbg, + index->hash_table + slot * 8); + if (sig == unit_id) + { + uint32_t row = read_4ubyte_unaligned (index->dbg, + index->indices + slot * 4); + if (row > index->unit_count) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + *unit_rowp = row; + return 0; + } + else if (sig == 0 + && read_4ubyte_unaligned (index->dbg, + index->indices + slot * 4) == 0) + break; + hash += hash2; + } + *unit_rowp = 0; + return 0; +} + +static int +__libdw_dwp_section_info (Dwarf_Package_Index *index, uint32_t unit_row, + unsigned int section, Dwarf_Off *offsetp, + Dwarf_Off *sizep) +{ + if (index == NULL) + return -1; + if (unit_row == 0) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + if (index->sections[section - 1] == UINT32_MAX) + { + if (offsetp != NULL) + *offsetp = 0; + if (sizep != NULL) + *sizep = 0; + return 0; + } + size_t i = (size_t)(unit_row - 1) * index->section_count + + index->sections[section - 1]; + if (offsetp != NULL) + *offsetp = read_4ubyte_unaligned (index->dbg, + index->section_offsets + i * 4); + if (sizep != NULL) + *sizep = read_4ubyte_unaligned (index->dbg, + index->section_sizes + i * 4); + return 0; +} + +int +internal_function +__libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off, + uint16_t version, uint8_t unit_type, uint64_t unit_id8, + uint32_t *unit_rowp, Dwarf_Off *abbrev_offsetp) +{ + if (version >= 5 + && unit_type != DW_UT_split_compile && unit_type != DW_UT_split_type) + { + not_dwp: + *unit_rowp = 0; + *abbrev_offsetp = 0; + return 0; + } + bool tu = unit_type == DW_UT_split_type || debug_types; + if (dbg->sectiondata[tu ? IDX_debug_tu_index : IDX_debug_cu_index] == NULL) + goto not_dwp; + Dwarf_Package_Index *index = __libdw_package_index (dbg, tu); + if (index == NULL) + return -1; + + /* This is always called for ascending offsets. The most obvious way for a + producer to generate the section offset table is sorted by offset; both + GNU dwp and llvm-dwp do this. In this common case, we can avoid the full + lookup. */ + if (index->last_unit_found < index->unit_count) + { + Dwarf_Off offset, size; + if (__libdw_dwp_section_info (index, index->last_unit_found + 1, + debug_types ? DW_SECT_TYPES : DW_SECT_INFO, + &offset, &size) != 0) + return -1; + if (offset <= off && off - offset < size) + { + *unit_rowp = ++index->last_unit_found; + goto done; + } + else + /* The units are not sorted. Don't try again. */ + index->last_unit_found = index->unit_count; + } + + if (version >= 5 || debug_types) + { + /* In DWARF 5 and in type units, the unit signature is available in the + unit header. */ + if (__libdw_dwp_unit_row (index, unit_id8, unit_rowp) != 0) + return -1; + } + else + { + /* In DWARF 4 compilation units, the unit signature is an attribute. We + can't parse attributes in the split unit until we get the abbreviation + table offset from the package index, which is a chicken-and-egg + problem. We could get the signature from the skeleton unit, but that + may not be available. + + Instead, we resort to a linear scan through the section offset table. + Finding all units is therefore quadratic in the number of units. + However, this will likely never be needed in practice because of the + sorted fast path above. If this ceases to be the case, we can try to + plumb through the skeleton unit's signature when it is available, or + build a sorted lookup table for binary search. */ + if (index->sections[DW_SECT_INFO - 1] == UINT32_MAX) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + for (uint32_t i = 0; i < index->unit_count; i++) + { + Dwarf_Off offset, size; + __libdw_dwp_section_info (index, i + 1, DW_SECT_INFO, &offset, + &size); + if (offset <= off && off - offset < size) + { + *unit_rowp = i + 1; + goto done; + } + } + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + + done: + return __libdw_dwp_section_info (index, *unit_rowp, DW_SECT_ABBREV, + abbrev_offsetp, NULL); +} + +int +dwarf_cu_dwp_section_info (Dwarf_CU *cu, unsigned int section, + Dwarf_Off *offsetp, Dwarf_Off *sizep) +{ + if (cu == NULL) + return -1; + if (section < DW_SECT_INFO || section > DW_SECT_RNGLISTS) + { + __libdw_seterrno (DWARF_E_UNKNOWN_SECTION); + return -1; + } + if (cu->dwp_row == 0) + { + if (offsetp != NULL) + *offsetp = 0; + if (sizep != NULL) + *sizep = 0; + return 0; + } + else + { + Dwarf_Package_Index *index + = cu->unit_type == DW_UT_split_compile + ? cu->dbg->cu_index : cu->dbg->tu_index; + return __libdw_dwp_section_info (index, cu->dwp_row, section, offsetp, + sizep); + } +} +INTDEF(dwarf_cu_dwp_section_info) diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index e51d5dd7..b7f817d9 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -77,6 +77,9 @@ dwarf_end (Dwarf *dwarf) { if (dwarf != NULL) { + free (dwarf->tu_index); + free (dwarf->cu_index); + if (dwarf->cfi != NULL) /* Clean up the CFI cache. */ __libdw_destroy_frame_cache (dwarf->cfi); diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c index 46ea16b3..0123cfa2 100644 --- a/libdw/dwarf_error.c +++ b/libdw/dwarf_error.c @@ -102,6 +102,7 @@ static const char *errmsgs[] = [DWARF_E_NOT_CUDIE] = N_("not a CU (unit) DIE"), [DWARF_E_UNKNOWN_LANGUAGE] = N_("unknown language code"), [DWARF_E_NO_DEBUG_ADDR] = N_(".debug_addr section missing"), + [DWARF_E_UNKNOWN_SECTION] = N_("unknown section"), }; #define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0])) diff --git a/libdw/libdw.h b/libdw/libdw.h index 64d1689a..545ad043 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -1081,6 +1081,29 @@ extern int dwarf_frame_register (Dwarf_Frame *frame, int regno, __nonnull_attribute__ (3, 4, 5); +/* Return offset and/or size of CU's contribution to SECTION in a DWARF package + file. + + If CU is not from a DWARF package file, the file does not have SECTION, or CU + does not contribute to SECTION, then *SIZEP is set to 0. + + SECTION is a DW_SECT section identifier. Note that the original GNU DWARF + package file extension for DWARF 4 used slightly different section + identifiers. This function uses the standardized section identifiers and + maps the GNU DWARF 4 identifiers to their standard DWARF 5 analogues: + DW_SECT_LOCLISTS (5) refers to .debug_locs.dwo for DWARF 4. + DW_SECT_MACRO (7) refers to .debug_macinfo.dwo for DWARF 4 or + .debug_macro.dwo for the GNU .debug_macro extension for DWARF 4 (section + identifier 8 is DW_SECT_RNGLISTS in DWARF 5, NOT DW_SECT_MACRO like in the + GNU extension.) + .debug_types.dwo does not have a DWARF 5 equivalent, so this function accepts + the original DW_SECT_TYPES (2). + + Returns 0 for success or -1 for errors. OFFSETP and SIZEP may be NULL. */ +extern int dwarf_cu_dwp_section_info (Dwarf_CU *cu, unsigned int section, + Dwarf_Off *offsetp, Dwarf_Off *sizep); + + /* Return error code of last failing function call. This value is kept separately for each thread. */ extern int dwarf_errno (void); diff --git a/libdw/libdw.map b/libdw/libdw.map index 5331ad45..3c5ce8dc 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -373,3 +373,8 @@ ELFUTILS_0.188 { dwfl_frame_reg; dwfl_report_offline_memory; } ELFUTILS_0.186; + +ELFUTILS_0.191 { + global: + dwarf_cu_dwp_section_info; +} ELFUTILS_0.188; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 5aca9082..674ea91e 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -147,6 +147,7 @@ enum DWARF_E_NOT_CUDIE, DWARF_E_UNKNOWN_LANGUAGE, DWARF_E_NO_DEBUG_ADDR, + DWARF_E_UNKNOWN_SECTION, }; @@ -231,6 +232,11 @@ struct Dwarf /* Cached info from the CFI section. */ struct Dwarf_CFI_s *cfi; + /* DWARF package file CU index section. */ + struct Dwarf_Package_Index_s *cu_index; + /* DWARF package file TU index section. */ + struct Dwarf_Package_Index_s *tu_index; + /* Fake loc CU. Used when synthesizing attributes for Dwarf_Ops that came from a location list entry in dwarf_getlocation_attr. Depending on version this is the .debug_loc or .debug_loclists @@ -343,6 +349,23 @@ struct Dwarf_Aranges_s } info[0]; }; +/* DWARF package file unit index. */ +typedef struct Dwarf_Package_Index_s +{ + Dwarf *dbg; + uint32_t section_count; + uint32_t unit_count; + uint32_t slot_count; + /* Mapping from DW_SECT_* - 1 to column number in the section tables, or + UINT32_MAX if not present. */ + uint32_t sections[DW_SECT_RNGLISTS]; + /* Row number of last unit found in the index. */ + uint32_t last_unit_found; + const unsigned char *hash_table; + const unsigned char *indices; + const unsigned char *section_offsets; + const unsigned char *section_sizes; +} Dwarf_Package_Index; /* CU representation. */ struct Dwarf_CU @@ -350,6 +373,8 @@ struct Dwarf_CU Dwarf *dbg; Dwarf_Off start; Dwarf_Off end; + /* Row number of this unit in DWARF package file index. */ + uint32_t dwp_row; uint8_t address_size; uint8_t offset_size; uint16_t version; @@ -421,6 +446,7 @@ INTDECL (dwarf_attr_integrate) INTDECL (dwarf_begin) INTDECL (dwarf_begin_elf) INTDECL (dwarf_child) +INTDECL (dwarf_cu_dwp_section_info) INTDECL (dwarf_default_lower_bound) INTDECL (dwarf_dieoffset) INTDECL (dwarf_diename) @@ -727,6 +753,13 @@ extern struct Dwarf *__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr) extern struct Dwarf_CU *__libdw_find_split_unit (Dwarf_CU *cu) internal_function; +/* Find a unit in a DWARF package file for __libdw_intern_next_unit. */ +extern int __libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off, + uint16_t version, uint8_t unit_type, + uint64_t unit_id8, uint32_t *unit_rowp, + Dwarf_Off *abbrev_offsetp) + __nonnull_attribute__ (1, 7, 8) internal_function; + /* Get abbreviation with given code. */ extern Dwarf_Abbrev *__libdw_findabbrev (struct Dwarf_CU *cu, unsigned int code) diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c index ed744231..6c7dcfb5 100644 --- a/libdw/libdw_findcu.c +++ b/libdw/libdw_findcu.c @@ -143,6 +143,13 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types) if (unlikely (*offsetp > data->d_size)) *offsetp = data->d_size; + uint32_t dwp_row; + Dwarf_Off dwp_abbrev_offset; + if (__libdw_dwp_find_unit (dbg, debug_types, oldoff, version, unit_type, + unit_id8, &dwp_row, &dwp_abbrev_offset) != 0) + return NULL; + abbrev_offset += dwp_abbrev_offset; + /* Create an entry for this CU. */ struct Dwarf_CU *newp = libdw_typed_alloc (dbg, struct Dwarf_CU); @@ -150,6 +157,7 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types) newp->sec_idx = sec_idx; newp->start = oldoff; newp->end = *offsetp; + newp->dwp_row = dwp_row; newp->address_size = address_size; newp->offset_size = offset_size; newp->version = version; diff --git a/tests/.gitignore b/tests/.gitignore index d00a883e..d38f0f9e 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -28,6 +28,7 @@ /backtrace-dwarf /buildid /core-dump-backtrace.lock +/cu-dwp-section-info /debugaltlink /debuginfod_build_id_find /debuglink diff --git a/tests/Makefile.am b/tests/Makefile.am index b075e3c3..d7c53144 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -63,6 +63,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ getphdrnum leb128 read_unaligned \ msg_tst system-elf-libelf-test system-elf-gelf-test \ nvidia_extended_linemap_libdw elf-print-reloc-syms \ + cu-dwp-section-info \ $(asm_TESTS) asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ @@ -212,7 +213,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ $(asm_TESTS) run-disasm-bpf.sh run-low_high_pc-dw-form-indirect.sh \ run-nvidia-extended-linemap-libdw.sh run-nvidia-extended-linemap-readelf.sh \ run-readelf-dw-form-indirect.sh run-strip-largealign.sh \ - run-readelf-Dd.sh run-dwfl-core-noncontig.sh + run-readelf-Dd.sh run-dwfl-core-noncontig.sh run-cu-dwp-section-info.sh if !BIARCH export ELFUTILS_DISABLE_BIARCH = 1 @@ -635,7 +636,11 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ run-funcretval++11.sh \ test-ar-duplicates.a.bz2 \ run-dwfl-core-noncontig.sh testcore-noncontig.bz2 \ - testfile-dwarf5-line-clang.bz2 + testfile-dwarf5-line-clang.bz2 \ + testfile-dwp-4.bz2 testfile-dwp-4.dwp.bz2 \ + testfile-dwp-4-strict.bz2 testfile-dwp-4-strict.dwp.bz2 \ + testfile-dwp-5.bz2 testfile-dwp-5.dwp.bz2 testfile-dwp.source \ + run-cu-dwp-section-info.sh if USE_VALGRIND @@ -812,6 +817,7 @@ leb128_LDADD = $(libelf) $(libdw) read_unaligned_LDADD = $(libelf) $(libdw) nvidia_extended_linemap_libdw_LDADD = $(libelf) $(libdw) elf_print_reloc_syms_LDADD = $(libelf) +cu_dwp_section_info_LDADD = $(libdw) # We want to test the libelf headers against the system elf.h header. # Don't include any -I CPPFLAGS. Except when we install our own elf.h. diff --git a/tests/cu-dwp-section-info.c b/tests/cu-dwp-section-info.c new file mode 100644 index 00000000..f1756979 --- /dev/null +++ b/tests/cu-dwp-section-info.c @@ -0,0 +1,73 @@ +/* Test program for dwarf_cu_dwp_section_info + Copyright (c) 2023 Meta Platforms, Inc. and affiliates. + 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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include ELFUTILS_HEADER(dw) + +int +main (int argc, char *argv[]) +{ + for (int i = 1; i < argc; i++) + { + printf ("file: %s\n", argv[i]); + int fd = open (argv[i], O_RDONLY); + Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ); + if (dbg == NULL) + { + printf ("%s not usable: %s\n", argv[i], dwarf_errmsg (-1)); + return -1; + } + + Dwarf_CU *cu = NULL; + while (dwarf_get_units (dbg, cu, &cu, NULL, NULL, NULL, NULL) == 0) + { +#define SECTION_INFO(section) do { \ + printf (#section ": "); \ + Dwarf_Off offset, size; \ + if (dwarf_cu_dwp_section_info (cu, DW_SECT_##section, \ + &offset, &size) == 0) \ + printf ("0x%" PRIx64 " 0x%" PRIx64 "\n", offset, size); \ + else \ + printf ("%s\n", dwarf_errmsg (-1)); \ + } while (0) + SECTION_INFO (INFO); + SECTION_INFO (TYPES); + SECTION_INFO (ABBREV); + SECTION_INFO (LINE); + SECTION_INFO (LOCLISTS); + SECTION_INFO (STR_OFFSETS); + SECTION_INFO (MACRO); + SECTION_INFO (RNGLISTS); + printf ("\n"); + } + + dwarf_end (dbg); + close (fd); + } + + return 0; +} diff --git a/tests/run-cu-dwp-section-info.sh b/tests/run-cu-dwp-section-info.sh new file mode 100755 index 00000000..202319c6 --- /dev/null +++ b/tests/run-cu-dwp-section-info.sh @@ -0,0 +1,168 @@ +#! /bin/sh +# Copyright (c) 2023 Meta Platforms, Inc. and affiliates. +# 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 testfile-dwp.source. +testfiles testfile-dwp-5.dwp testfile-dwp-4.dwp testfile-dwp-4-strict.dwp + +testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-5.dwp << EOF +file: testfile-dwp-5.dwp +INFO: 0x0 0x70 +TYPES: 0x0 0x0 +ABBREV: 0x0 0x160 +LINE: 0x0 0x7f +LOCLISTS: 0x0 0xdb +STR_OFFSETS: 0x0 0x75c +MACRO: 0x0 0x6c6 +RNGLISTS: 0x0 0x22 + +INFO: 0x70 0x108 +TYPES: 0x0 0x0 +ABBREV: 0x0 0x160 +LINE: 0x0 0x7f +LOCLISTS: 0x0 0xdb +STR_OFFSETS: 0x0 0x75c +MACRO: 0x0 0x6c6 +RNGLISTS: 0x0 0x22 + +INFO: 0x178 0x6e +TYPES: 0x0 0x0 +ABBREV: 0x160 0xca +LINE: 0x7f 0x7f +LOCLISTS: 0x0 0x0 +STR_OFFSETS: 0x75c 0x758 +MACRO: 0x6c6 0x6c5 +RNGLISTS: 0x0 0x0 + +INFO: 0x1e6 0x78 +TYPES: 0x0 0x0 +ABBREV: 0x160 0xca +LINE: 0x7f 0x7f +LOCLISTS: 0x0 0x0 +STR_OFFSETS: 0x75c 0x758 +MACRO: 0x6c6 0x6c5 +RNGLISTS: 0x0 0x0 + +INFO: 0x25e 0x193 +TYPES: 0x0 0x0 +ABBREV: 0x22a 0x18a +LINE: 0xfe 0x81 +LOCLISTS: 0xdb 0xc9 +STR_OFFSETS: 0xeb4 0x77c +MACRO: 0xd8b 0x6c6 +RNGLISTS: 0x22 0x43 + +EOF + +testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-4.dwp << EOF +file: testfile-dwp-4.dwp +INFO: 0x0 0x11e +TYPES: 0x0 0x0 +ABBREV: 0x0 0x172 +LINE: 0x0 0x52 +LOCLISTS: 0x0 0x11b +STR_OFFSETS: 0x0 0x754 +MACRO: 0x0 0x6c7 +RNGLISTS: 0x0 0x0 + +INFO: 0x11e 0x76 +TYPES: 0x0 0x0 +ABBREV: 0x172 0xd7 +LINE: 0x52 0x52 +LOCLISTS: 0x0 0x0 +STR_OFFSETS: 0x754 0x750 +MACRO: 0x6c7 0x6c6 +RNGLISTS: 0x0 0x0 + +INFO: 0x194 0x1c5 +TYPES: 0x0 0x0 +ABBREV: 0x249 0x19e +LINE: 0xa4 0x53 +LOCLISTS: 0x11b 0xf1 +STR_OFFSETS: 0xea4 0x774 +MACRO: 0xd8d 0x6c7 +RNGLISTS: 0x0 0x0 + +INFO: 0x0 0x0 +TYPES: 0x0 0x6f +ABBREV: 0x0 0x172 +LINE: 0x0 0x52 +LOCLISTS: 0x0 0x11b +STR_OFFSETS: 0x0 0x754 +MACRO: 0x0 0x6c7 +RNGLISTS: 0x0 0x0 + +INFO: 0x0 0x0 +TYPES: 0x6f 0x6d +ABBREV: 0x172 0xd7 +LINE: 0x52 0x52 +LOCLISTS: 0x0 0x0 +STR_OFFSETS: 0x754 0x750 +MACRO: 0x6c7 0x6c6 +RNGLISTS: 0x0 0x0 + +EOF + +testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-4-strict.dwp << EOF +file: testfile-dwp-4-strict.dwp +INFO: 0x0 0x105 +TYPES: 0x0 0x0 +ABBREV: 0x0 0x15f +LINE: 0x0 0x52 +LOCLISTS: 0x0 0xe2 +STR_OFFSETS: 0x0 0x24 +MACRO: 0x0 0x38e4 +RNGLISTS: 0x0 0x0 + +INFO: 0x105 0x72 +TYPES: 0x0 0x0 +ABBREV: 0x15f 0xd3 +LINE: 0x52 0x52 +LOCLISTS: 0x0 0x0 +STR_OFFSETS: 0x24 0x20 +MACRO: 0x38e4 0x38db +RNGLISTS: 0x0 0x0 + +INFO: 0x177 0x17b +TYPES: 0x0 0x0 +ABBREV: 0x232 0x157 +LINE: 0xa4 0x53 +LOCLISTS: 0xe2 0xb1 +STR_OFFSETS: 0x44 0x44 +MACRO: 0x71bf 0x38f5 +RNGLISTS: 0x0 0x0 + +INFO: 0x0 0x0 +TYPES: 0x0 0x6e +ABBREV: 0x0 0x15f +LINE: 0x0 0x52 +LOCLISTS: 0x0 0xe2 +STR_OFFSETS: 0x0 0x24 +MACRO: 0x0 0x38e4 +RNGLISTS: 0x0 0x0 + +INFO: 0x0 0x0 +TYPES: 0x6e 0x6b +ABBREV: 0x15f 0xd3 +LINE: 0x52 0x52 +LOCLISTS: 0x0 0x0 +STR_OFFSETS: 0x24 0x20 +MACRO: 0x38e4 0x38db +RNGLISTS: 0x0 0x0 + +EOF diff --git a/tests/testfile-dwp-4-strict.bz2 b/tests/testfile-dwp-4-strict.bz2 new file mode 100755 index 00000000..4f419c07 Binary files /dev/null and b/tests/testfile-dwp-4-strict.bz2 differ diff --git a/tests/testfile-dwp-4-strict.dwp.bz2 b/tests/testfile-dwp-4-strict.dwp.bz2 new file mode 100644 index 00000000..8cc1bbe2 Binary files /dev/null and b/tests/testfile-dwp-4-strict.dwp.bz2 differ diff --git a/tests/testfile-dwp-4.bz2 b/tests/testfile-dwp-4.bz2 new file mode 100755 index 00000000..ece5817b Binary files /dev/null and b/tests/testfile-dwp-4.bz2 differ diff --git a/tests/testfile-dwp-4.dwp.bz2 b/tests/testfile-dwp-4.dwp.bz2 new file mode 100644 index 00000000..1a63b42d Binary files /dev/null and b/tests/testfile-dwp-4.dwp.bz2 differ diff --git a/tests/testfile-dwp-5.bz2 b/tests/testfile-dwp-5.bz2 new file mode 100755 index 00000000..2c768eac Binary files /dev/null and b/tests/testfile-dwp-5.bz2 differ diff --git a/tests/testfile-dwp-5.dwp.bz2 b/tests/testfile-dwp-5.dwp.bz2 new file mode 100644 index 00000000..ba28e501 Binary files /dev/null and b/tests/testfile-dwp-5.dwp.bz2 differ diff --git a/tests/testfile-dwp.source b/tests/testfile-dwp.source new file mode 100644 index 00000000..b0b0a97c --- /dev/null +++ b/tests/testfile-dwp.source @@ -0,0 +1,102 @@ +# Nonsensical program used to generate example DWARF package files with type +# units, location lists, range lists, and macros. + +# = foobar.h = + +struct Foo +{ + int a, b; + int foo (); +}; + +struct Bar +{ + long a, b; + long bar (); +}; + +#define FROB(x) ((x) ^ 0x2a2a2a2a) +#define FRY(x) ((x) * 0x100000001b3) + +inline long +fibonacci (unsigned int n) +{ + if (n == 0) + return 0; + else + { + long a = 0; + long b = 1; + for (unsigned int i = 2; i < n; i++) + { + long tmp = a + b; + a = b; + b = tmp; + } + return b; + } +} + +# = foo.cc = + +#include "foobar.h" + +#define ZERO() (1 - 1) + +int +x_x (int x) +{ + for (int i = x; i > ZERO(); i--) + x *= x; + return x; +} + +int +Foo::foo () +{ + int x = a; + if (a > b) + x -= b; + return FROB (x_x (x)); +} + +# = bar.cc = + +#include "foobar.h" + +#define ONE 1 + +long +Bar::bar () +{ + if (a == b) + return ONE; + else + return a > b ? b : a; +} + +# = main.cc = + +#include "foobar.h" + +#define MAIN_ARGS int argc, char **argv + +int +main(MAIN_ARGS) +{ + struct Foo myfoo { argc, FROB (argc) }; + struct Bar mybar { fibonacci (argc), FRY (argc) }; + return myfoo.foo() + mybar.bar(); +} + +# Built with GCC at commit 80048aa13a6b ("debug/111409 - don't generate COMDAT +# macro sections for split DWARF"). +$ g++ -gdwarf-5 -gsplit-dwarf -fdebug-types-section -g3 -O2 foo.cc bar.cc main.cc -o testfile-dwp-5 +# GNU dwp as of binutils 2.41 only supports DWARF 4. +$ llvm-dwp -e testfile-dwp-5 -o testfile-dwp-5.dwp + +$ g++ -gdwarf-4 -gsplit-dwarf -fdebug-types-section -g3 -O2 foo.cc bar.cc main.cc -o testfile-dwp-4 +$ dwp -e testfile-dwp-4 + +$ g++ -gdwarf-4 -gstrict-dwarf -gsplit-dwarf -fdebug-types-section -g3 -O2 foo.cc bar.cc main.cc -o testfile-dwp-4-strict +$ dwp -e testfile-dwp-4-strict