libdw: Handle GNU DebugFission split ranges.

GNU DebugFission split dwarf handles DW_FORM_sec_offset specially for
attributes that point to ranges. The .debug_ranges section is not in
the .dwo file, but in the main/skeleton object file. The sec_offset is
not relocated (in the ELF file), but is an offset against the skeleton
DIE DW_AT_GNU_ranges_base attribute. dwarf_formudata is changed so it
still looks like a normal offset ptr into the .debug_ranges section.
dwarf_ranges is adapted to look for the .debug_ranges in the main object
file. dwarf_highpc and dwarf_lowpc now handle the highpc and lowpc
attributes being inherited for the split unit DIE from the skeleton.

A new testcase is added to iterate over all ranges in a split GNU
DebugFission file.

Signed-off-by: Mark Wielaard <mark@klomp.org>
This commit is contained in:
Mark Wielaard
2018-05-19 00:46:02 +02:00
parent a4c74cc67d
commit 144b73c49a
13 changed files with 281 additions and 26 deletions
+12
View File
@@ -1,3 +1,15 @@
2018-05-18 Mark Wielaard <mark@klomp.org>
* dwarf_formudata.c (__libdw_formptr): Handle the special case
of IDX_debug_ranges for DW_UT_split_compile with version < 5.
* dwarf_highpc.c (dwarf_highpc): Use dwarf_lowpc, check for
split compile cudie.
* dwarf_lowpc.c (dwarf_lowpc): Check for split compile cudie.
* dwarf_ranges.c (dwarf_ranges): Switch cu and sectiondata for
split compile units.
* libdwP.h (struct Dwarf_CU): Add ranges_base field.
(__libdw_cu_ranges_base): New static inline function.
2018-05-18 Mark Wielaard <mark@klomp.org>
* libdw_findcu.c (__libdw_intern_next_unit): Init files to NULL.
+46 -4
View File
@@ -43,6 +43,17 @@ __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
return NULL;
const Elf_Data *d = attr->cu->dbg->sectiondata[sec_index];
Dwarf_CU *skel = NULL; /* See below, needed for GNU DebugFission. */
if (unlikely (d == NULL
&& sec_index == IDX_debug_ranges
&& attr->cu->version < 5
&& attr->cu->unit_type == DW_UT_split_compile))
{
skel = __libdw_find_split_unit (attr->cu);
if (skel != NULL)
d = skel->dbg->sectiondata[IDX_debug_ranges];
}
if (unlikely (d == NULL))
{
__libdw_seterrno (err_nodata);
@@ -52,10 +63,41 @@ __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
Dwarf_Word offset;
if (attr->form == DW_FORM_sec_offset)
{
if (__libdw_read_offset (attr->cu->dbg, attr->cu->dbg,
cu_sec_idx (attr->cu), attr->valp,
attr->cu->offset_size, &offset, sec_index, 0))
return NULL;
/* GNU DebugFission is slightly odd. It uses DW_FORM_sec_offset
in split units, but they are really (unrelocated) offsets
from the skeleton DW_AT_GNU_ranges_base (which is only used
for the split unit, not the skeleton ranges itself, see also
DW_AT_rnglists_base, which is used in DWARF5 for both, but
points to the offsets index). So it isn't really a formptr,
but an offset + base calculation. */
if (unlikely (skel != NULL))
{
Elf_Data *data = attr->cu->dbg->sectiondata[cu_sec_idx (attr->cu)];
const unsigned char *datap = attr->valp;
size_t size = attr->cu->offset_size;
if (unlikely (data == NULL
|| datap < (const unsigned char *) data->d_buf
|| data->d_size < size
|| ((size_t) (datap
- (const unsigned char *) data->d_buf)
> data->d_size - size)))
goto invalid;
if (size == 4)
offset = read_4ubyte_unaligned (attr->cu->dbg, datap);
else
offset = read_8ubyte_unaligned (attr->cu->dbg, datap);
offset += __libdw_cu_ranges_base (skel);
}
else
{
if (__libdw_read_offset (attr->cu->dbg, attr->cu->dbg,
cu_sec_idx (attr->cu), attr->valp,
attr->cu->offset_size, &offset,
sec_index, 0))
return NULL;
}
}
else if (attr->cu->version > 3)
goto invalid;
+10 -8
View File
@@ -1,7 +1,6 @@
/* Return high PC attribute of DIE.
Copyright (C) 2003, 2005, 2012 Red Hat, Inc.
Copyright (C) 2003, 2005, 2012, 2018 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2003.
This file is free software; you can redistribute it and/or modify
it under the terms of either
@@ -39,8 +38,14 @@ int
dwarf_highpc (Dwarf_Die *die, Dwarf_Addr *return_addr)
{
Dwarf_Attribute attr_high_mem;
Dwarf_Attribute *attr_high = INTUSE(dwarf_attr) (die, DW_AT_high_pc,
&attr_high_mem);
Dwarf_Attribute *attr_high;
/* Split compile DIEs inherit high_pc from their skeleton DIE. */
if (is_cudie (die) && die->cu->unit_type == DW_UT_split_compile)
attr_high = INTUSE(dwarf_attr_integrate) (die, DW_AT_high_pc,
&attr_high_mem);
else
attr_high = INTUSE(dwarf_attr) (die, DW_AT_high_pc, &attr_high_mem);
if (attr_high == NULL)
return -1;
@@ -48,10 +53,7 @@ dwarf_highpc (Dwarf_Die *die, Dwarf_Addr *return_addr)
return INTUSE(dwarf_formaddr) (attr_high, return_addr);
/* DWARF 4 allows high_pc to be a constant offset from low_pc. */
Dwarf_Attribute attr_low_mem;
if (INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (die, DW_AT_low_pc,
&attr_low_mem),
return_addr) == 0)
if (INTUSE(dwarf_lowpc) (die, return_addr) == 0)
{
Dwarf_Word uval;
if (INTUSE(dwarf_formudata) (attr_high, &uval) == 0)
+8 -7
View File
@@ -1,7 +1,6 @@
/* Return low PC attribute of DIE.
Copyright (C) 2003, 2005 Red Hat, Inc.
Copyright (C) 2003, 2005, 2018 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2003.
This file is free software; you can redistribute it and/or modify
it under the terms of either
@@ -38,10 +37,12 @@
int
dwarf_lowpc (Dwarf_Die *die, Dwarf_Addr *return_addr)
{
Dwarf_Attribute attr_mem;
return INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (die, DW_AT_low_pc,
&attr_mem),
return_addr);
Dwarf_Attribute attr_mem, *attr;
/* Split compile DIEs inherit low_pc from their skeleton DIE. */
if (is_cudie (die) && die->cu->unit_type == DW_UT_split_compile)
attr = INTUSE(dwarf_attr_integrate) (die, DW_AT_low_pc, &attr_mem);
else
attr = INTUSE(dwarf_attr) (die, DW_AT_low_pc, &attr_mem);
return INTUSE(dwarf_formaddr) (attr, return_addr);
}
INTDEF(dwarf_lowpc)
+18 -4
View File
@@ -122,7 +122,17 @@ dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep,
/* We have to look for a noncontiguous range. */
size_t secidx = IDX_debug_ranges;
const Elf_Data *d = die->cu->dbg->sectiondata[secidx];
Dwarf_CU *cu = die->cu;
const Elf_Data *d = cu->dbg->sectiondata[secidx];
if (d == NULL && cu->unit_type == DW_UT_split_compile)
{
Dwarf_CU *skel = __libdw_find_split_unit (cu);
if (skel != NULL)
{
cu = skel;
d = cu->dbg->sectiondata[secidx];
}
}
const unsigned char *readp;
const unsigned char *readendp;
@@ -131,6 +141,10 @@ dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep,
Dwarf_Attribute attr_mem;
Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges,
&attr_mem);
if (attr == NULL
&& is_cudie (die)
&& die->cu->unit_type == DW_UT_split_compile)
attr = INTUSE(dwarf_attr_integrate) (die, DW_AT_ranges, &attr_mem);
if (attr == NULL)
/* No PC attributes in this DIE at all, so an empty range list. */
return 0;
@@ -144,7 +158,7 @@ dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep,
}
else
{
if (__libdw_offset_in_section (die->cu->dbg,
if (__libdw_offset_in_section (cu->dbg,
secidx, offset, 1))
return -1;
}
@@ -156,9 +170,9 @@ dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep,
Dwarf_Addr end;
next:
switch (__libdw_read_begin_end_pair_inc (die->cu->dbg, secidx,
switch (__libdw_read_begin_end_pair_inc (cu->dbg, secidx,
&readp, readendp,
die->cu->address_size,
cu->address_size,
&begin, &end, basep))
{
case 0:
+26
View File
@@ -352,6 +352,11 @@ struct Dwarf_CU
Don't access directly, call __libdw_cu_str_off_base. */
Dwarf_Off str_off_base;
/* The offset into the .debug_ranges section to use for GNU
DebugFission split units. Don't access directly, call
__libdw_cu_ranges_base. */
Dwarf_Off ranges_base;
/* Memory boundaries of this CU. */
void *startp;
void *endp;
@@ -1055,6 +1060,27 @@ static inline Dwarf_Off __libdw_cu_str_off_base (Dwarf_CU *cu)
}
static inline Dwarf_Off
__libdw_cu_ranges_base (Dwarf_CU *cu)
{
if (cu->ranges_base == (Dwarf_Off) -1)
{
Dwarf_Off offset = 0;
Dwarf_Die cu_die = CUDIE(cu);
Dwarf_Attribute attr;
if (dwarf_attr (&cu_die, DW_AT_GNU_ranges_base, &attr) != NULL)
{
Dwarf_Word off;
if (dwarf_formudata (&attr, &off) == 0)
offset = off;
}
cu->ranges_base = offset;
}
return cu->ranges_base;
}
/* Helper function to set debugdir field in Dwarf, used from dwarf_begin_elf
and libdwfl process_file. */
char * __libdw_debugdir (int fd);
+14
View File
@@ -1,3 +1,17 @@
2018-05-18 Mark Wielaard <mark@klomp.org>
* tests/Makefiles.am (check_PROGRAMS): Add all-dwarf-ranges.
(TESTS): Add run-all-dwarf-ranges.sh.
(EXTRA_DIST): Add run-all-dwarf-ranges.sh,
testfilesplitranges4.debug.bz2, testfile-ranges-hello.dwo.bz2
and testfile-ranges-world.dwo.bz2.
(all_dwarf_ranges_LDADD): New variable.
* all-dwarf-ranges.c: New test program.
* run-all-dwarf-ranges: New test runner.
* testfile-ranges-hello.dwo.bz2: New test file.
* testfile-ranges-world.dwo.bz2: Likewise.
* testfilesplitranges4.debug.bz2: Likewise.
2018-05-18 Mark Wielaard <mark@klomp.org>
* run-get-files.sh: Add testcases for testfile-splitdwarf-4,
+8 -3
View File
@@ -56,7 +56,8 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
elfshphehdr elfstrmerge dwelfgnucompressed elfgetchdr \
elfgetzdata elfputzdata zstrptr emptyfile vendorelf \
fillfile dwarf_default_lower_bound dwarf-die-addr-die \
get-units-invalid get-units-split attr-integrate-skel
get-units-invalid get-units-split attr-integrate-skel \
all-dwarf-ranges
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -141,7 +142,8 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
emptyfile vendorelf fillfile dwarf_default_lower_bound \
run-dwarf-die-addr-die.sh \
run-get-units-invalid.sh run-get-units-split.sh \
run-attr-integrate-skel.sh
run-attr-integrate-skel.sh \
run-all-dwarf-ranges.sh
if !BIARCH
export ELFUTILS_DISABLE_BIARCH = 1
@@ -366,7 +368,9 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
testfile-hello4.dwo.bz2 testfile-hello5.dwo.bz2 \
testfile-splitdwarf-4.bz2 testfile-splitdwarf-5.bz2 \
testfile-world5.dwo.bz2 testfile-world4.dwo.bz2 \
run-attr-integrate-skel.sh
run-attr-integrate-skel.sh \
run-all-dwarf-ranges.sh testfilesplitranges4.debug.bz2 \
testfile-ranges-hello.dwo.bz2 testfile-ranges-world.dwo.bz2
if USE_VALGRIND
valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1'
@@ -528,6 +532,7 @@ dwarf_die_addr_die_LDADD = $(libdw)
get_units_invalid_LDADD = $(libdw)
get_units_split_LDADD = $(libdw)
attr_integrate_skel_LDADD = $(libdw)
all_dwarf_ranges_LDADD = $(libdw)
# We want to test the libelf header against the system elf.h header.
# Don't include any -I CPPFLAGS.
+90
View File
@@ -0,0 +1,90 @@
/* Test program for dwarf_ranges
Copyright (C) 2015, 2018 Red Hat, Inc.
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/>. */
#include <config.h>
#include ELFUTILS_HEADER(dw)
#include <dwarf.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <inttypes.h>
static void
ranges_die (Dwarf_Die *die)
{
Dwarf_Addr base, start, end;
int ranges = dwarf_ranges (die, 0, &base, &start, &end);
if (ranges < 0)
puts (dwarf_errmsg (-1));
else if (ranges > 0)
{
printf ("die: %s (%x)\n", dwarf_diename (die) ?: "<unknown>",
dwarf_tag (die));
for (ptrdiff_t off = 0;
(off = dwarf_ranges (die, off, &base, &start, &end)); )
if (off == -1)
{
puts (dwarf_errmsg (-1));
break;
}
else
printf (" %"PRIx64"..%"PRIx64"\n", start, end);
printf ("\n");
}
}
static void
walk_tree (Dwarf_Die *dwarf_die)
{
Dwarf_Die die = *dwarf_die;
do
{
Dwarf_Die child;
ranges_die (&die);
if (dwarf_child (&die, &child) == 0)
walk_tree (&child);
}
while (dwarf_siblingof (&die, &die) == 0);
}
int
main (int argc, char *argv[])
{
assert (argc >= 2);
const char *name = argv[1];
int fd = open (name, O_RDONLY);
Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ);
Dwarf_CU *cu = NULL;
Dwarf_Die cudie, subdie;
uint8_t unit_type;
while (dwarf_get_units (dbg, cu, &cu, NULL,
&unit_type, &cudie, &subdie) == 0)
{
Dwarf_Die die = (unit_type == DW_UT_skeleton
? subdie : cudie);
walk_tree (&die);
}
dwarf_end (dbg);
return 0;
}
+49
View File
@@ -0,0 +1,49 @@
#! /bin/sh
# Copyright (C) 2018 Red Hat, Inc.
# 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 run-dwarf-ranges.sh
# Compiled with:
# gcc -c -O2 -o testfile-ranges-hello.o -gsplit-dwarf -gdwarf-4 hello.c
# gcc -c -O2 -o testfile-ranges-world.o -gsplit-dwarf -gdwarf-4 world.c
# gcc -o testfilesplitranges4 -O2 \
# testfile-ranges-hello.o testfile-ranges-world.o
# eu-strip -f testfilesplitranges4.debug testfilesplitranges4
testfiles testfilesplitranges4.debug
testfiles testfile-ranges-hello.dwo testfile-ranges-world.dwo
testrun_compare ${abs_builddir}/all-dwarf-ranges testfilesplitranges4.debug <<\EOF
die: hello.c (11)
4004e0..4004ff
4003e0..4003f7
die: world.c (11)
400500..400567
die: happy (1d)
8009e0..8009ff
8008e0..8008f7
die: sad (1d)
400530..400534
400535..40053f
EOF
exit 0
Binary file not shown.
Binary file not shown.
Binary file not shown.