+2018-04-06 Mark Wielaard <mark@klomp.org>
+
+ * dwarf_formaddr.c (__libdw_addrx): New function, extracted from...
+ (dwarf_formaddr): here. Use __libdw_addrx.
+ * dwarf_getlocation.c (getlocations_addr): Pass cu to
+ __libdw_read_begin_end_pair_inc.
+ * dwarf_ranges.c (__libdw_read_begin_end_pair_inc): Take cu as
+ argument. Handle .debug_rnglists.
+ (initial_offset): Handle .debug_rnglists and DW_FORM_rnglistx.
+ (dwarf_ranges): Likewise. Check cu isn't NULL before use. Pass cu to
+ __libdw_read_begin_end_pair_inc.
+ * libdwP.h (__libdw_read_begin_end_pair_inc): Take cu as argument.
+ (__libdw_cu_ranges_base): Handle DW_AT_rnglists_base.
+ (__libdw_addrx): New function definition.
+
2018-04-11 Mark Wielaard <mark@klomp.org>
* dwarf.h: Add DWARF5 range list entry DW_RLE encodings.
int
+__libdw_addrx (Dwarf_CU *cu, Dwarf_Word idx, Dwarf_Addr *addr)
+{
+ Dwarf_Off addr_off = __libdw_cu_addr_base (cu);
+ if (addr_off == (Dwarf_Off) -1)
+ return -1;
+
+ Dwarf *dbg = cu->dbg;
+ if (dbg->sectiondata[IDX_debug_addr] == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NO_DEBUG_ADDR);
+ return -1;
+ }
+
+ /* The section should at least contain room for one address. */
+ int address_size = cu->address_size;
+ if (cu->address_size > dbg->sectiondata[IDX_debug_addr]->d_size)
+ {
+ invalid_offset:
+ __libdw_seterrno (DWARF_E_INVALID_OFFSET);
+ return -1;
+ }
+
+ if (addr_off > (dbg->sectiondata[IDX_debug_addr]->d_size
+ - address_size))
+ goto invalid_offset;
+
+ idx *= address_size;
+ if (idx > (dbg->sectiondata[IDX_debug_addr]->d_size
+ - address_size - addr_off))
+ goto invalid_offset;
+
+ const unsigned char *datap;
+ datap = dbg->sectiondata[IDX_debug_addr]->d_buf + addr_off + idx;
+ if (address_size == 4)
+ *addr = read_4ubyte_unaligned (dbg, datap);
+ else
+ *addr = read_8ubyte_unaligned (dbg, datap);
+
+ return 0;
+}
+
+int
dwarf_formaddr (Dwarf_Attribute *attr, Dwarf_Addr *return_addr)
{
if (attr == NULL)
/* So we got an index. Lets see if it is valid and we can get the actual
address. */
-
- Dwarf_Off addr_off = __libdw_cu_addr_base (cu);
- if (addr_off == (Dwarf_Off) -1)
+ if (__libdw_addrx (cu, idx, return_addr) != 0)
return -1;
- if (dbg->sectiondata[IDX_debug_addr] == NULL)
- {
- __libdw_seterrno (DWARF_E_NO_DEBUG_ADDR);
- return -1;
- }
-
- /* The section should at least contain room for one address. */
- int address_size = cu->address_size;
- if (cu->address_size > dbg->sectiondata[IDX_debug_addr]->d_size)
- {
- invalid_offset:
- __libdw_seterrno (DWARF_E_INVALID_OFFSET);
- return -1;
- }
-
- if (addr_off > (dbg->sectiondata[IDX_debug_addr]->d_size
- - address_size))
- goto invalid_offset;
-
- idx *= address_size;
- if (idx > (dbg->sectiondata[IDX_debug_addr]->d_size
- - address_size - addr_off))
- goto invalid_offset;
-
- datap = dbg->sectiondata[IDX_debug_addr]->d_buf + addr_off + idx;
- if (address_size == 4)
- *return_addr = read_4ubyte_unaligned (dbg, datap);
- else
- *return_addr = read_8ubyte_unaligned (dbg, datap);
-
return 0;
}
INTDEF(dwarf_formaddr)
Dwarf_Addr end;
next:
- switch (__libdw_read_begin_end_pair_inc (dbg, secidx,
+ switch (__libdw_read_begin_end_pair_inc (cu, secidx,
&readp, readendp,
cu->address_size,
&begin, &end, basep))
- If it's end of rangelist, don't set anything and return 2
- If an error occurs, don't set anything and return -1. */
internal_function int
-__libdw_read_begin_end_pair_inc (Dwarf *dbg, int sec_index,
+__libdw_read_begin_end_pair_inc (Dwarf_CU *cu, int sec_index,
const unsigned char **addrp,
const unsigned char *addrend,
int width,
Dwarf_Addr *beginp, Dwarf_Addr *endp,
Dwarf_Addr *basep)
{
- Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1
- : (Elf64_Addr) (Elf32_Addr) -1);
- Dwarf_Addr begin;
- Dwarf_Addr end;
-
- const unsigned char *addr = *addrp;
- if (addrend - addr < width * 2)
+ Dwarf *dbg = cu->dbg;
+ if (sec_index == IDX_debug_ranges || sec_index == IDX_debug_loc)
{
- invalid:
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return -1;
- }
+ Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1
+ : (Elf64_Addr) (Elf32_Addr) -1);
+ Dwarf_Addr begin;
+ Dwarf_Addr end;
- bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address, begin);
- bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address, end);
- *addrp = addr;
+ const unsigned char *addr = *addrp;
+ if (addrend - addr < width * 2)
+ {
+ invalid:
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
- /* Unrelocated escape for begin means base address selection. */
- if (begin == escape && !begin_relocated)
- {
- if (unlikely (end == escape))
- goto invalid;
+ bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address,
+ begin);
+ bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address,
+ end);
+ *addrp = addr;
- *basep = end;
- return 1;
- }
+ /* Unrelocated escape for begin means base address selection. */
+ if (begin == escape && !begin_relocated)
+ {
+ if (unlikely (end == escape))
+ goto invalid;
+
+ *basep = end;
+ return 1;
+ }
- /* Unrelocated pair of zeroes means end of range list. */
- if (begin == 0 && end == 0 && !begin_relocated && !end_relocated)
- return 2;
+ /* Unrelocated pair of zeroes means end of range list. */
+ if (begin == 0 && end == 0 && !begin_relocated && !end_relocated)
+ return 2;
- /* Don't check for begin_relocated == end_relocated. Serve the data
- to the client even though it may be buggy. */
- *beginp = begin + *basep;
- *endp = end + *basep;
+ /* Don't check for begin_relocated == end_relocated. Serve the data
+ to the client even though it may be buggy. */
+ *beginp = begin + *basep;
+ *endp = end + *basep;
- return 0;
+ return 0;
+ }
+ else if (sec_index == IDX_debug_rnglists)
+ {
+ const unsigned char *addr = *addrp;
+ if (addrend - addr < 1)
+ goto invalid;
+
+ const char code = *addr++;
+ uint64_t begin = 0, end = 0, base = *basep, addr_idx;
+ switch (code)
+ {
+ case DW_RLE_end_of_list:
+ *addrp = addr;
+ return 2;
+
+ case DW_RLE_base_addressx:
+ if (addrend - addr < 1)
+ goto invalid;
+ get_uleb128 (addr_idx, addr, addrend);
+ if (__libdw_addrx (cu, addr_idx, &base) != 0)
+ return -1;
+
+ *basep = base;
+ *addrp = addr;
+ return 1;
+
+ case DW_RLE_startx_endx:
+ if (addrend - addr < 1)
+ goto invalid;
+ get_uleb128 (addr_idx, addr, addrend);
+ if (__libdw_addrx (cu, addr_idx, &begin) != 0)
+ return -1;
+ if (addrend - addr < 1)
+ goto invalid;
+ get_uleb128 (addr_idx, addr, addrend);
+ if (__libdw_addrx (cu, addr_idx, &end) != 0)
+ return -1;
+
+ *beginp = begin;
+ *endp = end;
+ *addrp = addr;
+ return 0;
+
+ case DW_RLE_startx_length:
+ if (addrend - addr < 1)
+ goto invalid;
+ get_uleb128 (addr_idx, addr, addrend);
+ if (__libdw_addrx (cu, addr_idx, &begin) != 0)
+ return -1;
+ if (addrend - addr < 1)
+ goto invalid;
+ get_uleb128 (end, addr, addrend);
+
+ *beginp = begin;
+ *endp = begin + end;
+ *addrp = addr;
+ return 0;
+
+ case DW_RLE_offset_pair:
+ if (addrend - addr < 1)
+ goto invalid;
+ get_uleb128 (begin, addr, addrend);
+ if (addrend - addr < 1)
+ goto invalid;
+ get_uleb128 (end, addr, addrend);
+
+ *beginp = begin + base;
+ *endp = end + base;
+ *addrp = addr;
+ return 0;
+
+ case DW_RLE_base_address:
+ if (addrend - addr < width)
+ goto invalid;
+ __libdw_read_address_inc (dbg, sec_index, &addr, width, &base);
+
+ *basep = base;
+ *addrp = addr;
+ return 1;
+
+ case DW_RLE_start_end:
+ if (addrend - addr < 2 * width)
+ goto invalid;
+ __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
+ __libdw_read_address_inc (dbg, sec_index, &addr, width, &end);
+
+ *beginp = begin;
+ *endp = end;
+ *addrp = addr;
+ return 0;
+
+ case DW_RLE_start_length:
+ if (addrend - addr < width)
+ goto invalid;
+ __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
+ if (addrend - addr < 1)
+ goto invalid;
+ get_uleb128 (end, addr, addrend);
+
+ *beginp = begin;
+ *endp = begin + end;
+ *addrp = addr;
+ return 0;
+
+ default:
+ goto invalid;
+ }
+ }
+ else
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
}
static int
initial_offset (Dwarf_Attribute *attr, ptrdiff_t *offset)
{
- size_t secidx = IDX_debug_ranges;
+ size_t secidx = (attr->cu->version < 5
+ ? IDX_debug_ranges : IDX_debug_rnglists);
Dwarf_Word start_offset;
- if (__libdw_formptr (attr, secidx,
- DWARF_E_NO_DEBUG_RANGES,
- NULL, &start_offset) == NULL)
- return -1;
+ if (attr->form == DW_FORM_rnglistx)
+ {
+ Dwarf_Word idx;
+ Dwarf_CU *cu = attr->cu;
+ const unsigned char *datap = attr->valp;
+ const unsigned char *endp = cu->endp;
+ if (datap >= endp)
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
+ get_uleb128 (idx, datap, endp);
+
+ Elf_Data *data = cu->dbg->sectiondata[secidx];
+ if (data == NULL && cu->unit_type == DW_UT_split_compile)
+ {
+ cu = __libdw_find_split_unit (cu);
+ if (cu != NULL)
+ data = cu->dbg->sectiondata[secidx];
+ }
+
+ if (data == NULL)
+ {
+ __libdw_seterrno (secidx == IDX_debug_ranges
+ ? DWARF_E_NO_DEBUG_RANGES
+ : DWARF_E_NO_DEBUG_RNGLISTS);
+ return -1;
+ }
+
+ Dwarf_Off range_base_off = __libdw_cu_ranges_base (cu);
+
+ /* The section should at least contain room for one offset. */
+ size_t sec_size = cu->dbg->sectiondata[secidx]->d_size;
+ size_t offset_size = cu->offset_size;
+ if (offset_size > sec_size)
+ {
+ invalid_offset:
+ __libdw_seterrno (DWARF_E_INVALID_OFFSET);
+ return -1;
+ }
+
+ /* And the base offset should be at least inside the section. */
+ if (range_base_off > (sec_size - offset_size))
+ goto invalid_offset;
+
+ size_t max_idx = (sec_size - offset_size - range_base_off) / offset_size;
+ if (idx > max_idx)
+ goto invalid_offset;
+
+ datap = (cu->dbg->sectiondata[secidx]->d_buf
+ + range_base_off + (idx * offset_size));
+ if (offset_size == 4)
+ start_offset = read_4ubyte_unaligned (cu->dbg, datap);
+ else
+ start_offset = read_8ubyte_unaligned (cu->dbg, datap);
+
+ start_offset += range_base_off;
+ }
+ else
+ {
+ if (__libdw_formptr (attr, secidx,
+ (secidx == IDX_debug_ranges
+ ? DWARF_E_NO_DEBUG_RANGES
+ : DWARF_E_NO_DEBUG_RNGLISTS),
+ NULL, &start_offset) == NULL)
+ return -1;
+ }
*offset = start_offset;
return 0;
return 0;
/* We have to look for a noncontiguous range. */
- size_t secidx = IDX_debug_ranges;
Dwarf_CU *cu = die->cu;
if (cu == NULL)
{
return -1;
}
+ size_t secidx = (cu->version < 5 ? IDX_debug_ranges : IDX_debug_rnglists);
const Elf_Data *d = cu->dbg->sectiondata[secidx];
if (d == NULL && cu->unit_type == DW_UT_split_compile)
{
Dwarf_Addr end;
next:
- switch (__libdw_read_begin_end_pair_inc (cu->dbg, secidx,
+ switch (__libdw_read_begin_end_pair_inc (cu, secidx,
&readp, readendp,
cu->address_size,
&begin, &end, basep))
- If it's base address selection record, set up *BASEP and return 1.
- If it's end of rangelist, don't set anything and return 2
- If an error occurs, don't set anything and return <0. */
-int __libdw_read_begin_end_pair_inc (Dwarf *dbg, int sec_index,
+int __libdw_read_begin_end_pair_inc (Dwarf_CU *cu, int sec_index,
const unsigned char **readp,
const unsigned char *readend,
int width,
}
+/* Either a direct offset into .debug_ranges for version < 5, or the
+ start of the offset table in .debug_rnglists for version > 5. */
static inline Dwarf_Off
__libdw_cu_ranges_base (Dwarf_CU *cu)
{
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)
+ if (cu->version < 5)
{
- Dwarf_Word off;
- if (dwarf_formudata (&attr, &off) == 0)
- offset = off;
+ if (dwarf_attr (&cu_die, DW_AT_GNU_ranges_base, &attr) != NULL)
+ {
+ Dwarf_Word off;
+ if (dwarf_formudata (&attr, &off) == 0)
+ offset = off;
+ }
+ }
+ else
+ {
+ if (dwarf_attr (&cu_die, DW_AT_rnglists_base, &attr) != NULL)
+ {
+ Dwarf_Word off;
+ if (dwarf_formudata (&attr, &off) == 0)
+ 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)
+ {
+ Dwarf *dbg = cu->dbg;
+ const unsigned char *readp = data->d_buf;
+ const unsigned char *const dataend
+ = (unsigned char *) data->d_buf + data->d_size;
+
+ uint64_t unit_length = read_4ubyte_unaligned_inc (dbg, readp);
+ unsigned int offset_size = 4;
+ if (unlikely (unit_length == 0xffffffff))
+ {
+ if (unlikely (readp > dataend - 8))
+ goto no_header;
+
+ unit_length = read_8ubyte_unaligned_inc (dbg, readp);
+ offset_size = 8;
+ }
+
+ if (readp > dataend - 8
+ || unit_length < 8
+ || unit_length > (uint64_t) (dataend - readp))
+ goto no_header;
+
+ uint16_t version = read_2ubyte_unaligned_inc (dbg, readp);
+ if (version != 5)
+ goto no_header;
+
+ uint8_t address_size = *readp++;
+ if (address_size != 4 && address_size != 8)
+ goto no_header;
+
+ uint8_t segment_size = *readp++;
+ if (segment_size != 0)
+ goto no_header;
+
+ uint32_t offset_entry_count;
+ offset_entry_count = read_4ubyte_unaligned_inc (dbg, readp);
+
+ const unsigned char *offset_array_start = readp;
+ if (offset_entry_count <= 0)
+ goto no_header;
+
+ uint64_t needed = offset_entry_count * offset_size;
+ if (unit_length - 8 < needed)
+ goto no_header;
+
+ offset = (Dwarf_Off) (offset_array_start
+ - (unsigned char *) data->d_buf);
+ }
}
+ no_header:
cu->ranges_base = offset;
}
}
+/* Given an address index for a CU return the address.
+ Returns -1 and sets libdw_errno if an error occurs. */
+int __libdw_addrx (Dwarf_CU *cu, Dwarf_Word idx, Dwarf_Addr *addr);
+
+
/* Helper function to set debugdir field in Dwarf, used from dwarf_begin_elf
and libdwfl process_file. */
char * __libdw_debugdir (int fd);
(listptr_cu): New function.
(print_debug_rnglists_section): Likewise.
(attr_callback): Call notice_listptr for DW_AT_ranges. Handle
- DW_AT_rnglists_base.
+ DW_AT_rnglists_base. Handle DW_FORM_rnglistx. DW_AT_start_scope
+ can also have a rnglist.
(print_debug_units): Do (silently) scan split DWARF also for
debug_ranges before DWARF5 to catch all rangelistptrs.
(print_debug): Recognize .debug_rnglists. Reset known_rnglistptr.
break;
case DW_FORM_sec_offset:
+ case DW_FORM_rnglistx:
case DW_FORM_implicit_const:
case DW_FORM_udata:
case DW_FORM_sdata:
return DWARF_CB_OK;
case DW_AT_ranges:
+ case DW_AT_start_scope:
{
bool nlpt;
if (cbargs->cu->version < 5)
+2018-04-06 Mark Wielaard <mark@klomp.org>
+
+ * testfileranges5.debug.bz2: New testfile.
+ * testfilesplitranges5.debug.bz2: Likewise.
+ * testfile-ranges-hello5.dwo.bz2: Likewise.
+ * testfile-ranges-world5.dwo.bz2: Likewise.
+ * run-dwarf-ranges.sh: Run on testfileranges5.debug.
+ * run-all-dwarf-ranges.sh: Run on testfilesplitranges5.debug.
+ * tests/Makefile.am (EXTRA_DIST): Add testfileranges5.debug.bz2,
+ testfilesplitranges5.debug.bz2, testfile-ranges-hello5.dwo.bz2 and
+ testfile-ranges-world5.dwo.bz2.
+
2018-04-11 Mark Wielaard <mark@klomp.org>
* run-readelf-ranges.sh: New test.
run-dwfl-addr-sect.sh run-early-offscn.sh \
run-dwarf-getmacros.sh \
run-dwarf-ranges.sh debug-ranges-no-lowpc.o.bz2 \
- testfileranges4.debug.bz2 \
+ testfileranges4.debug.bz2 testfileranges5.debug.bz2 \
+ testfilesplitranges5.debug.bz2 \
+ testfile-ranges-hello5.dwo.bz2 testfile-ranges-world5.dwo.bz2 \
run-test-flag-nobits.sh \
run-dwarf-getstring.sh run-rerequest_tag.sh run-alldts.sh \
testfile15.bz2 testfile15.debug.bz2 \
EOF
+# Same with -gdwarf-5
+# gcc -c -O2 -o testfile-ranges-hello5.o -gsplit-dwarf -gdwarf-5 hello.c
+# gcc -c -O2 -o testfile-ranges-world5.o -gsplit-dwarf -gdwarf-5 world.c
+# gcc -o testfilesplitranges5 -O2 testfile-ranges-hello5.o testfile-ranges-world5.o
+# eu-strip -f testfilesplitranges5.debug testfilesplitranges5
+
+testfiles testfilesplitranges5.debug
+testfiles testfile-ranges-hello5.dwo testfile-ranges-world5.dwo
+
+testrun_compare ${abs_builddir}/all-dwarf-ranges testfilesplitranges5.debug <<\EOF
+die: hello.c (11)
+ 401150..40117a
+ 401050..401067
+
+die: no_say (2e)
+ 401160..40117a
+
+die: main (2e)
+ 401050..401067
+
+die: subject (1d)
+ 401053..40105f
+
+die: subject (2e)
+ 401150..401160
+
+die: world.c (11)
+ 401180..4011e7
+
+die: no_main (2e)
+ 4011d0..4011e7
+
+die: no_subject (1d)
+ 4011d3..4011df
+
+die: say (2e)
+ 401180..4011c0
+
+die: happy (1d)
+ 40119b..40119b
+ 40119c..4011a6
+ 4011b0..4011b4
+ 4011b5..4011bf
+
+die: sad (1d)
+ 40119b..40119b
+ 40119c..4011a6
+ 4011b4..4011b4
+ 4011b5..4011bf
+
+die: no_subject (2e)
+ 4011c0..4011d0
+
+EOF
+
exit 0
4005a5..4005ad (base 400540)
EOF
+# Like above, but with -gdwarf-5.
+testfiles testfileranges5.debug
+testrun_compare ${abs_builddir}/dwarf-ranges testfileranges5.debug 0xc <<\EOF
+401150..40117a (base 0)
+401050..401067 (base 0)
+EOF
+
+testrun_compare ${abs_builddir}/dwarf-ranges testfileranges5.debug 0x2ce <<\EOF
+40119b..40119b (base 401180)
+40119c..4011a6 (base 401180)
+4011b0..4011b4 (base 401180)
+4011b5..4011bf (base 401180)
+EOF
+
+testrun_compare ${abs_builddir}/dwarf-ranges testfileranges5.debug 0x2ef <<\EOF
+40119b..40119b (base 401180)
+40119c..4011a6 (base 401180)
+4011b4..4011b4 (base 401180)
+4011b5..4011bf (base 401180)
+EOF
+
exit 0