Re: [PATCH v2] Add __libdw_getdieranges

2024-02-29 Thread Mark Wielaard
Hi Aaron,

On Tue, Feb 27, 2024 at 08:11:39PM -0500, Aaron Merey wrote:
> __libdw_getdieranges builds an aranges list by iterating over each
> CU and recording each address range.
> 
> This function is an alternative to dwarf_getaranges.  dwarf_getaranges
> attempts to read address ranges from .debug_aranges, which might be
> absent or incomplete.
> 
> This patch replaces dwarf_getaranges with __libdw_getdieranges in
> dwarf_addrdie and dwfl_module_addrdie.  The existing tests in
> run-getsrc-die.sh are also rerun with .debug_aranges removed from
> the testfiles.
> 
> https://sourceware.org/bugzilla/show_bug.cgi?id=22288
> https://sourceware.org/bugzilla/show_bug.cgi?id=30948
> 
> Signed-off-by: Aaron Merey 
> ---
> 
> v2 addresses feedback from Mark's review:
> https://sourceware.org/pipermail/elfutils-devel/2024q1/006853.html
> 
> Avoid calling free on arangelist when it's possibly corrupt.
> Run tests in run-getsrc-die.sh twice, once with .debug_aranges
> present in the testfile and once with the section removed.

This looks good to me.
Please also add a NEWS entry about this.

Thanks,

Mark


[PATCH v2] Add __libdw_getdieranges

2024-02-27 Thread Aaron Merey
__libdw_getdieranges builds an aranges list by iterating over each
CU and recording each address range.

This function is an alternative to dwarf_getaranges.  dwarf_getaranges
attempts to read address ranges from .debug_aranges, which might be
absent or incomplete.

This patch replaces dwarf_getaranges with __libdw_getdieranges in
dwarf_addrdie and dwfl_module_addrdie.  The existing tests in
run-getsrc-die.sh are also rerun with .debug_aranges removed from
the testfiles.

https://sourceware.org/bugzilla/show_bug.cgi?id=22288
https://sourceware.org/bugzilla/show_bug.cgi?id=30948

Signed-off-by: Aaron Merey 
---

v2 addresses feedback from Mark's review:
https://sourceware.org/pipermail/elfutils-devel/2024q1/006853.html

Avoid calling free on arangelist when it's possibly corrupt.
Run tests in run-getsrc-die.sh twice, once with .debug_aranges
present in the testfile and once with the section removed.

 libdw/dwarf_addrdie.c|   2 +-
 libdw/dwarf_getaranges.c | 186 ++-
 libdw/libdwP.h   |  13 ++-
 libdwfl/cu.c |   8 +-
 tests/run-getsrc-die.sh  |  22 -
 5 files changed, 179 insertions(+), 52 deletions(-)

diff --git a/libdw/dwarf_addrdie.c b/libdw/dwarf_addrdie.c
index 3a08ab75..48a1aaea 100644
--- a/libdw/dwarf_addrdie.c
+++ b/libdw/dwarf_addrdie.c
@@ -41,7 +41,7 @@ dwarf_addrdie (Dwarf *dbg, Dwarf_Addr addr, Dwarf_Die *result)
   size_t naranges;
   Dwarf_Off off;
 
-  if (INTUSE(dwarf_getaranges) (dbg, , ) != 0
+  if (__libdw_getdieranges (dbg, , ) != 0
   || INTUSE(dwarf_getarangeinfo) (INTUSE(dwarf_getarange_addr) (aranges,
addr),
  NULL, NULL, ) != 0)
diff --git a/libdw/dwarf_getaranges.c b/libdw/dwarf_getaranges.c
index 27439d37..7a3cd36b 100644
--- a/libdw/dwarf_getaranges.c
+++ b/libdw/dwarf_getaranges.c
@@ -33,7 +33,6 @@
 #endif
 
 #include 
-#include 
 #include "libdwP.h"
 #include 
 
@@ -54,6 +53,147 @@ compare_aranges (const void *a, const void *b)
   return 0;
 }
 
+/* Convert ARANGELIST into Dwarf_Aranges and store at ARANGES.  */
+static bool
+finalize_aranges (Dwarf *dbg, Dwarf_Aranges **aranges, size_t *naranges,
+ struct arangelist *arangelist, unsigned int narangelist)
+{
+  /* Allocate the array for the result.  */
+  void *buf = libdw_alloc (dbg, Dwarf_Aranges,
+  sizeof (Dwarf_Aranges)
+  + narangelist * sizeof (Dwarf_Arange), 1);
+
+  /* First use the buffer for the pointers, and sort the entries.
+ We'll write the pointers in the end of the buffer, and then
+ copy into the buffer from the beginning so the overlap works.  */
+  eu_static_assert (sizeof (Dwarf_Arange) >= sizeof (Dwarf_Arange *));
+  struct arangelist **sortaranges
+= (buf + sizeof (Dwarf_Aranges)
+   + ((sizeof (Dwarf_Arange) - sizeof sortaranges[0]) * narangelist));
+
+  /* The list is in LIFO order and usually they come in clumps with
+ ascending addresses.  So fill from the back to probably start with
+ runs already in order before we sort.  */
+  unsigned int i = narangelist;
+  while (i-- > 0)
+{
+  sortaranges[i] = arangelist;
+  arangelist = arangelist->next;
+}
+
+  /* Something went wrong if narangelist is less then the actual length
+ of arangelist. */
+  if (arangelist != NULL)
+{
+  __libdw_seterrno (DWARF_E_UNKNOWN_ERROR);
+  return false;
+}
+
+  /* Sort by ascending address.  */
+  qsort (sortaranges, narangelist, sizeof sortaranges[0], _aranges);
+
+  /* Now that they are sorted, put them in the final array.
+ The buffers overlap, so we've clobbered the early elements
+ of SORTARANGES by the time we're reading the later ones.  */
+  *aranges = buf;
+  (*aranges)->dbg = dbg;
+  (*aranges)->naranges = narangelist;
+  if (naranges != NULL)
+*naranges = narangelist;
+  for (i = 0; i < narangelist; ++i)
+{
+  struct arangelist *elt = sortaranges[i];
+  (*aranges)->info[i] = elt->arange;
+  free (elt);
+}
+
+  return true;
+}
+
+int
+__libdw_getdieranges (Dwarf *dbg, Dwarf_Aranges **aranges, size_t *naranges)
+{
+  if (dbg == NULL)
+return -1;
+
+  if (dbg->dieranges != NULL)
+{
+  *aranges = dbg->dieranges;
+  if (naranges != NULL)
+   *naranges = dbg->dieranges->naranges;
+  return 0;
+}
+
+  struct arangelist *arangelist = NULL;
+  unsigned int narangelist = 0;
+
+  Dwarf_CU *cu = NULL;
+  while (INTUSE(dwarf_get_units) (dbg, cu, , NULL, NULL, NULL, NULL) == 0)
+{
+  Dwarf_Addr base;
+  Dwarf_Addr low;
+  Dwarf_Addr high;
+
+  Dwarf_Die cudie = CUDIE (cu);
+
+  /* Skip CUs that only contain type information.  */
+  if (!INTUSE(dwarf_hasattr) (, DW_AT_low_pc)
+ && !INTUSE(dwarf_hasattr) (, DW_AT_ranges))
+   continue;
+
+  ptrdiff_t offset = 0;
+
+  /* Add arange for each range list entry or high_pc and low_pc.