summaryrefslogtreecommitdiff
path: root/libdw/dwarf_getlocation.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdw/dwarf_getlocation.c')
-rw-r--r--libdw/dwarf_getlocation.c323
1 files changed, 233 insertions, 90 deletions
diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c
index a4a2761e..fc59a2ab 100644
--- a/libdw/dwarf_getlocation.c
+++ b/libdw/dwarf_getlocation.c
@@ -1,7 +1,6 @@
/* Return location expression list.
- Copyright (C) 2000-2010, 2013-2015 Red Hat, Inc.
+ Copyright (C) 2000-2010, 2013-2015, 2017, 2018 Red Hat, Inc.
This file is part of elfutils.
- Written by Ulrich Drepper <drepper@redhat.com>, 2000.
This file is free software; you can redistribute it and/or modify
it under the terms of either
@@ -45,10 +44,34 @@ attr_ok (Dwarf_Attribute *attr)
if (attr == NULL)
return false;
- /* Must be one of the attributes listed below. */
+ /* If it is an exprloc, it is obviously OK. */
+ if (dwarf_whatform (attr) == DW_FORM_exprloc)
+ return true;
+
+ /* Otherwise must be one of the attributes listed below. Older
+ DWARF versions might have encoded the exprloc as block, and we
+ cannot easily distinquish attributes in the loclist class because
+ the same forms are used for different classes. */
switch (attr->code)
{
case DW_AT_location:
+ case DW_AT_byte_size:
+ case DW_AT_bit_offset:
+ case DW_AT_bit_size:
+ case DW_AT_lower_bound:
+ case DW_AT_bit_stride:
+ case DW_AT_upper_bound:
+ case DW_AT_count:
+ case DW_AT_allocated:
+ case DW_AT_associated:
+ case DW_AT_data_location:
+ case DW_AT_byte_stride:
+ case DW_AT_rank:
+ case DW_AT_call_value:
+ case DW_AT_call_target:
+ case DW_AT_call_target_clobbered:
+ case DW_AT_call_data_location:
+ case DW_AT_call_data_value:
case DW_AT_data_member_location:
case DW_AT_vtable_elem_location:
case DW_AT_string_length:
@@ -64,7 +87,7 @@ attr_ok (Dwarf_Attribute *attr)
break;
default:
- __libdw_seterrno (DWARF_E_NO_LOCLIST);
+ __libdw_seterrno (DWARF_E_NO_LOC_VALUE);
return false;
}
@@ -97,19 +120,23 @@ loc_compare (const void *p1, const void *p2)
}
/* For each DW_OP_implicit_value, we store a special entry in the cache.
- This points us directly to the block data for later fetching. */
-static void
+ This points us directly to the block data for later fetching.
+ Returns zero on success, -1 on bad DWARF or 1 if tsearch failed. */
+static int
store_implicit_value (Dwarf *dbg, void **cache, Dwarf_Op *op)
{
struct loc_block_s *block = libdw_alloc (dbg, struct loc_block_s,
sizeof (struct loc_block_s), 1);
const unsigned char *data = (const unsigned char *) (uintptr_t) op->number2;
- // Ignored, equal to op->number. And data length already checked.
- (void) __libdw_get_uleb128 (&data, data + len_leb128 (Dwarf_Word));
+ uint64_t len = __libdw_get_uleb128 (&data, data + len_leb128 (Dwarf_Word));
+ if (unlikely (len != op->number))
+ return -1;
block->addr = op;
block->data = (unsigned char *) data;
block->length = op->number;
- (void) tsearch (block, cache, loc_compare);
+ if (unlikely (tsearch (block, cache, loc_compare) == NULL))
+ return 1;
+ return 0;
}
int
@@ -147,6 +174,8 @@ check_constant_offset (Dwarf_Attribute *attr,
default:
return 1;
+ /* Note, we don't regard DW_FORM_data16 as a constant form,
+ even though technically it is according to the standard. */
case DW_FORM_data1:
case DW_FORM_data2:
case DW_FORM_data4:
@@ -299,6 +328,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
break;
case DW_OP_call_ref:
+ case DW_OP_GNU_variable_value:
/* DW_FORM_ref_addr, depends on offset size of CU. */
if (dbg == NULL || __libdw_read_offset_inc (dbg, sec_index, &data,
ref_size,
@@ -427,8 +457,14 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
case DW_OP_plus_uconst:
case DW_OP_regx:
case DW_OP_piece:
+ case DW_OP_convert:
case DW_OP_GNU_convert:
+ case DW_OP_reinterpret:
case DW_OP_GNU_reinterpret:
+ case DW_OP_addrx:
+ case DW_OP_GNU_addr_index:
+ case DW_OP_constx:
+ case DW_OP_GNU_const_index:
get_uleb128 (newloc->number, data, end_data);
break;
@@ -446,6 +482,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
break;
case DW_OP_bit_piece:
+ case DW_OP_regval_type:
case DW_OP_GNU_regval_type:
get_uleb128 (newloc->number, data, end_data);
if (unlikely (data >= end_data))
@@ -454,6 +491,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
break;
case DW_OP_implicit_value:
+ case DW_OP_entry_value:
case DW_OP_GNU_entry_value:
/* This cannot be used in a CFI expression. */
if (unlikely (dbg == NULL))
@@ -467,6 +505,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
data += newloc->number; /* Skip the block. */
break;
+ case DW_OP_implicit_pointer:
case DW_OP_GNU_implicit_pointer:
/* DW_FORM_ref_addr, depends on offset size of CU. */
if (dbg == NULL || __libdw_read_offset_inc (dbg, sec_index, &data,
@@ -479,13 +518,16 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
get_uleb128 (newloc->number2, data, end_data); /* Byte offset. */
break;
+ case DW_OP_deref_type:
case DW_OP_GNU_deref_type:
+ case DW_OP_xderef_type:
if (unlikely (data + 1 >= end_data))
goto invalid;
newloc->number = *data++;
get_uleb128 (newloc->number2, data, end_data);
break;
+ case DW_OP_const_type:
case DW_OP_GNU_const_type:
{
size_t size;
@@ -553,7 +595,16 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
result[n].offset = loclist->offset;
if (result[n].atom == DW_OP_implicit_value)
- store_implicit_value (dbg, cache, &result[n]);
+ {
+ int store = store_implicit_value (dbg, cache, &result[n]);
+ if (unlikely (store != 0))
+ {
+ if (store < 0)
+ goto invalid;
+ else
+ goto nomem;
+ }
+ }
struct loclist *loc = loclist;
loclist = loclist->next;
@@ -616,7 +667,13 @@ dwarf_getlocation (Dwarf_Attribute *attr, Dwarf_Op **llbuf, size_t *listlen)
if (result != 1)
return result;
- /* If it has a block form, it's a single location expression. */
+ /* If it has a block form, it's a single location expression.
+ Except for DW_FORM_data16, which is a 128bit constant. */
+ if (attr->form == DW_FORM_data16)
+ {
+ __libdw_seterrno (DWARF_E_NO_BLOCK);
+ return -1;
+ }
Dwarf_Block block;
if (INTUSE(dwarf_formblock) (attr, &block) != 0)
return -1;
@@ -624,47 +681,113 @@ dwarf_getlocation (Dwarf_Attribute *attr, Dwarf_Op **llbuf, size_t *listlen)
return getlocation (attr->cu, &block, llbuf, listlen, cu_sec_idx (attr->cu));
}
-static int
-attr_base_address (Dwarf_Attribute *attr, Dwarf_Addr *basep)
+Dwarf_Addr
+__libdw_cu_base_address (Dwarf_CU *cu)
{
- /* Fetch the CU's base address. */
- Dwarf_Die cudie = CUDIE (attr->cu);
-
- /* Find the base address of the compilation unit. It will
- normally be specified by DW_AT_low_pc. In DWARF-3 draft 4,
- the base address could be overridden by DW_AT_entry_pc. It's
- been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc
- for compilation units with discontinuous ranges. */
- Dwarf_Attribute attr_mem;
- if (unlikely (INTUSE(dwarf_lowpc) (&cudie, basep) != 0)
- && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
- DW_AT_entry_pc,
- &attr_mem),
- basep) != 0)
+ if (cu->base_address == (Dwarf_Addr) -1)
{
- if (INTUSE(dwarf_errno) () != 0)
- return -1;
-
- /* The compiler provided no base address when it should
- have. Buggy GCC does this when it used absolute
- addresses in the location list and no DW_AT_ranges. */
- *basep = 0;
+ Dwarf_Addr base;
+
+ /* Fetch the CU's base address. */
+ Dwarf_Die cudie = CUDIE (cu);
+
+ /* Find the base address of the compilation unit. It will
+ normally be specified by DW_AT_low_pc. In DWARF-3 draft 4,
+ the base address could be overridden by DW_AT_entry_pc. It's
+ been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc
+ for compilation units with discontinuous ranges. */
+ Dwarf_Attribute attr_mem;
+ if (INTUSE(dwarf_lowpc) (&cudie, &base) != 0
+ && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
+ DW_AT_entry_pc,
+ &attr_mem),
+ &base) != 0)
+ {
+ /* The compiler provided no base address when it should
+ have. Buggy GCC does this when it used absolute
+ addresses in the location list and no DW_AT_ranges. */
+ base = 0;
+ }
+ cu->base_address = base;
}
- return 0;
+
+ return cu->base_address;
}
static int
-initial_offset_base (Dwarf_Attribute *attr, ptrdiff_t *offset,
- Dwarf_Addr *basep)
+initial_offset (Dwarf_Attribute *attr, ptrdiff_t *offset)
{
- if (attr_base_address (attr, basep) != 0)
- return -1;
+ size_t secidx = (attr->cu->version < 5
+ ? IDX_debug_loc : IDX_debug_loclists);
Dwarf_Word start_offset;
- if (__libdw_formptr (attr, IDX_debug_loc,
- DWARF_E_NO_LOCLIST,
- NULL, &start_offset) == NULL)
- return -1;
+ if (attr->form == DW_FORM_loclistx)
+ {
+ 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_loc
+ ? DWARF_E_NO_DEBUG_LOC
+ : DWARF_E_NO_DEBUG_LOCLISTS);
+ return -1;
+ }
+
+ Dwarf_Off loc_base_off = __libdw_cu_locs_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 (loc_base_off > (sec_size - offset_size))
+ goto invalid_offset;
+
+ size_t max_idx = (sec_size - offset_size - loc_base_off) / offset_size;
+ if (idx > max_idx)
+ goto invalid_offset;
+
+ datap = (cu->dbg->sectiondata[secidx]->d_buf
+ + loc_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 += loc_base_off;
+ }
+ else
+ {
+ if (__libdw_formptr (attr, secidx,
+ (secidx == IDX_debug_loc
+ ? DWARF_E_NO_DEBUG_LOC
+ : DWARF_E_NO_DEBUG_LOCLISTS),
+ NULL, &start_offset) == NULL)
+ return -1;
+ }
*offset = start_offset;
return 0;
@@ -676,22 +799,19 @@ getlocations_addr (Dwarf_Attribute *attr, ptrdiff_t offset,
Dwarf_Addr address, const Elf_Data *locs, Dwarf_Op **expr,
size_t *exprlen)
{
- unsigned char *readp = locs->d_buf + offset;
- unsigned char *readendp = locs->d_buf + locs->d_size;
-
- next:
- if (readendp - readp < attr->cu->address_size * 2)
- {
- invalid:
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return -1;
- }
+ Dwarf_CU *cu = attr->cu;
+ Dwarf *dbg = cu->dbg;
+ size_t secidx = cu->version < 5 ? IDX_debug_loc : IDX_debug_loclists;
+ const unsigned char *readp = locs->d_buf + offset;
+ const unsigned char *readendp = locs->d_buf + locs->d_size;
Dwarf_Addr begin;
Dwarf_Addr end;
- switch (__libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc,
- &readp, attr->cu->address_size,
+ next:
+ switch (__libdw_read_begin_end_pair_inc (cu, secidx,
+ &readp, readendp,
+ cu->address_size,
&begin, &end, basep))
{
case 0: /* got location range. */
@@ -704,25 +824,38 @@ getlocations_addr (Dwarf_Attribute *attr, ptrdiff_t offset,
return -1;
}
- if (readendp - readp < 2)
- goto invalid;
-
/* We have a location expression. */
Dwarf_Block block;
- block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
- block.data = readp;
+ if (secidx == IDX_debug_loc)
+ {
+ if (readendp - readp < 2)
+ {
+ invalid:
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
+ block.length = read_2ubyte_unaligned_inc (dbg, readp);
+ }
+ else
+ {
+ if (readendp - readp < 1)
+ goto invalid;
+ get_uleb128 (block.length, readp, readendp);
+ }
+ block.data = (unsigned char *) readp;
if (readendp - readp < (ptrdiff_t) block.length)
goto invalid;
readp += block.length;
- *startp = *basep + begin;
- *endp = *basep + end;
+ /* Note these addresses include any base (if necessary) already. */
+ *startp = begin;
+ *endp = end;
/* If address is minus one we want them all, otherwise only matching. */
if (address != (Dwarf_Word) -1 && (address < *startp || address >= *endp))
goto next;
- if (getlocation (attr->cu, &block, expr, exprlen, IDX_debug_loc) != 0)
+ if (getlocation (cu, &block, expr, exprlen, secidx) != 0)
return -1;
return readp - (unsigned char *) locs->d_buf;
@@ -738,9 +871,11 @@ dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address,
if (llbufs == NULL)
maxlocs = SIZE_MAX;
- /* If it has a block form, it's a single location expression. */
+ /* If it has a block form, it's a single location expression.
+ Except for DW_FORM_data16, which is a 128bit constant. */
Dwarf_Block block;
- if (INTUSE(dwarf_formblock) (attr, &block) == 0)
+ if (attr->form != DW_FORM_data16
+ && INTUSE(dwarf_formblock) (attr, &block) == 0)
{
if (maxlocs == 0)
return 0;
@@ -751,11 +886,14 @@ dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address,
return listlens[0] == 0 ? 0 : 1;
}
- int error = INTUSE(dwarf_errno) ();
- if (unlikely (error != DWARF_E_NO_BLOCK))
+ if (attr->form != DW_FORM_data16)
{
- __libdw_seterrno (error);
- return -1;
+ int error = INTUSE(dwarf_errno) ();
+ if (unlikely (error != DWARF_E_NO_BLOCK))
+ {
+ __libdw_seterrno (error);
+ return -1;
+ }
}
int result = check_constant_offset (attr, &llbufs[0], &listlens[0]);
@@ -769,19 +907,19 @@ dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address,
size_t got = 0;
/* This is a true loclistptr, fetch the initial base address and offset. */
- if (initial_offset_base (attr, &off, &base) != 0)
+ base = __libdw_cu_base_address (attr->cu);
+ if (base == (Dwarf_Addr) -1)
return -1;
- const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
- if (d == NULL)
- {
- __libdw_seterrno (DWARF_E_NO_LOCLIST);
- return -1;
- }
+ if (initial_offset (attr, &off) != 0)
+ return -1;
+
+ size_t secidx = attr->cu->version < 5 ? IDX_debug_loc : IDX_debug_loclists;
+ const Elf_Data *d = attr->cu->dbg->sectiondata[secidx];
while (got < maxlocs
&& (off = getlocations_addr (attr, off, &base, &start, &end,
- address, d, &expr, &expr_len)) > 0)
+ address, d, &expr, &expr_len)) > 0)
{
/* This one matches the address. */
if (llbufs != NULL)
@@ -813,9 +951,11 @@ dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr *basep,
if (offset == 0)
{
- /* If it has a block form, it's a single location expression. */
+ /* If it has a block form, it's a single location expression.
+ Except for DW_FORM_data16, which is a 128bit constant. */
Dwarf_Block block;
- if (INTUSE(dwarf_formblock) (attr, &block) == 0)
+ if (attr->form != DW_FORM_data16
+ && INTUSE(dwarf_formblock) (attr, &block) == 0)
{
if (getlocation (attr->cu, &block, expr, exprlen,
cu_sec_idx (attr->cu)) != 0)
@@ -827,11 +967,14 @@ dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr *basep,
return 1;
}
- int error = INTUSE(dwarf_errno) ();
- if (unlikely (error != DWARF_E_NO_BLOCK))
+ if (attr->form != DW_FORM_data16)
{
- __libdw_seterrno (error);
- return -1;
+ int error = INTUSE(dwarf_errno) ();
+ if (unlikely (error != DWARF_E_NO_BLOCK))
+ {
+ __libdw_seterrno (error);
+ return -1;
+ }
}
int result = check_constant_offset (attr, expr, exprlen);
@@ -849,17 +992,17 @@ dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr *basep,
/* We must be looking at a true loclistptr, fetch the initial
base address and offset. */
- if (initial_offset_base (attr, &offset, basep) != 0)
+ *basep = __libdw_cu_base_address (attr->cu);
+ if (*basep == (Dwarf_Addr) -1)
return -1;
- }
- const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
- if (d == NULL)
- {
- __libdw_seterrno (DWARF_E_NO_LOCLIST);
- return -1;
+ if (initial_offset (attr, &offset) != 0)
+ return -1;
}
+ size_t secidx = attr->cu->version < 5 ? IDX_debug_loc : IDX_debug_loclists;
+ const Elf_Data *d = attr->cu->dbg->sectiondata[secidx];
+
return getlocations_addr (attr, offset, basep, startp, endp,
(Dwarf_Word) -1, d, expr, exprlen);
}