diff options
Diffstat (limited to 'libdw/dwarf_getlocation.c')
-rw-r--r-- | libdw/dwarf_getlocation.c | 346 |
1 files changed, 264 insertions, 82 deletions
diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index f680aa96..ede8c3c9 100644 --- a/libdw/dwarf_getlocation.c +++ b/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-2010 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,142 @@ 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) + return 1; + + switch (attr->form) + { + /* Punt for any non-constant form. */ + default: + return 1; + + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_sdata: + case DW_FORM_udata: + break; + + case DW_FORM_data4: + case DW_FORM_data8: + /* These are loclistptr, not constants. + XXX check cu->version > 3??? + */ + 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, unsigned int ref_size, + void **cache, const Dwarf_Block *block, + bool cfap, 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 +267,16 @@ 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; + if (__libdw_read_address_inc (dbg, sec_index, &data, + address_size, &newloc->number)) + return -1; + break; - newloc->number = read_8ubyte_unaligned_inc (dbg, data); - } + case DW_OP_call_ref: + /* DW_FORM_ref_addr, depends on offset size of CU. */ + if (__libdw_read_offset_inc (dbg, sec_index, &data, ref_size, + &newloc->number, IDX_debug_info, 0)) + return -1; break; case DW_OP_deref: @@ -202,7 +310,10 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, case DW_OP_reg0 ... DW_OP_reg31: 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 +322,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 +343,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 +353,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 +368,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 +406,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,46 +438,113 @@ 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; + } + + if (cfap) + ++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; *listlen = n; + if (cfap) + { + /* Synthesize the operation to push the CFA before the expression. */ + --n; + result[0].atom = DW_OP_call_frame_cfa; + result[0].number = 0; + result[0].number2 = 0; + result[0].offset = -1; + } + 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->version == 2 + ? cu->address_size + : cu->offset_size), + &cu->locs, block, + false, 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 +553,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 +577,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 +590,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 +613,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 +667,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; } |