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
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,
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#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)
{
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);
[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]))
__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);
dwfl_frame_reg;
dwfl_report_offline_memory;
} ELFUTILS_0.186;
+
+ELFUTILS_0.191 {
+ global:
+ dwarf_cu_dwp_section_info;
+} ELFUTILS_0.188;
DWARF_E_NOT_CUDIE,
DWARF_E_UNKNOWN_LANGUAGE,
DWARF_E_NO_DEBUG_ADDR,
+ DWARF_E_UNKNOWN_SECTION,
};
/* 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
} 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
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;
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)
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)
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);
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;
/backtrace-dwarf
/buildid
/core-dump-backtrace.lock
+/cu-dwp-section-info
/debugaltlink
/debuginfod_build_id_find
/debuglink
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 \
$(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
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
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.
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <dwarf.h>
+#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;
+}
--- /dev/null
+#! /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 <http://www.gnu.org/licenses/>.
+
+. $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
--- /dev/null
+# 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