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