* [PATCH] Support updating DWARF5 .debug_loclists.
@ 2020-09-28 8:07 Mark Wielaard
2020-09-29 8:42 ` Jakub Jelinek
0 siblings, 1 reply; 3+ messages in thread
From: Mark Wielaard @ 2020-09-28 8:07 UTC (permalink / raw)
To: dwz; +Cc: Mark Wielaard
This patch updates dwz to handle .debug_loclists as it updates .debug_locs.
Both sections can be present at the same time (if there are both DWARF5 CUs
and older DWARF CUs). So we need to keep both a loc_htab and loclists_htab.
* dwz.c (read_loclist_low_mem_phase1): Add cu as argument.
Track which section we are reading based on cu->cu_version.
Read DWARF5 loclists entries setting ptr and len up to the
start and length of the location lists to read.
(add_locexpr_dummy_dies): Pass cu to read_loclist_low_mem_phase1.
(loclists_htab): New static htab variable.
(read_loclist): Update like read_loclist_low_mem_phase1 and create
and update loclists_htab for DEBUG_LOCLISTS.
(checksum_die): Pass cu to read_loclists.
(adjust_loclists): New function like adjust_loclist for
DEBUG_LOCLISTS.
(write_loclists): New function like write_locs.
(cleanup): Delete loclists_htab.
(dwz): Call write_loclists.
At this time binutils readelf doesn't display .debug_loclists with view
pairs correctly. To test the updates are correct when GCC emits view
pairs as GNU extension you'll need elfutils eu-readelf with the following
patch: https://sourceware.org/pipermail/elfutils-devel/2020q3/002900.html
---
dwz.c | 356 +++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 302 insertions(+), 54 deletions(-)
diff --git a/dwz.c b/dwz.c
index e772954..588ee50 100644
--- a/dwz.c
+++ b/dwz.c
@@ -2374,50 +2374,114 @@ read_exprloc_low_mem_phase1 (DSO *dso, dw_die_ref die, unsigned char *ptr,
/* Add dummy DIEs for loclist at OFFSET. */
static int
-read_loclist_low_mem_phase1 (DSO *dso, dw_die_ref die, GElf_Addr offset)
+read_loclist_low_mem_phase1 (DSO *dso, dw_cu_ref cu, dw_die_ref die,
+ GElf_Addr offset)
{
unsigned char *ptr, *endsec;
GElf_Addr low, high;
- size_t len;
+ size_t len = 0;
+ int sec;
- ptr = debug_sections[DEBUG_LOC].data;
+ sec = cu->cu_version < 5 ? DEBUG_LOC : DEBUG_LOCLISTS;
+ ptr = debug_sections[sec].data;
if (ptr == NULL)
{
- error (0, 0, "%s: loclistptr attribute, yet no .debug_loc section",
- dso->filename);
+ error (0, 0, "%s: loclistptr attribute, yet no %s section",
+ dso->filename, debug_sections[sec].name);
return 1;
}
- if (offset >= debug_sections[DEBUG_LOC].size)
+ if (offset >= debug_sections[sec].size)
{
error (0, 0,
- "%s: loclistptr offset %Ld outside of .debug_loc section",
- dso->filename, (long long) offset);
+ "%s: loclistptr offset %Ld outside of %s section",
+ dso->filename, (long long) offset, debug_sections[sec].name);
return 1;
}
- endsec = ptr + debug_sections[DEBUG_LOC].size;
+ endsec = ptr + debug_sections[sec].size;
ptr += offset;
while (ptr < endsec)
{
- low = read_size (ptr, ptr_size);
- high = read_size (ptr + ptr_size, ptr_size);
- ptr += 2 * ptr_size;
- if (low == 0 && high == 0)
- break;
+ if (cu->cu_version < 5)
+ {
+ low = read_size (ptr, ptr_size);
+ high = read_size (ptr + ptr_size, ptr_size);
+ ptr += 2 * ptr_size;
+ if (low == 0 && high == 0)
+ break;
- if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff))
- continue;
+ if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff))
+ continue;
+
+ len = read_16 (ptr);
+ }
+ else
+ {
+ uint8_t lle = *ptr++;
+ switch (lle)
+ {
+ case DW_LLE_end_of_list:
+ return 0;
+
+ case DW_LLE_base_addressx:
+ skip_leb128 (ptr);
+ continue;
+
+ case DW_LLE_startx_endx:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_startx_length:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_offset_pair:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_default_location:
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_base_address:
+ ptr += ptr_size;
+ continue;
+
+ case DW_LLE_start_end:
+ ptr += 2 * ptr_size;
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_start_length:
+ ptr += ptr_size;
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ default:
+ error (0, 0,
+ "%s: unhandled location list entry 0x%x in %s section",
+ dso->filename, lle, debug_sections[sec].name);
+ return 1;
+ }
+ }
- len = read_16 (ptr);
if (unlikely (!(ptr + len <= endsec)))
{
error (0, 0,
- "%s: locexpr length 0x%Lx exceeds .debug_loc section",
- dso->filename, (long long) len);
+ "%s: locexpr length 0x%Lx exceeds %s section",
+ dso->filename, (long long) len, debug_sections[sec].name);
return 1;
}
- if (read_exprloc_low_mem_phase1 (dso, die, ptr, len))
- return 1;
+ if (len > 0)
+ if (read_exprloc_low_mem_phase1 (dso, die, ptr, len))
+ return 1;
ptr += len;
}
@@ -2492,13 +2556,13 @@ add_locexpr_dummy_dies (DSO *dso, dw_cu_ref cu, dw_die_ref die,
if ((cu->cu_version < 4 && form == DW_FORM_data4)
|| form == DW_FORM_sec_offset)
{
- if (read_loclist_low_mem_phase1 (dso, die, do_read_32 (ptr)))
+ if (read_loclist_low_mem_phase1 (dso, cu, die, do_read_32 (ptr)))
return 1;
break;
}
else if (cu->cu_version < 4 && form == DW_FORM_data8)
{
- if (read_loclist_low_mem_phase1 (dso, die, do_read_64 (ptr)))
+ if (read_loclist_low_mem_phase1 (dso, cu, die, do_read_64 (ptr)))
return 1;
break;
}
@@ -2526,10 +2590,12 @@ struct debug_loc_adjust
};
ALIGN_STRUCT (debug_loc_adjust)
-/* Hash table and obstack for recording .debug_loc adjustment ranges. */
+/* Hash table and obstack for recording .debug_loc and .debug_loclists
+ adjustment ranges. */
static htab_t loc_htab;
+static htab_t loclists_htab;
-/* Hash function for loc_htab. */
+/* Hash function for loc[lists]_htab. */
static hashval_t
loc_hash (const void *p)
{
@@ -2538,7 +2604,7 @@ loc_hash (const void *p)
return a->end_offset;
}
-/* Equality function for loc_htab. */
+/* Equality function for loc[lists]_htab. */
static int
loc_eq (const void *p, const void *q)
{
@@ -2552,47 +2618,109 @@ loc_eq (const void *p, const void *q)
DIE. Call read_exprloc on each of the DWARF expressions
contained in it. */
static int
-read_loclist (DSO *dso, dw_die_ref die, GElf_Addr offset)
+read_loclist (DSO *dso, dw_cu_ref cu, dw_die_ref die, GElf_Addr offset)
{
unsigned char *ptr, *endsec;
GElf_Addr low, high;
size_t len;
+ int sec;
bool need_adjust = false;
die->die_ck_state = CK_BAD;
- ptr = debug_sections[DEBUG_LOC].data;
+ sec = cu->cu_version < 5 ? DEBUG_LOC : DEBUG_LOCLISTS;
+ ptr = debug_sections[sec].data;
if (ptr == NULL)
{
- error (0, 0, "%s: loclistptr attribute, yet no .debug_loc section",
- dso->filename);
+ error (0, 0, "%s: loclistptr attribute, yet no %s section",
+ dso->filename, debug_sections[sec].name);
return 1;
}
- if (offset >= debug_sections[DEBUG_LOC].size)
+ if (offset >= debug_sections[sec].size)
{
error (0, 0,
- "%s: loclistptr offset %Ld outside of .debug_loc section",
- dso->filename, (long long) offset);
+ "%s: loclistptr offset %Ld outside of %s section",
+ dso->filename, (long long) offset, debug_sections[sec].name);
return 1;
}
- endsec = ptr + debug_sections[DEBUG_LOC].size;
+ endsec = ptr + debug_sections[sec].size;
ptr += offset;
while (ptr < endsec)
{
- low = read_size (ptr, ptr_size);
- high = read_size (ptr + ptr_size, ptr_size);
- ptr += 2 * ptr_size;
- if (low == 0 && high == 0)
- break;
+ if (cu->cu_version < 5)
+ {
+ low = read_size (ptr, ptr_size);
+ high = read_size (ptr + ptr_size, ptr_size);
+ ptr += 2 * ptr_size;
+ if (low == 0 && high == 0)
+ break;
- if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff))
- continue;
+ if (low == ~ (GElf_Addr) 0 || (ptr_size == 4 && low == 0xffffffff))
+ continue;
+
+ len = read_16 (ptr);
+ }
+ else
+ {
+ uint8_t lle = *ptr++;
+ switch (lle)
+ {
+ case DW_LLE_end_of_list:
+ return 0;
+
+ case DW_LLE_base_addressx:
+ skip_leb128 (ptr);
+ continue;
+
+ case DW_LLE_startx_endx:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_startx_length:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_offset_pair:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_default_location:
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_base_address:
+ ptr += ptr_size;
+ continue;
+
+ case DW_LLE_start_end:
+ ptr += 2 * ptr_size;
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_start_length:
+ ptr += ptr_size;
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ default:
+ error (0, 0,
+ "%s: unhandled location list entry 0x%x in %s section",
+ dso->filename, lle, debug_sections[sec].name);
+ return 1;
+ }
+ }
- len = read_16 (ptr);
if (unlikely (!(ptr + len <= endsec)))
{
error (0, 0,
- "%s: locexpr length 0x%Lx exceeds .debug_loc section",
- dso->filename, (long long) len);
+ "%s: locexpr length 0x%Lx exceeds %s section",
+ dso->filename, (long long) len, debug_sections[sec].name);
return 1;
}
@@ -2608,15 +2736,30 @@ read_loclist (DSO *dso, dw_die_ref die, GElf_Addr offset)
void **slot;
adj.start_offset = offset;
- adj.end_offset = ptr - debug_sections[DEBUG_LOC].data;
- adj.cu = die_cu (die);
- if (loc_htab == NULL)
+ adj.end_offset = ptr - debug_sections[sec].data;
+ adj.cu = cu;
+ if (sec == DEBUG_LOC)
{
- loc_htab = htab_try_create (50, loc_hash, loc_eq, NULL);
if (loc_htab == NULL)
- dwz_oom ();
+ {
+ loc_htab = htab_try_create (50, loc_hash, loc_eq, NULL);
+ if (loc_htab == NULL)
+ dwz_oom ();
+ }
+ slot = htab_find_slot_with_hash (loc_htab, &adj, adj.end_offset,
+ INSERT);
+ }
+ else
+ {
+ if (loclists_htab == NULL)
+ {
+ loclists_htab = htab_try_create (50, loc_hash, loc_eq, NULL);
+ if (loclists_htab == NULL)
+ dwz_oom ();
+ }
+ slot = htab_find_slot_with_hash (loclists_htab, &adj, adj.end_offset,
+ INSERT);
}
- slot = htab_find_slot_with_hash (loc_htab, &adj, adj.end_offset, INSERT);
if (slot == NULL)
dwz_oom ();
if (*slot == NULL)
@@ -2627,8 +2770,8 @@ read_loclist (DSO *dso, dw_die_ref die, GElf_Addr offset)
}
else if (((struct debug_loc_adjust *)*slot)->cu != adj.cu)
{
- error (0, 0, "%s: can't adjust .debug_loc section because multiple "
- "CUs refer to it", dso->filename);
+ error (0, 0, "%s: can't adjust %s section because multiple "
+ "CUs refer to it", dso->filename, debug_sections[sec].name);
return 1;
}
else if (((struct debug_loc_adjust *)*slot)->start_offset > offset)
@@ -2848,14 +2991,14 @@ checksum_die (DSO *dso, dw_cu_ref cu, dw_die_ref top_die, dw_die_ref die)
if ((cu->cu_version < 4 && form == DW_FORM_data4)
|| form == DW_FORM_sec_offset)
{
- if (read_loclist (dso, die, read_32 (ptr)))
+ if (read_loclist (dso, cu, die, read_32 (ptr)))
return 1;
ptr = old_ptr;
break;
}
else if (cu->cu_version < 4 && form == DW_FORM_data8)
{
- if (read_loclist (dso, die, read_64 (ptr)))
+ if (read_loclist (dso, cu, die, read_64 (ptr)))
return 1;
ptr = old_ptr;
break;
@@ -12128,6 +12271,85 @@ adjust_loclist (void **slot, void *data)
return 1;
}
+/* Adjust .debug_loclists range determined by *SLOT, called through
+ htab_traverse. */
+static int
+adjust_loclists (void **slot, void *data)
+{
+ struct debug_loc_adjust *adj = (struct debug_loc_adjust *) *slot;
+ unsigned char *ptr, *endsec;
+ size_t len = 0;
+
+ (void)data;
+
+ ptr = debug_sections[DEBUG_LOCLISTS].new_data + adj->start_offset;
+ endsec = ptr + debug_sections[DEBUG_LOCLISTS].size;
+
+ while (ptr < endsec)
+ {
+ uint8_t lle = *ptr++;
+ switch (lle)
+ {
+ case DW_LLE_end_of_list:
+ return 1;
+
+ case DW_LLE_base_addressx:
+ skip_leb128 (ptr);
+ continue;
+
+ case DW_LLE_startx_endx:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_startx_length:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_offset_pair:
+ skip_leb128 (ptr);
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_default_location:
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_base_address:
+ ptr += ptr_size;
+ continue;
+
+ case DW_LLE_start_end:
+ ptr += 2 * ptr_size;
+ len = read_uleb128 (ptr);
+ break;
+
+ case DW_LLE_start_length:
+ ptr += ptr_size;
+ skip_leb128 (ptr);
+ len = read_uleb128 (ptr);
+ break;
+
+ default:
+ error (0, 0, "unhandled location list entry 0x%x", lle);
+ return 1;
+ }
+
+ assert (ptr + len <= endsec);
+
+ adjust_exprloc (adj->cu, adj->cu->cu_die, adj->cu, adj->cu->cu_die,
+ ptr, len);
+
+ ptr += len;
+ }
+
+ return 1;
+}
+
/* Create new .debug_loc section in malloced memory if .debug_loc
needs to be adjusted. */
static void
@@ -12144,6 +12366,23 @@ write_loc (void)
htab_traverse (loc_htab, adjust_loclist, NULL);
}
+/* Create new .debug_loclists section in malloced memory if .debug_loclists
+ needs to be adjusted. */
+static void
+write_loclists (void)
+{
+ unsigned char *loc;
+ if (loclists_htab == NULL)
+ return;
+ loc = malloc (debug_sections[DEBUG_LOCLISTS].size);
+ if (loc == NULL)
+ dwz_oom ();
+ memcpy (loc, debug_sections[DEBUG_LOCLISTS].data,
+ debug_sections[DEBUG_LOCLISTS].size);
+ debug_sections[DEBUG_LOCLISTS].new_data = loc;
+ htab_traverse (loclists_htab, adjust_loclists, NULL);
+}
+
/* Create new .debug_types section in malloced memory. */
static void
write_types (void)
@@ -13439,6 +13678,9 @@ cleanup (void)
if (loc_htab != NULL)
htab_delete (loc_htab);
loc_htab = NULL;
+ if (loclists_htab != NULL)
+ htab_delete (loclists_htab);
+ loclists_htab = NULL;
if (dup_htab != NULL)
htab_delete (dup_htab);
dup_htab = NULL;
@@ -14440,6 +14682,12 @@ dwz (const char *file, const char *outfile, struct file_result *res,
fprintf (stderr, "write_loc\n");
}
write_loc ();
+ if (unlikely (progress_p))
+ {
+ report_progress ();
+ fprintf (stderr, "write_loclists\n");
+ }
+ write_loclists ();
if (unlikely (progress_p))
{
report_progress ();
--
2.18.4
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] Support updating DWARF5 .debug_loclists.
2020-09-28 8:07 [PATCH] Support updating DWARF5 .debug_loclists Mark Wielaard
@ 2020-09-29 8:42 ` Jakub Jelinek
2020-09-29 17:59 ` Mark Wielaard
0 siblings, 1 reply; 3+ messages in thread
From: Jakub Jelinek @ 2020-09-29 8:42 UTC (permalink / raw)
To: Mark Wielaard; +Cc: dwz
On Mon, Sep 28, 2020 at 10:07:59AM +0200, Mark Wielaard wrote:
> ptr += offset;
> while (ptr < endsec)
> {
> - low = read_size (ptr, ptr_size);
> - high = read_size (ptr + ptr_size, ptr_size);
> - ptr += 2 * ptr_size;
> - if (low == 0 && high == 0)
> - break;
> + if (cu->cu_version < 5)
I wonder if it wouldn't be clearer to use sec == DEBUG_LOC
in this spot (i.e. leave the decision based on cu_version just to the
start of the function).
> + case DW_LLE_start_length:
> + ptr += ptr_size;
> + skip_leb128 (ptr);
> + len = read_uleb128 (ptr);
> + break;
Wonder if we shouldn't handle case DW_LLE_GNU_view_part here, but perhaps
only for cu->cu_version == 5 because it isn't in a separate vendor code
space and thus in DWARF 6 it could mean something different.
> while (ptr < endsec)
> {
> - low = read_size (ptr, ptr_size);
> - high = read_size (ptr + ptr_size, ptr_size);
> - ptr += 2 * ptr_size;
> - if (low == 0 && high == 0)
> - break;
> + if (cu->cu_version < 5)
Ditto.
> + case DW_LLE_start_length:
> + ptr += ptr_size;
> + skip_leb128 (ptr);
> + len = read_uleb128 (ptr);
> + break;
Ditto.
Otherwise LGTM.
Jakub
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] Support updating DWARF5 .debug_loclists.
2020-09-29 8:42 ` Jakub Jelinek
@ 2020-09-29 17:59 ` Mark Wielaard
0 siblings, 0 replies; 3+ messages in thread
From: Mark Wielaard @ 2020-09-29 17:59 UTC (permalink / raw)
To: Jakub Jelinek; +Cc: dwz
Hi Jakub,
On Tue, 2020-09-29 at 10:42 +0200, Jakub Jelinek wrote:
> On Mon, Sep 28, 2020 at 10:07:59AM +0200, Mark Wielaard wrote:
> > ptr += offset;
> > while (ptr < endsec)
> > {
> > - low = read_size (ptr, ptr_size);
> > - high = read_size (ptr + ptr_size, ptr_size);
> > - ptr += 2 * ptr_size;
> > - if (low == 0 && high == 0)
> > - break;
> > + if (cu->cu_version < 5)
>
> I wonder if it wouldn't be clearer to use sec == DEBUG_LOC
> in this spot (i.e. leave the decision based on cu_version just to the
> start of the function).
Yes, it would be clearer. Changed.
> > + case DW_LLE_start_length:
> > + ptr += ptr_size;
> > + skip_leb128 (ptr);
> > + len = read_uleb128 (ptr);
> > + break;
>
> Wonder if we shouldn't handle case DW_LLE_GNU_view_part here, but perhaps
> only for cu->cu_version == 5 because it isn't in a separate vendor code
> space and thus in DWARF 6 it could mean something different.
Yes, it would. gcc doesn't emit it by default at the moment. But
handling it is easy:
case DW_LLE_GNU_view_pair:
if (cu->cu_version != 5)
error (0, 0,
"%s: DW_LLE_GNU_view_pair used with DWARF version %u\n",
dso->filename, cu->cu_version);
skip_leb128 (ptr);
skip_leb128 (ptr);
continue;
It will warn, but still try to handle it as is.
We probably never hit
this because dwz doesn't actually accept
DWARFv6 CUs, because they don't
exist yet :)
> Otherwise LGTM.
Thanks. Pushed with those changes. Tested against a
gcc -gvariable-location-views=incompat5 build.
BTW. I fixed binutils readelf so that it now also correctly shows
.debug_loclists:
https://sourceware.org/pipermail/binutils/2020-September/113510.html
Cheers,
Mark
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2020-09-29 17:59 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-28 8:07 [PATCH] Support updating DWARF5 .debug_loclists Mark Wielaard
2020-09-29 8:42 ` Jakub Jelinek
2020-09-29 17:59 ` Mark Wielaard
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).