summaryrefslogtreecommitdiff
path: root/elfutils/libdw/dwarf_getlocation.c
diff options
context:
space:
mode:
Diffstat (limited to 'elfutils/libdw/dwarf_getlocation.c')
-rw-r--r--elfutils/libdw/dwarf_getlocation.c304
1 files changed, 222 insertions, 82 deletions
diff --git a/elfutils/libdw/dwarf_getlocation.c b/elfutils/libdw/dwarf_getlocation.c
index f680aa96..17df8fe9 100644
--- a/elfutils/libdw/dwarf_getlocation.c
+++ b/elfutils/libdw/dwarf_getlocation.c
@@ -1,5 +1,5 @@
/* Return location expression list.
- Copyright (C) 2000, 2001, 2002, 2004, 2005, 2006 Red Hat, Inc.
+ Copyright (C) 2000-2009 Red Hat, Inc.
This file is part of Red Hat elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2000.
@@ -55,6 +55,7 @@
#include <dwarf.h>
#include <search.h>
#include <stdlib.h>
+#include <assert.h>
#include <libdwP.h>
@@ -111,27 +112,123 @@ loc_compare (const void *p1, const void *p2)
return 0;
}
+/* 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
+store_implicit_value (Dwarf *dbg, void **cache, Dwarf_Op *op,
+ unsigned char *data)
+{
+ struct loc_block_s *block = libdw_alloc (dbg, struct loc_block_s,
+ sizeof (struct loc_block_s), 1);
+ block->addr = op;
+ block->data = data + op->number2;
+ block->length = op->number;
+ (void) tsearch (block, cache, loc_compare);
+}
+
+int
+dwarf_getlocation_implicit_value (attr, op, return_block)
+ Dwarf_Attribute *attr;
+ const Dwarf_Op *op;
+ Dwarf_Block *return_block;
+{
+ if (attr == NULL)
+ return -1;
+
+ struct loc_block_s fake = { .addr = (void *) op };
+ struct loc_block_s **found = tfind (&fake, &attr->cu->locs, loc_compare);
+ if (unlikely (found == NULL))
+ {
+ __libdw_seterrno (DWARF_E_NO_BLOCK);
+ return -1;
+ }
+
+ return_block->length = (*found)->length;
+ return_block->data = (*found)->data;
+ return 0;
+}
+
+/* DW_AT_data_member_location can be a constant as well as a loclistptr.
+ Only data[48] indicate a loclistptr. */
static int
-getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
- Dwarf_Op **llbuf, size_t *listlen)
+check_constant_offset (Dwarf_Attribute *attr,
+ Dwarf_Op **llbuf, size_t *listlen)
{
- Dwarf *dbg = cu->dbg;
+ if (attr->code != DW_AT_data_member_location
+ || attr->form == DW_FORM_data4
+ || attr->form == DW_FORM_data8)
+ return 1;
+
+ /* Check whether we already cached this location. */
+ struct loc_s fake = { .addr = attr->valp };
+ struct loc_s **found = tfind (&fake, &attr->cu->locs, loc_compare);
+
+ if (found == NULL)
+ {
+ Dwarf_Word offset;
+ if (INTUSE(dwarf_formudata) (attr, &offset) != 0)
+ return -1;
+
+ Dwarf_Op *result = libdw_alloc (attr->cu->dbg,
+ Dwarf_Op, sizeof (Dwarf_Op), 1);
+
+ result->atom = DW_OP_plus_uconst;
+ result->number = offset;
+ result->number2 = 0;
+ result->offset = 0;
+
+ /* Insert a record in the search tree so we can find it again later. */
+ struct loc_s *newp = libdw_alloc (attr->cu->dbg,
+ struct loc_s, sizeof (struct loc_s),
+ 1);
+ newp->addr = attr->valp;
+ newp->loc = result;
+ newp->nloc = 1;
+
+ found = tsearch (newp, &attr->cu->locs, loc_compare);
+ }
+ assert ((*found)->nloc == 1);
+
+ if (llbuf != NULL)
+ {
+ *llbuf = (*found)->loc;
+ *listlen = 1;
+ }
+
+ return 0;
+}
+
+int
+internal_function
+__libdw_intern_expression (Dwarf *dbg,
+ bool other_byte_order, unsigned int address_size,
+ void **cache, const Dwarf_Block *block, bool valuep,
+ Dwarf_Op **llbuf, size_t *listlen, int sec_index)
+{
/* Check whether we already looked at this list. */
struct loc_s fake = { .addr = block->data };
- struct loc_s **found = tfind (&fake, &cu->locs, loc_compare);
+ struct loc_s **found = tfind (&fake, cache, loc_compare);
if (found != NULL)
{
/* We already saw it. */
*llbuf = (*found)->loc;
*listlen = (*found)->nloc;
+ if (valuep)
+ {
+ assert (*listlen > 1);
+ assert ((*llbuf)[*listlen - 1].atom == DW_OP_stack_value);
+ }
+
return 0;
}
const unsigned char *data = block->data;
const unsigned char *const end_data = data + block->length;
+ const struct { bool other_byte_order; } bo = { other_byte_order };
+
struct loclist *loclist = NULL;
unsigned int n = 0;
/* Decode the opcodes. It is possible in some situations to have a
@@ -151,24 +248,9 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
{
case DW_OP_addr:
/* Address, depends on address size of CU. */
- if (cu->address_size == 4)
- {
- if (unlikely (data + 4 > end_data))
- {
- invalid:
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return -1;
- }
-
- newloc->number = read_4ubyte_unaligned_inc (dbg, data);
- }
- else
- {
- if (unlikely (data + 8 > end_data))
- goto invalid;
-
- newloc->number = read_8ubyte_unaligned_inc (dbg, data);
- }
+ if (__libdw_read_address_inc (dbg, sec_index, &data,
+ address_size, &newloc->number))
+ return -1;
break;
case DW_OP_deref:
@@ -203,6 +285,10 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
case DW_OP_nop:
case DW_OP_push_object_address:
case DW_OP_call_ref:
+ case DW_OP_call_frame_cfa:
+ case DW_OP_form_tls_address:
+ case DW_OP_GNU_push_tls_address:
+ case DW_OP_stack_value:
/* No operand. */
break;
@@ -211,7 +297,11 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
case DW_OP_deref_size:
case DW_OP_xderef_size:
if (unlikely (data >= end_data))
- goto invalid;
+ {
+ invalid:
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
newloc->number = *data++;
break;
@@ -228,7 +318,7 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
if (unlikely (data + 2 > end_data))
goto invalid;
- newloc->number = read_2ubyte_unaligned_inc (dbg, data);
+ newloc->number = read_2ubyte_unaligned_inc (&bo, data);
break;
case DW_OP_const2s:
@@ -238,14 +328,14 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
if (unlikely (data + 2 > end_data))
goto invalid;
- newloc->number = read_2sbyte_unaligned_inc (dbg, data);
+ newloc->number = read_2sbyte_unaligned_inc (&bo, data);
break;
case DW_OP_const4u:
if (unlikely (data + 4 > end_data))
goto invalid;
- newloc->number = read_4ubyte_unaligned_inc (dbg, data);
+ newloc->number = read_4ubyte_unaligned_inc (&bo, data);
break;
case DW_OP_const4s:
@@ -253,21 +343,21 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
if (unlikely (data + 4 > end_data))
goto invalid;
- newloc->number = read_4sbyte_unaligned_inc (dbg, data);
+ newloc->number = read_4sbyte_unaligned_inc (&bo, data);
break;
case DW_OP_const8u:
if (unlikely (data + 8 > end_data))
goto invalid;
- newloc->number = read_8ubyte_unaligned_inc (dbg, data);
+ newloc->number = read_8ubyte_unaligned_inc (&bo, data);
break;
case DW_OP_const8s:
if (unlikely (data + 8 > end_data))
goto invalid;
- newloc->number = read_8sbyte_unaligned_inc (dbg, data);
+ newloc->number = read_8sbyte_unaligned_inc (&bo, data);
break;
case DW_OP_constu:
@@ -291,6 +381,25 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
get_sleb128 (newloc->number2, data);
break;
+ case DW_OP_bit_piece:
+ /* XXX Check size. */
+ get_uleb128 (newloc->number, data);
+ get_uleb128 (newloc->number2, data);
+ break;
+
+ case DW_OP_implicit_value:
+ /* This cannot be used in a CFI expression. */
+ if (unlikely (dbg == NULL))
+ goto invalid;
+
+ /* XXX Check size. */
+ get_uleb128 (newloc->number, data); /* Block length. */
+ if (unlikely ((Dwarf_Word) (end_data - data) < newloc->number))
+ goto invalid;
+ newloc->number2 = data - block->data; /* Relative block offset. */
+ data += newloc->number; /* Skip the block. */
+ break;
+
default:
goto invalid;
}
@@ -304,8 +413,33 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
goto invalid;
}
+ if (valuep)
+ {
+ struct loclist *newloc;
+ newloc = (struct loclist *) alloca (sizeof (struct loclist));
+ newloc->atom = DW_OP_stack_value;
+ newloc->number = 0;
+ newloc->number2 = 0;
+ newloc->offset = data - block->data;
+ newloc->next = loclist;
+ loclist = newloc;
+ ++n;
+ }
+
/* Allocate the array. */
- Dwarf_Op *result = libdw_alloc (dbg, Dwarf_Op, sizeof (Dwarf_Op), n);
+ Dwarf_Op *result;
+ if (dbg != NULL)
+ result = libdw_alloc (dbg, Dwarf_Op, sizeof (Dwarf_Op), n);
+ else
+ {
+ result = malloc (sizeof *result * n);
+ if (result == NULL)
+ {
+ nomem:
+ __libdw_seterrno (DWARF_E_NOMEM);
+ return -1;
+ }
+ }
/* Store the result. */
*llbuf = result;
@@ -313,37 +447,62 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
do
{
- /* We populate the array from the back since the list is
- backwards. */
+ /* We populate the array from the back since the list is backwards. */
--n;
result[n].atom = loclist->atom;
result[n].number = loclist->number;
result[n].number2 = loclist->number2;
result[n].offset = loclist->offset;
+ if (result[n].atom == DW_OP_implicit_value)
+ store_implicit_value (dbg, cache, &result[n], block->data);
+
loclist = loclist->next;
}
while (n > 0);
- /* Insert a record in the search tree so that we can find it again
- later. */
- struct loc_s *newp = libdw_alloc (dbg, struct loc_s, sizeof (struct loc_s),
- 1);
+ /* Insert a record in the search tree so that we can find it again later. */
+ struct loc_s *newp;
+ if (dbg != NULL)
+ newp = libdw_alloc (dbg, struct loc_s, sizeof (struct loc_s), 1);
+ else
+ {
+ newp = malloc (sizeof *newp);
+ if (newp == NULL)
+ {
+ free (result);
+ goto nomem;
+ }
+ }
+
newp->addr = block->data;
newp->loc = result;
newp->nloc = *listlen;
- (void) tsearch (newp, &cu->locs, loc_compare);
+ (void) tsearch (newp, cache, loc_compare);
/* We did it. */
return 0;
}
+static int
+getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
+ Dwarf_Op **llbuf, size_t *listlen, int sec_index)
+{
+ return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order,
+ cu->address_size, &cu->locs, block, false,
+ llbuf, listlen, sec_index);
+}
+
int
dwarf_getlocation (attr, llbuf, listlen)
Dwarf_Attribute *attr;
Dwarf_Op **llbuf;
size_t *listlen;
{
+ int result = check_constant_offset (attr, llbuf, listlen);
+ if (result != 1)
+ return result;
+
if (! attr_ok (attr))
return -1;
@@ -352,7 +511,7 @@ dwarf_getlocation (attr, llbuf, listlen)
if (INTUSE(dwarf_formblock) (attr, &block) != 0)
return -1;
- return getlocation (attr->cu, &block, llbuf, listlen);
+ return getlocation (attr->cu, &block, llbuf, listlen, IDX_debug_info);
}
int
@@ -376,7 +535,8 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
if (maxlocs == 0)
return 0;
if (llbufs != NULL &&
- getlocation (attr->cu, &block, &llbufs[0], &listlens[0]) != 0)
+ getlocation (attr->cu, &block, &llbufs[0], &listlens[0],
+ IDX_debug_info) != 0)
return -1;
return listlens[0] == 0 ? 0 : 1;
}
@@ -388,25 +548,21 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
return -1;
}
- /* Must have the form data4 or data8 which act as an offset. */
- Dwarf_Word offset;
- if (unlikely (INTUSE(dwarf_formudata) (attr, &offset) != 0))
- return -1;
+ int result = check_constant_offset (attr, &llbufs[0], &listlens[0]);
+ if (result != 1)
+ return result ?: 1;
- const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
- if (unlikely (d == NULL))
- {
- __libdw_seterrno (DWARF_E_NO_LOCLIST);
- return -1;
- }
+ unsigned char *endp;
+ unsigned char *readp = __libdw_formptr (attr, IDX_debug_loc,
+ DWARF_E_NO_LOCLIST, &endp, NULL);
+ if (readp == NULL)
+ return -1;
Dwarf_Addr base = (Dwarf_Addr) -1;
- unsigned char *readp = d->d_buf + offset;
size_t got = 0;
while (got < maxlocs)
{
- if ((unsigned char *) d->d_buf + d->d_size - readp
- < attr->cu->address_size * 2)
+ if (endp - readp < attr->cu->address_size * 2)
{
invalid:
__libdw_seterrno (DWARF_E_INVALID_DWARF);
@@ -415,42 +571,25 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
Dwarf_Addr begin;
Dwarf_Addr end;
- if (attr->cu->address_size == 8)
- {
- begin = read_8ubyte_unaligned_inc (attr->cu->dbg, readp);
- end = read_8ubyte_unaligned_inc (attr->cu->dbg, readp);
-
- if (begin == (Elf64_Addr) -1l) /* Base address entry. */
- {
- base = end;
- if (unlikely (base == (Dwarf_Addr) -1))
- goto invalid;
- continue;
- }
- }
- else
- {
- begin = read_4ubyte_unaligned_inc (attr->cu->dbg, readp);
- end = read_4ubyte_unaligned_inc (attr->cu->dbg, readp);
-
- if (begin == (Elf32_Addr) -1) /* Base address entry. */
- {
- base = end;
- continue;
- }
- }
- if (begin == 0 && end == 0) /* End of list entry. */
+ int status
+ = __libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc,
+ &readp, attr->cu->address_size,
+ &begin, &end, &base);
+ if (status == 2) /* End of list entry. */
break;
+ else if (status == 1) /* Base address selected. */
+ continue;
+ else if (status < 0)
+ return status;
- if ((unsigned char *) d->d_buf + d->d_size - readp < 2)
+ if (endp - readp < 2)
goto invalid;
/* We have a location expression. */
block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
block.data = readp;
- if ((unsigned char *) d->d_buf + d->d_size - readp
- < (ptrdiff_t) block.length)
+ if (endp - readp < (ptrdiff_t) block.length)
goto invalid;
readp += block.length;
@@ -486,7 +625,8 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
/* This one matches the address. */
if (llbufs != NULL
&& unlikely (getlocation (attr->cu, &block,
- &llbufs[got], &listlens[got]) != 0))
+ &llbufs[got], &listlens[got],
+ IDX_debug_loc) != 0))
return -1;
++got;
}