public inbox for elfutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support
@ 2023-12-06  9:22 Omar Sandoval
  2023-12-06  9:22 ` [PATCH v2 1/4] libdw: Parse DWARF package file index sections Omar Sandoval
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Omar Sandoval @ 2023-12-06  9:22 UTC (permalink / raw)
  To: elfutils-devel

From: Omar Sandoval <osandov@fb.com>

Hi,

This is version 2 of my patch series adding support for DWARF package
files to libdw and the elfutils tools. Version 1 is here [1].

Patches 1-3 add the main implementation and tests for dwp files.

Most of this support is internal to libdw, but patch 1 adds a new public
function, dwarf_cu_dwp_section_info. drgn's dwp branch [2] demonstrates
how that function will be used. Also see [3] for more context on why
drgn needs this.

Patch 4 adds support and tests for an LLVM extension to the dwp format.
The "extension" is ugly because of an oversight in the design of the
format that LLVM had to make the best of, but unfortunately it's
necessary for a lot of our use cases.

With this patch series, drgn's test suite passes against a Linux kernel
build using .dwp.

Changes from v1:

* Rebased on main and dropped patches that were already merged.
* Moved ChangeLog entries to commit messages.
* Updated version in libdw.map to 0.191.
* Moved DW_SECT_TYPES definition to dwarf.h.
* Added copyright years.
* Added error handling for dwarf_cu_dwp_section_info calls in
  str_offsets_base_off, __libdw_cu_ranges_base, and __libdw_cu_locs_base
* Changed memset initialization of index->sections to an explicit
  loop.
* Added comment explaining __libdw_link_skel_split change.

There were a couple of things that were mentioned in review that I
didn't change:

* I kept dwarf_cu_dwp_section_info in patch 1 instead of separating it
  into its own patch so that I could test the dwp index implementation
  in the same commit that I introduced it in.
* I didn't make try_dwp_file return an error since try_split_file that
  it's based on doesn't either.

Thanks!
Omar

1: https://sourceware.org/pipermail/elfutils-devel/2023q3/006410.html
2: https://github.com/osandov/drgn/tree/dwp
3: https://sourceware.org/pipermail/elfutils-devel/2023q4/006630.html

Omar Sandoval (4):
  libdw: Parse DWARF package file index sections
  libdw: Try .dwp file in __libdw_find_split_unit()
  libdw: Apply DWARF package file section offsets where appropriate
  libdw: Handle overflowed DW_SECT_INFO offsets in DWARF package file
    indexes

 libdw/Makefile.am                             |    2 +-
 libdw/dwarf.h                                 |    2 +-
 libdw/dwarf_begin_elf.c                       |    1 +
 libdw/dwarf_cu_dwp_section_info.c             |  531 +++++++
 libdw/dwarf_end.c                             |   24 +-
 libdw/dwarf_error.c                           |    1 +
 libdw/dwarf_getlocation.c                     |    6 +
 libdw/dwarf_getmacros.c                       |   26 +-
 libdw/libdw.h                                 |   23 +
 libdw/libdw.map                               |    5 +
 libdw/libdwP.h                                |  101 +-
 libdw/libdw_find_split_unit.c                 |   75 +-
 libdw/libdw_findcu.c                          |    8 +
 tests/.gitignore                              |    1 +
 tests/Makefile.am                             |   15 +-
 tests/cu-dwp-section-info.c                   |   73 +
 tests/run-all-dwarf-ranges.sh                 |  114 ++
 tests/run-cu-dwp-section-info.sh              |  168 ++
 tests/run-dwarf-getmacros.sh                  | 1412 +++++++++++++++++
 tests/run-get-units-split.sh                  |   18 +
 tests/run-large-elf-file.sh                   |  174 ++
 tests/run-varlocs.sh                          |  112 ++
 tests/testfile-dwp-4-cu-index-overflow.bz2    |  Bin 0 -> 4490 bytes
 .../testfile-dwp-4-cu-index-overflow.dwp.bz2  |  Bin 0 -> 5584 bytes
 tests/testfile-dwp-4-strict.bz2               |  Bin 0 -> 4169 bytes
 tests/testfile-dwp-4-strict.dwp.bz2           |  Bin 0 -> 6871 bytes
 tests/testfile-dwp-4.bz2                      |  Bin 0 -> 4194 bytes
 tests/testfile-dwp-4.dwp.bz2                  |  Bin 0 -> 10098 bytes
 tests/testfile-dwp-5-cu-index-overflow.bz2    |  Bin 0 -> 4544 bytes
 .../testfile-dwp-5-cu-index-overflow.dwp.bz2  |  Bin 0 -> 5790 bytes
 tests/testfile-dwp-5.bz2                      |  Bin 0 -> 4223 bytes
 tests/testfile-dwp-5.dwp.bz2                  |  Bin 0 -> 10313 bytes
 tests/testfile-dwp-cu-index-overflow.source   |   86 +
 tests/testfile-dwp.source                     |  102 ++
 34 files changed, 3051 insertions(+), 29 deletions(-)
 create mode 100644 libdw/dwarf_cu_dwp_section_info.c
 create mode 100644 tests/cu-dwp-section-info.c
 create mode 100755 tests/run-cu-dwp-section-info.sh
 create mode 100755 tests/testfile-dwp-4-cu-index-overflow.bz2
 create mode 100644 tests/testfile-dwp-4-cu-index-overflow.dwp.bz2
 create mode 100755 tests/testfile-dwp-4-strict.bz2
 create mode 100644 tests/testfile-dwp-4-strict.dwp.bz2
 create mode 100755 tests/testfile-dwp-4.bz2
 create mode 100644 tests/testfile-dwp-4.dwp.bz2
 create mode 100755 tests/testfile-dwp-5-cu-index-overflow.bz2
 create mode 100644 tests/testfile-dwp-5-cu-index-overflow.dwp.bz2
 create mode 100755 tests/testfile-dwp-5.bz2
 create mode 100644 tests/testfile-dwp-5.dwp.bz2
 create mode 100644 tests/testfile-dwp-cu-index-overflow.source
 create mode 100644 tests/testfile-dwp.source

-- 
2.43.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 1/4] libdw: Parse DWARF package file index sections
  2023-12-06  9:22 [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support Omar Sandoval
@ 2023-12-06  9:22 ` Omar Sandoval
  2024-02-15 17:40   ` Mark Wielaard
  2023-12-06  9:22 ` [PATCH v2 2/4] libdw: Try .dwp file in __libdw_find_split_unit() Omar Sandoval
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Omar Sandoval @ 2023-12-06  9:22 UTC (permalink / raw)
  To: elfutils-devel

From: Omar Sandoval <osandov@fb.com>

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 <osandov@fb.com>
---
 libdw/Makefile.am                   |   2 +-
 libdw/dwarf.h                       |   2 +-
 libdw/dwarf_cu_dwp_section_info.c   | 371 ++++++++++++++++++++++++++++
 libdw/dwarf_end.c                   |   3 +
 libdw/dwarf_error.c                 |   1 +
 libdw/libdw.h                       |  23 ++
 libdw/libdw.map                     |   5 +
 libdw/libdwP.h                      |  33 +++
 libdw/libdw_findcu.c                |   8 +
 tests/.gitignore                    |   1 +
 tests/Makefile.am                   |  11 +-
 tests/cu-dwp-section-info.c         |  73 ++++++
 tests/run-cu-dwp-section-info.sh    | 168 +++++++++++++
 tests/testfile-dwp-4-strict.bz2     | Bin 0 -> 4169 bytes
 tests/testfile-dwp-4-strict.dwp.bz2 | Bin 0 -> 6871 bytes
 tests/testfile-dwp-4.bz2            | Bin 0 -> 4194 bytes
 tests/testfile-dwp-4.dwp.bz2        | Bin 0 -> 10098 bytes
 tests/testfile-dwp-5.bz2            | Bin 0 -> 4223 bytes
 tests/testfile-dwp-5.dwp.bz2        | Bin 0 -> 10313 bytes
 tests/testfile-dwp.source           | 102 ++++++++
 20 files changed, 798 insertions(+), 5 deletions(-)
 create mode 100644 libdw/dwarf_cu_dwp_section_info.c
 create mode 100644 tests/cu-dwp-section-info.c
 create mode 100755 tests/run-cu-dwp-section-info.sh
 create mode 100755 tests/testfile-dwp-4-strict.bz2
 create mode 100644 tests/testfile-dwp-4-strict.dwp.bz2
 create mode 100755 tests/testfile-dwp-4.bz2
 create mode 100644 tests/testfile-dwp-4.dwp.bz2
 create mode 100755 tests/testfile-dwp-5.bz2
 create mode 100644 tests/testfile-dwp-5.dwp.bz2
 create mode 100644 tests/testfile-dwp.source

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 <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)
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 aef42267..7f8d69b5 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;
@@ -684,6 +709,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)
@@ -1388,6 +1420,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)
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 5bebb2c4..0caabf25 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 2373c980..34014570 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -62,7 +62,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
 		  dwelf_elf_e_machine_string \
 		  getphdrnum leb128 read_unaligned \
 		  msg_tst system-elf-libelf-test system-elf-gelf-test \
-		  nvidia_extended_linemap_libdw \
+		  nvidia_extended_linemap_libdw cu-dwp-section-info \
 		  $(asm_TESTS)
 
 asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
@@ -212,7 +212,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
@@ -634,7 +634,11 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
 	     testfile-largealign.o.bz2 run-strip-largealign.sh \
 	     run-funcretval++11.sh \
 	     test-ar-duplicates.a.bz2 \
-	     run-dwfl-core-noncontig.sh testcore-noncontig.bz2
+	     run-dwfl-core-noncontig.sh testcore-noncontig.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
@@ -810,6 +814,7 @@ getphdrnum_LDADD = $(libelf) $(libdw)
 leb128_LDADD = $(libelf) $(libdw)
 read_unaligned_LDADD = $(libelf) $(libdw)
 nvidia_extended_linemap_libdw_LDADD = $(libelf) $(libdw)
+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 <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;
+}
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 <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
diff --git a/tests/testfile-dwp-4-strict.bz2 b/tests/testfile-dwp-4-strict.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..4f419c07d45aea608e02da002e8c09bc35b3553b
GIT binary patch
literal 4169
zcmV-P5Vr3^T4*^jL0KkKS?l7w2>=>zfB*mg|NsC0|NsC0|NsB*|Nrhk>Fs?-|9$`e
zZp?pw@BQEpe)+iVPBzSvd3tYQL>mutsobk0dyaaM)74v6W)(>1wvOSE5Salc%6T+R
zP3fki(wcftM$(>|A?VtX>Ux?P4K!({o~NYrPgBxuOw`e)jG8oJXafQPlQfMqG|&J6
zpbZ%f9*L)@0g?zb35ha#Xei04q3SgC223N=G-LxnXwU!yAkYA4000^Q0B8UJ0000D
z02*i-WJ#u`kdxH(o~A=h4H`6I0NR=W&<E;)rkVgW8UO$Q05oJ78hV-l!fB%*2AF^V
z$N&HY0001J00w|G02%-QXaE2J01SW)14e)YK)`?ung9R*X_G;aXaH?UMADvVsM?J-
zsigf+NjB9D8Z`9)8fXU8^o;;LO#>j%(Dc**85uMHJwO9M000000000OKuD1^K$wL*
z(V}4{jF^+uY^L;6)bdYJnun+cjW$rxqtrBQK=m_8rf82-)My@|rkWZ6&}h>@G-PPd
zG#WH$G#Unj8tnqtX*FHvqx9}v9ef^W359p6o-jY)A!aNuEn^kS)e}i$H8CU0k<!Nb
zh5@G1$YS?E(hq3RmKGgjTy8uh5`D`+S4bhCLuj?88pX_sz5hSZP4k&THqh>2EM%h8
zjDe9>8!Qz=S}&w1uh7JIMCIk*U#3_cl`Z)3B3{fC<LGyNBtgh7<kZGiH|kYBbwRH`
zpQ379)!xo8K)8Ti=v}AcWU!ECYN^q=?35WW!kZ%~pK7AYC8UxSz=}*Z0J<0&snWz%
z=J8?7p?UfEHY{oGtVbZBYnw|}dGYry?i2fy6<r8P+sQWpB*+W!EdhoFz%am?<t?i{
zGMkudIc+xs3kWSTKoXv5$^v#ejuzV?=XRD@bnD_s#>vLnXI>T*CE+p)NPs+nilq=b
z4F-aSAX(2-axfyc%*Ij>c<SkZj9FRRY0*Rljul>^0|Ryn3y1(<00^jgi>W2Y-#fVb
z@3ZgX<F`4ysNG|yM<5u;6*D6;pa3rQ@30aG8LcLkz8FVUEh?(qa&Cqc7HdxXApxms
zEsiA+L_|a}#6(0l5^g|+Z)s8Kx3T5Ck3}m1ARY(U+{(K7!-GozLMFl?C81Q5*^-id
z_D>d79<SJVl1D0-YdkI|Q<L-h>tH45*n}T*G*B^&n;C%0hFk|`GI`q?dx|s)e9MLA
zFcLHE&h;B-*>cPZ^V<R9dam~Fn6QsZ`nzO`Cr701trtz|PzY?&Gy;<@lJfM)^*S7`
z?=H=^2cwZao()N%2K%z$$rXUW8%R!u(T+$!q?!eo(ga8vv4}tu9aC(CB$1~9goG`a
z1`rWMt<9v&#vw#7G)yxZ8UTQT5a|TBXbZ7msK{lC&g2sVgaDXj#e+6bHV<mt17Hxu
z*=aP!HdN9Pfr%HBLG9WAqYb)hIeYRzD%c?&^`t7E<N|XEEPgmk9#^)G8i!Y_ltWVh
zOQ2vyMiLSh{J?{NU`tb`0me2-%9Ao+5rhK?B(dc-G~pykuQbGxt4<AK!UT!bK!{*g
z;iQ`rX)SHL*iH%9=jlizMl@M@B*9)t8C6T8%dOOncA=Qeco;F*5f7bGc##n4L%b0W
zx^r<_77^ksZ2UZIQj#}OcXbM$6z=EQK-ViEHU7lc&H^C=h9F2{09w2#D;YuSFo!KH
zS`$|_AscUfiy+5~ahEbQcM9%6*Z1{Z6lzH}B<T<+ISNvw8Eb-ER-yxPDiL@PWFTj>
z+6Kbh3aM_HXBMp~kPWewvL7|LmZDz1(IC`mAoxHMQLj(*u6V;GJlO<FBT`+TZPeMl
zV8LQ!%raiOXZ>~!4_mA1FmwC<ZpOaCp1C+;TYV$GI+C+-xObSkd(FR1v4(Yc%?7in
z-b}Fsm_U$(f&~;HkcdfISFdCtXSdd|==e7+CcOg+CQ;GJF<ubVCYuxE`XmrG4eeiQ
zN`(_Gr&NSPlLK(UjU}wmkvY1guC>Gx7?)7RRR|GC+W$WJ)EF8quq<dtUNBrHk!?MK
z8mEHwdKpgJm5_PXPf8mbKYKCqJ*!<M3f^jUE!;5~0G@{!fD7wossvUp`2Z#61sCW}
zjR=!yY+S&^RKZo;KyTi>>|qpQUWbvd?;i_xw((jGlQKPLQO{@Sh}r(nzsbw{-v7CJ
zqP_swlP{#yAU8K+^Wf3|pfF2-;}&KmnqosUs;ap(c#I1Mgll|%u+Rt;P-XNw`4M`A
zlxvVemaYM2TETgANme|KK$70;G}$h@60_O&u=o@>_muQ=f4Zqa3zPs5LV`7u6am2J
zYuRjo3L+2>R<&3(=qJWX6c~!plXxj7FoFFp%5$N@l{Y*6qh(vXz+s@5&ry?vfy0`Y
zy=Bq?R%|Lx>kD0)kmN2D$?{s_25sX;5^$c;0|;jv)UK)rE1~BZlq92<fRn_Jf|uDD
zC})Szl5Nx`7dM~=^KfhR0F7(~%Hi$HNb`xhow`8E!$g9nL6VRcI(?}^c`<0siutsZ
zTX!yv-UlI*Xquc(2Q)U&4s3=_Q7^jj_-5BXGr%H+w9pt>3j0FuZq7kg?}aHemvqvj
z0|P)(xDZjKrx2h(!HKjENa&!r$qiQ0RXhqIR?S;d>^bvAEe2|1B!sNbtVPlIMQb~o
z2d-BEoQhoc2&9T6BJo^lm+jx9S}|-K6v1#Q8G7|W9AfkMds(!q8vR44b8S#mIusGZ
z&RjVqT))>KakQ=Xy~}oOrvTa~s30-J7C@-QiB)cw5vm+@P!L0iJvnB-Ep9@>@j~6M
zxA@ilVncHy0ECk>Vp4HWDFDC-W?c&z>Nxy`)cSq5=heCBdz&acc)Xrg4%(vDrbwrL
zpdFqZK)Hd4?&nz;5^*O`02qv3DuFS08RFTfw)F=L7e$@Cb^t}NoX+4)pw{798g?)_
zN>*YJK(pSv**14qtB!`qHi?mLx=e0iC}b9fZ65#;P0`v{D=63u1Dv<E#e5lrqJWc`
zmW{|uBY0YjJz4`Te%g=-NpxLSb!PtgIrU(3DJ1q>eTyoMi;Mzh0$7tpe-zXNFQ|F3
z=v59Mf2k>FL~QRv=mYCxrRTCejcG2N!-XYYcL4;xWaI*UYz+heAIqlei`lOHa~Au<
z8rw>Er%R#E@x{}^?7hXg_vu~c?);7z0RE@}1S~nA1#f#RnO7wzNhFd<AW$hJlF;Mm
zimjLw`bc3rQv{a*$P#KbsZ>caVTQ6!-Hqj1Tur-BNi$@LCb6IQ|7~1;PdUEY)Mcz3
z#s7hoMZeU1NUtq^Mscg(<iV#gfEs`W5oVGqXCe|y^F1w^OPxR;u9mt#`DWt$03TrN
z?SLB;mP$)#0C%Y+2`$ku0sspR^tP6#HwZE-QInn4O1o;?`~2L+T>PCyOVA957l1PX
zP0CCRx^lGkddpp+=y;8d!$vpJdiIQ1y+GOA3j?N9jMd#Ig@%!v6rv*2Z(XKKt2jZ^
zU-7$RUgU1DK~@6XMtD^bSrUO$%7#Up&ZI-L21SgJ4#ZL1J=`G#NU-Z1Qepxw9W#*5
z9EMCqZWFPt#3+!JrmkWt71=R!4}3+lDi)#w;~|B%TWaf@2F!<BO`=1qKDi=U7c;Nk
zv>pPo2~I_Qq*!8rI1<*Jcj!qY#dn-O=+@nUR!y%yk^P1N{m;AA0tS3Z7WASx)w2>t
zC=fnIin`loR`G@PoXVnD<=eBAfc?1!>vg3)=021H(cEW%XPO!vHzyJjNck@8023n(
zM+7WVPIr@N=*HJh<TQB6274(fu4LDZNGKnT%H#NW45@r_vdyOj$D6p8=&Ge)2o^R#
z04CyGSn8)~+p0*JD`?a5q=C)uGO)>E|5y4$;#)O<E+d!aXXo730Nj99l8#$5fwnUB
zZbE;Db8x@`gadI(?QcLH@B+->2~z&YE3oo_1YWpZXP<oSx!k~#`F}<1uhSLFH{cz(
z1zBJl8mns-obRFvPbG-GCHF=#qk47n-`rqaq*4G5&M}SL+NLU;zB^cR_QQd6BS!h0
zi3p?}wFlA@{YfseENJ$pR7<N#eu6q~uD7}%$Rfz4@fJ{>^7xC3u1RsrDS;(Wi|OI&
z<4;wGA#Czg;vqWLV3?Pc$+Q6EuM#P_0OnRfvDO5Y;GvzDBaD*afEDxQAW%DqwuN~m
zR==+dL_#UjRT7}WoDb@YYaR=md1gq`U{3%}w$sZi=ZOU6fkKE-#Fh}SM1^A(29lx`
zCRC;tfGTl7VbXd6RFc0bUEsA+U@xN_*U33h%=8QsG0y=NCE^22O?be8DXC;WZ4?3a
zm#;}O7ZO8q2w90LAcU5V_Q_qMqIU<$Xj=hPromeXAAeA+=uoY1DkXt5P6tBGc<Qx&
zMzNI~1eO~3$V^sICfH*%HV~?8%GdF=4dt;0LJjzm*vQJZ8t@DVftiLgn3n)}*`*?-
zL{7NmM+%rtoU6LMObWs+Hp7ExLyLp}m63f&jyeipGE9<7W3EX&pi@eBf@Y{)2oY~a
zNt9*qh;T#z+_nidgM11M=Hp2;Jj6Dd$LI)PB)ApjBoB~cpv4l)0nJ*Y7Ff_msdf}K
z-hx=n1YMydl#*0)goHwA4(z0_09ZvS01Zfh!jfWEkclLbB$6>)5a(#3EitxQN;Jd6
zuR`e|^;Ilrkhgi{v`MT9BtjOJ+dRlXW*L+YfWQo4W;0!ES!K{wB(LlZJmF6<Yowe@
zHQhFAzENbDkYK<iP*m1Y8oXo%=|hym0js1HNhPYOdPrIp!gHtsEDVweTILiY1uk_Y
zz^5S!u@(Jfq4=&u020>lQ53Y_pmD^VDJAJaKw1>@RBGiYyPmZytCp%yWWxolmd4v}
zmeQ(Ifz;o&#?0^h%r6%+qO|RZyK|XjhE@#K*9k0Gu*gF0!I0n#1qnE7ZTZdmnqVNd
za!hywMQB5USb<6=^^qgQ)GIK&2ym1bNjU8WO9lK{qfYw(v!D~+rB>dYPz=`KeG&)t
z%!a*MW;ORr0H_)TyY~XZu8II|MTxd6BAi`DAV^!L(Sf&ZN8yMPYZr*P6`6~L>=*=X
z!Ua57q{M<lg>>3}aj&Kgv~~;B14So|GY&hpY1t)02I94KZ`Mj+g%k{`+@Us&MoN~g
za-;3MiV#}v$9g3QCV&{U&d)9K))G)CaZ>fIdKaX?xfpshGY>iMWa>|s2U8^6oQbAY
zSFcIN^0w+_NeGV01uTD|S8Ip^O3%TJAxin5+}idjL{F}fYcoKTe_Tz>IOkzDpu`0n
z|8mo1&h^2qP^s12z#Ec_2)2<d-Y!*l=9&~k0dkt<Ef6A<BvVwGSi-}fZWIyo*{wJ6
z3?cNyQ?KwGyB?4YgDnO#4d1t|lU2@D7WqeyW9RI3zk@ov_MyTi^!{eI*8j9m^XTLe
zQWrZJhQ*0gEDJ%IhR@GKMU^21Dy>U*x15k2s#<n)Q(?qt^{sCkXuBcY(2ztLk;ukS
TiAwT$ul!xf6yZWcuZr*_TcUC^

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-4-strict.dwp.bz2 b/tests/testfile-dwp-4-strict.dwp.bz2
new file mode 100644
index 0000000000000000000000000000000000000000..8cc1bbe2a8042bd06c9119c276326371261afbe0
GIT binary patch
literal 6871
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

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-4.bz2 b/tests/testfile-dwp-4.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..ece5817bd88a5cf195bdaa109f8f41a8d4595093
GIT binary patch
literal 4194
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

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-4.dwp.bz2 b/tests/testfile-dwp-4.dwp.bz2
new file mode 100644
index 0000000000000000000000000000000000000000..1a63b42d987cf1f64f8c994f8f16b7a5d0af029b
GIT binary patch
literal 10098
zcmV-&Cym%bT4*^jL0KkKS<b(?jQ}$-fB*mg|NsC0|NsC0|NsC0|NsC0|NsC0|NsC0
z|NsC0|Nr1A>R)ZEp8x;_z5p@kJAeQezz;wGJ!bJl019pIyY20+yUkuk<Llm?zVCJL
z`YoCPr`@aRyLveQ#7Jh!mO-JWN=b^K9ggQXD?n&6km{8vGKh($A*jrwDd@vN83w0<
zWjxY)6+Z@(0&Pr6dV5n7CZ2;yg*=!EGCdk-X*{L^CX6Sj%#+a?V^e9UdYPp4GJ2TM
zhMuMqY8w+%PbuWaMFvPiO-Zz6GI=J7eyQpW1Juy<F%77CjT$rn0MjOor~^YssL*Mk
z^%^vKO$`7Ck^lf{>Um870iXbA(V?IK4Lv{rG{`{+BT18LO;dYGl0p86qaf31G<tbS
z`jP4CPfbr#(rlB;ew5Q~Q)I~6rqmi50qOt%00xFZiI4!%kOn}|4FRE}CTeM?qI)WS
zR1l<*2+&mVs%;u4w5N)DQKp(qo{8#uo>8`_G-y3Rqtp!=Jw|{44^Z@hq2z#QXnKGE
z05s6h10ZRk>S*-<3`eK{(9i%jsR){Bk(!wrM%pGO7z8FNjQ|0rgFy6*L7?)DGepE`
zri_{ffY4}qfHD9802u%SKxhG=8fl;!X{LsNM4*8I00=S)dJ(EU>ZZ}OhNH@CjT333
zO*D;^#;2%yfs;+OCYe1lfr1Q=QIN#RlL%yFdW@N-f*K4)n@Ab}JwV9Q^+qF1AoQB<
za%;lU-g?0{n++bkq1jQ}0{vF8T-ge*Swhm51`+66M^1YWC$=;7t5dSJ#$D2S<~r1@
zG<h%@`1!R}7a@^XE0R1;A$1@hx51!m2oZ;*p#YCj3I-fBKXv1jYJvs4^uAM;HW;`5
zTss2ZEfeT_QMBHbSS71hH&?z?R3KSZ&V+~ew!%Rm3a6x+8XISG984&tOpt^kW$lED
z0UMS%L}%*MoXP8`3dj!y&R7F$2sRtNvz2p&(3LG*;W!{aL~K1_QRp}dW9e<=9=70C
z2k0^9hbKMiH1-}Fef7}4Gn0fXIQs8DnV@UBV~fW6^x36_nl-dyh<a-lX6BQ2h<j>h
zO5URqKSYjz#p!E>sA%6%4P+t0%$}d2EFJ!WO&77-nj929+0?H-vpj%Bw820f))ala
zKsw+)Yujje*n&}yZHi(<Y7+U}&lXk%!9vq?c@nT;LzFiMaVtF=*)Tbgmp0|_UK-zU
z)yLz0Zt|bHeqG5NE$!cM<#0YN`6h=2iVN&c<={g3_UE$NjgGCWK5@L0aVpLTW{()&
ziKg$}a)u2yVJn=@FX>0nU8#i`4NRC<<=I52B-M>t7JeI1yJqd1p}we2Wd>@I4TYid
zbA9|`ZR1f?blW)NYVwnDaFt5-7P5@y&2vcS=Q3lBEUb4+W)qPfkw{8%rqL;wz*Sy5
z5l*wFu@Ks{GfdNXZ!u<@8^T*2Fc%B8(+j2&B65zBeuFllsfj#aFPoQ^{J2<zv{5X>
zQH>9)r43UlDJeer7|~#a-xO?*nMh5&Jmk1mV={KZ5sb=i=p9jvd~k?>#G9IW*Ge)|
zXn1nK`J~#sHN0FV9ycXZ)(Xj_!x@0gWH(66#70u_F})&YuNIiC5g89k`2j$TfU|tG
zNJ3qcagmy?k&MV>Br>d7hzX4Zr8rqBvOG0J%sSkr#zLuCwlY}Rs;}*i5GkNE(vX;t
zY^jvMsN*L}JgI0mNXQoM5+WssLAMcz-c#T!xw4-q84M<aNmtu1n&v_kJ3*Lh2J)2M
zBp)v98*#G+LILv0BFCVUU6$f0k`)Mx>PXBJ3|T1GCLXB<gBh<u)jnak3W}_0NIqCv
za2N?qCrrczS2G*tua{>P%GWR_%n6vxB;xS-XmqicS(Nb<Rh;GW>0rcbElh+);=KV8
zn1Hh|=<F&}5s=1AsLDE7Au*U8WJSB<gXY16rdu8}7zoWy2GJ>mkeUWwGn(r(#D*(s
z5>c)CjF`?;a9gBi6A^6R5fPVE4oG1pHb}~PsuLM#vPxSrrJY4(mW^GNF^tG#TN^4e
zLLy5H-7}Au$qP!ngA7DN<}(qhTpuD(zRc!xXpnem^0|DFYY~W<Z(SlGRI$}r)>%kQ
zV`c>A;USQKvOr&1A~6q4I7~oTG8*P$;Q3MIF`UX`B{xS%4tb<5E23$%#FAQL66f%Z
z9i02L#J8UyBEhv58km69^FJ-wwb|djY7Y3bL@ytg%DW-9I-&-RT2H6P)80wI;3-<d
zh;Aa$ijyNz1Hc$zcjhQ_V@!m~lPRL|aGHE@__s)&|FeiptintI(^$w%3{*1%GLtO9
z+~a~wGYQbg5GDd(h?!Z2U?xIjCKzU6nF)qrnF*3%nVXQkJ!YBZ`5f1WA=uDp(T)}}
z&#|+JObp0GlPNGWGfX`$I1@dyCNNCQB*QZ>lOw=m<N9M8Y<m)LVTNR8d6A4056$rj
zrqeDsjL6J6MmU%_<ij$PGbF>qIKaaU%*wnku<QIGyRYg5^KBbk3uuvR-NjMxj;EPI
z3N-RLtrNDL>B!KZeef?@MvRo!V;p1`F!oP8*bwT}%vE&u5G$V(b@x-Bn=d<hvJ+E*
z8eTN)VW#`8=5EI?Ll2gvF+()e(IO~MhBzzWnbRcDxzH<DMkDd5Q<WrhqkX95!9{{5
zvW5s#y6P9)r!ELnaQDkyR6*qItK~XstSL>`ipZm?PP4CGeHFEpMiZg{tfNyAltA2#
zitEP;>MLClbDLr)I@udk%FrCy>x7daos*3!Uf8X1=A2%}D~`Cp)<y7`R(xprmfpCj
zwv3!8j2~+;$8yoB=U|99O)#up2Mo2Ta^T*WifFww*MjqND)5mc5|-~qmoc1O!Qt>$
ztP<2{{N)-W7y$_En&Xv&0%RsbtHS|_4Y?Vj9%bSkIWSL8I5?8yAu<zn9d4Z=$m+M~
zHfCX(U}k1!VUXq)DKZeiOd&`}NJvOSAq<3sgc1<QNI**sFvv<06Cne?1TZFHnF<-1
zkc5OFfF@d4NeKuDNnw@=X<(3nfRK=tkcL7?2xX9ffq;fW7zt&W0zz2`LS!aFWF`=q
z2xK6ThC*Z|WFdf(LIy$^2pI@S0tNyCSYRY12uTQJA&`)SBqSl3nUt6b31(OcW>{oq
z7#U<Ch8cz!W*JCk83<*D8JU6@VUUs-WF(ko8HO1kWQ2woVV*K^h8ZRTW*BBkh8Rfi
zAkY}W_@4r0Ez9k^&n5&Vl&8__%i=B>kk8y`U?KWOQqRqoWzHx8>(P3SJ}eL%jbLGC
zsT_Ur=lTctavs;4?||<PM-{Gy7?5GeP$eWF0m@huF)E;-x&<HKXff0s4@qaw>F2$#
zXl_~$S+4*ilmELvLf)^l|5W(L$?WvHe%SOr&Szt>+{=IZN1Y_kr__)ikNSxZ`S!E(
zht=>x4=brmI<OHPewgZVDVHhsqs#5+`=3=uMLj6qFy#H+uGXo4(3OezJsvbSQ|-)A
zPU<9NmaVJq!gsqhi#?C6L}p8_>O9Rqz$f2*&Nb>jA)b-7O;0Txp&9Wj?f34&s#{fQ
z^5KPSZg$PTbG<0Xtff?JWipdFmRjdov9absD`cjUH?-LloxbN?x|#mRVTFHgg=S`0
zpkwntLsHZ^wbzH=qERVvTbHk@mrk27SFON_lul#ja@y@MQ>${&=F_`*@<d#?hi^9-
zrJ=25Liyf(tG={M%rxG6XKzzi{TtCd&#qXd<)?*vg6*Vrv~*U&0b6l*h8@6Rz*aB5
zxx$Z%Ol3X>b|MMmi}4R$|5DWz@%5UU%o@u%H!n@F`_+h<q*1oA^70X_?i&PIZBRC;
zNammfgbpGBRD1ac+#v>O9_f!btd!=JpCi!?34@<_z>5U{L0MjCMP+gi=%NGpiF5fu
z<MO?gQuMiK9P{qqAJ9@dN1{BF(Z4EHoHF5v0ri)zrJk;*i@aDDY~=Rb3-zX+MkZKf
zN#Jew%wsSo_rh3X)`sJG0aE+5h^W=tx>ILwg@3ffqY)0P@wX_()^BDcFUBCd!Cu=2
z$0Et57Dm8pciFsHv2)|+_R;p3_oGnWb$+8cb2y0De3;j(t`6_O!QV+z_*q(<o*Z2C
zhEr+HeLl|~qD4k8)Y<*r{H6B)E60<D#eP<cPa)tw%5UeW*}(7en$gYV%EW2-JA~8k
zk-e_T7}WVtd;K$y8OH>8;(vj`pHn4G<C<6uXem_5a97*vHLP_pOd30-%2I<`HQ6^8
zb8@s4yW3i5S*2oRX)>-^Mn3P{Z(i+O_s;F_Xtew*glJ(QRMBu2q8irlXP>WXDrBr3
z!EpFesl9h~s)9xZ1$}c49gV}}P70H2X|z(XmyT9eS5MzctFrPe(S<nLxL}7)p)N0>
zyosS}s)e@1!*GX@?KV>n_*3lkt7N4%Rt_+(wNPs|rnqMZ#bgY(hMy$_4j4W{5RgX8
z^`u2sq$=7)LC8W748AX8`78WU<$ixjR%-ynJJQ#(ad@K@2?~!Xc$J1M0#7W2fHG8M
zS}I2aneM0*dAN6zxpeBbP34Z?bB!}8sj<?x@86v4b$XozN;e3)yb-S(w|4{L=ah*H
z#JXd0Fy<FsY@Jr-!q`=7so>`z;$^L@VQ;SLn7^&qyoRrNq`Tf)XtPVU_>db2L87~}
zikYl*&i8FOg4SBbd477iuG%hDo}SVpcH-J%<_6+gl8!$}zd?s}qn%+3bm9mxoQ!2O
z)+#R(V)-d1CB*DJq|6L@#@~7KJ2cU~BS`pMNC+FbWL{`ra1@-w&*x?*T(_5VPp6S_
zRTV)5{u@K;pVvrfuAtmV&_2vhvyOeGnt^i_e|z9iwT_*L)K!XI-m#{JTvqYxYh^`N
zzOc#BQ@*`BdI5mL!SCCi(g5^q$+`LKHQ7vd6GPzc^|r{EKgdrLOE94cKod-XNMsN=
zs6dWM8KfP-{6my2&RL{1Wn#!<&lpRB8w3)ls0Ev$*WRqHfR6%!do0(g4-tMd1#y_&
z9U;QIKZ(bchcqD)Y8Aa}5Wx4wg8$QB(VKX_K*3T^Q5?j#{-+o9m*~Z{LP(MZf)UM&
zxb}bEp1d*~5<`SoXLCG>hlLt~3BQ|uX+M_3)LD9O1L^%=>T7YGFQ5F)o+FO~CMVRH
z{hA*O$N!;d|66&&O=5U%esvp-#9jVup2+NEJ+G(DAK`kVfd1{q8zvTJQJxR1!)w~}
z0tAA^5D8>KZxnk`$%&MP;P)@oB6`H!PZPwFW0jeam#yb-tg_?QL78XXYf-IcMHM9#
zuHMjEigL^|tMR*SZGRQ5Wtj$L{#Q}j?W#Ns&F*2~=l*c!+oK4)ZH?7tZDU24M;00D
zHpkQ`q)CDQQs5W(0_2w(oqul}KdT(xzK{!iq+S6Y_F3H~+(CIG90Q}WUet*odwVA>
z?T43m_WXo7e6{YToUbiD<cy4__d&<}7Jxt2dqUutDS`zbank)?5}j3j#cfUuy$e_V
zLJ%+R@TZ`W(=If}Q=?922QPVp3||oBAfennmIt^I;gI8@=K8*MIFtf$8@KGcc*n_w
z(&*K^*%*+1FOctF7vY_$X>liH)PPrp?b{gZ5U96j5eL0gKlVcM61+3c2g%1zyiII8
zwR;-#e8BJ~#^sBz{4Ls^(ujW^;&Ic<r5y=ugct?b^h=$+>4DGa)%!pFy+NUn<(G<G
z(&l`7$4mc{u`YC%t_i^|x?BB=$m%ac;96V3^Zm>WCc=e4st!Yh$$|v}d7a-e#5c{L
zh@t0kqq2?i)I35s`l}*4_2wN2C?r81ILnT>xQ~%>@(|0oOG(-z4&UdWr&MR3#DOLR
zFN!`~?RAQSj*~f8C}0S^-T{D$I*(AfgqKanfb$?fDNxYR^b>VGFQb0KqM`e>+V}D$
zgdY5d1UU1Ki?Jk-3T}dT3(Z1k?sImZG%Ylj>&JB>U>_*_!Mg^dcTKGpu)X&@)u4^N
zn_B44nu3p&?EK!idoB~*HIzveGs-9)UzL8wIpsPJ+pEVPH)T%plSCe_AJQS{3-@xE
zjrjODZ-*)5c98B+j@5`~4D6XOV#Nq=E8zF9hycie7HxoMyDc!&CwbJ&b<+8u5#aFi
z1St2eTqj6SB`h$jIeN_vJkOTQ%k&M~zLR_K6rS+^>*ilzd*{%<fcIzby)b>V_|D^K
z_z55zSU;}%0KI$x`kH$3AC%qoj&B6@XG&fu2LjmfL}GDtDU3`w*wo-SGv}tpHBe;_
zNSIS7c_W-TV|sj3K;NOW70MwJBnFB^xwI_91Rs|!b0K#Icr8^vvF4S-oL2(H1Tr}Z
zASrCgK-pB|p5et9${oSGJn_Yx_ou-qi@<Q&IE5}rXRTM4Lq?J=3`lTdJCaE8=WqiK
zxiyR@)(P349{i)fvW?_GPa7TIt9+rgF3&lk)tVBZdy8g}LqsL~0P7#l0qgyo>F8;U
z!SN2z`={3=HHwKaVkt1=hBntsF>MbVnadwD$`sD@az}BcO(ez`GyxYDk?*oR6FThC
zpU7X&2;?3?=$9q%4A4LmrzJP&AY;&C3Wm&fQHecq=}*pZ4u$j%i2$sIi9)mqofgM@
zFPr0lb*G4abLyUX=NTB$7?wn`N=VdCm*Hch(XBa$6!@oLc1NCn>EKRKLHz;B!^HWX
z_x@eakaYfToT&TjD>FJLMe^N)^ew;E*v$BM&^^bmxNX=#oKES;(crX&mI3sH90sRx
zhiA9f`0PD5yvg@Z`(G)!b3ieP_TPB@14HpW+0<!@R30MY2ZIoN6Y#-$g#%C&4-fD(
zMOEa{BXk9k>k7L-DWsNs)9RjA+_lS}Gg5Zo8lRY<t+M(5X3($f&eQhq^xf>)K+uJ#
z>lP(=K?vl4_!#+z@|5-s;o=>!pvg4_Bjq^;z|=JrC5%d_inELD@_FtwdxM@{VKLK?
zSO-3dL3)E+pGXh3Gf?4;FO+ywcx+0u66jf2X>o>KMZiAyygQ)eClH*N0>Ke_?$O6S
zdHqMjKQY>PCz@?Sh(kM#1M!lu>j)!EeR8J5UUE<2DhDh81GH>SB`KRVr9rkmQv>f5
z50tO~{fzoY7}=fKbbh0Ze89Zn1QgPVay-zX0HQo2&01A>i0#(?lrMW)N4$a_eI)=)
zKMIp|AFPS$p?gpZz}sGG;}HJ*G5)m>%Yz3{7TEJ|NgTM}@ooZoX?!$oA6FuH2Xlk7
z<nsu-_{oED-$e(Y;K9E?(dxIB=r`HVG2fVH;qZRW&krnzlPdKP9zsV^NKz*$z89hL
zY&fznrNa+M8_`P)?!<{j-OD7A2&BoldQs6v*_maQbPxDGHM-~EUq<?u(7vhRL(x8A
z^p7;Mzj5Tx;C{i)nO>xk$DW&~I#Y;3xJL-;4%~1~EToZuRLHQNJ+twiS#WGj8LK|g
zq+cMB#(pY|qPnvpTb^n4O_}kJ(tD*OaF2j=!{rm(odMMx>CO>s(h}(ofc586bY&x#
zI)kG8tY6IMN=X!t_%N(|v-%v_Jehl@2@MS7QfOsVCfCmB`jPv?agWuvj~&1#dA|X@
zTX)^(TirkBbS5O3{-Te4S%jjdO9hPqw;8l#BJ2>APjKFe!wh+aM~9|95{8DfuOLF8
zqQ-SO)3lt>Sl-1#n93*JH)roY;Dz-hK-%Xp;1Iv?%xp#SULQbhdIOLs1;!gaiJ@=L
z)@hvL>DbB4;}70ZMWO(-!(dEd=xa0>V0qnRFcyzXXx(k2ejVlyN*=@BcU+va$r-~a
zbb~a0La|Azfx_tR4N8xq1o(n%Ah!@`Hz)v211Ok5vm0o8dx7Ze_r6oUEaMYe@OsXq
z=k2-b8sNhBk6Zj-R_!QFPA{~56Yo#KCZXFf40p}`(Z6LW=D*H-x0(I>llVv2KW@|P
zUmExiN^GE>6ZoF^kioDGJQK8@0#oOYBQ}H^ktj+K1i&x~9w3JkXADe31IP@V149P!
zmM~}~4BKW}fqbe;L8vuwT58VLEg)9ui?y1}Bo?JZAXq)6kbs!bZ0Karv2bKAW1>|s
zkf`eVS^`&FU=!K9k7xPM&%aRp5$~Duj%cRh3m_)QG=OLyLO|~fdipqwTuu^7(L&_*
zQ|Z{?^kt5|Zi&oHfWpy6R>%mi2v~<H(7SM~?UPxq1ag@9M2+=9<>aEMh@+`E`1u-M
zV;mSfhfW-`XU|v)*@3WgDmXVcnoABka$~j<MoA=NlDq}lx}3tU)Kkrp99TaenY1>I
zG&s%@j{}+8fYl8~z-+PtGLVB{M2u+FxFbMx+yBIdvr3GKmzlIT9tW8HKj*ri^FMt1
zr_O#w)bHlmL7~2s2$D!fPgk`)uTNJ#Cx#QOpbn-2*oL;owz}cldD~DhVRi)`+o_|Y
zMy#u25xCLD;atrJObIk?i-}VbHB6azHyRMmiPGTY;u#fVdyZEU<xGMXZqO)TV1tPY
z)CxdO$fw3YSw2zmXliqBu!+R_hlTUKtNnNW%}@Khrw<vbY9EjKTh+l!g;sejTE10b
zAtv{r;|rY(>1xz-DDn;i1`SJTAty;Ajt=IAw|T<yOha9Gr;d_}S}LhK3XZ3d$xb|a
zosNc=*RZ7nU~e1X;P2jH2BVEK%{#8Q4IIo|jZ7TeHMre{9_{D`ZWtOA;>t2fr9Qi8
z@QHB%BgW1feg&pn;Fyb!IO$Jtai>Yd;KNgh$jY%I8!b5sfroJX7<PD4P$Ed<0cHz@
zx|@jh7aRRhz<9vfH4TQ2jxC29wmXf(qNcoXI>yy@Xl7Z=VM5~V0x?ZV7Xbts<4ZeY
z9AMNHvr`}oh-ewIF{y|`4x<pqM`Jp|LT1AOr7xaX7R<~BY5*P!kNT%bywNduZ-#y&
z=bk9#jg5-TYP(xVbaA1iIuYg$XBMqahRw{cYEZUeXmpfl1&o76CBqU9dAVrHIuvI~
zk%@lSK?o$2(2KD_tQsmE!&RlTOGa3TBEjSW%+|2BvtTFRyh`w}jP-8YaKg8xfz3}C
zuPRyqil`Av097DSU={Skz{;aEKy19tN(VdW^UGRwM_A>oj*ZGRbQ*;NA?8g*PbZI^
z;c&di#T;*n<#uSDq?FRp5kND~O^Jx`I-Rn*G2q@<IB3-<?H%EO*tZ7_%*2->gPmPf
z)iRuF$lSXxaG`K%LX5!FG)oy`YjG|Wa2wdgvSKi5;~TYS3`GO9BH39pgec*)7Px82
zmBpIu2~AMf%p7w7aMm4wW*KKRC}+-t#5=>NA_yRL+DD;d?s)d(WEs#7YCJ*2aNKsh
zWt=!0ygCONp@U;IqFk$im|#(@$iO9vm2nNn6~{eBjz!Hg)ZNIVGAb4bN(~AQ*yM)N
z!oi3@H_*xA%4xCnZ6YNGo9J01qf=Eb!xB3k=LVp!19LjI7|@79uKXO#%&}!FQ!<4H
zj?)tx7`;1m4Ky?y2U1wnOSZYJy~6IMnIy?yLAZCm#6CJgJjA3M1OGs{4F>M^KD7C7
z`Fh`D{J*O+{ZvELf3x1T4Wm!ogc`Icf$SVL$@n}RnJ3P1_de0Fx_N;*w9jYb1Kc{^
z^EU3b{f*_W!c!UU9g`9G<3dk*9*8`UX8j|?VR<TMs-h|?sv@eb3i*S7XUKV6AUCYb
z5xs>|c>QDPT0i?fn0?FfKMWf19uv;<J`Q>*!YOaKKI8z6PY0he^NnAmh+ri;g+I`V
z+2-92?|uyL;tv>?LwUf{fV)pzPy>+c1_yj8OIz_YaN#CUYOI4J3oOoev-r-(aC4p`
zkE<doDypg?qONK^IofvnC>zNWnP%@#gq$gGxPix>aH-_mFiOlZPE%Cc4~gXErX2XL
zQKzLV#hciL&R<8<k82@Lgi648`)3dZY148+Dnd;}4?CB+<<T$%<am{(QRUBN@i4S?
zNJp-jwl;}5^*j0H6d3OfRCQlo&3HPcS)6hWXpD-p06E>(z9wEYj#>)R%-P2;tK%TI
zSlM9fR6)eeQ(YkD!<Kipz+uJXYXLssV22IZG0ljmil~aJs;LnuPEj7TcmoKXFd-!4
z?|cWJ@o4%ZQYU|Yx||g-svR&YoHGK8gerHUEePw#*&Mi#AR!^b^^~Ou2cf&Rg90V!
zn8q;UwDZ%Pdhid6MCsH4z$O7)4QWb<6h~k@db)AUN+~r%kyTYyR7GAO%&Ll_sH!T8
zqNu7Wil>Z;jfCeyCnty|1^J+yxo+0fN@Qy{Xep|<<KTVRLE#gBUr^zVOO}Tg65e`m
zaFlg&@P(W>ddFI%ZC&RIiVHayV_0<aK=E6?HHWmu<_{3~V;E%m8Jj-$8rhJLI9qzL
zs0MT>3Fdh$>SsZ7xNSRmJTq88c_JPJVN?J%!yI9vMnE8)a0)OZ15v&1z|bDYPy~W+
zA~7PXyEA0NS$9|rL~9B!S*t-^M}9Z*ia47fpHv%@Vq<M!&=?HdZ)_$2m%9*Y1;H2~
z(6^zu#WxZ*;=#c%#v;hVqFjWHBq86>O`fZaDFo$pE6iuO2fn(r$Q_lj0;uk=9v)Yo
z3hX58Ik1|M^#+GPT)&~`1T5IxPAJ59WdnqHgO6;{0N{x3M8r)D<xw&VW7Vm31mrkx
zgw`Y|nGUQ(2`&(bpf+kv2n3dJP$(8AG{bVnOxsr09kFvVDDCGdRw1<-lt9@70&biU
zI4lBD9rxe57HI&>0WKs2;jYj*Wa*>3m;+3qf||;V5;7>j3?(dU63C|jFqE;ZODYXx
zfkjhhBUw!}=HU&EvAi|{Y~e92ytAV4SO$rF16idLN?V4lK?o(yrYJ}efRG|3uL`-N
z8s!5RS%#yCjLq7f4!@dw?sb@T){U0%=VY1lo1KM6bt&;eZ9pNjy(04PXQn>Xh05}2
z^2YuFIWmC_T!c^Nh2AD1v^sGFGPi1c86Rfm-X?i6snuOCRYhzj;yN995o<{6R8>`2
zSXD)%MRmO)fG0m(S`%!<dd^t!J<c%if*vl)349dgjMRZhG$@Jrq*t<~5Q-?Bv$@@%
zoI-Q$y*&*&12_+sR~qt7{!wr-(>cKav2GI-#3E1ta(0&9CXbtvI;F#_l%|B_5@CZ8
z<8gq@0j-m)Fc@f^g!HMkY-4~JUmJ+6oxoL0;aNKtxnPEl2F@k3Mii~DiBkBAU~AQ?
znC}y~G6X0k20(?d1jD(wq>2pOU(*aUyE8WhASXU@Z;p+Phm|i8M>J6i6e?3=IFAq{
z(yApU@>aO%Gs`<_)N<$nfF&I0*^I-w#%K|cQqK8bEU<jCl%|=Hh?$zGnw3eV1k#jK
zn=_&<VJc<vRi<ft4KCCYFN%8B<SmSdyP*b`LdDclvRf!twQL^J6(*DuG@+$$MzxIG
zTF-ZNSz%HU%q+pCD63hrqmD&`Z066OInhM-aR*>9!+G~Dfz!7jK4XKkP!<3qzUj0O
z1fBtHom*G|U@O7X8RJ#Q0bnHPnK(@-(!&tc7!dLv*2pje0JXaZg^SY0*HI6R4L0wJ
z$VM$Dxab39I67IF5QkmDzRxFipj0yft_GVYoevQ+O>rb`Yxk{G%vvQj?u}a*>Mubn
zU<{`u)@+`S#@7|3lhFWLZx|3KE6`GnHOCFSoy%8kv7~I74twj3g7@IK*{H(KDPppr
zfgwt@X-JT{TY+gLw7lWAsasM|xr>e^3b?2p3W6QtBjbP}Kmm{+5B?$Rhj97LXKNH^
zb9Z{*uRFqT*cdWUB-J70K>JiDhL#HF>j?FO6_~WKPVjQd_|QF|k^+;N*#g58$8>5r
z?%<P`(>>yu!9y@LN6=7UNbyKhb;uo~Ubn@Zt4_29I`14vC=TwE1eD+p7KK$u*!%|Y
zR1b;4jzK8o4aP{7C@DaxB3%jz6_AqBAgk!XUHxYs(%!R;>mqOz;^QzFedoB{)OCNu
z*VrFX)_hlyV}Lz2(_c|Twv{<fUd1T!ypy#cO=Cp2?gYO`ZRfgdEA0ni+J}@=9eUR-
zhSLDlSVhdX^6|G%uJwIC7Hl8HdWt%QBJ-4cAaospv^kyMqvsNVh(gnkh~q0yB<4z6
z6UW-n41=`rq0{6XN#g_8hn9Fi)dGrzxlkJsV(`ObF9<7W?o6~!pCXFu23Oav!5~`{
z7I#k<UKX6%UlDti6t8RETG>m61z=HxF`@N6tAI$(Gzh+dMT%+8G8xB?;?mbmk!R7O
z!x2m>^??*L@9p#kWjvqQa_~LrB!q)TFoGJ&JfX!r9vT=PSs4LxV2uhSV2};Dd@gvK
zIA@vT^d3CVJp&E1tIRE+hi4R_5ZLjSx4X}f-~p#$d}PMO4=v}3@H+>*CRp~52VzoT
z@!nr^I|GM+)a?rMbCn8{z-~G3zZ>opro7C~SI5E`l*H1x<YlFT^Nc)T1u_g=piewh
z-6uU7YJqz9dU*lx$Hx%Xf4+%WhZ&c51IYu$$~Kb*`({wI#y5E>pBUo=xr$&^8atQF
zNDL@sw15gOHCAjxYDNHSgK+M%VNqcYh-OzJ2Ds~#og$K0xl;rPVH^k%mTbbR6bhmj
zFKZZD;p{qq{2ngXe{O;LP$Nf)jSc-&I-o<P?{@hx>){^hk_8-t69?lYAtF|643odx
z9or+;p!ZGP9#Hs5Vg=2BjgG*W9n+Ignndz!EfJdUC~tx33%bIl8MPcHHz=+RAA3>6
z3O4Yth>djtuxbEJHX|q`lEP|Ov@PF+_F6-2@V0SnE2_d4C?x`7h4FWSPK+97GpnCQ
z?9O{{(~`YjB=hjekOK|)<ZU<IJ9@2mb3@!iZlIqBW&m<(1MOZ@2S-_V8JVS+tAH;?
z-Jr}!&0&p(-Kr~sdxS<$zebrS0cI@OW|qght0{*)<k)i4ayaY1z2oOjgL9~RYF{3l
z^}6Rkg)^{ca&rcbTuy<SdYy7mcz0|yg`VZ;Iqwqw`wS0B>!f2l`Mq7aJs5lt52Dsu
z=ASX}6fpq8iJCp$9<#Iu9njW&S>vjYhu^4oh!&&4>Z}F%=*X{LKG=7EJZuNsd`XX$
zdf(r1aEN;sWH~+8r_ibCU<crQT$M-RqQ1!bDk5xv`gj57q7E;*pdt31eoTqT4=sQc
zho6)U!6CyBNjV3^K9JQ_{s|DmCzlTdeMmy25GTKUK9*0Ht*`sLzbDU)v!Qf7u{0ni
z&wD_mUnrdsWvCoc7O@5N5<8*kXXIeuf#HyNBuZD$abRznz$p<r*lmiPq5%3IY)}T{
z?1~}0`?C}S-umvRKs{%dF$1`SQ<(ump6d&i5}i~Dk|(ahFPrR)0C=TPr?4S8$eB-4
z5f8;VsEXPO7{z`}A`N;dw}_v9Pe};#<o7aFIlT-7Klz^WQeaP*CzTv;9sux?cEq9D
U5*}Ari~r*8NT&)C8Q1qQpcbj`Pyhe`

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-5.bz2 b/tests/testfile-dwp-5.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..2c768eacb441e0d516f8b0d5ed327893c129246b
GIT binary patch
literal 4223
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
V;WDbQ*Tw(h?ntK!5)wFoa$u}k#e@I=

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-5.dwp.bz2 b/tests/testfile-dwp-5.dwp.bz2
new file mode 100644
index 0000000000000000000000000000000000000000..ba28e5010c3b7bb4a848ba143d01786aa80ba269
GIT binary patch
literal 10313
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
WM7|mb`|m1%%`ln(+h>5R`2PSJoD2*A

literal 0
HcmV?d00001

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
-- 
2.43.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 2/4] libdw: Try .dwp file in __libdw_find_split_unit()
  2023-12-06  9:22 [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support Omar Sandoval
  2023-12-06  9:22 ` [PATCH v2 1/4] libdw: Parse DWARF package file index sections Omar Sandoval
@ 2023-12-06  9:22 ` Omar Sandoval
  2024-02-15 23:17   ` Mark Wielaard
  2023-12-06  9:22 ` [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate Omar Sandoval
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Omar Sandoval @ 2023-12-06  9:22 UTC (permalink / raw)
  To: elfutils-devel

From: Omar Sandoval <osandov@fb.com>

Try opening the file in the location suggested by the standard (the
skeleton file name + ".dwp") and looking up the unit in the package
index.  The rest is similar to .dwo files, with slightly different
cleanup since a single Dwarf handle is shared.

	* libdw/libdw_find_split_unit.c (try_dwp_file): New function.
	(__libdw_find_split_unit): Call try_dwp_file.
	* libdw/libdwP.h (Dwarf): Add dwp_dwarf and dwp_fd.
	(__libdw_dwp_findcu_id): New declaration.
	(__libdw_link_skel_split): Handle .debug_addr for dwp.
	* libdw/libdw_begin_elf.c (dwarf_begin_elf): Initialize
	result->dwp_fd.
	* libdw/dwarf_end.c (dwarf_end): Free dwarf->dwp_dwarf and close
	dwarf->dwp_fd.
	(cu_free): Don't free split dbg if it is dwp_dwarf.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libdw/dwarf_begin_elf.c           |  1 +
 libdw/dwarf_cu_dwp_section_info.c | 19 ++++++++
 libdw/dwarf_end.c                 | 10 ++++-
 libdw/libdwP.h                    | 23 ++++++++--
 libdw/libdw_find_split_unit.c     | 75 ++++++++++++++++++++++++++++---
 5 files changed, 119 insertions(+), 9 deletions(-)

diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
index 323a91d0..ca2b7e2a 100644
--- a/libdw/dwarf_begin_elf.c
+++ b/libdw/dwarf_begin_elf.c
@@ -567,6 +567,7 @@ dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp)
 
   result->elf = elf;
   result->alt_fd = -1;
+  result->dwp_fd = -1;
 
   /* Initialize the memory handling.  Initial blocks are allocated on first
      actual allocation.  */
diff --git a/libdw/dwarf_cu_dwp_section_info.c b/libdw/dwarf_cu_dwp_section_info.c
index 4a4eac8c..298f36f9 100644
--- a/libdw/dwarf_cu_dwp_section_info.c
+++ b/libdw/dwarf_cu_dwp_section_info.c
@@ -340,6 +340,25 @@ __libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off,
 				   abbrev_offsetp, NULL);
 }
 
+Dwarf_CU *
+internal_function
+__libdw_dwp_findcu_id (Dwarf *dbg, uint64_t unit_id8)
+{
+  Dwarf_Package_Index *index = __libdw_package_index (dbg, false);
+  uint32_t unit_row;
+  Dwarf_Off offset;
+  Dwarf_CU *cu;
+  if (__libdw_dwp_unit_row (index, unit_id8, &unit_row) == 0
+      && __libdw_dwp_section_info (index, unit_row, DW_SECT_INFO, &offset,
+				   NULL) == 0
+      && (cu = __libdw_findcu (dbg, offset, false)) != NULL
+      && cu->unit_type == DW_UT_split_compile
+      && cu->unit_id8 == unit_id8)
+    return cu;
+  else
+    return NULL;
+}
+
 int
 dwarf_cu_dwp_section_info (Dwarf_CU *cu, unsigned int section,
 			   Dwarf_Off *offsetp, Dwarf_Off *sizep)
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index b7f817d9..78224ddb 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -66,7 +66,9 @@ cu_free (void *arg)
 	  /* The fake_addr_cu might be shared, only release one.  */
 	  if (p->dbg->fake_addr_cu == p->split->dbg->fake_addr_cu)
 	    p->split->dbg->fake_addr_cu = NULL;
-	  INTUSE(dwarf_end) (p->split->dbg);
+	  /* There is only one DWP file. We free it later.  */
+	  if (p->split->dbg != p->dbg->dwp_dwarf)
+	    INTUSE(dwarf_end) (p->split->dbg);
 	}
     }
 }
@@ -147,6 +149,12 @@ dwarf_end (Dwarf *dwarf)
 	  close (dwarf->alt_fd);
 	}
 
+      if (dwarf->dwp_fd != -1)
+	{
+	  INTUSE(dwarf_end) (dwarf->dwp_dwarf);
+	  close (dwarf->dwp_fd);
+	}
+
       /* The cached path and dir we found the Dwarf ELF file in.  */
       free (dwarf->elfpath);
       free (dwarf->debugdir);
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 7f8d69b5..54445886 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -180,6 +180,9 @@ struct Dwarf
   /* dwz alternate DWARF file.  */
   Dwarf *alt_dwarf;
 
+  /* DWARF package file.  */
+  Dwarf *dwp_dwarf;
+
   /* The section data.  */
   Elf_Data *sectiondata[IDX_last];
 
@@ -197,6 +200,9 @@ struct Dwarf
      close this file descriptor.  */
   int alt_fd;
 
+  /* File descriptor of DWARF package file.  */
+  int dwp_fd;
+
   /* Information for traversing the .debug_pubnames section.  This is
      an array and separately allocated with malloc.  */
   struct pubnames_s
@@ -716,6 +722,10 @@ extern int __libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off,
 				  Dwarf_Off *abbrev_offsetp)
      __nonnull_attribute__ (1, 7, 8) internal_function;
 
+/* Find the compilation unit in a DWARF package file with the given id.  */
+extern Dwarf_CU *__libdw_dwp_findcu_id (Dwarf *dbg, uint64_t unit_id8)
+     __nonnull_attribute__ (1) internal_function;
+
 /* Get abbreviation with given code.  */
 extern Dwarf_Abbrev *__libdw_findabbrev (struct Dwarf_CU *cu,
 					 unsigned int code)
@@ -1367,12 +1377,19 @@ __libdw_link_skel_split (Dwarf_CU *skel, Dwarf_CU *split)
 
   /* Get .debug_addr and addr_base greedy.
      We also need it for the fake addr cu.
-     There is only one per split debug.  */
+     This needs to be done for each split unit (one per .dwo file, or multiple
+     per .dwp file).  */
   Dwarf *dbg = skel->dbg;
   Dwarf *sdbg = split->dbg;
-  if (sdbg->sectiondata[IDX_debug_addr] == NULL
-      && dbg->sectiondata[IDX_debug_addr] != NULL)
+  if (dbg->sectiondata[IDX_debug_addr] != NULL
+      /* If this split file hasn't been linked yet...  */
+      && (sdbg->sectiondata[IDX_debug_addr] == NULL
+	  /* ... or it was linked to the same skeleton file for another
+	     unit...  */
+	  || (sdbg->sectiondata[IDX_debug_addr]
+	      == dbg->sectiondata[IDX_debug_addr])))
     {
+      /* ... then link the address information for this file and unit.  */
       sdbg->sectiondata[IDX_debug_addr]
 	= dbg->sectiondata[IDX_debug_addr];
       split->addr_base = __libdw_cu_addr_base (skel);
diff --git a/libdw/libdw_find_split_unit.c b/libdw/libdw_find_split_unit.c
index 533f8072..e1e78951 100644
--- a/libdw/libdw_find_split_unit.c
+++ b/libdw/libdw_find_split_unit.c
@@ -85,6 +85,67 @@ try_split_file (Dwarf_CU *cu, const char *dwo_path)
     }
 }
 
+static void
+try_dwp_file (Dwarf_CU *cu)
+{
+  if (cu->dbg->dwp_dwarf == NULL)
+    {
+      if (cu->dbg->elfpath != NULL)
+	{
+	  /* The DWARF 5 standard says "the package file is typically placed in
+	     the same directory as the application, and is given the same name
+	     with a '.dwp' extension".  */
+	  size_t elfpath_len = strlen (cu->dbg->elfpath);
+	  char *dwp_path = malloc (elfpath_len + 5);
+	  if (dwp_path == NULL)
+	    {
+	      __libdw_seterrno (DWARF_E_NOMEM);
+	      return;
+	    }
+	  memcpy (dwp_path, cu->dbg->elfpath, elfpath_len);
+	  strcpy (dwp_path + elfpath_len, ".dwp");
+	  int dwp_fd = open (dwp_path, O_RDONLY);
+	  free (dwp_path);
+	  if (dwp_fd != -1)
+	    {
+	      Dwarf *dwp_dwarf = dwarf_begin (dwp_fd, DWARF_C_READ);
+	      /* There's no way to know whether we got the correct file until
+		 we look up the unit, but it should at least be a dwp file.  */
+	      if (dwp_dwarf != NULL
+		  && (dwp_dwarf->sectiondata[IDX_debug_cu_index] != NULL
+		      || dwp_dwarf->sectiondata[IDX_debug_tu_index] != NULL))
+		{
+		  cu->dbg->dwp_dwarf = dwp_dwarf;
+		  cu->dbg->dwp_fd = dwp_fd;
+		}
+	      else
+		close (dwp_fd);
+	    }
+	}
+      if (cu->dbg->dwp_dwarf == NULL)
+	cu->dbg->dwp_dwarf = (Dwarf *) -1;
+    }
+
+  if (cu->dbg->dwp_dwarf != (Dwarf *) -1)
+    {
+      Dwarf_CU *split = __libdw_dwp_findcu_id (cu->dbg->dwp_dwarf,
+					       cu->unit_id8);
+      if (split != NULL)
+	{
+	  if (tsearch (split->dbg, &cu->dbg->split_tree,
+		       __libdw_finddbg_cb) == NULL)
+	    {
+	      /* Something went wrong.  Don't link.  */
+	      __libdw_seterrno (DWARF_E_NOMEM);
+	      return;
+	    }
+
+	  /* Link skeleton and split compile units.  */
+	  __libdw_link_skel_split (cu, split);
+	}
+    }
+}
+
 Dwarf_CU *
 internal_function
 __libdw_find_split_unit (Dwarf_CU *cu)
@@ -98,14 +159,18 @@ __libdw_find_split_unit (Dwarf_CU *cu)
      same id as the skeleton.  */
   if (cu->unit_type == DW_UT_skeleton)
     {
+      /* First, try the dwp file.  */
+      try_dwp_file (cu);
+
       Dwarf_Die cudie = CUDIE (cu);
       Dwarf_Attribute dwo_name;
-      /* It is fine if dwo_dir doesn't exists, but then dwo_name needs
-	 to be an absolute path.  */
-      if (dwarf_attr (&cudie, DW_AT_dwo_name, &dwo_name) != NULL
-	  || dwarf_attr (&cudie, DW_AT_GNU_dwo_name, &dwo_name) != NULL)
+      /* Try a dwo file.  It is fine if dwo_dir doesn't exist, but then
+	 dwo_name needs to be an absolute path.  */
+      if (cu->split == (Dwarf_CU *) -1
+	  && (dwarf_attr (&cudie, DW_AT_dwo_name, &dwo_name) != NULL
+	      || dwarf_attr (&cudie, DW_AT_GNU_dwo_name, &dwo_name) != NULL))
 	{
-	  /* First try the dwo file name in the same directory
+	  /* Try the dwo file name in the same directory
 	     as we found the skeleton file.  */
 	  const char *dwo_file = dwarf_formstring (&dwo_name);
 	  const char *debugdir = cu->dbg->debugdir;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate
  2023-12-06  9:22 [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support Omar Sandoval
  2023-12-06  9:22 ` [PATCH v2 1/4] libdw: Parse DWARF package file index sections Omar Sandoval
  2023-12-06  9:22 ` [PATCH v2 2/4] libdw: Try .dwp file in __libdw_find_split_unit() Omar Sandoval
@ 2023-12-06  9:22 ` Omar Sandoval
  2024-02-16 15:00   ` Mark Wielaard
  2023-12-06  9:22 ` [PATCH v2 4/4] libdw: Handle overflowed DW_SECT_INFO offsets in DWARF package file indexes Omar Sandoval
  2024-01-04  0:53 ` [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support Omar Sandoval
  4 siblings, 1 reply; 14+ messages in thread
From: Omar Sandoval @ 2023-12-06  9:22 UTC (permalink / raw)
  To: elfutils-devel

From: Omar Sandoval <osandov@fb.com>

The final piece of DWARF package file support is that offsets have to be
interpreted relative to the section offset from the package index.
.debug_abbrev.dwo is already covered, so sprinkle around calls to
dwarf_cu_dwp_section_info for the remaining sections: .debug_line.dwo,
.debug_loclists.dwo/.debug_loc.dwo, .debug_str_offsets.dwo,
.debug_macro.dwo/.debug_macinfo.dwo, and .debug_rnglists.dwo.  With all
of that in place, we can finally test various libdw functions on dwp
files.

	* libdw/dwarf_getmacros.c (get_macinfo_table): Call
	dwarf_cu_dwp_section_info and add offset to line_offset.
	(get_offset_from): Call dwarf_cu_dwp_section_info and add offset
	to *retp.
	* libdw/libdwP.h (str_offsets_base_off): Call
	dwarf_cu_dwp_section_info and add offset.
	(__libdw_cu_ranges_base): Ditto.
	(__libdw_cu_locs_base): Ditto.
	* libdw/dwarf_getlocation.c (initial_offset): Call
	dwarf_cu_dwp_section_info and add offset to start_offset.
	* tests/run-varlocs.sh: Check testfile-dwp-5 and testfile-dwp-4.
	* tests/run-all-dwarf-ranges.sh: Check testfile-dwp-5 and
	testfile-dwp-4.
	* tests/run-dwarf-getmacros.sh: Check testfile-dwp-5 and
	testfile-dwp-4-strict.
	* tests/run-get-units-split.sh: Check testfile-dwp-5,
	testfile-dwp-4, and testfile-dwp-4-strict.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libdw/dwarf_getlocation.c     |    6 +
 libdw/dwarf_getmacros.c       |   26 +-
 libdw/libdwP.h                |   42 +-
 tests/run-all-dwarf-ranges.sh |  114 +++
 tests/run-dwarf-getmacros.sh  | 1412 +++++++++++++++++++++++++++++++++
 tests/run-get-units-split.sh  |   18 +
 tests/run-varlocs.sh          |  112 +++
 7 files changed, 1715 insertions(+), 15 deletions(-)

diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c
index 553fdc98..37b32fc1 100644
--- a/libdw/dwarf_getlocation.c
+++ b/libdw/dwarf_getlocation.c
@@ -812,6 +812,12 @@ initial_offset (Dwarf_Attribute *attr, ptrdiff_t *offset)
 			    : DWARF_E_NO_DEBUG_LOCLISTS),
 			    NULL, &start_offset) == NULL)
 	return -1;
+
+      Dwarf_Off loc_off;
+      if (INTUSE(dwarf_cu_dwp_section_info) (attr->cu, DW_SECT_LOCLISTS,
+					     &loc_off, NULL) != 0)
+	return -1;
+      start_offset += loc_off;
     }
 
   *offset = start_offset;
diff --git a/libdw/dwarf_getmacros.c b/libdw/dwarf_getmacros.c
index a3a78884..2667eb45 100644
--- a/libdw/dwarf_getmacros.c
+++ b/libdw/dwarf_getmacros.c
@@ -47,7 +47,15 @@ get_offset_from (Dwarf_Die *die, int name, Dwarf_Word *retp)
     return -1;
 
   /* Offset into the corresponding section.  */
-  return INTUSE(dwarf_formudata) (&attr, retp);
+  if (INTUSE(dwarf_formudata) (&attr, retp) != 0)
+    return -1;
+
+  Dwarf_Off offset;
+  if (INTUSE(dwarf_cu_dwp_section_info) (die->cu, DW_SECT_MACRO, &offset, NULL)
+      != 0)
+    return -1;
+  *retp += offset;
+  return 0;
 }
 
 static int
@@ -131,6 +139,14 @@ get_macinfo_table (Dwarf *dbg, Dwarf_Word macoff, Dwarf_Die *cudie)
   else if (cudie->cu->unit_type == DW_UT_split_compile
 	   && dbg->sectiondata[IDX_debug_line] != NULL)
     line_offset = 0;
+  if (line_offset != (Dwarf_Off) -1)
+    {
+      Dwarf_Off dwp_offset;
+      if (INTUSE(dwarf_cu_dwp_section_info) (cudie->cu, DW_SECT_LINE,
+					     &dwp_offset, NULL) != 0)
+	return NULL;
+      line_offset += dwp_offset;
+    }
 
   Dwarf_Macro_Op_Table *table = libdw_alloc (dbg, Dwarf_Macro_Op_Table,
 					     macinfo_data_size, 1);
@@ -188,6 +204,14 @@ get_table_for_offset (Dwarf *dbg, Dwarf_Word macoff,
 	if (unlikely (INTUSE(dwarf_formudata) (attr, &line_offset) != 0))
 	  return NULL;
     }
+  if (line_offset != (Dwarf_Off) -1 && cudie != NULL)
+    {
+      Dwarf_Off dwp_offset;
+      if (INTUSE(dwarf_cu_dwp_section_info) (cudie->cu, DW_SECT_LINE,
+					     &dwp_offset, NULL) != 0)
+	return NULL;
+      line_offset += dwp_offset;
+    }
 
   uint8_t address_size;
   if (cudie != NULL)
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 54445886..64d86bbd 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -1108,25 +1108,30 @@ str_offsets_base_off (Dwarf *dbg, Dwarf_CU *cu)
 	cu = first_cu;
     }
 
+  Dwarf_Off off = 0;
   if (cu != NULL)
     {
       if (cu->str_off_base == (Dwarf_Off) -1)
 	{
+	  Dwarf_Off dwp_offset;
+	  if (dwarf_cu_dwp_section_info (cu, DW_SECT_STR_OFFSETS, &dwp_offset,
+					 NULL) == 0)
+	    off = dwp_offset;
 	  Dwarf_Die cu_die = CUDIE(cu);
 	  Dwarf_Attribute attr;
 	  if (dwarf_attr (&cu_die, DW_AT_str_offsets_base, &attr) != NULL)
 	    {
-	      Dwarf_Word off;
-	      if (dwarf_formudata (&attr, &off) == 0)
+	      Dwarf_Word base;
+	      if (dwarf_formudata (&attr, &base) == 0)
 		{
-		  cu->str_off_base = off;
+		  cu->str_off_base = off + base;
 		  return cu->str_off_base;
 		}
 	    }
 	  /* For older DWARF simply assume zero (no header).  */
 	  if (cu->version < 5)
 	    {
-	      cu->str_off_base = 0;
+	      cu->str_off_base = off;
 	      return cu->str_off_base;
 	    }
 
@@ -1139,7 +1144,6 @@ str_offsets_base_off (Dwarf *dbg, Dwarf_CU *cu)
 
   /* No str_offsets_base attribute, we have to assume "zero".
      But there could be a header first.  */
-  Dwarf_Off off = 0;
   if (dbg == NULL)
     goto no_header;
 
@@ -1181,7 +1185,7 @@ str_offsets_base_off (Dwarf *dbg, Dwarf_CU *cu)
   /* padding */
   read_2ubyte_unaligned_inc (dbg, readp);
 
-  off = (Dwarf_Off) (readp - start);
+  off += (Dwarf_Off) (readp - start);
 
  no_header:
   if (cu != NULL)
@@ -1219,18 +1223,23 @@ __libdw_cu_ranges_base (Dwarf_CU *cu)
 	}
       else
 	{
+	  Dwarf_Off dwp_offset;
+	  if (dwarf_cu_dwp_section_info (cu, DW_SECT_RNGLISTS, &dwp_offset,
+					 NULL) == 0)
+	    offset = dwp_offset;
+
 	  if (dwarf_attr (&cu_die, DW_AT_rnglists_base, &attr) != NULL)
 	    {
 	      Dwarf_Word off;
 	      if (dwarf_formudata (&attr, &off) == 0)
-		offset = off;
+		offset += off;
 	    }
 
 	  /* There wasn't an rnglists_base, if the Dwarf does have a
 	     .debug_rnglists section, then it might be we need the
 	     base after the first header. */
 	  Elf_Data *data = cu->dbg->sectiondata[IDX_debug_rnglists];
-	  if (offset == 0 && data != NULL)
+	  if (offset == dwp_offset && data != NULL)
 	    {
 	      Dwarf *dbg = cu->dbg;
 	      const unsigned char *readp = data->d_buf;
@@ -1276,8 +1285,8 @@ __libdw_cu_ranges_base (Dwarf_CU *cu)
 	      if (unit_length - 8 < needed)
 		goto no_header;
 
-	      offset = (Dwarf_Off) (offset_array_start
-				    - (unsigned char *) data->d_buf);
+	      offset += (Dwarf_Off) (offset_array_start
+				     - (unsigned char *) data->d_buf);
 	    }
 	}
     no_header:
@@ -1295,20 +1304,25 @@ __libdw_cu_locs_base (Dwarf_CU *cu)
   if (cu->locs_base == (Dwarf_Off) -1)
     {
       Dwarf_Off offset = 0;
+      Dwarf_Off dwp_offset;
+      if (dwarf_cu_dwp_section_info (cu, DW_SECT_LOCLISTS, &dwp_offset, NULL)
+	  == 0)
+	offset = dwp_offset;
+
       Dwarf_Die cu_die = CUDIE(cu);
       Dwarf_Attribute attr;
       if (dwarf_attr (&cu_die, DW_AT_loclists_base, &attr) != NULL)
 	{
 	  Dwarf_Word off;
 	  if (dwarf_formudata (&attr, &off) == 0)
-	    offset = off;
+	    offset += off;
 	}
 
       /* There wasn't an loclists_base, if the Dwarf does have a
 	 .debug_loclists section, then it might be we need the
 	 base after the first header. */
       Elf_Data *data = cu->dbg->sectiondata[IDX_debug_loclists];
-      if (offset == 0 && data != NULL)
+      if (offset == dwp_offset && data != NULL)
 	{
 	  Dwarf *dbg = cu->dbg;
 	  const unsigned char *readp = data->d_buf;
@@ -1354,8 +1368,8 @@ __libdw_cu_locs_base (Dwarf_CU *cu)
 	  if (unit_length - 8 < needed)
 	    goto no_header;
 
-	  offset = (Dwarf_Off) (offset_array_start
-				- (unsigned char *) data->d_buf);
+	  offset += (Dwarf_Off) (offset_array_start
+				 - (unsigned char *) data->d_buf);
 	}
 
     no_header:
diff --git a/tests/run-all-dwarf-ranges.sh b/tests/run-all-dwarf-ranges.sh
index cefb4231..61141cfa 100755
--- a/tests/run-all-dwarf-ranges.sh
+++ b/tests/run-all-dwarf-ranges.sh
@@ -126,4 +126,118 @@ die: no_subject (2e)
 
 EOF
 
+# See testfile-dwp.source.
+testfiles testfile-dwp-5 testfile-dwp-5.dwp
+testfiles testfile-dwp-4 testfile-dwp-4.dwp
+
+testrun_compare ${abs_builddir}/all-dwarf-ranges testfile-dwp-5 << EOF
+die: foo.cc (11)
+ 401190..401200
+
+die: foo (2e)
+ 4011c0..401200
+
+die: x_x (1d)
+ 4011cb..4011eb
+ 4011f8..401200
+
+die: <unknown> (b)
+ 4011cb..4011eb
+ 4011f8..401200
+
+die: x_x (2e)
+ 401190..4011bd
+
+die: <unknown> (b)
+ 401190..401190
+ 401192..4011bb
+
+die: bar.cc (11)
+ 401200..40121b
+
+die: bar (2e)
+ 401200..40121b
+
+die: main.cc (11)
+ 401020..4010a0
+
+die: main (2e)
+ 401020..4010a0
+
+die: fibonacci (1d)
+ 401030..401032
+ 401036..401060
+ 401099..4010a0
+
+die: fibonacci (1d)
+ 40103a..401060
+ 401099..4010a0
+
+die: <unknown> (b)
+ 40103a..401060
+ 401099..4010a0
+
+die: <unknown> (b)
+ 40103a..401044
+ 401050..401060
+
+die: <unknown> (b)
+ 401050..401053
+ 401056..401059
+
+EOF
+
+testrun_compare ${abs_builddir}/all-dwarf-ranges testfile-dwp-4 << EOF
+die: foo.cc (11)
+ 401190..401200
+
+die: foo (2e)
+ 4011c0..401200
+
+die: x_x (1d)
+ 4011cb..4011eb
+ 4011f8..401200
+
+die: <unknown> (b)
+ 4011cb..4011eb
+ 4011f8..401200
+
+die: x_x (2e)
+ 401190..4011bd
+
+die: bar.cc (11)
+ 401200..40121b
+
+die: bar (2e)
+ 401200..40121b
+
+die: main.cc (11)
+ 401020..4010a0
+
+die: main (2e)
+ 401020..4010a0
+
+die: fibonacci (1d)
+ 401030..401032
+ 401036..401060
+ 401099..4010a0
+
+die: fibonacci (1d)
+ 40103a..401060
+ 401099..4010a0
+
+die: <unknown> (b)
+ 40103a..401060
+ 401099..4010a0
+
+die: <unknown> (b)
+ 40103a..401044
+ 401050..401060
+
+die: <unknown> (b)
+ 401050..401053
+ 401056..401059
+
+EOF
+
 exit 0
diff --git a/tests/run-dwarf-getmacros.sh b/tests/run-dwarf-getmacros.sh
index 0a488fa3..220c2426 100755
--- a/tests/run-dwarf-getmacros.sh
+++ b/tests/run-dwarf-getmacros.sh
@@ -707,4 +707,1416 @@ file /home/petr/proj/elfutils/master/elfutils/x.c
 /file
 EOF
 
+# See testfile-dwp.source.
+testfiles testfile-dwp-5 testfile-dwp-5.dwp
+testfiles testfile-dwp-4-strict testfile-dwp-4-strict.dwp
+
+# Not testfile-dwp-4 because it's unclear what to do about
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99319.
+for file in testfile-dwp-5 testfile-dwp-4-strict; do
+	testrun_compare ${abs_builddir}/dwarf-getmacros "$file" '' '' << EOF
+CU foo.cc
+__STDC__ 1
+__cplusplus 201703L
+__STDC_UTF_16__ 1
+__STDC_UTF_32__ 1
+__STDC_HOSTED__ 1
+__GNUC__ 14
+__GNUC_MINOR__ 0
+__GNUC_PATCHLEVEL__ 0
+__VERSION__ "14.0.0 20230920 (experimental)"
+__ATOMIC_RELAXED 0
+__ATOMIC_SEQ_CST 5
+__ATOMIC_ACQUIRE 2
+__ATOMIC_RELEASE 3
+__ATOMIC_ACQ_REL 4
+__ATOMIC_CONSUME 1
+__OPTIMIZE__ 1
+__FINITE_MATH_ONLY__ 0
+_LP64 1
+__LP64__ 1
+__SIZEOF_INT__ 4
+__SIZEOF_LONG__ 8
+__SIZEOF_LONG_LONG__ 8
+__SIZEOF_SHORT__ 2
+__SIZEOF_FLOAT__ 4
+__SIZEOF_DOUBLE__ 8
+__SIZEOF_LONG_DOUBLE__ 16
+__SIZEOF_SIZE_T__ 8
+__CHAR_BIT__ 8
+__BIGGEST_ALIGNMENT__ 16
+__ORDER_LITTLE_ENDIAN__ 1234
+__ORDER_BIG_ENDIAN__ 4321
+__ORDER_PDP_ENDIAN__ 3412
+__BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
+__FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
+__SIZEOF_POINTER__ 8
+__GNUC_EXECUTION_CHARSET_NAME "UTF-8"
+__GNUC_WIDE_EXECUTION_CHARSET_NAME "UTF-32LE"
+__GNUG__ 14
+__SIZE_TYPE__ long unsigned int
+__PTRDIFF_TYPE__ long int
+__WCHAR_TYPE__ int
+__WINT_TYPE__ unsigned int
+__INTMAX_TYPE__ long int
+__UINTMAX_TYPE__ long unsigned int
+__CHAR16_TYPE__ short unsigned int
+__CHAR32_TYPE__ unsigned int
+__SIG_ATOMIC_TYPE__ int
+__INT8_TYPE__ signed char
+__INT16_TYPE__ short int
+__INT32_TYPE__ int
+__INT64_TYPE__ long int
+__UINT8_TYPE__ unsigned char
+__UINT16_TYPE__ short unsigned int
+__UINT32_TYPE__ unsigned int
+__UINT64_TYPE__ long unsigned int
+__INT_LEAST8_TYPE__ signed char
+__INT_LEAST16_TYPE__ short int
+__INT_LEAST32_TYPE__ int
+__INT_LEAST64_TYPE__ long int
+__UINT_LEAST8_TYPE__ unsigned char
+__UINT_LEAST16_TYPE__ short unsigned int
+__UINT_LEAST32_TYPE__ unsigned int
+__UINT_LEAST64_TYPE__ long unsigned int
+__INT_FAST8_TYPE__ signed char
+__INT_FAST16_TYPE__ long int
+__INT_FAST32_TYPE__ long int
+__INT_FAST64_TYPE__ long int
+__UINT_FAST8_TYPE__ unsigned char
+__UINT_FAST16_TYPE__ long unsigned int
+__UINT_FAST32_TYPE__ long unsigned int
+__UINT_FAST64_TYPE__ long unsigned int
+__INTPTR_TYPE__ long int
+__UINTPTR_TYPE__ long unsigned int
+__GXX_WEAK__ 1
+__DEPRECATED 1
+__GXX_RTTI 1
+__cpp_rtti 199711L
+__GXX_EXPERIMENTAL_CXX0X__ 1
+__cpp_binary_literals 201304L
+__cpp_hex_float 201603L
+__cpp_runtime_arrays 198712L
+__cpp_raw_strings 200710L
+__cpp_unicode_literals 200710L
+__cpp_user_defined_literals 200809L
+__cpp_lambdas 200907L
+__cpp_decltype 200707L
+__cpp_attributes 200809L
+__cpp_rvalue_reference 200610L
+__cpp_rvalue_references 200610L
+__cpp_variadic_templates 200704L
+__cpp_initializer_lists 200806L
+__cpp_delegating_constructors 200604L
+__cpp_nsdmi 200809L
+__cpp_inheriting_constructors 201511L
+__cpp_ref_qualifiers 200710L
+__cpp_alias_templates 200704L
+__cpp_return_type_deduction 201304L
+__cpp_init_captures 201304L
+__cpp_generic_lambdas 201304L
+__cpp_decltype_auto 201304L
+__cpp_aggregate_nsdmi 201304L
+__cpp_variable_templates 201304L
+__cpp_digit_separators 201309L
+__cpp_unicode_characters 201411L
+__cpp_static_assert 201411L
+__cpp_namespace_attributes 201411L
+__cpp_enumerator_attributes 201411L
+__cpp_nested_namespace_definitions 201411L
+__cpp_fold_expressions 201603L
+__cpp_nontype_template_args 201411L
+__cpp_range_based_for 201603L
+__cpp_constexpr 201603L
+__cpp_if_constexpr 201606L
+__cpp_capture_star_this 201603L
+__cpp_inline_variables 201606L
+__cpp_aggregate_bases 201603L
+__cpp_deduction_guides 201703L
+__cpp_noexcept_function_type 201510L
+__cpp_template_auto 201606L
+__cpp_structured_bindings 201606L
+__cpp_variadic_using 201611L
+__cpp_guaranteed_copy_elision 201606L
+__cpp_nontype_template_parameter_auto 201606L
+__cpp_sized_deallocation 201309L
+__cpp_aligned_new 201606L
+__STDCPP_DEFAULT_NEW_ALIGNMENT__ 16
+__cpp_template_template_args 201611L
+__cpp_threadsafe_static_init 200806L
+__STDCPP_THREADS__ 1
+__EXCEPTIONS 1
+__cpp_exceptions 199711L
+__GXX_ABI_VERSION 1019
+__SCHAR_MAX__ 0x7f
+__SHRT_MAX__ 0x7fff
+__INT_MAX__ 0x7fffffff
+__LONG_MAX__ 0x7fffffffffffffffL
+__LONG_LONG_MAX__ 0x7fffffffffffffffLL
+__WCHAR_MAX__ 0x7fffffff
+__WCHAR_MIN__ (-__WCHAR_MAX__ - 1)
+__WINT_MAX__ 0xffffffffU
+__WINT_MIN__ 0U
+__PTRDIFF_MAX__ 0x7fffffffffffffffL
+__SIZE_MAX__ 0xffffffffffffffffUL
+__SCHAR_WIDTH__ 8
+__SHRT_WIDTH__ 16
+__INT_WIDTH__ 32
+__LONG_WIDTH__ 64
+__LONG_LONG_WIDTH__ 64
+__WCHAR_WIDTH__ 32
+__WINT_WIDTH__ 32
+__PTRDIFF_WIDTH__ 64
+__SIZE_WIDTH__ 64
+__GLIBCXX_TYPE_INT_N_0 __int128
+__GLIBCXX_BITSIZE_INT_N_0 128
+__INTMAX_MAX__ 0x7fffffffffffffffL
+__INTMAX_C(c) c ## L
+__UINTMAX_MAX__ 0xffffffffffffffffUL
+__UINTMAX_C(c) c ## UL
+__INTMAX_WIDTH__ 64
+__SIG_ATOMIC_MAX__ 0x7fffffff
+__SIG_ATOMIC_MIN__ (-__SIG_ATOMIC_MAX__ - 1)
+__SIG_ATOMIC_WIDTH__ 32
+__INT8_MAX__ 0x7f
+__INT16_MAX__ 0x7fff
+__INT32_MAX__ 0x7fffffff
+__INT64_MAX__ 0x7fffffffffffffffL
+__UINT8_MAX__ 0xff
+__UINT16_MAX__ 0xffff
+__UINT32_MAX__ 0xffffffffU
+__UINT64_MAX__ 0xffffffffffffffffUL
+__INT_LEAST8_MAX__ 0x7f
+__INT8_C(c) c
+__INT_LEAST8_WIDTH__ 8
+__INT_LEAST16_MAX__ 0x7fff
+__INT16_C(c) c
+__INT_LEAST16_WIDTH__ 16
+__INT_LEAST32_MAX__ 0x7fffffff
+__INT32_C(c) c
+__INT_LEAST32_WIDTH__ 32
+__INT_LEAST64_MAX__ 0x7fffffffffffffffL
+__INT64_C(c) c ## L
+__INT_LEAST64_WIDTH__ 64
+__UINT_LEAST8_MAX__ 0xff
+__UINT8_C(c) c
+__UINT_LEAST16_MAX__ 0xffff
+__UINT16_C(c) c
+__UINT_LEAST32_MAX__ 0xffffffffU
+__UINT32_C(c) c ## U
+__UINT_LEAST64_MAX__ 0xffffffffffffffffUL
+__UINT64_C(c) c ## UL
+__INT_FAST8_MAX__ 0x7f
+__INT_FAST8_WIDTH__ 8
+__INT_FAST16_MAX__ 0x7fffffffffffffffL
+__INT_FAST16_WIDTH__ 64
+__INT_FAST32_MAX__ 0x7fffffffffffffffL
+__INT_FAST32_WIDTH__ 64
+__INT_FAST64_MAX__ 0x7fffffffffffffffL
+__INT_FAST64_WIDTH__ 64
+__UINT_FAST8_MAX__ 0xff
+__UINT_FAST16_MAX__ 0xffffffffffffffffUL
+__UINT_FAST32_MAX__ 0xffffffffffffffffUL
+__UINT_FAST64_MAX__ 0xffffffffffffffffUL
+__INTPTR_MAX__ 0x7fffffffffffffffL
+__INTPTR_WIDTH__ 64
+__UINTPTR_MAX__ 0xffffffffffffffffUL
+__GCC_IEC_559 2
+__GCC_IEC_559_COMPLEX 2
+__FLT_EVAL_METHOD__ 0
+__FLT_EVAL_METHOD_TS_18661_3__ 0
+__DEC_EVAL_METHOD__ 2
+__FLT_RADIX__ 2
+__FLT_MANT_DIG__ 24
+__FLT_DIG__ 6
+__FLT_MIN_EXP__ (-125)
+__FLT_MIN_10_EXP__ (-37)
+__FLT_MAX_EXP__ 128
+__FLT_MAX_10_EXP__ 38
+__FLT_DECIMAL_DIG__ 9
+__FLT_MAX__ 3.40282346638528859811704183484516925e+38F
+__FLT_NORM_MAX__ 3.40282346638528859811704183484516925e+38F
+__FLT_MIN__ 1.17549435082228750796873653722224568e-38F
+__FLT_EPSILON__ 1.19209289550781250000000000000000000e-7F
+__FLT_DENORM_MIN__ 1.40129846432481707092372958328991613e-45F
+__FLT_HAS_DENORM__ 1
+__FLT_HAS_INFINITY__ 1
+__FLT_HAS_QUIET_NAN__ 1
+__FLT_IS_IEC_60559__ 1
+__DBL_MANT_DIG__ 53
+__DBL_DIG__ 15
+__DBL_MIN_EXP__ (-1021)
+__DBL_MIN_10_EXP__ (-307)
+__DBL_MAX_EXP__ 1024
+__DBL_MAX_10_EXP__ 308
+__DBL_DECIMAL_DIG__ 17
+__DBL_MAX__ double(1.79769313486231570814527423731704357e+308L)
+__DBL_NORM_MAX__ double(1.79769313486231570814527423731704357e+308L)
+__DBL_MIN__ double(2.22507385850720138309023271733240406e-308L)
+__DBL_EPSILON__ double(2.22044604925031308084726333618164062e-16L)
+__DBL_DENORM_MIN__ double(4.94065645841246544176568792868221372e-324L)
+__DBL_HAS_DENORM__ 1
+__DBL_HAS_INFINITY__ 1
+__DBL_HAS_QUIET_NAN__ 1
+__DBL_IS_IEC_60559__ 1
+__LDBL_MANT_DIG__ 64
+__LDBL_DIG__ 18
+__LDBL_MIN_EXP__ (-16381)
+__LDBL_MIN_10_EXP__ (-4931)
+__LDBL_MAX_EXP__ 16384
+__LDBL_MAX_10_EXP__ 4932
+__DECIMAL_DIG__ 21
+__LDBL_DECIMAL_DIG__ 21
+__LDBL_MAX__ 1.18973149535723176502126385303097021e+4932L
+__LDBL_NORM_MAX__ 1.18973149535723176502126385303097021e+4932L
+__LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L
+__LDBL_EPSILON__ 1.08420217248550443400745280086994171e-19L
+__LDBL_DENORM_MIN__ 3.64519953188247460252840593361941982e-4951L
+__LDBL_HAS_DENORM__ 1
+__LDBL_HAS_INFINITY__ 1
+__LDBL_HAS_QUIET_NAN__ 1
+__LDBL_IS_IEC_60559__ 1
+__FLT16_MANT_DIG__ 11
+__FLT16_DIG__ 3
+__FLT16_MIN_EXP__ (-13)
+__FLT16_MIN_10_EXP__ (-4)
+__FLT16_MAX_EXP__ 16
+__FLT16_MAX_10_EXP__ 4
+__FLT16_DECIMAL_DIG__ 5
+__FLT16_MAX__ 6.55040000000000000000000000000000000e+4F16
+__FLT16_NORM_MAX__ 6.55040000000000000000000000000000000e+4F16
+__FLT16_MIN__ 6.10351562500000000000000000000000000e-5F16
+__FLT16_EPSILON__ 9.76562500000000000000000000000000000e-4F16
+__FLT16_DENORM_MIN__ 5.96046447753906250000000000000000000e-8F16
+__FLT16_HAS_DENORM__ 1
+__FLT16_HAS_INFINITY__ 1
+__FLT16_HAS_QUIET_NAN__ 1
+__FLT16_IS_IEC_60559__ 1
+__FLT32_MANT_DIG__ 24
+__FLT32_DIG__ 6
+__FLT32_MIN_EXP__ (-125)
+__FLT32_MIN_10_EXP__ (-37)
+__FLT32_MAX_EXP__ 128
+__FLT32_MAX_10_EXP__ 38
+__FLT32_DECIMAL_DIG__ 9
+__FLT32_MAX__ 3.40282346638528859811704183484516925e+38F32
+__FLT32_NORM_MAX__ 3.40282346638528859811704183484516925e+38F32
+__FLT32_MIN__ 1.17549435082228750796873653722224568e-38F32
+__FLT32_EPSILON__ 1.19209289550781250000000000000000000e-7F32
+__FLT32_DENORM_MIN__ 1.40129846432481707092372958328991613e-45F32
+__FLT32_HAS_DENORM__ 1
+__FLT32_HAS_INFINITY__ 1
+__FLT32_HAS_QUIET_NAN__ 1
+__FLT32_IS_IEC_60559__ 1
+__FLT64_MANT_DIG__ 53
+__FLT64_DIG__ 15
+__FLT64_MIN_EXP__ (-1021)
+__FLT64_MIN_10_EXP__ (-307)
+__FLT64_MAX_EXP__ 1024
+__FLT64_MAX_10_EXP__ 308
+__FLT64_DECIMAL_DIG__ 17
+__FLT64_MAX__ 1.79769313486231570814527423731704357e+308F64
+__FLT64_NORM_MAX__ 1.79769313486231570814527423731704357e+308F64
+__FLT64_MIN__ 2.22507385850720138309023271733240406e-308F64
+__FLT64_EPSILON__ 2.22044604925031308084726333618164062e-16F64
+__FLT64_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F64
+__FLT64_HAS_DENORM__ 1
+__FLT64_HAS_INFINITY__ 1
+__FLT64_HAS_QUIET_NAN__ 1
+__FLT64_IS_IEC_60559__ 1
+__FLT128_MANT_DIG__ 113
+__FLT128_DIG__ 33
+__FLT128_MIN_EXP__ (-16381)
+__FLT128_MIN_10_EXP__ (-4931)
+__FLT128_MAX_EXP__ 16384
+__FLT128_MAX_10_EXP__ 4932
+__FLT128_DECIMAL_DIG__ 36
+__FLT128_MAX__ 1.18973149535723176508575932662800702e+4932F128
+__FLT128_NORM_MAX__ 1.18973149535723176508575932662800702e+4932F128
+__FLT128_MIN__ 3.36210314311209350626267781732175260e-4932F128
+__FLT128_EPSILON__ 1.92592994438723585305597794258492732e-34F128
+__FLT128_DENORM_MIN__ 6.47517511943802511092443895822764655e-4966F128
+__FLT128_HAS_DENORM__ 1
+__FLT128_HAS_INFINITY__ 1
+__FLT128_HAS_QUIET_NAN__ 1
+__FLT128_IS_IEC_60559__ 1
+__FLT32X_MANT_DIG__ 53
+__FLT32X_DIG__ 15
+__FLT32X_MIN_EXP__ (-1021)
+__FLT32X_MIN_10_EXP__ (-307)
+__FLT32X_MAX_EXP__ 1024
+__FLT32X_MAX_10_EXP__ 308
+__FLT32X_DECIMAL_DIG__ 17
+__FLT32X_MAX__ 1.79769313486231570814527423731704357e+308F32x
+__FLT32X_NORM_MAX__ 1.79769313486231570814527423731704357e+308F32x
+__FLT32X_MIN__ 2.22507385850720138309023271733240406e-308F32x
+__FLT32X_EPSILON__ 2.22044604925031308084726333618164062e-16F32x
+__FLT32X_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F32x
+__FLT32X_HAS_DENORM__ 1
+__FLT32X_HAS_INFINITY__ 1
+__FLT32X_HAS_QUIET_NAN__ 1
+__FLT32X_IS_IEC_60559__ 1
+__FLT64X_MANT_DIG__ 64
+__FLT64X_DIG__ 18
+__FLT64X_MIN_EXP__ (-16381)
+__FLT64X_MIN_10_EXP__ (-4931)
+__FLT64X_MAX_EXP__ 16384
+__FLT64X_MAX_10_EXP__ 4932
+__FLT64X_DECIMAL_DIG__ 21
+__FLT64X_MAX__ 1.18973149535723176502126385303097021e+4932F64x
+__FLT64X_NORM_MAX__ 1.18973149535723176502126385303097021e+4932F64x
+__FLT64X_MIN__ 3.36210314311209350626267781732175260e-4932F64x
+__FLT64X_EPSILON__ 1.08420217248550443400745280086994171e-19F64x
+__FLT64X_DENORM_MIN__ 3.64519953188247460252840593361941982e-4951F64x
+__FLT64X_HAS_DENORM__ 1
+__FLT64X_HAS_INFINITY__ 1
+__FLT64X_HAS_QUIET_NAN__ 1
+__FLT64X_IS_IEC_60559__ 1
+__BFLT16_MANT_DIG__ 8
+__BFLT16_DIG__ 2
+__BFLT16_MIN_EXP__ (-125)
+__BFLT16_MIN_10_EXP__ (-37)
+__BFLT16_MAX_EXP__ 128
+__BFLT16_MAX_10_EXP__ 38
+__BFLT16_DECIMAL_DIG__ 4
+__BFLT16_MAX__ 3.38953138925153547590470800371487867e+38BF16
+__BFLT16_NORM_MAX__ 3.38953138925153547590470800371487867e+38BF16
+__BFLT16_MIN__ 1.17549435082228750796873653722224568e-38BF16
+__BFLT16_EPSILON__ 7.81250000000000000000000000000000000e-3BF16
+__BFLT16_DENORM_MIN__ 9.18354961579912115600575419704879436e-41BF16
+__BFLT16_HAS_DENORM__ 1
+__BFLT16_HAS_INFINITY__ 1
+__BFLT16_HAS_QUIET_NAN__ 1
+__BFLT16_IS_IEC_60559__ 0
+__DEC32_MANT_DIG__ 7
+__DEC32_MIN_EXP__ (-94)
+__DEC32_MAX_EXP__ 97
+__DEC32_MIN__ 1E-95DF
+__DEC32_MAX__ 9.999999E96DF
+__DEC32_EPSILON__ 1E-6DF
+__DEC32_SUBNORMAL_MIN__ 0.000001E-95DF
+__DEC64_MANT_DIG__ 16
+__DEC64_MIN_EXP__ (-382)
+__DEC64_MAX_EXP__ 385
+__DEC64_MIN__ 1E-383DD
+__DEC64_MAX__ 9.999999999999999E384DD
+__DEC64_EPSILON__ 1E-15DD
+__DEC64_SUBNORMAL_MIN__ 0.000000000000001E-383DD
+__DEC128_MANT_DIG__ 34
+__DEC128_MIN_EXP__ (-6142)
+__DEC128_MAX_EXP__ 6145
+__DEC128_MIN__ 1E-6143DL
+__DEC128_MAX__ 9.999999999999999999999999999999999E6144DL
+__DEC128_EPSILON__ 1E-33DL
+__DEC128_SUBNORMAL_MIN__ 0.000000000000000000000000000000001E-6143DL
+__REGISTER_PREFIX__ 
+__USER_LABEL_PREFIX__ 
+__GNUC_STDC_INLINE__ 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1
+__GCC_ATOMIC_BOOL_LOCK_FREE 2
+__GCC_ATOMIC_CHAR_LOCK_FREE 2
+__GCC_ATOMIC_CHAR16_T_LOCK_FREE 2
+__GCC_ATOMIC_CHAR32_T_LOCK_FREE 2
+__GCC_ATOMIC_WCHAR_T_LOCK_FREE 2
+__GCC_ATOMIC_SHORT_LOCK_FREE 2
+__GCC_ATOMIC_INT_LOCK_FREE 2
+__GCC_ATOMIC_LONG_LOCK_FREE 2
+__GCC_ATOMIC_LLONG_LOCK_FREE 2
+__GCC_ATOMIC_TEST_AND_SET_TRUEVAL 1
+__GCC_DESTRUCTIVE_SIZE 64
+__GCC_CONSTRUCTIVE_SIZE 64
+__GCC_ATOMIC_POINTER_LOCK_FREE 2
+__HAVE_SPECULATION_SAFE_VALUE 1
+__GCC_HAVE_DWARF2_CFI_ASM 1
+__PRAGMA_REDEFINE_EXTNAME 1
+__SIZEOF_INT128__ 16
+__SIZEOF_WCHAR_T__ 4
+__SIZEOF_WINT_T__ 4
+__SIZEOF_PTRDIFF_T__ 8
+__amd64 1
+__amd64__ 1
+__x86_64 1
+__x86_64__ 1
+__SIZEOF_FLOAT80__ 16
+__SIZEOF_FLOAT128__ 16
+__ATOMIC_HLE_ACQUIRE 65536
+__ATOMIC_HLE_RELEASE 131072
+__GCC_ASM_FLAG_OUTPUTS__ 1
+__k8 1
+__k8__ 1
+__code_model_small__ 1
+__MMX__ 1
+__SSE__ 1
+__SSE2__ 1
+__FXSR__ 1
+__SSE_MATH__ 1
+__SSE2_MATH__ 1
+__MMX_WITH_SSE__ 1
+__SEG_FS 1
+__SEG_GS 1
+__gnu_linux__ 1
+__linux 1
+__linux__ 1
+linux 1
+__unix 1
+__unix__ 1
+unix 1
+__ELF__ 1
+__DECIMAL_BID_FORMAT__ 1
+_GNU_SOURCE 1
+file /home/osandov/src/elfutils/tests/foo.cc
+ file /usr/include/stdc-predef.h
+  _STDC_PREDEF_H 1
+  __STDC_IEC_559__ 1
+  __STDC_IEC_60559_BFP__ 201404L
+  __STDC_IEC_559_COMPLEX__ 1
+  __STDC_IEC_60559_COMPLEX__ 201404L
+  __STDC_ISO_10646__ 201706L
+ /file
+ file /home/osandov/src/elfutils/tests/foobar.h
+  FROB(x) ((x) ^ 0x2a2a2a2a)
+  FRY(x) ((x) * 0x100000001b3)
+ /file
+ ZERO() (1 - 1)
+/file
+CU bar.cc
+__STDC__ 1
+__cplusplus 201703L
+__STDC_UTF_16__ 1
+__STDC_UTF_32__ 1
+__STDC_HOSTED__ 1
+__GNUC__ 14
+__GNUC_MINOR__ 0
+__GNUC_PATCHLEVEL__ 0
+__VERSION__ "14.0.0 20230920 (experimental)"
+__ATOMIC_RELAXED 0
+__ATOMIC_SEQ_CST 5
+__ATOMIC_ACQUIRE 2
+__ATOMIC_RELEASE 3
+__ATOMIC_ACQ_REL 4
+__ATOMIC_CONSUME 1
+__OPTIMIZE__ 1
+__FINITE_MATH_ONLY__ 0
+_LP64 1
+__LP64__ 1
+__SIZEOF_INT__ 4
+__SIZEOF_LONG__ 8
+__SIZEOF_LONG_LONG__ 8
+__SIZEOF_SHORT__ 2
+__SIZEOF_FLOAT__ 4
+__SIZEOF_DOUBLE__ 8
+__SIZEOF_LONG_DOUBLE__ 16
+__SIZEOF_SIZE_T__ 8
+__CHAR_BIT__ 8
+__BIGGEST_ALIGNMENT__ 16
+__ORDER_LITTLE_ENDIAN__ 1234
+__ORDER_BIG_ENDIAN__ 4321
+__ORDER_PDP_ENDIAN__ 3412
+__BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
+__FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
+__SIZEOF_POINTER__ 8
+__GNUC_EXECUTION_CHARSET_NAME "UTF-8"
+__GNUC_WIDE_EXECUTION_CHARSET_NAME "UTF-32LE"
+__GNUG__ 14
+__SIZE_TYPE__ long unsigned int
+__PTRDIFF_TYPE__ long int
+__WCHAR_TYPE__ int
+__WINT_TYPE__ unsigned int
+__INTMAX_TYPE__ long int
+__UINTMAX_TYPE__ long unsigned int
+__CHAR16_TYPE__ short unsigned int
+__CHAR32_TYPE__ unsigned int
+__SIG_ATOMIC_TYPE__ int
+__INT8_TYPE__ signed char
+__INT16_TYPE__ short int
+__INT32_TYPE__ int
+__INT64_TYPE__ long int
+__UINT8_TYPE__ unsigned char
+__UINT16_TYPE__ short unsigned int
+__UINT32_TYPE__ unsigned int
+__UINT64_TYPE__ long unsigned int
+__INT_LEAST8_TYPE__ signed char
+__INT_LEAST16_TYPE__ short int
+__INT_LEAST32_TYPE__ int
+__INT_LEAST64_TYPE__ long int
+__UINT_LEAST8_TYPE__ unsigned char
+__UINT_LEAST16_TYPE__ short unsigned int
+__UINT_LEAST32_TYPE__ unsigned int
+__UINT_LEAST64_TYPE__ long unsigned int
+__INT_FAST8_TYPE__ signed char
+__INT_FAST16_TYPE__ long int
+__INT_FAST32_TYPE__ long int
+__INT_FAST64_TYPE__ long int
+__UINT_FAST8_TYPE__ unsigned char
+__UINT_FAST16_TYPE__ long unsigned int
+__UINT_FAST32_TYPE__ long unsigned int
+__UINT_FAST64_TYPE__ long unsigned int
+__INTPTR_TYPE__ long int
+__UINTPTR_TYPE__ long unsigned int
+__GXX_WEAK__ 1
+__DEPRECATED 1
+__GXX_RTTI 1
+__cpp_rtti 199711L
+__GXX_EXPERIMENTAL_CXX0X__ 1
+__cpp_binary_literals 201304L
+__cpp_hex_float 201603L
+__cpp_runtime_arrays 198712L
+__cpp_raw_strings 200710L
+__cpp_unicode_literals 200710L
+__cpp_user_defined_literals 200809L
+__cpp_lambdas 200907L
+__cpp_decltype 200707L
+__cpp_attributes 200809L
+__cpp_rvalue_reference 200610L
+__cpp_rvalue_references 200610L
+__cpp_variadic_templates 200704L
+__cpp_initializer_lists 200806L
+__cpp_delegating_constructors 200604L
+__cpp_nsdmi 200809L
+__cpp_inheriting_constructors 201511L
+__cpp_ref_qualifiers 200710L
+__cpp_alias_templates 200704L
+__cpp_return_type_deduction 201304L
+__cpp_init_captures 201304L
+__cpp_generic_lambdas 201304L
+__cpp_decltype_auto 201304L
+__cpp_aggregate_nsdmi 201304L
+__cpp_variable_templates 201304L
+__cpp_digit_separators 201309L
+__cpp_unicode_characters 201411L
+__cpp_static_assert 201411L
+__cpp_namespace_attributes 201411L
+__cpp_enumerator_attributes 201411L
+__cpp_nested_namespace_definitions 201411L
+__cpp_fold_expressions 201603L
+__cpp_nontype_template_args 201411L
+__cpp_range_based_for 201603L
+__cpp_constexpr 201603L
+__cpp_if_constexpr 201606L
+__cpp_capture_star_this 201603L
+__cpp_inline_variables 201606L
+__cpp_aggregate_bases 201603L
+__cpp_deduction_guides 201703L
+__cpp_noexcept_function_type 201510L
+__cpp_template_auto 201606L
+__cpp_structured_bindings 201606L
+__cpp_variadic_using 201611L
+__cpp_guaranteed_copy_elision 201606L
+__cpp_nontype_template_parameter_auto 201606L
+__cpp_sized_deallocation 201309L
+__cpp_aligned_new 201606L
+__STDCPP_DEFAULT_NEW_ALIGNMENT__ 16
+__cpp_template_template_args 201611L
+__cpp_threadsafe_static_init 200806L
+__STDCPP_THREADS__ 1
+__EXCEPTIONS 1
+__cpp_exceptions 199711L
+__GXX_ABI_VERSION 1019
+__SCHAR_MAX__ 0x7f
+__SHRT_MAX__ 0x7fff
+__INT_MAX__ 0x7fffffff
+__LONG_MAX__ 0x7fffffffffffffffL
+__LONG_LONG_MAX__ 0x7fffffffffffffffLL
+__WCHAR_MAX__ 0x7fffffff
+__WCHAR_MIN__ (-__WCHAR_MAX__ - 1)
+__WINT_MAX__ 0xffffffffU
+__WINT_MIN__ 0U
+__PTRDIFF_MAX__ 0x7fffffffffffffffL
+__SIZE_MAX__ 0xffffffffffffffffUL
+__SCHAR_WIDTH__ 8
+__SHRT_WIDTH__ 16
+__INT_WIDTH__ 32
+__LONG_WIDTH__ 64
+__LONG_LONG_WIDTH__ 64
+__WCHAR_WIDTH__ 32
+__WINT_WIDTH__ 32
+__PTRDIFF_WIDTH__ 64
+__SIZE_WIDTH__ 64
+__GLIBCXX_TYPE_INT_N_0 __int128
+__GLIBCXX_BITSIZE_INT_N_0 128
+__INTMAX_MAX__ 0x7fffffffffffffffL
+__INTMAX_C(c) c ## L
+__UINTMAX_MAX__ 0xffffffffffffffffUL
+__UINTMAX_C(c) c ## UL
+__INTMAX_WIDTH__ 64
+__SIG_ATOMIC_MAX__ 0x7fffffff
+__SIG_ATOMIC_MIN__ (-__SIG_ATOMIC_MAX__ - 1)
+__SIG_ATOMIC_WIDTH__ 32
+__INT8_MAX__ 0x7f
+__INT16_MAX__ 0x7fff
+__INT32_MAX__ 0x7fffffff
+__INT64_MAX__ 0x7fffffffffffffffL
+__UINT8_MAX__ 0xff
+__UINT16_MAX__ 0xffff
+__UINT32_MAX__ 0xffffffffU
+__UINT64_MAX__ 0xffffffffffffffffUL
+__INT_LEAST8_MAX__ 0x7f
+__INT8_C(c) c
+__INT_LEAST8_WIDTH__ 8
+__INT_LEAST16_MAX__ 0x7fff
+__INT16_C(c) c
+__INT_LEAST16_WIDTH__ 16
+__INT_LEAST32_MAX__ 0x7fffffff
+__INT32_C(c) c
+__INT_LEAST32_WIDTH__ 32
+__INT_LEAST64_MAX__ 0x7fffffffffffffffL
+__INT64_C(c) c ## L
+__INT_LEAST64_WIDTH__ 64
+__UINT_LEAST8_MAX__ 0xff
+__UINT8_C(c) c
+__UINT_LEAST16_MAX__ 0xffff
+__UINT16_C(c) c
+__UINT_LEAST32_MAX__ 0xffffffffU
+__UINT32_C(c) c ## U
+__UINT_LEAST64_MAX__ 0xffffffffffffffffUL
+__UINT64_C(c) c ## UL
+__INT_FAST8_MAX__ 0x7f
+__INT_FAST8_WIDTH__ 8
+__INT_FAST16_MAX__ 0x7fffffffffffffffL
+__INT_FAST16_WIDTH__ 64
+__INT_FAST32_MAX__ 0x7fffffffffffffffL
+__INT_FAST32_WIDTH__ 64
+__INT_FAST64_MAX__ 0x7fffffffffffffffL
+__INT_FAST64_WIDTH__ 64
+__UINT_FAST8_MAX__ 0xff
+__UINT_FAST16_MAX__ 0xffffffffffffffffUL
+__UINT_FAST32_MAX__ 0xffffffffffffffffUL
+__UINT_FAST64_MAX__ 0xffffffffffffffffUL
+__INTPTR_MAX__ 0x7fffffffffffffffL
+__INTPTR_WIDTH__ 64
+__UINTPTR_MAX__ 0xffffffffffffffffUL
+__GCC_IEC_559 2
+__GCC_IEC_559_COMPLEX 2
+__FLT_EVAL_METHOD__ 0
+__FLT_EVAL_METHOD_TS_18661_3__ 0
+__DEC_EVAL_METHOD__ 2
+__FLT_RADIX__ 2
+__FLT_MANT_DIG__ 24
+__FLT_DIG__ 6
+__FLT_MIN_EXP__ (-125)
+__FLT_MIN_10_EXP__ (-37)
+__FLT_MAX_EXP__ 128
+__FLT_MAX_10_EXP__ 38
+__FLT_DECIMAL_DIG__ 9
+__FLT_MAX__ 3.40282346638528859811704183484516925e+38F
+__FLT_NORM_MAX__ 3.40282346638528859811704183484516925e+38F
+__FLT_MIN__ 1.17549435082228750796873653722224568e-38F
+__FLT_EPSILON__ 1.19209289550781250000000000000000000e-7F
+__FLT_DENORM_MIN__ 1.40129846432481707092372958328991613e-45F
+__FLT_HAS_DENORM__ 1
+__FLT_HAS_INFINITY__ 1
+__FLT_HAS_QUIET_NAN__ 1
+__FLT_IS_IEC_60559__ 1
+__DBL_MANT_DIG__ 53
+__DBL_DIG__ 15
+__DBL_MIN_EXP__ (-1021)
+__DBL_MIN_10_EXP__ (-307)
+__DBL_MAX_EXP__ 1024
+__DBL_MAX_10_EXP__ 308
+__DBL_DECIMAL_DIG__ 17
+__DBL_MAX__ double(1.79769313486231570814527423731704357e+308L)
+__DBL_NORM_MAX__ double(1.79769313486231570814527423731704357e+308L)
+__DBL_MIN__ double(2.22507385850720138309023271733240406e-308L)
+__DBL_EPSILON__ double(2.22044604925031308084726333618164062e-16L)
+__DBL_DENORM_MIN__ double(4.94065645841246544176568792868221372e-324L)
+__DBL_HAS_DENORM__ 1
+__DBL_HAS_INFINITY__ 1
+__DBL_HAS_QUIET_NAN__ 1
+__DBL_IS_IEC_60559__ 1
+__LDBL_MANT_DIG__ 64
+__LDBL_DIG__ 18
+__LDBL_MIN_EXP__ (-16381)
+__LDBL_MIN_10_EXP__ (-4931)
+__LDBL_MAX_EXP__ 16384
+__LDBL_MAX_10_EXP__ 4932
+__DECIMAL_DIG__ 21
+__LDBL_DECIMAL_DIG__ 21
+__LDBL_MAX__ 1.18973149535723176502126385303097021e+4932L
+__LDBL_NORM_MAX__ 1.18973149535723176502126385303097021e+4932L
+__LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L
+__LDBL_EPSILON__ 1.08420217248550443400745280086994171e-19L
+__LDBL_DENORM_MIN__ 3.64519953188247460252840593361941982e-4951L
+__LDBL_HAS_DENORM__ 1
+__LDBL_HAS_INFINITY__ 1
+__LDBL_HAS_QUIET_NAN__ 1
+__LDBL_IS_IEC_60559__ 1
+__FLT16_MANT_DIG__ 11
+__FLT16_DIG__ 3
+__FLT16_MIN_EXP__ (-13)
+__FLT16_MIN_10_EXP__ (-4)
+__FLT16_MAX_EXP__ 16
+__FLT16_MAX_10_EXP__ 4
+__FLT16_DECIMAL_DIG__ 5
+__FLT16_MAX__ 6.55040000000000000000000000000000000e+4F16
+__FLT16_NORM_MAX__ 6.55040000000000000000000000000000000e+4F16
+__FLT16_MIN__ 6.10351562500000000000000000000000000e-5F16
+__FLT16_EPSILON__ 9.76562500000000000000000000000000000e-4F16
+__FLT16_DENORM_MIN__ 5.96046447753906250000000000000000000e-8F16
+__FLT16_HAS_DENORM__ 1
+__FLT16_HAS_INFINITY__ 1
+__FLT16_HAS_QUIET_NAN__ 1
+__FLT16_IS_IEC_60559__ 1
+__FLT32_MANT_DIG__ 24
+__FLT32_DIG__ 6
+__FLT32_MIN_EXP__ (-125)
+__FLT32_MIN_10_EXP__ (-37)
+__FLT32_MAX_EXP__ 128
+__FLT32_MAX_10_EXP__ 38
+__FLT32_DECIMAL_DIG__ 9
+__FLT32_MAX__ 3.40282346638528859811704183484516925e+38F32
+__FLT32_NORM_MAX__ 3.40282346638528859811704183484516925e+38F32
+__FLT32_MIN__ 1.17549435082228750796873653722224568e-38F32
+__FLT32_EPSILON__ 1.19209289550781250000000000000000000e-7F32
+__FLT32_DENORM_MIN__ 1.40129846432481707092372958328991613e-45F32
+__FLT32_HAS_DENORM__ 1
+__FLT32_HAS_INFINITY__ 1
+__FLT32_HAS_QUIET_NAN__ 1
+__FLT32_IS_IEC_60559__ 1
+__FLT64_MANT_DIG__ 53
+__FLT64_DIG__ 15
+__FLT64_MIN_EXP__ (-1021)
+__FLT64_MIN_10_EXP__ (-307)
+__FLT64_MAX_EXP__ 1024
+__FLT64_MAX_10_EXP__ 308
+__FLT64_DECIMAL_DIG__ 17
+__FLT64_MAX__ 1.79769313486231570814527423731704357e+308F64
+__FLT64_NORM_MAX__ 1.79769313486231570814527423731704357e+308F64
+__FLT64_MIN__ 2.22507385850720138309023271733240406e-308F64
+__FLT64_EPSILON__ 2.22044604925031308084726333618164062e-16F64
+__FLT64_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F64
+__FLT64_HAS_DENORM__ 1
+__FLT64_HAS_INFINITY__ 1
+__FLT64_HAS_QUIET_NAN__ 1
+__FLT64_IS_IEC_60559__ 1
+__FLT128_MANT_DIG__ 113
+__FLT128_DIG__ 33
+__FLT128_MIN_EXP__ (-16381)
+__FLT128_MIN_10_EXP__ (-4931)
+__FLT128_MAX_EXP__ 16384
+__FLT128_MAX_10_EXP__ 4932
+__FLT128_DECIMAL_DIG__ 36
+__FLT128_MAX__ 1.18973149535723176508575932662800702e+4932F128
+__FLT128_NORM_MAX__ 1.18973149535723176508575932662800702e+4932F128
+__FLT128_MIN__ 3.36210314311209350626267781732175260e-4932F128
+__FLT128_EPSILON__ 1.92592994438723585305597794258492732e-34F128
+__FLT128_DENORM_MIN__ 6.47517511943802511092443895822764655e-4966F128
+__FLT128_HAS_DENORM__ 1
+__FLT128_HAS_INFINITY__ 1
+__FLT128_HAS_QUIET_NAN__ 1
+__FLT128_IS_IEC_60559__ 1
+__FLT32X_MANT_DIG__ 53
+__FLT32X_DIG__ 15
+__FLT32X_MIN_EXP__ (-1021)
+__FLT32X_MIN_10_EXP__ (-307)
+__FLT32X_MAX_EXP__ 1024
+__FLT32X_MAX_10_EXP__ 308
+__FLT32X_DECIMAL_DIG__ 17
+__FLT32X_MAX__ 1.79769313486231570814527423731704357e+308F32x
+__FLT32X_NORM_MAX__ 1.79769313486231570814527423731704357e+308F32x
+__FLT32X_MIN__ 2.22507385850720138309023271733240406e-308F32x
+__FLT32X_EPSILON__ 2.22044604925031308084726333618164062e-16F32x
+__FLT32X_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F32x
+__FLT32X_HAS_DENORM__ 1
+__FLT32X_HAS_INFINITY__ 1
+__FLT32X_HAS_QUIET_NAN__ 1
+__FLT32X_IS_IEC_60559__ 1
+__FLT64X_MANT_DIG__ 64
+__FLT64X_DIG__ 18
+__FLT64X_MIN_EXP__ (-16381)
+__FLT64X_MIN_10_EXP__ (-4931)
+__FLT64X_MAX_EXP__ 16384
+__FLT64X_MAX_10_EXP__ 4932
+__FLT64X_DECIMAL_DIG__ 21
+__FLT64X_MAX__ 1.18973149535723176502126385303097021e+4932F64x
+__FLT64X_NORM_MAX__ 1.18973149535723176502126385303097021e+4932F64x
+__FLT64X_MIN__ 3.36210314311209350626267781732175260e-4932F64x
+__FLT64X_EPSILON__ 1.08420217248550443400745280086994171e-19F64x
+__FLT64X_DENORM_MIN__ 3.64519953188247460252840593361941982e-4951F64x
+__FLT64X_HAS_DENORM__ 1
+__FLT64X_HAS_INFINITY__ 1
+__FLT64X_HAS_QUIET_NAN__ 1
+__FLT64X_IS_IEC_60559__ 1
+__BFLT16_MANT_DIG__ 8
+__BFLT16_DIG__ 2
+__BFLT16_MIN_EXP__ (-125)
+__BFLT16_MIN_10_EXP__ (-37)
+__BFLT16_MAX_EXP__ 128
+__BFLT16_MAX_10_EXP__ 38
+__BFLT16_DECIMAL_DIG__ 4
+__BFLT16_MAX__ 3.38953138925153547590470800371487867e+38BF16
+__BFLT16_NORM_MAX__ 3.38953138925153547590470800371487867e+38BF16
+__BFLT16_MIN__ 1.17549435082228750796873653722224568e-38BF16
+__BFLT16_EPSILON__ 7.81250000000000000000000000000000000e-3BF16
+__BFLT16_DENORM_MIN__ 9.18354961579912115600575419704879436e-41BF16
+__BFLT16_HAS_DENORM__ 1
+__BFLT16_HAS_INFINITY__ 1
+__BFLT16_HAS_QUIET_NAN__ 1
+__BFLT16_IS_IEC_60559__ 0
+__DEC32_MANT_DIG__ 7
+__DEC32_MIN_EXP__ (-94)
+__DEC32_MAX_EXP__ 97
+__DEC32_MIN__ 1E-95DF
+__DEC32_MAX__ 9.999999E96DF
+__DEC32_EPSILON__ 1E-6DF
+__DEC32_SUBNORMAL_MIN__ 0.000001E-95DF
+__DEC64_MANT_DIG__ 16
+__DEC64_MIN_EXP__ (-382)
+__DEC64_MAX_EXP__ 385
+__DEC64_MIN__ 1E-383DD
+__DEC64_MAX__ 9.999999999999999E384DD
+__DEC64_EPSILON__ 1E-15DD
+__DEC64_SUBNORMAL_MIN__ 0.000000000000001E-383DD
+__DEC128_MANT_DIG__ 34
+__DEC128_MIN_EXP__ (-6142)
+__DEC128_MAX_EXP__ 6145
+__DEC128_MIN__ 1E-6143DL
+__DEC128_MAX__ 9.999999999999999999999999999999999E6144DL
+__DEC128_EPSILON__ 1E-33DL
+__DEC128_SUBNORMAL_MIN__ 0.000000000000000000000000000000001E-6143DL
+__REGISTER_PREFIX__ 
+__USER_LABEL_PREFIX__ 
+__GNUC_STDC_INLINE__ 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1
+__GCC_ATOMIC_BOOL_LOCK_FREE 2
+__GCC_ATOMIC_CHAR_LOCK_FREE 2
+__GCC_ATOMIC_CHAR16_T_LOCK_FREE 2
+__GCC_ATOMIC_CHAR32_T_LOCK_FREE 2
+__GCC_ATOMIC_WCHAR_T_LOCK_FREE 2
+__GCC_ATOMIC_SHORT_LOCK_FREE 2
+__GCC_ATOMIC_INT_LOCK_FREE 2
+__GCC_ATOMIC_LONG_LOCK_FREE 2
+__GCC_ATOMIC_LLONG_LOCK_FREE 2
+__GCC_ATOMIC_TEST_AND_SET_TRUEVAL 1
+__GCC_DESTRUCTIVE_SIZE 64
+__GCC_CONSTRUCTIVE_SIZE 64
+__GCC_ATOMIC_POINTER_LOCK_FREE 2
+__HAVE_SPECULATION_SAFE_VALUE 1
+__GCC_HAVE_DWARF2_CFI_ASM 1
+__PRAGMA_REDEFINE_EXTNAME 1
+__SIZEOF_INT128__ 16
+__SIZEOF_WCHAR_T__ 4
+__SIZEOF_WINT_T__ 4
+__SIZEOF_PTRDIFF_T__ 8
+__amd64 1
+__amd64__ 1
+__x86_64 1
+__x86_64__ 1
+__SIZEOF_FLOAT80__ 16
+__SIZEOF_FLOAT128__ 16
+__ATOMIC_HLE_ACQUIRE 65536
+__ATOMIC_HLE_RELEASE 131072
+__GCC_ASM_FLAG_OUTPUTS__ 1
+__k8 1
+__k8__ 1
+__code_model_small__ 1
+__MMX__ 1
+__SSE__ 1
+__SSE2__ 1
+__FXSR__ 1
+__SSE_MATH__ 1
+__SSE2_MATH__ 1
+__MMX_WITH_SSE__ 1
+__SEG_FS 1
+__SEG_GS 1
+__gnu_linux__ 1
+__linux 1
+__linux__ 1
+linux 1
+__unix 1
+__unix__ 1
+unix 1
+__ELF__ 1
+__DECIMAL_BID_FORMAT__ 1
+_GNU_SOURCE 1
+file /home/osandov/src/elfutils/tests/bar.cc
+ file /usr/include/stdc-predef.h
+  _STDC_PREDEF_H 1
+  __STDC_IEC_559__ 1
+  __STDC_IEC_60559_BFP__ 201404L
+  __STDC_IEC_559_COMPLEX__ 1
+  __STDC_IEC_60559_COMPLEX__ 201404L
+  __STDC_ISO_10646__ 201706L
+ /file
+ file /home/osandov/src/elfutils/tests/foobar.h
+  FROB(x) ((x) ^ 0x2a2a2a2a)
+  FRY(x) ((x) * 0x100000001b3)
+ /file
+ ONE 1
+/file
+CU main.cc
+__STDC__ 1
+__cplusplus 201703L
+__STDC_UTF_16__ 1
+__STDC_UTF_32__ 1
+__STDC_HOSTED__ 1
+__GNUC__ 14
+__GNUC_MINOR__ 0
+__GNUC_PATCHLEVEL__ 0
+__VERSION__ "14.0.0 20230920 (experimental)"
+__ATOMIC_RELAXED 0
+__ATOMIC_SEQ_CST 5
+__ATOMIC_ACQUIRE 2
+__ATOMIC_RELEASE 3
+__ATOMIC_ACQ_REL 4
+__ATOMIC_CONSUME 1
+__OPTIMIZE__ 1
+__FINITE_MATH_ONLY__ 0
+_LP64 1
+__LP64__ 1
+__SIZEOF_INT__ 4
+__SIZEOF_LONG__ 8
+__SIZEOF_LONG_LONG__ 8
+__SIZEOF_SHORT__ 2
+__SIZEOF_FLOAT__ 4
+__SIZEOF_DOUBLE__ 8
+__SIZEOF_LONG_DOUBLE__ 16
+__SIZEOF_SIZE_T__ 8
+__CHAR_BIT__ 8
+__BIGGEST_ALIGNMENT__ 16
+__ORDER_LITTLE_ENDIAN__ 1234
+__ORDER_BIG_ENDIAN__ 4321
+__ORDER_PDP_ENDIAN__ 3412
+__BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
+__FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
+__SIZEOF_POINTER__ 8
+__GNUC_EXECUTION_CHARSET_NAME "UTF-8"
+__GNUC_WIDE_EXECUTION_CHARSET_NAME "UTF-32LE"
+__GNUG__ 14
+__SIZE_TYPE__ long unsigned int
+__PTRDIFF_TYPE__ long int
+__WCHAR_TYPE__ int
+__WINT_TYPE__ unsigned int
+__INTMAX_TYPE__ long int
+__UINTMAX_TYPE__ long unsigned int
+__CHAR16_TYPE__ short unsigned int
+__CHAR32_TYPE__ unsigned int
+__SIG_ATOMIC_TYPE__ int
+__INT8_TYPE__ signed char
+__INT16_TYPE__ short int
+__INT32_TYPE__ int
+__INT64_TYPE__ long int
+__UINT8_TYPE__ unsigned char
+__UINT16_TYPE__ short unsigned int
+__UINT32_TYPE__ unsigned int
+__UINT64_TYPE__ long unsigned int
+__INT_LEAST8_TYPE__ signed char
+__INT_LEAST16_TYPE__ short int
+__INT_LEAST32_TYPE__ int
+__INT_LEAST64_TYPE__ long int
+__UINT_LEAST8_TYPE__ unsigned char
+__UINT_LEAST16_TYPE__ short unsigned int
+__UINT_LEAST32_TYPE__ unsigned int
+__UINT_LEAST64_TYPE__ long unsigned int
+__INT_FAST8_TYPE__ signed char
+__INT_FAST16_TYPE__ long int
+__INT_FAST32_TYPE__ long int
+__INT_FAST64_TYPE__ long int
+__UINT_FAST8_TYPE__ unsigned char
+__UINT_FAST16_TYPE__ long unsigned int
+__UINT_FAST32_TYPE__ long unsigned int
+__UINT_FAST64_TYPE__ long unsigned int
+__INTPTR_TYPE__ long int
+__UINTPTR_TYPE__ long unsigned int
+__GXX_WEAK__ 1
+__DEPRECATED 1
+__GXX_RTTI 1
+__cpp_rtti 199711L
+__GXX_EXPERIMENTAL_CXX0X__ 1
+__cpp_binary_literals 201304L
+__cpp_hex_float 201603L
+__cpp_runtime_arrays 198712L
+__cpp_raw_strings 200710L
+__cpp_unicode_literals 200710L
+__cpp_user_defined_literals 200809L
+__cpp_lambdas 200907L
+__cpp_decltype 200707L
+__cpp_attributes 200809L
+__cpp_rvalue_reference 200610L
+__cpp_rvalue_references 200610L
+__cpp_variadic_templates 200704L
+__cpp_initializer_lists 200806L
+__cpp_delegating_constructors 200604L
+__cpp_nsdmi 200809L
+__cpp_inheriting_constructors 201511L
+__cpp_ref_qualifiers 200710L
+__cpp_alias_templates 200704L
+__cpp_return_type_deduction 201304L
+__cpp_init_captures 201304L
+__cpp_generic_lambdas 201304L
+__cpp_decltype_auto 201304L
+__cpp_aggregate_nsdmi 201304L
+__cpp_variable_templates 201304L
+__cpp_digit_separators 201309L
+__cpp_unicode_characters 201411L
+__cpp_static_assert 201411L
+__cpp_namespace_attributes 201411L
+__cpp_enumerator_attributes 201411L
+__cpp_nested_namespace_definitions 201411L
+__cpp_fold_expressions 201603L
+__cpp_nontype_template_args 201411L
+__cpp_range_based_for 201603L
+__cpp_constexpr 201603L
+__cpp_if_constexpr 201606L
+__cpp_capture_star_this 201603L
+__cpp_inline_variables 201606L
+__cpp_aggregate_bases 201603L
+__cpp_deduction_guides 201703L
+__cpp_noexcept_function_type 201510L
+__cpp_template_auto 201606L
+__cpp_structured_bindings 201606L
+__cpp_variadic_using 201611L
+__cpp_guaranteed_copy_elision 201606L
+__cpp_nontype_template_parameter_auto 201606L
+__cpp_sized_deallocation 201309L
+__cpp_aligned_new 201606L
+__STDCPP_DEFAULT_NEW_ALIGNMENT__ 16
+__cpp_template_template_args 201611L
+__cpp_threadsafe_static_init 200806L
+__STDCPP_THREADS__ 1
+__EXCEPTIONS 1
+__cpp_exceptions 199711L
+__GXX_ABI_VERSION 1019
+__SCHAR_MAX__ 0x7f
+__SHRT_MAX__ 0x7fff
+__INT_MAX__ 0x7fffffff
+__LONG_MAX__ 0x7fffffffffffffffL
+__LONG_LONG_MAX__ 0x7fffffffffffffffLL
+__WCHAR_MAX__ 0x7fffffff
+__WCHAR_MIN__ (-__WCHAR_MAX__ - 1)
+__WINT_MAX__ 0xffffffffU
+__WINT_MIN__ 0U
+__PTRDIFF_MAX__ 0x7fffffffffffffffL
+__SIZE_MAX__ 0xffffffffffffffffUL
+__SCHAR_WIDTH__ 8
+__SHRT_WIDTH__ 16
+__INT_WIDTH__ 32
+__LONG_WIDTH__ 64
+__LONG_LONG_WIDTH__ 64
+__WCHAR_WIDTH__ 32
+__WINT_WIDTH__ 32
+__PTRDIFF_WIDTH__ 64
+__SIZE_WIDTH__ 64
+__GLIBCXX_TYPE_INT_N_0 __int128
+__GLIBCXX_BITSIZE_INT_N_0 128
+__INTMAX_MAX__ 0x7fffffffffffffffL
+__INTMAX_C(c) c ## L
+__UINTMAX_MAX__ 0xffffffffffffffffUL
+__UINTMAX_C(c) c ## UL
+__INTMAX_WIDTH__ 64
+__SIG_ATOMIC_MAX__ 0x7fffffff
+__SIG_ATOMIC_MIN__ (-__SIG_ATOMIC_MAX__ - 1)
+__SIG_ATOMIC_WIDTH__ 32
+__INT8_MAX__ 0x7f
+__INT16_MAX__ 0x7fff
+__INT32_MAX__ 0x7fffffff
+__INT64_MAX__ 0x7fffffffffffffffL
+__UINT8_MAX__ 0xff
+__UINT16_MAX__ 0xffff
+__UINT32_MAX__ 0xffffffffU
+__UINT64_MAX__ 0xffffffffffffffffUL
+__INT_LEAST8_MAX__ 0x7f
+__INT8_C(c) c
+__INT_LEAST8_WIDTH__ 8
+__INT_LEAST16_MAX__ 0x7fff
+__INT16_C(c) c
+__INT_LEAST16_WIDTH__ 16
+__INT_LEAST32_MAX__ 0x7fffffff
+__INT32_C(c) c
+__INT_LEAST32_WIDTH__ 32
+__INT_LEAST64_MAX__ 0x7fffffffffffffffL
+__INT64_C(c) c ## L
+__INT_LEAST64_WIDTH__ 64
+__UINT_LEAST8_MAX__ 0xff
+__UINT8_C(c) c
+__UINT_LEAST16_MAX__ 0xffff
+__UINT16_C(c) c
+__UINT_LEAST32_MAX__ 0xffffffffU
+__UINT32_C(c) c ## U
+__UINT_LEAST64_MAX__ 0xffffffffffffffffUL
+__UINT64_C(c) c ## UL
+__INT_FAST8_MAX__ 0x7f
+__INT_FAST8_WIDTH__ 8
+__INT_FAST16_MAX__ 0x7fffffffffffffffL
+__INT_FAST16_WIDTH__ 64
+__INT_FAST32_MAX__ 0x7fffffffffffffffL
+__INT_FAST32_WIDTH__ 64
+__INT_FAST64_MAX__ 0x7fffffffffffffffL
+__INT_FAST64_WIDTH__ 64
+__UINT_FAST8_MAX__ 0xff
+__UINT_FAST16_MAX__ 0xffffffffffffffffUL
+__UINT_FAST32_MAX__ 0xffffffffffffffffUL
+__UINT_FAST64_MAX__ 0xffffffffffffffffUL
+__INTPTR_MAX__ 0x7fffffffffffffffL
+__INTPTR_WIDTH__ 64
+__UINTPTR_MAX__ 0xffffffffffffffffUL
+__GCC_IEC_559 2
+__GCC_IEC_559_COMPLEX 2
+__FLT_EVAL_METHOD__ 0
+__FLT_EVAL_METHOD_TS_18661_3__ 0
+__DEC_EVAL_METHOD__ 2
+__FLT_RADIX__ 2
+__FLT_MANT_DIG__ 24
+__FLT_DIG__ 6
+__FLT_MIN_EXP__ (-125)
+__FLT_MIN_10_EXP__ (-37)
+__FLT_MAX_EXP__ 128
+__FLT_MAX_10_EXP__ 38
+__FLT_DECIMAL_DIG__ 9
+__FLT_MAX__ 3.40282346638528859811704183484516925e+38F
+__FLT_NORM_MAX__ 3.40282346638528859811704183484516925e+38F
+__FLT_MIN__ 1.17549435082228750796873653722224568e-38F
+__FLT_EPSILON__ 1.19209289550781250000000000000000000e-7F
+__FLT_DENORM_MIN__ 1.40129846432481707092372958328991613e-45F
+__FLT_HAS_DENORM__ 1
+__FLT_HAS_INFINITY__ 1
+__FLT_HAS_QUIET_NAN__ 1
+__FLT_IS_IEC_60559__ 1
+__DBL_MANT_DIG__ 53
+__DBL_DIG__ 15
+__DBL_MIN_EXP__ (-1021)
+__DBL_MIN_10_EXP__ (-307)
+__DBL_MAX_EXP__ 1024
+__DBL_MAX_10_EXP__ 308
+__DBL_DECIMAL_DIG__ 17
+__DBL_MAX__ double(1.79769313486231570814527423731704357e+308L)
+__DBL_NORM_MAX__ double(1.79769313486231570814527423731704357e+308L)
+__DBL_MIN__ double(2.22507385850720138309023271733240406e-308L)
+__DBL_EPSILON__ double(2.22044604925031308084726333618164062e-16L)
+__DBL_DENORM_MIN__ double(4.94065645841246544176568792868221372e-324L)
+__DBL_HAS_DENORM__ 1
+__DBL_HAS_INFINITY__ 1
+__DBL_HAS_QUIET_NAN__ 1
+__DBL_IS_IEC_60559__ 1
+__LDBL_MANT_DIG__ 64
+__LDBL_DIG__ 18
+__LDBL_MIN_EXP__ (-16381)
+__LDBL_MIN_10_EXP__ (-4931)
+__LDBL_MAX_EXP__ 16384
+__LDBL_MAX_10_EXP__ 4932
+__DECIMAL_DIG__ 21
+__LDBL_DECIMAL_DIG__ 21
+__LDBL_MAX__ 1.18973149535723176502126385303097021e+4932L
+__LDBL_NORM_MAX__ 1.18973149535723176502126385303097021e+4932L
+__LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L
+__LDBL_EPSILON__ 1.08420217248550443400745280086994171e-19L
+__LDBL_DENORM_MIN__ 3.64519953188247460252840593361941982e-4951L
+__LDBL_HAS_DENORM__ 1
+__LDBL_HAS_INFINITY__ 1
+__LDBL_HAS_QUIET_NAN__ 1
+__LDBL_IS_IEC_60559__ 1
+__FLT16_MANT_DIG__ 11
+__FLT16_DIG__ 3
+__FLT16_MIN_EXP__ (-13)
+__FLT16_MIN_10_EXP__ (-4)
+__FLT16_MAX_EXP__ 16
+__FLT16_MAX_10_EXP__ 4
+__FLT16_DECIMAL_DIG__ 5
+__FLT16_MAX__ 6.55040000000000000000000000000000000e+4F16
+__FLT16_NORM_MAX__ 6.55040000000000000000000000000000000e+4F16
+__FLT16_MIN__ 6.10351562500000000000000000000000000e-5F16
+__FLT16_EPSILON__ 9.76562500000000000000000000000000000e-4F16
+__FLT16_DENORM_MIN__ 5.96046447753906250000000000000000000e-8F16
+__FLT16_HAS_DENORM__ 1
+__FLT16_HAS_INFINITY__ 1
+__FLT16_HAS_QUIET_NAN__ 1
+__FLT16_IS_IEC_60559__ 1
+__FLT32_MANT_DIG__ 24
+__FLT32_DIG__ 6
+__FLT32_MIN_EXP__ (-125)
+__FLT32_MIN_10_EXP__ (-37)
+__FLT32_MAX_EXP__ 128
+__FLT32_MAX_10_EXP__ 38
+__FLT32_DECIMAL_DIG__ 9
+__FLT32_MAX__ 3.40282346638528859811704183484516925e+38F32
+__FLT32_NORM_MAX__ 3.40282346638528859811704183484516925e+38F32
+__FLT32_MIN__ 1.17549435082228750796873653722224568e-38F32
+__FLT32_EPSILON__ 1.19209289550781250000000000000000000e-7F32
+__FLT32_DENORM_MIN__ 1.40129846432481707092372958328991613e-45F32
+__FLT32_HAS_DENORM__ 1
+__FLT32_HAS_INFINITY__ 1
+__FLT32_HAS_QUIET_NAN__ 1
+__FLT32_IS_IEC_60559__ 1
+__FLT64_MANT_DIG__ 53
+__FLT64_DIG__ 15
+__FLT64_MIN_EXP__ (-1021)
+__FLT64_MIN_10_EXP__ (-307)
+__FLT64_MAX_EXP__ 1024
+__FLT64_MAX_10_EXP__ 308
+__FLT64_DECIMAL_DIG__ 17
+__FLT64_MAX__ 1.79769313486231570814527423731704357e+308F64
+__FLT64_NORM_MAX__ 1.79769313486231570814527423731704357e+308F64
+__FLT64_MIN__ 2.22507385850720138309023271733240406e-308F64
+__FLT64_EPSILON__ 2.22044604925031308084726333618164062e-16F64
+__FLT64_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F64
+__FLT64_HAS_DENORM__ 1
+__FLT64_HAS_INFINITY__ 1
+__FLT64_HAS_QUIET_NAN__ 1
+__FLT64_IS_IEC_60559__ 1
+__FLT128_MANT_DIG__ 113
+__FLT128_DIG__ 33
+__FLT128_MIN_EXP__ (-16381)
+__FLT128_MIN_10_EXP__ (-4931)
+__FLT128_MAX_EXP__ 16384
+__FLT128_MAX_10_EXP__ 4932
+__FLT128_DECIMAL_DIG__ 36
+__FLT128_MAX__ 1.18973149535723176508575932662800702e+4932F128
+__FLT128_NORM_MAX__ 1.18973149535723176508575932662800702e+4932F128
+__FLT128_MIN__ 3.36210314311209350626267781732175260e-4932F128
+__FLT128_EPSILON__ 1.92592994438723585305597794258492732e-34F128
+__FLT128_DENORM_MIN__ 6.47517511943802511092443895822764655e-4966F128
+__FLT128_HAS_DENORM__ 1
+__FLT128_HAS_INFINITY__ 1
+__FLT128_HAS_QUIET_NAN__ 1
+__FLT128_IS_IEC_60559__ 1
+__FLT32X_MANT_DIG__ 53
+__FLT32X_DIG__ 15
+__FLT32X_MIN_EXP__ (-1021)
+__FLT32X_MIN_10_EXP__ (-307)
+__FLT32X_MAX_EXP__ 1024
+__FLT32X_MAX_10_EXP__ 308
+__FLT32X_DECIMAL_DIG__ 17
+__FLT32X_MAX__ 1.79769313486231570814527423731704357e+308F32x
+__FLT32X_NORM_MAX__ 1.79769313486231570814527423731704357e+308F32x
+__FLT32X_MIN__ 2.22507385850720138309023271733240406e-308F32x
+__FLT32X_EPSILON__ 2.22044604925031308084726333618164062e-16F32x
+__FLT32X_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F32x
+__FLT32X_HAS_DENORM__ 1
+__FLT32X_HAS_INFINITY__ 1
+__FLT32X_HAS_QUIET_NAN__ 1
+__FLT32X_IS_IEC_60559__ 1
+__FLT64X_MANT_DIG__ 64
+__FLT64X_DIG__ 18
+__FLT64X_MIN_EXP__ (-16381)
+__FLT64X_MIN_10_EXP__ (-4931)
+__FLT64X_MAX_EXP__ 16384
+__FLT64X_MAX_10_EXP__ 4932
+__FLT64X_DECIMAL_DIG__ 21
+__FLT64X_MAX__ 1.18973149535723176502126385303097021e+4932F64x
+__FLT64X_NORM_MAX__ 1.18973149535723176502126385303097021e+4932F64x
+__FLT64X_MIN__ 3.36210314311209350626267781732175260e-4932F64x
+__FLT64X_EPSILON__ 1.08420217248550443400745280086994171e-19F64x
+__FLT64X_DENORM_MIN__ 3.64519953188247460252840593361941982e-4951F64x
+__FLT64X_HAS_DENORM__ 1
+__FLT64X_HAS_INFINITY__ 1
+__FLT64X_HAS_QUIET_NAN__ 1
+__FLT64X_IS_IEC_60559__ 1
+__BFLT16_MANT_DIG__ 8
+__BFLT16_DIG__ 2
+__BFLT16_MIN_EXP__ (-125)
+__BFLT16_MIN_10_EXP__ (-37)
+__BFLT16_MAX_EXP__ 128
+__BFLT16_MAX_10_EXP__ 38
+__BFLT16_DECIMAL_DIG__ 4
+__BFLT16_MAX__ 3.38953138925153547590470800371487867e+38BF16
+__BFLT16_NORM_MAX__ 3.38953138925153547590470800371487867e+38BF16
+__BFLT16_MIN__ 1.17549435082228750796873653722224568e-38BF16
+__BFLT16_EPSILON__ 7.81250000000000000000000000000000000e-3BF16
+__BFLT16_DENORM_MIN__ 9.18354961579912115600575419704879436e-41BF16
+__BFLT16_HAS_DENORM__ 1
+__BFLT16_HAS_INFINITY__ 1
+__BFLT16_HAS_QUIET_NAN__ 1
+__BFLT16_IS_IEC_60559__ 0
+__DEC32_MANT_DIG__ 7
+__DEC32_MIN_EXP__ (-94)
+__DEC32_MAX_EXP__ 97
+__DEC32_MIN__ 1E-95DF
+__DEC32_MAX__ 9.999999E96DF
+__DEC32_EPSILON__ 1E-6DF
+__DEC32_SUBNORMAL_MIN__ 0.000001E-95DF
+__DEC64_MANT_DIG__ 16
+__DEC64_MIN_EXP__ (-382)
+__DEC64_MAX_EXP__ 385
+__DEC64_MIN__ 1E-383DD
+__DEC64_MAX__ 9.999999999999999E384DD
+__DEC64_EPSILON__ 1E-15DD
+__DEC64_SUBNORMAL_MIN__ 0.000000000000001E-383DD
+__DEC128_MANT_DIG__ 34
+__DEC128_MIN_EXP__ (-6142)
+__DEC128_MAX_EXP__ 6145
+__DEC128_MIN__ 1E-6143DL
+__DEC128_MAX__ 9.999999999999999999999999999999999E6144DL
+__DEC128_EPSILON__ 1E-33DL
+__DEC128_SUBNORMAL_MIN__ 0.000000000000000000000000000000001E-6143DL
+__REGISTER_PREFIX__ 
+__USER_LABEL_PREFIX__ 
+__GNUC_STDC_INLINE__ 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1
+__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1
+__GCC_ATOMIC_BOOL_LOCK_FREE 2
+__GCC_ATOMIC_CHAR_LOCK_FREE 2
+__GCC_ATOMIC_CHAR16_T_LOCK_FREE 2
+__GCC_ATOMIC_CHAR32_T_LOCK_FREE 2
+__GCC_ATOMIC_WCHAR_T_LOCK_FREE 2
+__GCC_ATOMIC_SHORT_LOCK_FREE 2
+__GCC_ATOMIC_INT_LOCK_FREE 2
+__GCC_ATOMIC_LONG_LOCK_FREE 2
+__GCC_ATOMIC_LLONG_LOCK_FREE 2
+__GCC_ATOMIC_TEST_AND_SET_TRUEVAL 1
+__GCC_DESTRUCTIVE_SIZE 64
+__GCC_CONSTRUCTIVE_SIZE 64
+__GCC_ATOMIC_POINTER_LOCK_FREE 2
+__HAVE_SPECULATION_SAFE_VALUE 1
+__GCC_HAVE_DWARF2_CFI_ASM 1
+__PRAGMA_REDEFINE_EXTNAME 1
+__SIZEOF_INT128__ 16
+__SIZEOF_WCHAR_T__ 4
+__SIZEOF_WINT_T__ 4
+__SIZEOF_PTRDIFF_T__ 8
+__amd64 1
+__amd64__ 1
+__x86_64 1
+__x86_64__ 1
+__SIZEOF_FLOAT80__ 16
+__SIZEOF_FLOAT128__ 16
+__ATOMIC_HLE_ACQUIRE 65536
+__ATOMIC_HLE_RELEASE 131072
+__GCC_ASM_FLAG_OUTPUTS__ 1
+__k8 1
+__k8__ 1
+__code_model_small__ 1
+__MMX__ 1
+__SSE__ 1
+__SSE2__ 1
+__FXSR__ 1
+__SSE_MATH__ 1
+__SSE2_MATH__ 1
+__MMX_WITH_SSE__ 1
+__SEG_FS 1
+__SEG_GS 1
+__gnu_linux__ 1
+__linux 1
+__linux__ 1
+linux 1
+__unix 1
+__unix__ 1
+unix 1
+__ELF__ 1
+__DECIMAL_BID_FORMAT__ 1
+_GNU_SOURCE 1
+file /home/osandov/src/elfutils/tests/main.cc
+ file /usr/include/stdc-predef.h
+  _STDC_PREDEF_H 1
+  __STDC_IEC_559__ 1
+  __STDC_IEC_60559_BFP__ 201404L
+  __STDC_IEC_559_COMPLEX__ 1
+  __STDC_IEC_60559_COMPLEX__ 201404L
+  __STDC_ISO_10646__ 201706L
+ /file
+ file /home/osandov/src/elfutils/tests/foobar.h
+  FROB(x) ((x) ^ 0x2a2a2a2a)
+  FRY(x) ((x) * 0x100000001b3)
+ /file
+ MAIN_ARGS int argc, char **argv
+/file
+EOF
+done
+
 exit 0
diff --git a/tests/run-get-units-split.sh b/tests/run-get-units-split.sh
index 7a43c670..6c7a4f25 100755
--- a/tests/run-get-units-split.sh
+++ b/tests/run-get-units-split.sh
@@ -63,4 +63,22 @@ EOF
 testrun_on_self_exe ${abs_builddir}/get-units-split
 testrun_on_self_lib ${abs_builddir}/get-units-split
 
+# See testfile-dwp.source.
+testfiles testfile-dwp-5 testfile-dwp-5.dwp
+testfiles testfile-dwp-4 testfile-dwp-4.dwp
+testfiles testfile-dwp-4-strict testfile-dwp-4-strict.dwp
+
+for file in testfile-dwp-5 testfile-dwp-4 testfile-dwp-4-strict; do
+	testrun_compare ${abs_builddir}/get-units-split "$file" << EOF
+file: $file
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: foo.cc
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: bar.cc
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: main.cc
+
+EOF
+done
+
 exit 0
diff --git a/tests/run-varlocs.sh b/tests/run-varlocs.sh
index cbbcf6c7..4ac5f367 100755
--- a/tests/run-varlocs.sh
+++ b/tests/run-varlocs.sh
@@ -601,4 +601,116 @@ module 'testfile-splitdwarf4-not-split4.debug'
       [4011a0,4011a1) {lit0, stack_value}
 EOF
 
+# See testfile-dwp.source.
+testfiles testfile-dwp-5 testfile-dwp-5.dwp
+testfiles testfile-dwp-4 testfile-dwp-4.dwp
+
+testrun_compare ${abs_builddir}/varlocs -e testfile-dwp-5 << EOF
+module 'testfile-dwp-5'
+[84] CU 'foo.cc'@401190
+  [c6] function 'foo'@4011c0
+    frame_base: {call_frame_cfa {bregx(7,8)}}
+    [e1] parameter 'this'
+      [4011c0,401200) {reg5}
+    [ea] variable 'x'
+      [4011c2,4011d4) {reg0}
+      [4011d4,4011d7) {reg1}
+      [4011d7,4011d9) {breg1(1), stack_value}
+      [4011f1,401200) {reg0}
+  [f9] inlined function 'x_x'@4011cb
+    [104] parameter 'x'
+      [4011cb,4011eb) {reg0}
+      [4011f1,401200) {reg0}
+  [14a] function 'x_x'@401190
+    frame_base: {call_frame_cfa {bregx(7,8)}}
+    [15b] parameter 'x'
+      [401190,4011a1) {reg5}
+      [4011a1,4011bd) {reg0}
+module 'testfile-dwp-5'
+[1fa] CU 'bar.cc'@401200
+  [23c] function 'bar'@401200
+    frame_base: {call_frame_cfa {bregx(7,8)}}
+    [253] parameter 'this'
+      [401200,40121b) {reg5}
+module 'testfile-dwp-5'
+[272] CU 'main.cc'@0
+  [2c7] function 'main'@401020
+    frame_base: {call_frame_cfa {bregx(7,8)}}
+    [2e0] parameter 'argc'
+      [401020,401068) {reg5}
+      [401068,401080) {fbreg(-40)}
+      [401080,401084) {breg5(0)}
+      [401084,401099) {entry_value(1) {reg5}, stack_value}
+      [401099,4010a0) {reg5}
+    [2ec] parameter 'argv'
+      [401020,40104b) {reg4}
+      [40104b,401099) {entry_value(1) {reg4}, stack_value}
+      [401099,4010a0) {reg4}
+    [2f8] variable 'myfoo'
+      [401020,4010a0) {fbreg(-40)}
+    [303] variable 'mybar'
+      [401020,4010a0) {fbreg(-32)}
+  [30d] inlined function 'fibonacci'@401030
+    [31c] parameter 'n'
+      [401030,401060) {reg5}
+      [401099,4010a0) {reg5}
+  [326] inlined function 'fibonacci'
+    [32f] parameter 'n'
+      <no value>
+EOF
+
+testrun_compare ${abs_builddir}/varlocs -e testfile-dwp-4 << EOF
+module 'testfile-dwp-4'
+[b] CU 'foo.cc'@401190
+  [54] function 'foo'@4011c0
+    frame_base: {call_frame_cfa {bregx(7,8)}}
+    [6f] parameter 'this'
+      [4011c0,401200) {reg5}
+    [78] variable 'x'
+      [4011c2,4011d4) {reg0}
+      [4011d4,4011d7) {reg1}
+      [4011d7,4011d9) {breg1(1), stack_value}
+      [4011f1,401200) {reg0}
+  [8a] inlined function 'x_x'@4011cb
+    [98] parameter 'x'
+      [4011cb,4011eb) {reg0}
+      [4011f1,401200) {reg0}
+  [e7] function 'x_x'@401190
+    frame_base: {call_frame_cfa {bregx(7,8)}}
+    [f8] parameter 'x'
+      [401190,4011a1) {reg5}
+      [4011a1,4011bd) {reg0}
+module 'testfile-dwp-4'
+[129] CU 'bar.cc'@401200
+  [172] function 'bar'@401200
+    frame_base: {call_frame_cfa {bregx(7,8)}}
+    [189] parameter 'this'
+      [401200,40121b) {reg5}
+module 'testfile-dwp-4'
+[19f] CU 'main.cc'@0
+  [1fd] function 'main'@401020
+    frame_base: {call_frame_cfa {bregx(7,8)}}
+    [216] parameter 'argc'
+      [401020,401068) {reg5}
+      [401068,401080) {fbreg(-40)}
+      [401080,401084) {breg5(0)}
+      [401084,401097) {GNU_entry_value(1) {reg5}, stack_value}
+      [401099,4010a0) {reg5}
+    [228] parameter 'argv'
+      [401020,40104b) {reg4}
+      [40104b,401099) {GNU_entry_value(1) {reg4}, stack_value}
+      [401099,4010a0) {reg4}
+    [23a] variable 'myfoo'
+      [401020,4010a0) {fbreg(-40)}
+    [247] variable 'mybar'
+      [401020,4010a0) {fbreg(-32)}
+  [253] inlined function 'fibonacci'@401030
+    [265] parameter 'n'
+      [401030,401060) {reg5}
+      [401099,4010a0) {reg5}
+  [272] inlined function 'fibonacci'
+    [27e] parameter 'n'
+      <no value>
+EOF
+
 exit 0
-- 
2.43.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 4/4] libdw: Handle overflowed DW_SECT_INFO offsets in DWARF package file indexes
  2023-12-06  9:22 [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support Omar Sandoval
                   ` (2 preceding siblings ...)
  2023-12-06  9:22 ` [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate Omar Sandoval
@ 2023-12-06  9:22 ` Omar Sandoval
  2024-01-04  0:53 ` [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support Omar Sandoval
  4 siblings, 0 replies; 14+ messages in thread
From: Omar Sandoval @ 2023-12-06  9:22 UTC (permalink / raw)
  To: elfutils-devel

From: Omar Sandoval <osandov@fb.com>

Meta uses DWARF package files for our large, statically-linked C++
applications.  Some of our largest applications have more than 4GB in
.debug_info.dwo, but the section offsets in .debug_cu_index and
.debug_tu_index are 32 bits; see the discussion here [1].  We
implemented a workaround/extension for this in LLVM.  Implement the
equivalent in libdw.

To test this, we need files with more than 4GB in .debug_info.dwo.  I
created these artificially by editing GCC's assembly output.  They
compress down to 6KB.  I test them from run-large-elf-file.sh to take
advantage of the existing checks for large file support.

1: https://discourse.llvm.org/t/dwarf-dwp-4gb-limit/63902.

 	* libdw/dwarf_end.c (dwarf_package_index_free): New function.
	* tests/testfile-dwp-4-cu-index-overflow.bz2: New test file.
	* tests/testfile-dwp-4-cu-index-overflow.dwp.bz2: New test file.
	* tests/testfile-dwp-5-cu-index-overflow.bz2: New test file.
	* tests/testfile-dwp-5-cu-index-overflow.dwp.bz2: New test file.
	* tests/testfile-dwp-cu-index-overflow.source: New file.
	* tests/run-large-elf-file.sh: Check
	testfile-dwp-5-cu-index-overflow and
	testfile-dwp-4-cu-index-overflow.

Signed-off-by: Omar Sandoval <osandov@fb.com>
---
 libdw/dwarf_cu_dwp_section_info.c             | 147 ++++++++++++++-
 libdw/dwarf_end.c                             |  15 +-
 libdw/libdwP.h                                |   3 +
 tests/Makefile.am                             |   6 +-
 tests/run-large-elf-file.sh                   | 174 ++++++++++++++++++
 tests/testfile-dwp-4-cu-index-overflow.bz2    | Bin 0 -> 4490 bytes
 .../testfile-dwp-4-cu-index-overflow.dwp.bz2  | Bin 0 -> 5584 bytes
 tests/testfile-dwp-5-cu-index-overflow.bz2    | Bin 0 -> 4544 bytes
 .../testfile-dwp-5-cu-index-overflow.dwp.bz2  | Bin 0 -> 5790 bytes
 tests/testfile-dwp-cu-index-overflow.source   |  86 +++++++++
 10 files changed, 425 insertions(+), 6 deletions(-)
 create mode 100755 tests/testfile-dwp-4-cu-index-overflow.bz2
 create mode 100644 tests/testfile-dwp-4-cu-index-overflow.dwp.bz2
 create mode 100755 tests/testfile-dwp-5-cu-index-overflow.bz2
 create mode 100644 tests/testfile-dwp-5-cu-index-overflow.dwp.bz2
 create mode 100644 tests/testfile-dwp-cu-index-overflow.source

diff --git a/libdw/dwarf_cu_dwp_section_info.c b/libdw/dwarf_cu_dwp_section_info.c
index 298f36f9..3d11c87a 100644
--- a/libdw/dwarf_cu_dwp_section_info.c
+++ b/libdw/dwarf_cu_dwp_section_info.c
@@ -30,6 +30,8 @@
 # include <config.h>
 #endif
 
+#include <assert.h>
+
 #include "libdwP.h"
 
 static Dwarf_Package_Index *
@@ -110,7 +112,9 @@ __libdw_read_package_index (Dwarf *dbg, bool tu)
 
   index->dbg = dbg;
   /* Set absent sections to UINT32_MAX.  */
-  memset (index->sections, 0xff, sizeof (index->sections));
+  for (size_t i = 0;
+       i < sizeof (index->sections) / sizeof (index->sections[0]); i++)
+    index->sections[i] = UINT32_MAX;
   for (size_t i = 0; i < section_count; i++)
     {
       uint32_t section = read_4ubyte_unaligned (dbg, sections + i * 4);
@@ -161,6 +165,7 @@ __libdw_read_package_index (Dwarf *dbg, bool tu)
   index->indices = indices;
   index->section_offsets = section_offsets;
   index->section_sizes = section_sizes;
+  index->debug_info_offsets = NULL;
 
   return index;
 }
@@ -177,6 +182,137 @@ __libdw_package_index (Dwarf *dbg, bool tu)
   if (index == NULL)
     return NULL;
 
+  /* Offsets in the section offset table are 32-bit unsigned integers.  In
+     practice, the .debug_info.dwo section for very large executables can be
+     larger than 4GB.  GNU dwp as of binutils 2.41 and llvm-dwp before LLVM 15
+     both accidentally truncate offsets larger than 4GB.
+
+     LLVM 15 detects the overflow and errors out instead; see LLVM commit
+     f8df8114715b ("[DWP][DWARF] Detect and error on debug info offset
+     overflow").  However, lldb in LLVM 16 supports using dwp files with
+     truncated offsets by recovering them directly from the unit headers in the
+     .debug_info.dwo section; see LLVM commit c0db06227721 ("[DWARFLibrary] Add
+     support to re-construct cu-index").  Since LLVM 17, the overflow error can
+     be turned into a warning instead; see LLVM commit 53a483cee801 ("[DWP] add
+     overflow check for llvm-dwp tools if offset overflow").
+
+     LLVM's support for > 4GB offsets is effectively an extension to the DWARF
+     package file format, which we implement here.  The strategy is to walk the
+     unit headers in .debug_info.dwo in lockstep with the DW_SECT_INFO columns
+     in the section offset tables.  As long as they are in the same order
+     (which they are in practice for both GNU dwp and llvm-dwp), we can
+     correlate the truncated offset and produce a corrected array of offsets.
+
+     Note that this will be fixed properly in DWARF 6:
+     https://dwarfstd.org/issues/220708.2.html.  */
+  if (index->sections[DW_SECT_INFO - 1] != UINT32_MAX
+      && dbg->sectiondata[IDX_debug_info]->d_size > UINT32_MAX)
+    {
+      Dwarf_Package_Index *cu_index, *tu_index = NULL;
+      if (tu)
+	{
+	  tu_index = index;
+	  assert (dbg->cu_index == NULL);
+	  cu_index = __libdw_read_package_index (dbg, false);
+	  if (cu_index == NULL)
+	    {
+	      free(index);
+	      return NULL;
+	    }
+	}
+      else
+	{
+	  cu_index = index;
+	  if (dbg->sectiondata[IDX_debug_tu_index] != NULL
+	      && dbg->sectiondata[IDX_debug_types] == NULL)
+	    {
+	      assert (dbg->tu_index == NULL);
+	      tu_index = __libdw_read_package_index (dbg, true);
+	      if (tu_index == NULL)
+		{
+		  free(index);
+		  return NULL;
+		}
+	    }
+	}
+
+      cu_index->debug_info_offsets = malloc (cu_index->unit_count
+					     * sizeof (Dwarf_Off));
+      if (cu_index->debug_info_offsets == NULL)
+	{
+	  free (tu_index);
+	  free (cu_index);
+	  __libdw_seterrno (DWARF_E_NOMEM);
+	  return NULL;
+	}
+      if (tu_index != NULL)
+	{
+	  tu_index->debug_info_offsets = malloc (tu_index->unit_count
+						 * sizeof (Dwarf_Off));
+	  if (tu_index->debug_info_offsets == NULL)
+	    {
+	      free (tu_index);
+	      free (cu_index->debug_info_offsets);
+	      free (cu_index);
+	      __libdw_seterrno (DWARF_E_NOMEM);
+	      return NULL;
+	    }
+	}
+
+      Dwarf_Off off = 0;
+      uint32_t cui = 0, tui = 0;
+      uint32_t cu_count = cu_index->unit_count;
+      const unsigned char *cu_offset
+	= cu_index->section_offsets + cu_index->sections[DW_SECT_INFO - 1] * 4;
+      uint32_t tu_count = 0;
+      const unsigned char *tu_offset;
+      if (tu_index != NULL)
+	{
+	  tu_count = tu_index->unit_count;
+	  tu_offset = tu_index->section_offsets
+		      + tu_index->sections[DW_SECT_INFO - 1] * 4;
+	}
+      while (cui < cu_count || tui < tu_count)
+	{
+	  Dwarf_Off next_off;
+	  uint8_t unit_type;
+	  if (__libdw_next_unit (dbg, false, off, &next_off, NULL, NULL,
+				 &unit_type, NULL, NULL, NULL, NULL, NULL)
+	      != 0)
+	    {
+	    not_sorted:
+	      free (cu_index->debug_info_offsets);
+	      cu_index->debug_info_offsets = NULL;
+	      if (tu_index != NULL)
+		{
+		  free (tu_index->debug_info_offsets);
+		  tu_index->debug_info_offsets = NULL;
+		}
+	      break;
+	    }
+	  if (unit_type != DW_UT_split_type && cui < cu_count)
+	    {
+	      if ((off & UINT32_MAX) != read_4ubyte_unaligned (dbg, cu_offset))
+		goto not_sorted;
+	      cu_index->debug_info_offsets[cui++] = off;
+	      cu_offset += cu_index->section_count * 4;
+	    }
+	  else if (unit_type == DW_UT_split_type && tui < tu_count)
+	    {
+	      if ((off & UINT32_MAX) != read_4ubyte_unaligned (dbg, tu_offset))
+		goto not_sorted;
+	      tu_index->debug_info_offsets[tui++] = off;
+	      tu_offset += tu_index->section_count * 4;
+	    }
+	  off = next_off;
+	}
+
+      if (tu)
+	dbg->cu_index = cu_index;
+      else if (tu_index != NULL)
+	dbg->tu_index = tu_index;
+    }
+
   if (tu)
     dbg->tu_index = index;
   else
@@ -244,8 +380,13 @@ __libdw_dwp_section_info (Dwarf_Package_Index *index, uint32_t unit_row,
   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 (section == DW_SECT_INFO && index->debug_info_offsets != NULL)
+	*offsetp = index->debug_info_offsets[unit_row - 1];
+      else
+	*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);
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index 78224ddb..ed8d27be 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -40,6 +40,17 @@
 #include "cfi.h"
 
 
+static void
+dwarf_package_index_free (Dwarf_Package_Index *index)
+{
+  if (index != NULL)
+    {
+      free (index->debug_info_offsets);
+      free (index);
+    }
+}
+
+
 static void
 noop_free (void *arg __attribute__ ((unused)))
 {
@@ -79,8 +90,8 @@ dwarf_end (Dwarf *dwarf)
 {
   if (dwarf != NULL)
     {
-      free (dwarf->tu_index);
-      free (dwarf->cu_index);
+      dwarf_package_index_free (dwarf->tu_index);
+      dwarf_package_index_free (dwarf->cu_index);
 
       if (dwarf->cfi != NULL)
 	/* Clean up the CFI cache.  */
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 64d86bbd..4a4c20ac 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -371,6 +371,9 @@ typedef struct Dwarf_Package_Index_s
   const unsigned char *indices;
   const unsigned char *section_offsets;
   const unsigned char *section_sizes;
+  /* If DW_SECT_INFO section offsets were truncated to 32 bits, recovered
+     64-bit offsets.  */
+  Dwarf_Off *debug_info_offsets;
 } Dwarf_Package_Index;
 
 /* CU representation.  */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 34014570..3bb01ea0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -638,7 +638,11 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
 	     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
+	     run-cu-dwp-section-info.sh testfile-dwp-5-cu-index-overflow \
+	     testfile-dwp-5-cu-index-overflow.dwp \
+	     testfile-dwp-4-cu-index-overflow \
+	     testfile-dwp-4-cu-index-overflow.dwp \
+	     testfile-dwp-cu-index-overflow.source
 
 
 if USE_VALGRIND
diff --git a/tests/run-large-elf-file.sh b/tests/run-large-elf-file.sh
index 7116de53..8108cb4b 100755
--- a/tests/run-large-elf-file.sh
+++ b/tests/run-large-elf-file.sh
@@ -122,4 +122,178 @@ test_file testfile38
 # 64bit, big endian, non-rel
 test_file testfile27
 
+# See testfile-dwp-cu-index-overflow.source
+testfiles testfile-dwp-5-cu-index-overflow testfile-dwp-5-cu-index-overflow.dwp
+
+testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-5-cu-index-overflow.dwp << EOF
+file: testfile-dwp-5-cu-index-overflow.dwp
+INFO: 0x0 0x8000004c
+TYPES: 0x0 0x0
+ABBREV: 0x0 0x50
+LINE: 0x0 0x61
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x0 0x1c
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+INFO: 0x8000004c 0x6f
+TYPES: 0x0 0x0
+ABBREV: 0x50 0x15e
+LINE: 0x61 0x63
+LOCLISTS: 0x0 0xd4
+STR_OFFSETS: 0x1c 0x24
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x22
+
+INFO: 0x800000bb 0xff
+TYPES: 0x0 0x0
+ABBREV: 0x50 0x15e
+LINE: 0x61 0x63
+LOCLISTS: 0x0 0xd4
+STR_OFFSETS: 0x1c 0x24
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x22
+
+INFO: 0x800001ba 0x8000004c
+TYPES: 0x0 0x0
+ABBREV: 0x1ae 0x50
+LINE: 0xc4 0x61
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x40 0x1c
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+INFO: 0x100000206 0x6c
+TYPES: 0x0 0x0
+ABBREV: 0x1fe 0xc8
+LINE: 0x125 0x63
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x5c 0x20
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+INFO: 0x100000272 0x6f
+TYPES: 0x0 0x0
+ABBREV: 0x1fe 0xc8
+LINE: 0x125 0x63
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x5c 0x20
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+INFO: 0x1000002e1 0x182
+TYPES: 0x0 0x0
+ABBREV: 0x2c6 0x188
+LINE: 0x188 0x65
+LOCLISTS: 0xd4 0xee
+STR_OFFSETS: 0x7c 0x44
+MACRO: 0x0 0x0
+RNGLISTS: 0x22 0x43
+
+EOF
+
+testrun_compare ${abs_builddir}/get-units-split testfile-dwp-5-cu-index-overflow << EOF
+file: testfile-dwp-5-cu-index-overflow
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: filler1.cc
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: foo.cc
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: filler2.cc
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: bar.cc
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: main.cc
+
+EOF
+
+rm -f testfile-dwp-5-cu-index-overflow testfile-dwp-5-cu-index-overflow.dwp
+
+# See testfile-dwp-cu-index-overflow.source
+testfiles testfile-dwp-4-cu-index-overflow testfile-dwp-4-cu-index-overflow.dwp
+
+testrun_compare ${abs_builddir}/cu-dwp-section-info testfile-dwp-4-cu-index-overflow.dwp << EOF
+file: testfile-dwp-4-cu-index-overflow.dwp
+INFO: 0x0 0x8000004b
+TYPES: 0x0 0x0
+ABBREV: 0x0 0x58
+LINE: 0x0 0x2c
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x0 0x14
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+INFO: 0x8000004b 0x116
+TYPES: 0x0 0x0
+ABBREV: 0x58 0x16f
+LINE: 0x2c 0x34
+LOCLISTS: 0x0 0x110
+STR_OFFSETS: 0x14 0x1c
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+INFO: 0x80000161 0x8000004b
+TYPES: 0x0 0x0
+ABBREV: 0x1c7 0x58
+LINE: 0x60 0x2c
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x30 0x14
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+INFO: 0x1000001ac 0x6e
+TYPES: 0x0 0x0
+ABBREV: 0x21f 0xd4
+LINE: 0x8c 0x34
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x44 0x18
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+INFO: 0x10000021a 0x1b5
+TYPES: 0x0 0x0
+ABBREV: 0x2f3 0x19b
+LINE: 0xc0 0x35
+LOCLISTS: 0x110 0x12a
+STR_OFFSETS: 0x5c 0x3c
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+INFO: 0x0 0x0
+TYPES: 0x0 0x6e
+ABBREV: 0x58 0x16f
+LINE: 0x2c 0x34
+LOCLISTS: 0x0 0x110
+STR_OFFSETS: 0x14 0x1c
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+INFO: 0x0 0x0
+TYPES: 0x6e 0x6b
+ABBREV: 0x21f 0xd4
+LINE: 0x8c 0x34
+LOCLISTS: 0x0 0x0
+STR_OFFSETS: 0x44 0x18
+MACRO: 0x0 0x0
+RNGLISTS: 0x0 0x0
+
+EOF
+
+testrun_compare ${abs_builddir}/get-units-split testfile-dwp-4-cu-index-overflow << EOF
+file: testfile-dwp-4-cu-index-overflow
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: filler1.cc
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: foo.cc
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: filler2.cc
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: bar.cc
+Got cudie unit_type: 4
+Found a skeleton unit, with split die: main.cc
+
+EOF
+
+rm -f testfile-dwp-4-cu-index-overflow testfile-dwp-4-cu-index-overflow.dwp
+
 exit 0
diff --git a/tests/testfile-dwp-4-cu-index-overflow.bz2 b/tests/testfile-dwp-4-cu-index-overflow.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..2aaa4f40b8d0bacf75f617a9ad642a6ead10a6ef
GIT binary patch
literal 4490
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

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-4-cu-index-overflow.dwp.bz2 b/tests/testfile-dwp-4-cu-index-overflow.dwp.bz2
new file mode 100644
index 0000000000000000000000000000000000000000..0718c1b2c40310655cdb78ae9859b7b682a5fcaf
GIT binary patch
literal 5584
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
Pwxd_;8J$6MsC@D-pOqt+

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-5-cu-index-overflow.bz2 b/tests/testfile-dwp-5-cu-index-overflow.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..07185fe56fcbfcf9a6fa01e6ef9428fee3843f34
GIT binary patch
literal 4544
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

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-5-cu-index-overflow.dwp.bz2 b/tests/testfile-dwp-5-cu-index-overflow.dwp.bz2
new file mode 100644
index 0000000000000000000000000000000000000000..dce34e3f51d637e4773c3c8e4ebeb4666b4c0571
GIT binary patch
literal 5790
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
ZQVs!Wj1ebEH_TeZF<rUYrnf)Q{|jo{jkEv&

literal 0
HcmV?d00001

diff --git a/tests/testfile-dwp-cu-index-overflow.source b/tests/testfile-dwp-cu-index-overflow.source
new file mode 100644
index 00000000..2de15441
--- /dev/null
+++ b/tests/testfile-dwp-cu-index-overflow.source
@@ -0,0 +1,86 @@
+# Dummy program that we patch to generate a dwp file with more than 4GB of
+# .debug_info.
+
+# Generate 2 dummy files that result in DWARF blocks.
+$ for (( i = 1; i <= 2; i++ )); do echo 'constexpr int filler'$i'[] = { 1 };' > filler$i.cc; done
+$ g++ -O2 -g -gsplit-dwarf -fdebug-types-section -dA -S filler{1,2}.cc foo.cc bar.cc main.cc
+# Patch the DWARF blocks to be 2GB.
+$ for (( i = 1; i <= 2; i++ )); do patch -p1 << EOF
+--- a/filler$i.s
++++ b/filler$i.s
+@@ -7,5 +7,5 @@
+ 	.section	.debug_info.dwo,"e",@progbits
+ .Ldebug_info0:
+-	.long	0x49	# Length of Compilation Unit Info
++	.long	0x80000048	# Length of Compilation Unit Info
+ 	.value	0x5	# DWARF version number
+ 	.byte	0x5	# DW_UT_split_compile
+@@ -51,9 +51,6 @@
+ 	.long	0x29	# DW_AT_type
+ 			# DW_AT_const_expr
+-	.byte	0x4	# DW_AT_const_value
+-	.byte	0x1	# fp or vector constant word 0
+-	.byte	0	# fp or vector constant word 1
+-	.byte	0	# fp or vector constant word 2
+-	.byte	0	# fp or vector constant word 3
++	.long	0x80000000	# DW_AT_const_value
++	.fill	0x80000000
+ 	.byte	0	# end of children of DIE 0x14
+ 	.section	.debug_info,"",@progbits
+@@ -171,5 +168,5 @@
+ 	.uleb128 0x19	# (DW_FORM_flag_present)
+ 	.uleb128 0x1c	# (DW_AT_const_value)
+-	.uleb128 0xa	# (DW_FORM_block1)
++	.uleb128 0x4	# (DW_FORM_block4)
+ 	.byte	0
+ 	.byte	0
+EOF
+done
+$ for (( i = 1; i <= 2; i++ )); do as filler$i.s -o filler$i.o; done
+$ as foo.s -o foo.o
+$ as bar.s -o bar.o
+$ as main.s -o main.o
+$ g++ filler1.o foo.o filler2.o bar.o main.o -o testfile-dwp-5-cu-index-overflow
+# -continue-on-cu-index-overflow was added in LLVM 17:
+# https://reviews.llvm.org/D144565.
+$ llvm-dwp -continue-on-cu-index-overflow filler1.o foo.o filler2.o bar.o main.o -o testfile-dwp-5-cu-index-overflow.dwp
+
+# Same thing for DWARF 4.
+$ g++ -O2 -g -gdwarf-4 -gsplit-dwarf -fdebug-types-section -dA -S filler{1,2}.cc foo.cc bar.cc main.cc
+$ for (( i = 1; i <= 2; i++ )); do patch -p1 << EOF
+--- a/filler$i.s
++++ b/filler$i.s
+@@ -6,5 +6,5 @@
+ 	.section	.debug_info.dwo,"e",@progbits
+ .Ldebug_info0:
+-	.long	0x48	# Length of Compilation Unit Info
++	.long	0x80000047	# Length of Compilation Unit Info
+ 	.value	0x4	# DWARF version number
+ 	.long	.Ldebug_abbrev0	# Offset Into Abbrev. Section
+@@ -49,9 +49,6 @@
+ 	.long	0x28	# DW_AT_type
+ 			# DW_AT_const_expr
+-	.byte	0x4	# DW_AT_const_value
+-	.byte	0x1	# fp or vector constant word 0
+-	.byte	0	# fp or vector constant word 1
+-	.byte	0	# fp or vector constant word 2
+-	.byte	0	# fp or vector constant word 3
++	.long	0x80000000	# DW_AT_const_value
++	.fill	0x80000000
+ 	.byte	0	# end of children of DIE 0xb
+ 	.section	.debug_info,"",@progbits
+@@ -172,5 +169,5 @@
+ 	.uleb128 0x19	# (DW_FORM_flag_present)
+ 	.uleb128 0x1c	# (DW_AT_const_value)
+-	.uleb128 0xa	# (DW_FORM_block1)
++	.uleb128 0x4	# (DW_FORM_block4)
+ 	.byte	0
+ 	.byte	0
+EOF
+done
+$ for (( i = 1; i <= 2; i++ )); do as filler$i.s -o filler$i.o; done
+$ as foo.s -o foo.o
+$ as bar.s -o bar.o
+$ as main.s -o main.o
+$ g++ filler1.o foo.o filler2.o bar.o main.o -o testfile-dwp-4-cu-index-overflow
+$ llvm-dwp -continue-on-cu-index-overflow filler1.o foo.o filler2.o bar.o main.o -o testfile-dwp-4-cu-index-overflow.dwp
-- 
2.43.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support
  2023-12-06  9:22 [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support Omar Sandoval
                   ` (3 preceding siblings ...)
  2023-12-06  9:22 ` [PATCH v2 4/4] libdw: Handle overflowed DW_SECT_INFO offsets in DWARF package file indexes Omar Sandoval
@ 2024-01-04  0:53 ` Omar Sandoval
  4 siblings, 0 replies; 14+ messages in thread
From: Omar Sandoval @ 2024-01-04  0:53 UTC (permalink / raw)
  To: elfutils-devel

On Wed, Dec 06, 2023 at 01:22:15AM -0800, Omar Sandoval wrote:
> From: Omar Sandoval <osandov@fb.com>
> 
> Hi,
> 
> This is version 2 of my patch series adding support for DWARF package
> files to libdw and the elfutils tools. Version 1 is here [1].
> 
> Patches 1-3 add the main implementation and tests for dwp files.
> 
> Most of this support is internal to libdw, but patch 1 adds a new public
> function, dwarf_cu_dwp_section_info. drgn's dwp branch [2] demonstrates
> how that function will be used. Also see [3] for more context on why
> drgn needs this.
> 
> Patch 4 adds support and tests for an LLVM extension to the dwp format.
> The "extension" is ugly because of an oversight in the design of the
> format that LLVM had to make the best of, but unfortunately it's
> necessary for a lot of our use cases.
> 
> With this patch series, drgn's test suite passes against a Linux kernel
> build using .dwp.
> 
> Changes from v1:
> 
> * Rebased on main and dropped patches that were already merged.
> * Moved ChangeLog entries to commit messages.
> * Updated version in libdw.map to 0.191.
> * Moved DW_SECT_TYPES definition to dwarf.h.
> * Added copyright years.
> * Added error handling for dwarf_cu_dwp_section_info calls in
>   str_offsets_base_off, __libdw_cu_ranges_base, and __libdw_cu_locs_base
> * Changed memset initialization of index->sections to an explicit
>   loop.
> * Added comment explaining __libdw_link_skel_split change.
> 
> There were a couple of things that were mentioned in review that I
> didn't change:
> 
> * I kept dwarf_cu_dwp_section_info in patch 1 instead of separating it
>   into its own patch so that I could test the dwp index implementation
>   in the same commit that I introduced it in.
> * I didn't make try_dwp_file return an error since try_split_file that
>   it's based on doesn't either.
> 
> Thanks!
> Omar
> 
> 1: https://sourceware.org/pipermail/elfutils-devel/2023q3/006410.html
> 2: https://github.com/osandov/drgn/tree/dwp
> 3: https://sourceware.org/pipermail/elfutils-devel/2023q4/006630.html
> 
> Omar Sandoval (4):
>   libdw: Parse DWARF package file index sections
>   libdw: Try .dwp file in __libdw_find_split_unit()
>   libdw: Apply DWARF package file section offsets where appropriate
>   libdw: Handle overflowed DW_SECT_INFO offsets in DWARF package file
>     indexes

Ping, and happy new year :)

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/4] libdw: Parse DWARF package file index sections
  2023-12-06  9:22 ` [PATCH v2 1/4] libdw: Parse DWARF package file index sections Omar Sandoval
@ 2024-02-15 17:40   ` Mark Wielaard
  0 siblings, 0 replies; 14+ messages in thread
From: Mark Wielaard @ 2024-02-15 17:40 UTC (permalink / raw)
  To: Omar Sandoval, elfutils-devel

Hi Omar,

On Wed, 2023-12-06 at 01:22 -0800, Omar Sandoval wrote:
> 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].

You convinced me that dwarf_cu_dwp_section_info should be a public
function. And speaking of eu-readelf, we should also make it parse the
.debug_tu_index and .debug_cu_index sections. But that is for another
time.

Pushed with one merge conflict resolved.

Please look over the review comments below to see if I interpreted
anything wrongly.

> 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 <osandov@fb.com>
> ---
>  libdw/Makefile.am                   |   2 +-
>  libdw/dwarf.h                       |   2 +-
>  libdw/dwarf_cu_dwp_section_info.c   | 371 ++++++++++++++++++++++++++++
>  libdw/dwarf_end.c                   |   3 +
>  libdw/dwarf_error.c                 |   1 +
>  libdw/libdw.h                       |  23 ++
>  libdw/libdw.map                     |   5 +
>  libdw/libdwP.h                      |  33 +++
>  libdw/libdw_findcu.c                |   8 +
>  tests/.gitignore                    |   1 +
>  tests/Makefile.am                   |  11 +-
>  tests/cu-dwp-section-info.c         |  73 ++++++
>  tests/run-cu-dwp-section-info.sh    | 168 +++++++++++++
>  tests/testfile-dwp-4-strict.bz2     | Bin 0 -> 4169 bytes
>  tests/testfile-dwp-4-strict.dwp.bz2 | Bin 0 -> 6871 bytes
>  tests/testfile-dwp-4.bz2            | Bin 0 -> 4194 bytes
>  tests/testfile-dwp-4.dwp.bz2        | Bin 0 -> 10098 bytes
>  tests/testfile-dwp-5.bz2            | Bin 0 -> 4223 bytes
>  tests/testfile-dwp-5.dwp.bz2        | Bin 0 -> 10313 bytes
>  tests/testfile-dwp.source           | 102 ++++++++
>  20 files changed, 798 insertions(+), 5 deletions(-)
>  create mode 100644 libdw/dwarf_cu_dwp_section_info.c
>  create mode 100644 tests/cu-dwp-section-info.c
>  create mode 100755 tests/run-cu-dwp-section-info.sh
>  create mode 100755 tests/testfile-dwp-4-strict.bz2
>  create mode 100644 tests/testfile-dwp-4-strict.dwp.bz2
>  create mode 100755 tests/testfile-dwp-4.bz2
>  create mode 100644 tests/testfile-dwp-4.dwp.bz2
>  create mode 100755 tests/testfile-dwp-5.bz2
>  create mode 100644 tests/testfile-dwp-5.dwp.bz2
>  create mode 100644 tests/testfile-dwp.source
> 
> 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

OK.

> 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,

OK.

> 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 <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;
> +}

Sets up Dwarf_Package_Index structure and associates it with a Dwarf.
Looks good. Thanks for the diligent overflow checks. 

> +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;
> +}

Gets the Dwarf_Package_Index associated with a Dwarf. OK.
(Will need locking in the future for thread-safety.)

> +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;
> +}

Given a unit_id provides the row in the Dwarf_Package_Index if it
exists. rows start at 1, so returning 0 with *unit_rowp also 0
indicates lookup failure (returning -1 indicates the index is malformed
in some way). OK.

> +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;
> +}

Given a row and section returns the offset and size into to index.
row being zero is an error. Doesn't need additional out of bounds
checks because those should have been checked in
__libdw_read_package_index. OK.

> +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);
> +}

As used by __libdw_intern_next_unit. OK.

> +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)

To wrap it all up. Probably a good idea to limit the sections to those
known (although the code could in principle recognize others, it
currently ignores unknown section types). OK.

> 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);

OK.

> 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]))
> 

OK.
 
> 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.  */

OK. char count is at max 80.

>  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;

OK. We shouldn't forget to add a NEWS entry for this.

> diff --git a/libdw/libdwP.h b/libdw/libdwP.h
> index aef42267..7f8d69b5 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,
>  };
> 

OK.

>  
> @@ -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

OK.

> @@ -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

OK.

> @@ -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;

OK.

> @@ -684,6 +709,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)

OK.

> @@ -1388,6 +1420,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)

OK.

> 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);

__libdw_dwp_find_unit will return zero (and set raw and offset to zero)
if the dbg isn't a dwp or the unit_type. OK.
 
> @@ -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;

OK.

> diff --git a/tests/.gitignore b/tests/.gitignore
> index 5bebb2c4..0caabf25 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

OK.

> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 2373c980..34014570 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -62,7 +62,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
>  		  dwelf_elf_e_machine_string \
>  		  getphdrnum leb128 read_unaligned \
>  		  msg_tst system-elf-libelf-test system-elf-gelf-test \
> -		  nvidia_extended_linemap_libdw \
> +		  nvidia_extended_linemap_libdw cu-dwp-section-info \
>  		  $(asm_TESTS)
>  
>  asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
> @@ -212,7 +212,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
> @@ -634,7 +634,11 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
>  	     testfile-largealign.o.bz2 run-strip-largealign.sh \
>  	     run-funcretval++11.sh \
>  	     test-ar-duplicates.a.bz2 \
> -	     run-dwfl-core-noncontig.sh testcore-noncontig.bz2
> +	     run-dwfl-core-noncontig.sh testcore-noncontig.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

OK. This hunk doesn't apply anymore, but easily fixed.

> @@ -810,6 +814,7 @@ getphdrnum_LDADD = $(libelf) $(libdw)
>  leb128_LDADD = $(libelf) $(libdw)
>  read_unaligned_LDADD = $(libelf) $(libdw)
>  nvidia_extended_linemap_libdw_LDADD = $(libelf) $(libdw)
> +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.

Likewise.

> 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 <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;
> +}

Nice test.

> 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 <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

OK.

> 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

Fun.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/4] libdw: Try .dwp file in __libdw_find_split_unit()
  2023-12-06  9:22 ` [PATCH v2 2/4] libdw: Try .dwp file in __libdw_find_split_unit() Omar Sandoval
@ 2024-02-15 23:17   ` Mark Wielaard
  0 siblings, 0 replies; 14+ messages in thread
From: Mark Wielaard @ 2024-02-15 23:17 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: elfutils-devel

Hi Omar,

On Wed, Dec 06, 2023 at 01:22:17AM -0800, Omar Sandoval wrote:
> Try opening the file in the location suggested by the standard (the
> skeleton file name + ".dwp") and looking up the unit in the package
> index.  The rest is similar to .dwo files, with slightly different
> cleanup since a single Dwarf handle is shared.

This is indeed an heuristic for finding the dwp file that should work.

I do wonder if this is how distros will do it for separate .debug
files. I can imagine combining separate debuginfo files with dwz
and/or placing the .dwo and index sections into the actual .debug file
itself. But we can play with that later.

The code seems to integrate really nicely with single file split .dwo.

> 
> 	* libdw/libdw_find_split_unit.c (try_dwp_file): New function.
> 	(__libdw_find_split_unit): Call try_dwp_file.
> 	* libdw/libdwP.h (Dwarf): Add dwp_dwarf and dwp_fd.
> 	(__libdw_dwp_findcu_id): New declaration.
> 	(__libdw_link_skel_split): Handle .debug_addr for dwp.
> 	* libdw/libdw_begin_elf.c (dwarf_begin_elf): Initialize
> 	result->dwp_fd.
> 	* libdw/dwarf_end.c (dwarf_end): Free dwarf->dwp_dwarf and close
> 	dwarf->dwp_fd.
> 	(cu_free): Don't free split dbg if it is dwp_dwarf.
> 
> Signed-off-by: Omar Sandoval <osandov@fb.com>
> ---
>  libdw/dwarf_begin_elf.c           |  1 +
>  libdw/dwarf_cu_dwp_section_info.c | 19 ++++++++
>  libdw/dwarf_end.c                 | 10 ++++-
>  libdw/libdwP.h                    | 23 ++++++++--
>  libdw/libdw_find_split_unit.c     | 75 ++++++++++++++++++++++++++++---
>  5 files changed, 119 insertions(+), 9 deletions(-)
> 
> diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
> index 323a91d0..ca2b7e2a 100644
> --- a/libdw/dwarf_begin_elf.c
> +++ b/libdw/dwarf_begin_elf.c
> @@ -567,6 +567,7 @@ dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp)
>  
>    result->elf = elf;
>    result->alt_fd = -1;
> +  result->dwp_fd = -1;
>  
>    /* Initialize the memory handling.  Initial blocks are allocated on first
>       actual allocation.  */

OK.

> diff --git a/libdw/dwarf_cu_dwp_section_info.c b/libdw/dwarf_cu_dwp_section_info.c
> index 4a4eac8c..298f36f9 100644
> --- a/libdw/dwarf_cu_dwp_section_info.c
> +++ b/libdw/dwarf_cu_dwp_section_info.c
> @@ -340,6 +340,25 @@ __libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off,
>  				   abbrev_offsetp, NULL);
>  }
>  
> +Dwarf_CU *
> +internal_function
> +__libdw_dwp_findcu_id (Dwarf *dbg, uint64_t unit_id8)
> +{
> +  Dwarf_Package_Index *index = __libdw_package_index (dbg, false);
> +  uint32_t unit_row;
> +  Dwarf_Off offset;
> +  Dwarf_CU *cu;
> +  if (__libdw_dwp_unit_row (index, unit_id8, &unit_row) == 0
> +      && __libdw_dwp_section_info (index, unit_row, DW_SECT_INFO, &offset,
> +				   NULL) == 0
> +      && (cu = __libdw_findcu (dbg, offset, false)) != NULL
> +      && cu->unit_type == DW_UT_split_compile
> +      && cu->unit_id8 == unit_id8)
> +    return cu;
> +  else
> +    return NULL;
> +}
> +

Called from try_dwp_file to find the to split compile unit through the
index. When found try_dwp_file will put this cu into the split_tree.
OK.

>  int
>  dwarf_cu_dwp_section_info (Dwarf_CU *cu, unsigned int section,
>  			   Dwarf_Off *offsetp, Dwarf_Off *sizep)
> diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
> index b7f817d9..78224ddb 100644
> --- a/libdw/dwarf_end.c
> +++ b/libdw/dwarf_end.c
> @@ -66,7 +66,9 @@ cu_free (void *arg)
>  	  /* The fake_addr_cu might be shared, only release one.  */
>  	  if (p->dbg->fake_addr_cu == p->split->dbg->fake_addr_cu)
>  	    p->split->dbg->fake_addr_cu = NULL;
> -	  INTUSE(dwarf_end) (p->split->dbg);
> +	  /* There is only one DWP file. We free it later.  */
> +	  if (p->split->dbg != p->dbg->dwp_dwarf)
> +	    INTUSE(dwarf_end) (p->split->dbg);
>  	}
>      }
>  }
> @@ -147,6 +149,12 @@ dwarf_end (Dwarf *dwarf)
>  	  close (dwarf->alt_fd);
>  	}
>  
> +      if (dwarf->dwp_fd != -1)
> +	{
> +	  INTUSE(dwarf_end) (dwarf->dwp_dwarf);
> +	  close (dwarf->dwp_fd);
> +	}
> +
>        /* The cached path and dir we found the Dwarf ELF file in.  */
>        free (dwarf->elfpath);
>        free (dwarf->debugdir);

OK.

> diff --git a/libdw/libdwP.h b/libdw/libdwP.h
> index 7f8d69b5..54445886 100644
> --- a/libdw/libdwP.h
> +++ b/libdw/libdwP.h
> @@ -180,6 +180,9 @@ struct Dwarf
>    /* dwz alternate DWARF file.  */
>    Dwarf *alt_dwarf;
>  
> +  /* DWARF package file.  */
> +  Dwarf *dwp_dwarf;
> +
>    /* The section data.  */
>    Elf_Data *sectiondata[IDX_last];
>  
> @@ -197,6 +200,9 @@ struct Dwarf
>       close this file descriptor.  */
>    int alt_fd;
>  
> +  /* File descriptor of DWARF package file.  */
> +  int dwp_fd;
> +
>    /* Information for traversing the .debug_pubnames section.  This is
>       an array and separately allocated with malloc.  */
>    struct pubnames_s

OK.

> @@ -716,6 +722,10 @@ extern int __libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off,
>  				  Dwarf_Off *abbrev_offsetp)
>       __nonnull_attribute__ (1, 7, 8) internal_function;
>  
> +/* Find the compilation unit in a DWARF package file with the given id.  */
> +extern Dwarf_CU *__libdw_dwp_findcu_id (Dwarf *dbg, uint64_t unit_id8)
> +     __nonnull_attribute__ (1) internal_function;
> +
>  /* Get abbreviation with given code.  */
>  extern Dwarf_Abbrev *__libdw_findabbrev (struct Dwarf_CU *cu,
>  					 unsigned int code)

OK.

> @@ -1367,12 +1377,19 @@ __libdw_link_skel_split (Dwarf_CU *skel, Dwarf_CU *split)
>  
>    /* Get .debug_addr and addr_base greedy.
>       We also need it for the fake addr cu.
> -     There is only one per split debug.  */
> +     This needs to be done for each split unit (one per .dwo file, or multiple
> +     per .dwp file).  */
>    Dwarf *dbg = skel->dbg;
>    Dwarf *sdbg = split->dbg;
> -  if (sdbg->sectiondata[IDX_debug_addr] == NULL
> -      && dbg->sectiondata[IDX_debug_addr] != NULL)
> +  if (dbg->sectiondata[IDX_debug_addr] != NULL
> +      /* If this split file hasn't been linked yet...  */
> +      && (sdbg->sectiondata[IDX_debug_addr] == NULL
> +	  /* ... or it was linked to the same skeleton file for another
> +	     unit...  */
> +	  || (sdbg->sectiondata[IDX_debug_addr]
> +	      == dbg->sectiondata[IDX_debug_addr])))
>      {
> +      /* ... then link the address information for this file and unit.  */
>        sdbg->sectiondata[IDX_debug_addr]
>  	= dbg->sectiondata[IDX_debug_addr];
>        split->addr_base = __libdw_cu_addr_base (skel);

OK. This can now happen when the offset into the .debug_addr.dwo is
different.

> diff --git a/libdw/libdw_find_split_unit.c b/libdw/libdw_find_split_unit.c
> index 533f8072..e1e78951 100644
> --- a/libdw/libdw_find_split_unit.c
> +++ b/libdw/libdw_find_split_unit.c
> @@ -85,6 +85,67 @@ try_split_file (Dwarf_CU *cu, const char *dwo_path)
>      }
>  }
>  
> +static void
> +try_dwp_file (Dwarf_CU *cu)
> +{
> +  if (cu->dbg->dwp_dwarf == NULL)
> +    {
> +      if (cu->dbg->elfpath != NULL)
> +	{
> +	  /* The DWARF 5 standard says "the package file is typically placed in
> +	     the same directory as the application, and is given the same name
> +	     with a '.dwp' extension".  */
> +	  size_t elfpath_len = strlen (cu->dbg->elfpath);
> +	  char *dwp_path = malloc (elfpath_len + 5);
> +	  if (dwp_path == NULL)
> +	    {
> +	      __libdw_seterrno (DWARF_E_NOMEM);
> +	      return;
> +	    }
> +	  memcpy (dwp_path, cu->dbg->elfpath, elfpath_len);
> +	  strcpy (dwp_path + elfpath_len, ".dwp");
> +	  int dwp_fd = open (dwp_path, O_RDONLY);
> +	  free (dwp_path);
> +	  if (dwp_fd != -1)
> +	    {
> +	      Dwarf *dwp_dwarf = dwarf_begin (dwp_fd, DWARF_C_READ);
> +	      /* There's no way to know whether we got the correct file until
> +		 we look up the unit, but it should at least be a dwp file.  */
> +	      if (dwp_dwarf != NULL
> +		  && (dwp_dwarf->sectiondata[IDX_debug_cu_index] != NULL
> +		      || dwp_dwarf->sectiondata[IDX_debug_tu_index] != NULL))
> +		{
> +		  cu->dbg->dwp_dwarf = dwp_dwarf;
> +		  cu->dbg->dwp_fd = dwp_fd;
> +		}
> +	      else
> +		close (dwp_fd);
> +	    }
> +	}
> +      if (cu->dbg->dwp_dwarf == NULL)
> +	cu->dbg->dwp_dwarf = (Dwarf *) -1;
> +    }

OK, makes sure this is only tried once for each Dwarf. So if the .dwp
file isn't found for one skeleton CU, it will not be tried for any
other.

> +
> +  if (cu->dbg->dwp_dwarf != (Dwarf *) -1)
> +    {
> +      Dwarf_CU *split = __libdw_dwp_findcu_id (cu->dbg->dwp_dwarf,
> +					       cu->unit_id8);
> +      if (split != NULL)
> +	{
> +	  if (tsearch (split->dbg, &cu->dbg->split_tree,
> +		       __libdw_finddbg_cb) == NULL)
> +	    {
> +	      /* Something went wrong.  Don't link.  */
> +	      __libdw_seterrno (DWARF_E_NOMEM);
> +	      return;
> +	    }
> +
> +	  /* Link skeleton and split compile units.  */
> +	  __libdw_link_skel_split (cu, split);
> +	}
> +    }
> +}

OK, works as if it was a separate .dwo.

> +
>  Dwarf_CU *
>  internal_function
>  __libdw_find_split_unit (Dwarf_CU *cu)
> @@ -98,14 +159,18 @@ __libdw_find_split_unit (Dwarf_CU *cu)
>       same id as the skeleton.  */
>    if (cu->unit_type == DW_UT_skeleton)
>      {
> +      /* First, try the dwp file.  */
> +      try_dwp_file (cu);
> +
>        Dwarf_Die cudie = CUDIE (cu);
>        Dwarf_Attribute dwo_name;
> -      /* It is fine if dwo_dir doesn't exists, but then dwo_name needs
> -	 to be an absolute path.  */
> -      if (dwarf_attr (&cudie, DW_AT_dwo_name, &dwo_name) != NULL
> -	  || dwarf_attr (&cudie, DW_AT_GNU_dwo_name, &dwo_name) != NULL)
> +      /* Try a dwo file.  It is fine if dwo_dir doesn't exist, but then
> +	 dwo_name needs to be an absolute path.  */
> +      if (cu->split == (Dwarf_CU *) -1
> +	  && (dwarf_attr (&cudie, DW_AT_dwo_name, &dwo_name) != NULL
> +	      || dwarf_attr (&cudie, DW_AT_GNU_dwo_name, &dwo_name) != NULL))
>  	{
> -	  /* First try the dwo file name in the same directory
> +	  /* Try the dwo file name in the same directory
>  	     as we found the skeleton file.  */
>  	  const char *dwo_file = dwarf_formstring (&dwo_name);
>  	  const char *debugdir = cu->dbg->debugdir;

Nicely integrated.

Pushed,

Mark

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate
  2023-12-06  9:22 ` [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate Omar Sandoval
@ 2024-02-16 15:00   ` Mark Wielaard
  2024-02-23  0:53     ` Omar Sandoval
  0 siblings, 1 reply; 14+ messages in thread
From: Mark Wielaard @ 2024-02-16 15:00 UTC (permalink / raw)
  To: Omar Sandoval, elfutils-devel

Hi Omar,

On Wed, 2023-12-06 at 01:22 -0800, Omar Sandoval wrote:
> The final piece of DWARF package file support is that offsets have to be
> interpreted relative to the section offset from the package index.
> .debug_abbrev.dwo is already covered, so sprinkle around calls to
> dwarf_cu_dwp_section_info for the remaining sections: .debug_line.dwo,
> .debug_loclists.dwo/.debug_loc.dwo, .debug_str_offsets.dwo,
> .debug_macro.dwo/.debug_macinfo.dwo, and .debug_rnglists.dwo.  With all
> of that in place, we can finally test various libdw functions on dwp
> files.

So the offsets for DW_SECT_INFO, DW_SECT_TYPES and DW_SECT_ABBREV were
already taken into account when setting up a cu from a dwp.

With this patch __libdw_cu_str_off_base/str_offsets_base_off handles
DW_SECT_STR_OFFSETS which is used in dwarf_formstring and
dwarf_getmacros.

__libdw_cu_ranges_base handles DW_SECT_RNGLISTS, which is used by
dwarf_ranges. And __libdw_formptr has a special case for
DW_FORM_sec_offset for IDX_debug_ranges && version < 5 && unit_type ==
DW_UT_split_compile to also uses __libdw_cu_ranges_base.

__libdw_cu_locs_base handles DW_SECT_LOCLISTS which is used in
dwarf_getlocation through initial_offset. I do wonder why the special
case in __libdw_formptr isn't needed here too.

dwarf_getmacros handles DW_SECT_MACRO through get_offset_from. And when
the macros need to refer to the line table, it also handles
DW_SECT_LINE.

Don't we also need to handle DW_SECT_LINE in dwarf_getsrclines and
dwarf_next_lines when looking for DW_AT_stmt_list?

> 	* libdw/dwarf_getmacros.c (get_macinfo_table): Call
> 	dwarf_cu_dwp_section_info and add offset to line_offset.
> 	(get_offset_from): Call dwarf_cu_dwp_section_info and add offset
> 	to *retp.
> 	* libdw/libdwP.h (str_offsets_base_off): Call
> 	dwarf_cu_dwp_section_info and add offset.
> 	(__libdw_cu_ranges_base): Ditto.
> 	(__libdw_cu_locs_base): Ditto.
> 	* libdw/dwarf_getlocation.c (initial_offset): Call
> 	dwarf_cu_dwp_section_info and add offset to start_offset.
> 	* tests/run-varlocs.sh: Check testfile-dwp-5 and testfile-dwp-4.
> 	* tests/run-all-dwarf-ranges.sh: Check testfile-dwp-5 and
> 	testfile-dwp-4.
> 	* tests/run-dwarf-getmacros.sh: Check testfile-dwp-5 and
> 	testfile-dwp-4-strict.
> 	* tests/run-get-units-split.sh: Check testfile-dwp-5,
> 	testfile-dwp-4, and testfile-dwp-4-strict.

The code and tests look good. run-varlocs.sh seems good, which seems to
confirm DW_SECT_LOCLISTS is handled correctly (but why doesn't it need
a hack similar to ranges in __libdw_formptr?).

We might want to add a test for run-next-lines.sh and run-next-
files.sh?

Thanks,

Mark

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate
  2024-02-16 15:00   ` Mark Wielaard
@ 2024-02-23  0:53     ` Omar Sandoval
  2024-02-23  1:03       ` Omar Sandoval
  2024-02-24 13:30       ` Mark Wielaard
  0 siblings, 2 replies; 14+ messages in thread
From: Omar Sandoval @ 2024-02-23  0:53 UTC (permalink / raw)
  To: Mark Wielaard; +Cc: elfutils-devel

On Fri, Feb 16, 2024 at 04:00:47PM +0100, Mark Wielaard wrote:
> Hi Omar,
> 
> On Wed, 2023-12-06 at 01:22 -0800, Omar Sandoval wrote:
> > The final piece of DWARF package file support is that offsets have to be
> > interpreted relative to the section offset from the package index.
> > .debug_abbrev.dwo is already covered, so sprinkle around calls to
> > dwarf_cu_dwp_section_info for the remaining sections: .debug_line.dwo,
> > .debug_loclists.dwo/.debug_loc.dwo, .debug_str_offsets.dwo,
> > .debug_macro.dwo/.debug_macinfo.dwo, and .debug_rnglists.dwo.  With all
> > of that in place, we can finally test various libdw functions on dwp
> > files.
> 
> So the offsets for DW_SECT_INFO, DW_SECT_TYPES and DW_SECT_ABBREV were
> already taken into account when setting up a cu from a dwp.
> 
> With this patch __libdw_cu_str_off_base/str_offsets_base_off handles
> DW_SECT_STR_OFFSETS which is used in dwarf_formstring and
> dwarf_getmacros.
> 
> __libdw_cu_ranges_base handles DW_SECT_RNGLISTS, which is used by
> dwarf_ranges. And __libdw_formptr has a special case for
> DW_FORM_sec_offset for IDX_debug_ranges && version < 5 && unit_type ==
> DW_UT_split_compile to also uses __libdw_cu_ranges_base.
> 
> __libdw_cu_locs_base handles DW_SECT_LOCLISTS which is used in
> dwarf_getlocation through initial_offset. I do wonder why the special
> case in __libdw_formptr isn't needed here too.
> 
> dwarf_getmacros handles DW_SECT_MACRO through get_offset_from. And when
> the macros need to refer to the line table, it also handles
> DW_SECT_LINE.
> 
> Don't we also need to handle DW_SECT_LINE in dwarf_getsrclines and
> dwarf_next_lines when looking for DW_AT_stmt_list?

.debug_line is the odd one out in split DWARF: the skeleton file
contains the full .debug_line, and the DWO or DWP files have a skeleton
.debug_line.dwo that only contains the directory and file name tables
(for DW_AT_file and macro info to reference). dwarf_getsrclines and co.
read from the skeleton file, not the DWP file, meaning they shouldn't
use DW_SECT_LINE.

> > 	* libdw/dwarf_getmacros.c (get_macinfo_table): Call
> > 	dwarf_cu_dwp_section_info and add offset to line_offset.
> > 	(get_offset_from): Call dwarf_cu_dwp_section_info and add offset
> > 	to *retp.
> > 	* libdw/libdwP.h (str_offsets_base_off): Call
> > 	dwarf_cu_dwp_section_info and add offset.
> > 	(__libdw_cu_ranges_base): Ditto.
> > 	(__libdw_cu_locs_base): Ditto.
> > 	* libdw/dwarf_getlocation.c (initial_offset): Call
> > 	dwarf_cu_dwp_section_info and add offset to start_offset.
> > 	* tests/run-varlocs.sh: Check testfile-dwp-5 and testfile-dwp-4.
> > 	* tests/run-all-dwarf-ranges.sh: Check testfile-dwp-5 and
> > 	testfile-dwp-4.
> > 	* tests/run-dwarf-getmacros.sh: Check testfile-dwp-5 and
> > 	testfile-dwp-4-strict.
> > 	* tests/run-get-units-split.sh: Check testfile-dwp-5,
> > 	testfile-dwp-4, and testfile-dwp-4-strict.
> 
> The code and tests look good. run-varlocs.sh seems good, which seems to
> confirm DW_SECT_LOCLISTS is handled correctly (but why doesn't it need
> a hack similar to ranges in __libdw_formptr?).

I think it's because ranges have the uniquely weird behavior in DWARF 4
GNU Debug Fission that DW_AT_GNU_ranges_base is in the skeleton file but
used to interpret the split file. This was cleaned up for DWARF 5 (as I
documented in commit c9c9ffae725009b192b40e2d89035f353ae7055f), and
there was no base attribute for location lists in DWARF 4, so it's not
applicable.

> We might want to add a test for run-next-lines.sh and run-next-
> files.sh?

Good idea, I'll send an updated version with those tests.

Thanks!
Omar

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate
  2024-02-23  0:53     ` Omar Sandoval
@ 2024-02-23  1:03       ` Omar Sandoval
  2024-02-24 14:00         ` Mark Wielaard
  2024-02-24 13:30       ` Mark Wielaard
  1 sibling, 1 reply; 14+ messages in thread
From: Omar Sandoval @ 2024-02-23  1:03 UTC (permalink / raw)
  To: Mark Wielaard; +Cc: elfutils-devel

On Thu, Feb 22, 2024 at 04:53:19PM -0800, Omar Sandoval wrote:
> On Fri, Feb 16, 2024 at 04:00:47PM +0100, Mark Wielaard wrote:
> > Hi Omar,
> > 
> > On Wed, 2023-12-06 at 01:22 -0800, Omar Sandoval wrote:
> > > The final piece of DWARF package file support is that offsets have to be
> > > interpreted relative to the section offset from the package index.
> > > .debug_abbrev.dwo is already covered, so sprinkle around calls to
> > > dwarf_cu_dwp_section_info for the remaining sections: .debug_line.dwo,
> > > .debug_loclists.dwo/.debug_loc.dwo, .debug_str_offsets.dwo,
> > > .debug_macro.dwo/.debug_macinfo.dwo, and .debug_rnglists.dwo.  With all
> > > of that in place, we can finally test various libdw functions on dwp
> > > files.
> > 
> > So the offsets for DW_SECT_INFO, DW_SECT_TYPES and DW_SECT_ABBREV were
> > already taken into account when setting up a cu from a dwp.
> > 
> > With this patch __libdw_cu_str_off_base/str_offsets_base_off handles
> > DW_SECT_STR_OFFSETS which is used in dwarf_formstring and
> > dwarf_getmacros.
> > 
> > __libdw_cu_ranges_base handles DW_SECT_RNGLISTS, which is used by
> > dwarf_ranges. And __libdw_formptr has a special case for
> > DW_FORM_sec_offset for IDX_debug_ranges && version < 5 && unit_type ==
> > DW_UT_split_compile to also uses __libdw_cu_ranges_base.
> > 
> > __libdw_cu_locs_base handles DW_SECT_LOCLISTS which is used in
> > dwarf_getlocation through initial_offset. I do wonder why the special
> > case in __libdw_formptr isn't needed here too.
> > 
> > dwarf_getmacros handles DW_SECT_MACRO through get_offset_from. And when
> > the macros need to refer to the line table, it also handles
> > DW_SECT_LINE.
> > 
> > Don't we also need to handle DW_SECT_LINE in dwarf_getsrclines and
> > dwarf_next_lines when looking for DW_AT_stmt_list?
> 
> .debug_line is the odd one out in split DWARF: the skeleton file
> contains the full .debug_line, and the DWO or DWP files have a skeleton
> .debug_line.dwo that only contains the directory and file name tables
> (for DW_AT_file and macro info to reference). dwarf_getsrclines and co.
> read from the skeleton file, not the DWP file, meaning they shouldn't
> use DW_SECT_LINE.

Ah, I guess you can call dwarf_getsrclines/dwarf_next_lines on the dwp
file itself, where DW_SECT_LINE may be applicable. I need to think about
that some more...

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate
  2024-02-23  0:53     ` Omar Sandoval
  2024-02-23  1:03       ` Omar Sandoval
@ 2024-02-24 13:30       ` Mark Wielaard
  1 sibling, 0 replies; 14+ messages in thread
From: Mark Wielaard @ 2024-02-24 13:30 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: elfutils-devel

Hi Omar,

On Thu, Feb 22, 2024 at 04:53:19PM -0800, Omar Sandoval wrote:
> On Fri, Feb 16, 2024 at 04:00:47PM +0100, Mark Wielaard wrote:
> > The code and tests look good. run-varlocs.sh seems good, which seems to
> > confirm DW_SECT_LOCLISTS is handled correctly (but why doesn't it need
> > a hack similar to ranges in __libdw_formptr?).
> 
> I think it's because ranges have the uniquely weird behavior in DWARF 4
> GNU Debug Fission that DW_AT_GNU_ranges_base is in the skeleton file but
> used to interpret the split file. This was cleaned up for DWARF 5 (as I
> documented in commit c9c9ffae725009b192b40e2d89035f353ae7055f), and
> there was no base attribute for location lists in DWARF 4, so it's not
> applicable.

Thanks. I forgot about commit c9c9ffae7 "libdw: Handle DW_AT_ranges in
split DWARF 5 skeleton in dwarf_ranges". That documents it nicely.

Cheers,

Mark

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate
  2024-02-23  1:03       ` Omar Sandoval
@ 2024-02-24 14:00         ` Mark Wielaard
  2024-02-26 19:31           ` Omar Sandoval
  0 siblings, 1 reply; 14+ messages in thread
From: Mark Wielaard @ 2024-02-24 14:00 UTC (permalink / raw)
  To: Omar Sandoval; +Cc: elfutils-devel

Hi Omar,

On Thu, Feb 22, 2024 at 05:03:44PM -0800, Omar Sandoval wrote:
> On Thu, Feb 22, 2024 at 04:53:19PM -0800, Omar Sandoval wrote:
> > On Fri, Feb 16, 2024 at 04:00:47PM +0100, Mark Wielaard wrote:
> > > Don't we also need to handle DW_SECT_LINE in dwarf_getsrclines and
> > > dwarf_next_lines when looking for DW_AT_stmt_list?
> > 
> > .debug_line is the odd one out in split DWARF: the skeleton file
> > contains the full .debug_line, and the DWO or DWP files have a skeleton
> > .debug_line.dwo that only contains the directory and file name tables
> > (for DW_AT_file and macro info to reference). dwarf_getsrclines and co.
> > read from the skeleton file, not the DWP file, meaning they shouldn't
> > use DW_SECT_LINE.
> 
> Ah, I guess you can call dwarf_getsrclines/dwarf_next_lines on the dwp
> file itself, where DW_SECT_LINE may be applicable. I need to think about
> that some more...

So reading the DWARF5 spec, it says a split DWARF CU/TU DIE may have a
DW_AT_stmt_list, which are interpreted as relative to the base offset
for .debug_line.dwo. And these tables contain only the directory and
filename lists needed to interpret DW_AT_decl_file attributes in the
debugging information entries. Actual line number tables remain in the
.debug_line section, and remain in the relocatable object (.o) files.

So I think the intention is that the main .debug_line (skeleton)
section does contain the actual line number table, but only those
file/dir table entries that are referenced from that. Not any that are
only referenced from the DW_AT_decl_file attributes (which should only
appear in the split DWARF DIEs). Maybe in practice these overlap
completely, so there is no savings and they are in practice
identical. But I don't see anything in the spec that implies you
should interpret a lineptr in the .debug_info.dwo as relative to the
.debug_line section in the skeleton.

Cheers,

Mark

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate
  2024-02-24 14:00         ` Mark Wielaard
@ 2024-02-26 19:31           ` Omar Sandoval
  0 siblings, 0 replies; 14+ messages in thread
From: Omar Sandoval @ 2024-02-26 19:31 UTC (permalink / raw)
  To: Mark Wielaard; +Cc: elfutils-devel

On Sat, Feb 24, 2024 at 03:00:04PM +0100, Mark Wielaard wrote:
> Hi Omar,
> 
> On Thu, Feb 22, 2024 at 05:03:44PM -0800, Omar Sandoval wrote:
> > On Thu, Feb 22, 2024 at 04:53:19PM -0800, Omar Sandoval wrote:
> > > On Fri, Feb 16, 2024 at 04:00:47PM +0100, Mark Wielaard wrote:
> > > > Don't we also need to handle DW_SECT_LINE in dwarf_getsrclines and
> > > > dwarf_next_lines when looking for DW_AT_stmt_list?
> > > 
> > > .debug_line is the odd one out in split DWARF: the skeleton file
> > > contains the full .debug_line, and the DWO or DWP files have a skeleton
> > > .debug_line.dwo that only contains the directory and file name tables
> > > (for DW_AT_file and macro info to reference). dwarf_getsrclines and co.
> > > read from the skeleton file, not the DWP file, meaning they shouldn't
> > > use DW_SECT_LINE.
> > 
> > Ah, I guess you can call dwarf_getsrclines/dwarf_next_lines on the dwp
> > file itself, where DW_SECT_LINE may be applicable. I need to think about
> > that some more...
> 
> So reading the DWARF5 spec, it says a split DWARF CU/TU DIE may have a
> DW_AT_stmt_list, which are interpreted as relative to the base offset
> for .debug_line.dwo. And these tables contain only the directory and
> filename lists needed to interpret DW_AT_decl_file attributes in the
> debugging information entries. Actual line number tables remain in the
> .debug_line section, and remain in the relocatable object (.o) files.
> 
> So I think the intention is that the main .debug_line (skeleton)
> section does contain the actual line number table, but only those
> file/dir table entries that are referenced from that. Not any that are
> only referenced from the DW_AT_decl_file attributes (which should only
> appear in the split DWARF DIEs). Maybe in practice these overlap
> completely, so there is no savings and they are in practice
> identical. But I don't see anything in the spec that implies you
> should interpret a lineptr in the .debug_info.dwo as relative to the
> .debug_line section in the skeleton.

Sorry, my reply was confusing, and I hadn't checked all of the relevant
functions yet. Let me try again:

As you said, the skeleton file has a .debug_line with the actual line
number table, and the DWP file has a .debug_line.dwo with only
file/directory name tables. If we're reading from the skeleton file,
then we don't need to apply the DW_SECT_LINE offset.

dwarf_getsrclines in particular always uses the skeleton's .debug_line
even if called on a split CU, so it doesn't need to be updated.
dwarf_getsrcfiles and dwarf_next_lines can use the split
.debug_line.dwo, so they do need to be updated.

Updated patch series incoming...

Thanks,
Omar

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2024-02-26 19:31 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-06  9:22 [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support Omar Sandoval
2023-12-06  9:22 ` [PATCH v2 1/4] libdw: Parse DWARF package file index sections Omar Sandoval
2024-02-15 17:40   ` Mark Wielaard
2023-12-06  9:22 ` [PATCH v2 2/4] libdw: Try .dwp file in __libdw_find_split_unit() Omar Sandoval
2024-02-15 23:17   ` Mark Wielaard
2023-12-06  9:22 ` [PATCH v2 3/4] libdw: Apply DWARF package file section offsets where appropriate Omar Sandoval
2024-02-16 15:00   ` Mark Wielaard
2024-02-23  0:53     ` Omar Sandoval
2024-02-23  1:03       ` Omar Sandoval
2024-02-24 14:00         ` Mark Wielaard
2024-02-26 19:31           ` Omar Sandoval
2024-02-24 13:30       ` Mark Wielaard
2023-12-06  9:22 ` [PATCH v2 4/4] libdw: Handle overflowed DW_SECT_INFO offsets in DWARF package file indexes Omar Sandoval
2024-01-04  0:53 ` [PATCH v2 0/4] elfutils: DWARF package (.dwp) file support Omar Sandoval

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).