diff options
Diffstat (limited to 'src/libdw/dwarf_getlocation.c')
-rw-r--r-- | src/libdw/dwarf_getlocation.c | 853 |
1 files changed, 0 insertions, 853 deletions
diff --git a/src/libdw/dwarf_getlocation.c b/src/libdw/dwarf_getlocation.c deleted file mode 100644 index 068f3853..00000000 --- a/src/libdw/dwarf_getlocation.c +++ /dev/null @@ -1,853 +0,0 @@ -/* Return location expression list. - Copyright (C) 2000-2010, 2013, 2014 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 - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see <http://www.gnu.org/licenses/>. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <dwarf.h> -#include <search.h> -#include <stdlib.h> -#include <assert.h> - -#include <libdwP.h> - - -static bool -attr_ok (Dwarf_Attribute *attr) -{ - if (attr == NULL) - return false; - - /* Must be one of the attributes listed below. */ - switch (attr->code) - { - case DW_AT_location: - case DW_AT_data_member_location: - case DW_AT_vtable_elem_location: - case DW_AT_string_length: - case DW_AT_use_location: - case DW_AT_frame_base: - case DW_AT_return_addr: - case DW_AT_static_link: - case DW_AT_segment: - case DW_AT_GNU_call_site_value: - case DW_AT_GNU_call_site_data_value: - case DW_AT_GNU_call_site_target: - case DW_AT_GNU_call_site_target_clobbered: - break; - - default: - __libdw_seterrno (DWARF_E_NO_LOCLIST); - return false; - } - - return true; -} - - -struct loclist -{ - uint8_t atom; - Dwarf_Word number; - Dwarf_Word number2; - Dwarf_Word offset; - struct loclist *next; -}; - - -static int -loc_compare (const void *p1, const void *p2) -{ - const struct loc_s *l1 = (const struct loc_s *) p1; - const struct loc_s *l2 = (const struct loc_s *) p2; - - if ((uintptr_t) l1->addr < (uintptr_t) l2->addr) - return -1; - if ((uintptr_t) l1->addr > (uintptr_t) l2->addr) - return 1; - - 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) -{ - 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)); - block->addr = op; - block->data = (unsigned char *) data; - 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 -check_constant_offset (Dwarf_Attribute *attr, - Dwarf_Op **llbuf, size_t *listlen) -{ - 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_data4: - case DW_FORM_data8: - case DW_FORM_sdata: - case DW_FORM_udata: - break; - } - - /* 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) -{ - /* Empty location expressions don't have any ops to intern. */ - if (block->length == 0) - { - *listlen = 0; - return 0; - } - - /* Check whether we already looked at this list. */ - struct loc_s fake = { .addr = block->data }; - 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; - - if (cfap) - { - /* Synthesize the operation to push the CFA before the expression. */ - struct loclist *newloc; - newloc = (struct loclist *) alloca (sizeof (struct loclist)); - newloc->atom = DW_OP_call_frame_cfa; - newloc->number = 0; - newloc->number2 = 0; - newloc->offset = -1; - newloc->next = loclist; - loclist = newloc; - ++n; - } - - /* Decode the opcodes. It is possible in some situations to have a - block of size zero. */ - while (data < end_data) - { - struct loclist *newloc; - newloc = (struct loclist *) alloca (sizeof (struct loclist)); - newloc->number = 0; - newloc->number2 = 0; - newloc->offset = data - block->data; - newloc->next = loclist; - loclist = newloc; - ++n; - - switch ((newloc->atom = *data++)) - { - case DW_OP_addr: - /* Address, depends on address size of CU. */ - if (__libdw_read_address_inc (dbg, sec_index, &data, - address_size, &newloc->number)) - return -1; - break; - - 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: - case DW_OP_dup: - case DW_OP_drop: - case DW_OP_over: - case DW_OP_swap: - case DW_OP_rot: - case DW_OP_xderef: - case DW_OP_abs: - case DW_OP_and: - case DW_OP_div: - case DW_OP_minus: - case DW_OP_mod: - case DW_OP_mul: - case DW_OP_neg: - case DW_OP_not: - case DW_OP_or: - case DW_OP_plus: - case DW_OP_shl: - case DW_OP_shr: - case DW_OP_shra: - case DW_OP_xor: - case DW_OP_eq: - case DW_OP_ge: - case DW_OP_gt: - case DW_OP_le: - case DW_OP_lt: - case DW_OP_ne: - case DW_OP_lit0 ... DW_OP_lit31: - case DW_OP_reg0 ... DW_OP_reg31: - case DW_OP_nop: - case DW_OP_push_object_address: - 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; - - case DW_OP_const1u: - case DW_OP_pick: - case DW_OP_deref_size: - case DW_OP_xderef_size: - if (unlikely (data >= end_data)) - { - invalid: - __libdw_seterrno (DWARF_E_INVALID_DWARF); - return -1; - } - - newloc->number = *data++; - break; - - case DW_OP_const1s: - if (unlikely (data >= end_data)) - goto invalid; - - newloc->number = *((int8_t *) data); - ++data; - break; - - case DW_OP_const2u: - if (unlikely (data + 2 > end_data)) - goto invalid; - - newloc->number = read_2ubyte_unaligned_inc (&bo, data); - break; - - case DW_OP_const2s: - case DW_OP_skip: - case DW_OP_bra: - case DW_OP_call2: - if (unlikely (data + 2 > end_data)) - goto invalid; - - 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 (&bo, data); - break; - - case DW_OP_const4s: - case DW_OP_call4: - case DW_OP_GNU_parameter_ref: - if (unlikely (data + 4 > end_data)) - goto invalid; - - 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 (&bo, data); - break; - - case DW_OP_const8s: - if (unlikely (data + 8 > end_data)) - goto invalid; - - newloc->number = read_8sbyte_unaligned_inc (&bo, data); - break; - - case DW_OP_constu: - case DW_OP_plus_uconst: - case DW_OP_regx: - case DW_OP_piece: - case DW_OP_GNU_convert: - case DW_OP_GNU_reinterpret: - get_uleb128 (newloc->number, data, end_data); - break; - - case DW_OP_consts: - case DW_OP_breg0 ... DW_OP_breg31: - case DW_OP_fbreg: - get_sleb128 (newloc->number, data, end_data); - break; - - case DW_OP_bregx: - get_uleb128 (newloc->number, data, end_data); - if (unlikely (data >= end_data)) - goto invalid; - get_sleb128 (newloc->number2, data, end_data); - break; - - case DW_OP_bit_piece: - case DW_OP_GNU_regval_type: - get_uleb128 (newloc->number, data, end_data); - if (unlikely (data >= end_data)) - goto invalid; - get_uleb128 (newloc->number2, data, end_data); - break; - - case DW_OP_implicit_value: - case DW_OP_GNU_entry_value: - /* This cannot be used in a CFI expression. */ - if (unlikely (dbg == NULL)) - goto invalid; - - /* start of block inc. len. */ - newloc->number2 = (Dwarf_Word) (uintptr_t) data; - get_uleb128 (newloc->number, data, end_data); /* Block length. */ - if (unlikely ((Dwarf_Word) (end_data - data) < newloc->number)) - goto invalid; - data += newloc->number; /* Skip the block. */ - break; - - case DW_OP_GNU_implicit_pointer: - /* 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; - if (unlikely (data >= end_data)) - goto invalid; - get_uleb128 (newloc->number2, data, end_data); /* Byte offset. */ - break; - - case DW_OP_GNU_deref_type: - if (unlikely (data + 1 >= end_data)) - goto invalid; - newloc->number = *data++; - get_uleb128 (newloc->number2, data, end_data); - break; - - case DW_OP_GNU_const_type: - { - size_t size; - get_uleb128 (newloc->number, data, end_data); - if (unlikely (data >= end_data)) - goto invalid; - - /* start of block inc. len. */ - newloc->number2 = (Dwarf_Word) (uintptr_t) data; - size = *data++; - if (unlikely ((Dwarf_Word) (end_data - data) < size)) - goto invalid; - data += size; /* Skip the block. */ - } - break; - - default: - goto invalid; - } - } - - if (unlikely (n == 0)) - { - /* This is not allowed. - It would mean an empty location expression, which we handled - already as a special case above. */ - 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; - 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; - - do - { - /* 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]); - - 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; - 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, 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) -{ - /* Empty location expressions don't have any ops to intern. - Note that synthetic empty_cu doesn't have an associated DWARF dbg. */ - if (block->length == 0) - { - *listlen = 0; - return 0; - } - - 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; -{ - if (! attr_ok (attr)) - return -1; - - int result = check_constant_offset (attr, llbuf, listlen); - if (result != 1) - return result; - - /* If it has a block form, it's a single location expression. */ - Dwarf_Block block; - if (INTUSE(dwarf_formblock) (attr, &block) != 0) - return -1; - - return getlocation (attr->cu, &block, llbuf, listlen, cu_sec_idx (attr->cu)); -} - -static int -attr_base_address (attr, basep) - Dwarf_Attribute *attr; - Dwarf_Addr *basep; -{ - /* 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 (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; - } - return 0; -} - -static int -initial_offset_base (attr, offset, basep) - Dwarf_Attribute *attr; - ptrdiff_t *offset; - Dwarf_Addr *basep; -{ - if (attr_base_address (attr, basep) != 0) - return -1; - - Dwarf_Word start_offset; - if (__libdw_formptr (attr, IDX_debug_loc, - DWARF_E_NO_LOCLIST, - NULL, &start_offset) == NULL) - return -1; - - *offset = start_offset; - return 0; -} - -static ptrdiff_t -getlocations_addr (attr, offset, basep, startp, endp, address, - locs, expr, exprlen) - Dwarf_Attribute *attr; - ptrdiff_t offset; - Dwarf_Addr *basep; - Dwarf_Addr *startp; - Dwarf_Addr *endp; - Dwarf_Addr address; - 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_Addr begin; - Dwarf_Addr end; - - switch (__libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc, - &readp, attr->cu->address_size, - &begin, &end, basep)) - { - case 0: /* got location range. */ - break; - case 1: /* base address setup. */ - goto next; - case 2: /* end of loclist */ - return 0; - default: /* error */ - 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 (readendp - readp < (ptrdiff_t) block.length) - goto invalid; - readp += block.length; - - *startp = *basep + begin; - *endp = *basep + 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) - return -1; - - return readp - (unsigned char *) locs->d_buf; -} - -int -dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs) - Dwarf_Attribute *attr; - Dwarf_Addr address; - Dwarf_Op **llbufs; - size_t *listlens; - size_t maxlocs; -{ - if (! attr_ok (attr)) - return -1; - - if (llbufs == NULL) - maxlocs = SIZE_MAX; - - /* If it has a block form, it's a single location expression. */ - Dwarf_Block block; - if (INTUSE(dwarf_formblock) (attr, &block) == 0) - { - if (maxlocs == 0) - return 0; - if (llbufs != NULL && - getlocation (attr->cu, &block, &llbufs[0], &listlens[0], - cu_sec_idx (attr->cu)) != 0) - return -1; - return listlens[0] == 0 ? 0 : 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]); - if (result != 1) - return result ?: 1; - - Dwarf_Addr base, start, end; - Dwarf_Op *expr; - size_t expr_len; - ptrdiff_t off = 0; - size_t got = 0; - - /* This is a true loclistptr, fetch the initial base address and offset. */ - if (initial_offset_base (attr, &off, &base) != 0) - return -1; - - const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc]; - if (d == NULL) - { - __libdw_seterrno (DWARF_E_NO_LOCLIST); - return -1; - } - - while (got < maxlocs - && (off = getlocations_addr (attr, off, &base, &start, &end, - address, d, &expr, &expr_len)) > 0) - { - /* This one matches the address. */ - if (llbufs != NULL) - { - llbufs[got] = expr; - listlens[got] = expr_len; - } - ++got; - } - - /* We might stop early, so off can be zero or positive on success. */ - if (off < 0) - return -1; - - return got; -} - -ptrdiff_t -dwarf_getlocations (attr, offset, basep, startp, endp, expr, exprlen) - Dwarf_Attribute *attr; - ptrdiff_t offset; - Dwarf_Addr *basep; - Dwarf_Addr *startp; - Dwarf_Addr *endp; - Dwarf_Op **expr; - size_t *exprlen; -{ - if (! attr_ok (attr)) - return -1; - - /* 1 is an invalid offset, meaning no more locations. */ - if (offset == 1) - return 0; - - if (offset == 0) - { - /* If it has a block form, it's a single location expression. */ - Dwarf_Block block; - if (INTUSE(dwarf_formblock) (attr, &block) == 0) - { - if (getlocation (attr->cu, &block, expr, exprlen, - cu_sec_idx (attr->cu)) != 0) - return -1; - - /* This is the one and only location covering everything. */ - *startp = 0; - *endp = -1; - 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); - if (result != 1) - { - if (result == 0) - { - /* This is the one and only location covering everything. */ - *startp = 0; - *endp = -1; - return 1; - } - return result; - } - - /* We must be looking at a true loclistptr, fetch the initial - base address and offset. */ - if (initial_offset_base (attr, &offset, basep) != 0) - return -1; - } - - const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc]; - if (d == NULL) - { - __libdw_seterrno (DWARF_E_NO_LOCLIST); - return -1; - } - - return getlocations_addr (attr, offset, basep, startp, endp, - (Dwarf_Word) -1, d, expr, exprlen); -} |