diff options
author | Roland McGrath <roland@redhat.com> | 2005-10-28 07:07:19 +0000 |
---|---|---|
committer | Roland McGrath <roland@redhat.com> | 2005-10-28 07:07:19 +0000 |
commit | 6724c90d02659f7466b67b357563042e403d154e (patch) | |
tree | 9671d9d41baee4b5256feac1f9f49d45aaa95500 /libdw/dwarf_getlocation.c | |
parent | 07d4f2fc1cb53f170a71bc13617bbdd9cb1c3c60 (diff) | |
download | elfutils-6724c90d02659f7466b67b357563042e403d154e.tar.gz |
NEWS updates
Diffstat (limited to 'libdw/dwarf_getlocation.c')
-rw-r--r-- | libdw/dwarf_getlocation.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c new file mode 100644 index 00000000..4a75f97d --- /dev/null +++ b/libdw/dwarf_getlocation.c @@ -0,0 +1,449 @@ +/* Return location expression list. + Copyright (C) 2000, 2001, 2002, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <drepper@redhat.com>, 2000. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <dwarf.h> +#include <search.h> +#include <stdlib.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: + 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; +} + +static int +getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, + Dwarf_Op **llbuf, size_t *listlen) +{ + Dwarf *dbg = cu->dbg; + + /* 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); + if (found != NULL) + { + /* We already saw it. */ + *llbuf = (*found)->loc; + *listlen = (*found)->nloc; + + return 0; + } + + const unsigned char *data = block->data; + const unsigned char *const end_data = data + block->length; + + struct loclist *loclist = NULL; + unsigned int n = 0; + /* 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 (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); + } + 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_ref: + /* 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)) + goto invalid; + + 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 (dbg, 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 (dbg, data); + break; + + case DW_OP_const4u: + if (unlikely (data + 4 > end_data)) + goto invalid; + + newloc->number = read_4ubyte_unaligned_inc (dbg, data); + break; + + case DW_OP_const4s: + case DW_OP_call4: + if (unlikely (data + 4 > end_data)) + goto invalid; + + newloc->number = read_4sbyte_unaligned_inc (dbg, data); + break; + + case DW_OP_const8u: + if (unlikely (data + 8 > end_data)) + goto invalid; + + newloc->number = read_8ubyte_unaligned_inc (dbg, data); + break; + + case DW_OP_const8s: + if (unlikely (data + 8 > end_data)) + goto invalid; + + newloc->number = read_8sbyte_unaligned_inc (dbg, data); + break; + + case DW_OP_constu: + case DW_OP_plus_uconst: + case DW_OP_regx: + case DW_OP_piece: + /* XXX Check size. */ + get_uleb128 (newloc->number, data); + break; + + case DW_OP_consts: + case DW_OP_breg0 ... DW_OP_breg31: + case DW_OP_fbreg: + /* XXX Check size. */ + get_sleb128 (newloc->number, data); + break; + + case DW_OP_bregx: + /* XXX Check size. */ + get_uleb128 (newloc->number, data); + get_sleb128 (newloc->number2, data); + break; + + default: + goto invalid; + } + } + + if (unlikely (n == 0)) + { + /* This is not allowed. + + XXX Is it? */ + goto invalid; + } + + /* Allocate the array. */ + Dwarf_Op *result = libdw_alloc (dbg, Dwarf_Op, sizeof (Dwarf_Op), n); + + /* 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; + + 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); + newp->addr = block->data; + newp->loc = result; + newp->nloc = *listlen; + (void) tsearch (newp, &cu->locs, loc_compare); + + /* We did it. */ + return 0; +} + +int +dwarf_getlocation (attr, llbuf, listlen) + Dwarf_Attribute *attr; + Dwarf_Op **llbuf; + size_t *listlen; +{ + if (! attr_ok (attr)) + return -1; + + /* 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); +} + +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]) != 0) + return -1; + return listlens[0] == 0 ? 0 : 1; + } + + int error = INTUSE(dwarf_errno) (); + if (error != DWARF_E_NO_BLOCK) + { + __libdw_seterrno (error); + return -1; + } + + /* Must have the form data4 or data8 which act as an offset. */ + Dwarf_Word offset; + if (INTUSE(dwarf_formudata) (attr, &offset) != 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; + } + + /* Fetch the CU's base address. */ + Dwarf_Addr base; + 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 (INTUSE(dwarf_lowpc) (&cudie, &base) != 0 + && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie, DW_AT_entry_pc, + &attr_mem), + &base) != 0) + return -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) + { + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + + 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; + 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. */ + break; + + if ((unsigned char *) d->d_buf + d->d_size - readp < 2) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + + /* 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) + goto invalid; + readp += block.length; + + if (address >= base + begin && address < base + end) + { + /* This one matches the address. */ + if (llbufs != NULL + && getlocation (attr->cu, &block, + &llbufs[got], &listlens[got]) != 0) + return -1; + ++got; + } + } + + return got; +} |