summaryrefslogtreecommitdiffstats
path: root/libdw/dwarf_formudata.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdw/dwarf_formudata.c')
-rw-r--r--libdw/dwarf_formudata.c160
1 files changed, 143 insertions, 17 deletions
diff --git a/libdw/dwarf_formudata.c b/libdw/dwarf_formudata.c
index e41981a4..26f86f12 100644
--- a/libdw/dwarf_formudata.c
+++ b/libdw/dwarf_formudata.c
@@ -1,5 +1,5 @@
/* Return unsigned constant represented by attribute.
- Copyright (C) 2003-2012, 2014 Red Hat, Inc.
+ Copyright (C) 2003-2012, 2014, 2017 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <[email protected]>, 2003.
@@ -34,15 +34,26 @@
#include <dwarf.h>
#include "libdwP.h"
-internal_function unsigned char *
+internal_function const unsigned char *
__libdw_formptr (Dwarf_Attribute *attr, int sec_index,
- int err_nodata, unsigned char **endpp,
+ int err_nodata, const unsigned char **endpp,
Dwarf_Off *offsetp)
{
if (attr == NULL)
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;
@@ -141,11 +183,24 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval)
case DW_AT_string_length:
case DW_AT_use_location:
case DW_AT_vtable_elem_location:
- /* loclistptr */
- if (__libdw_formptr (attr, IDX_debug_loc,
- DWARF_E_NO_LOCLIST, NULL,
- return_uval) == NULL)
- return -1;
+ case DW_AT_GNU_locviews:
+ case DW_AT_loclists_base:
+ if (attr->cu->version < 5)
+ {
+ /* loclistptr */
+ if (__libdw_formptr (attr, IDX_debug_loc,
+ DWARF_E_NO_DEBUG_LOC, NULL,
+ return_uval) == NULL)
+ return -1;
+ }
+ else
+ {
+ /* loclist, loclistsptr */
+ if (__libdw_formptr (attr, IDX_debug_loclists,
+ DWARF_E_NO_DEBUG_LOCLISTS, NULL,
+ return_uval) == NULL)
+ return -1;
+ }
break;
case DW_AT_macro_info:
@@ -157,6 +212,7 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval)
break;
case DW_AT_GNU_macros:
+ case DW_AT_macros:
/* macptr into .debug_macro */
if (__libdw_formptr (attr, IDX_debug_macro,
DWARF_E_NO_ENTRY, NULL,
@@ -166,11 +222,24 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval)
case DW_AT_ranges:
case DW_AT_start_scope:
- /* rangelistptr */
- if (__libdw_formptr (attr, IDX_debug_ranges,
- DWARF_E_NO_DEBUG_RANGES, NULL,
- return_uval) == NULL)
- return -1;
+ case DW_AT_GNU_ranges_base:
+ case DW_AT_rnglists_base:
+ if (attr->cu->version < 5)
+ {
+ /* rangelistptr */
+ if (__libdw_formptr (attr, IDX_debug_ranges,
+ DWARF_E_NO_DEBUG_RANGES, NULL,
+ return_uval) == NULL)
+ return -1;
+ }
+ else
+ {
+ /* rnglistsptr */
+ if (__libdw_formptr (attr, IDX_debug_rnglists,
+ DWARF_E_NO_DEBUG_RNGLISTS, NULL,
+ return_uval) == NULL)
+ return -1;
+ }
break;
case DW_AT_stmt_list:
@@ -181,6 +250,23 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval)
return -1;
break;
+ case DW_AT_addr_base:
+ case DW_AT_GNU_addr_base:
+ /* addrptr */
+ if (__libdw_formptr (attr, IDX_debug_addr,
+ DWARF_E_NO_DEBUG_ADDR, NULL,
+ return_uval) == NULL)
+ return -1;
+ break;
+
+ case DW_AT_str_offsets_base:
+ /* stroffsetsptr */
+ if (__libdw_formptr (attr, IDX_debug_str_offsets,
+ DWARF_E_NO_STR_OFFSETS, NULL,
+ return_uval) == NULL)
+ return -1;
+ break;
+
default:
/* sec_offset can only be used by one of the above attrs. */
if (attr->form == DW_FORM_sec_offset)
@@ -216,11 +302,51 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval)
break;
case DW_FORM_udata:
+ case DW_FORM_rnglistx:
+ case DW_FORM_loclistx:
if (datap + 1 > endp)
goto invalid;
get_uleb128 (*return_uval, datap, endp);
break;
+ case DW_FORM_implicit_const:
+ // The data comes from the abbrev, which has been bounds checked.
+ get_sleb128_unchecked (*return_uval, datap);
+ break;
+
+ /* These are indexes into the .debug_addr section, normally resolved
+ with dwarf_formaddr. Here treat as constants. */
+ case DW_FORM_GNU_addr_index:
+ case DW_FORM_addrx:
+ if (datap >= endp)
+ goto invalid;
+ get_uleb128 (*return_uval, datap, endp);
+ break;
+
+ case DW_FORM_addrx1:
+ if (datap >= endp - 1)
+ goto invalid;
+ *return_uval = *datap;
+ break;
+
+ case DW_FORM_addrx2:
+ if (datap >= endp - 2)
+ goto invalid;
+ *return_uval = read_2ubyte_unaligned (attr->cu->dbg, datap);
+ break;
+
+ case DW_FORM_addrx3:
+ if (datap >= endp - 3)
+ goto invalid;
+ *return_uval = read_3ubyte_unaligned (attr->cu->dbg, datap);
+ break;
+
+ case DW_FORM_addrx4:
+ if (datap >= endp - 4)
+ goto invalid;
+ *return_uval = read_4ubyte_unaligned (attr->cu->dbg, datap);
+ break;
+
default:
__libdw_seterrno (DWARF_E_NO_CONSTANT);
return -1;