diff options
author | Mark Wielaard <mjw@redhat.com> | 2013-08-30 23:55:12 +0200 |
---|---|---|
committer | Mark Wielaard <mjw@redhat.com> | 2013-09-06 12:09:45 +0200 |
commit | b2535b6a6be7717cdd41834d76e5cb48cb446b83 (patch) | |
tree | 6f90ce8b4e754103beaf72cef9778fe744ddc65d | |
parent | 66eaae9bcc1608efad65e3aa0204afbb3cb1a83d (diff) | |
download | elfutils-b2535b6a6be7717cdd41834d76e5cb48cb446b83.tar.gz |
libdw: Add new functions dwarf_getlocation_attr and dwarf_getlocation_die.
Some location expression operations have a DIE associated with them.
Examples are some of the new GNU typed DWARF extensions, DW_OP_GNU_convert,
DW_OP_GNU_reinterpret, DW_OP_GNU_const_type, DW_OP_GNU_regval_type and
DW_OP_GNU_deref_type. Others have (block) values associated with them,
like DW_OP_GNU_entry_value and DW_OP_GNU_const_type.
It is not always easy to access these values. The DIE offset is given in
various formats either as global offset or CU relative offset. The (block)
value might be constant or a location description. And the block might be
encoded with a uleb128 or ubyte length. The new functions help to easily
get at the DIE or attribute value.
In theory dwarf_getlocation_attr could be used for all cases, since
besides returning DW_AT_const_value or DW_AT_location, it could also
return an attribute referencing a DIE. But at least one operation,
DW_OP_GNU_const_type, has both a (type) DIE and a constant (block)
value associated with it. And directly getting the DIE when needed
is easier than first having to retrieve a (synthesized) attribute
and then getting the actual (type) DIE.
Expression operations that reference an actual DIE for the
DW_AT_location or DW_AT_const_value, like DW_OP_call2, DW_OP_call4,
DW_OP_callref and DW_OP_GNU_implicit_pointer can be used with both
dwarf_getlocation_attr and dwarf_getlocation_die.
DW_OP_implicit_value and DW_OP_GNU_implicit_pointer already had
their own special accessors (dwarf_getlocation_implicit_value
and dwarf_getlocation_implicit_pointer), but it seemed consistent
to include them in the new more generic accessors too.
Signed-off-by: Mark Wielaard <mjw@redhat.com>
-rw-r--r-- | libdw/ChangeLog | 19 | ||||
-rw-r--r-- | libdw/Makefile.am | 3 | ||||
-rw-r--r-- | libdw/dwarf_getlocation.c | 37 | ||||
-rw-r--r-- | libdw/dwarf_getlocation_attr.c | 103 | ||||
-rw-r--r-- | libdw/dwarf_getlocation_die.c | 78 | ||||
-rw-r--r-- | libdw/libdw.h | 23 | ||||
-rw-r--r-- | libdw/libdw.map | 2 | ||||
-rw-r--r-- | libdw/libdwP.h | 3 |
8 files changed, 250 insertions, 18 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 153ea746..c8398b23 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,22 @@ +2013-08-24 Mark Wielaard <mjw@redhat.com> + + * dwarf_getlocation.c (store_implicit_value): Don't take data + as argument, get block data from op number2. Return false when + block data length and op number don't match up. + (__libdw_intern_expression): Store start of block for + DW_OP_implicit_value and DW_OP_GNU_entry_value instead of + relative data offset. Also store block start (including length) + for DW_OP_GNU_const_type. Don't pass data to store_implicit_value. + * dwarf_getlocation_attr.c: New file. + * dwarf_getlocation_die.c: Likewise. + * libdw.h (dwarf_getlocation_die): New function definition. + (dwarf_getlocation_attr): Likewise. + * libdwP.h: Declare internal dwarf_getlocation_die. + * libdw.map (ELFUTILS_0.157): Add dwarf_getlocation_die and + dwarf_getlocation_attr. + * Makefile.am (libdw_a_SOURCES): Add dwarf_getlocation_die.c and + dwarf_getlocation_attr.c. + 2013-08-23 Mark Wielaard <mjw@redhat.com> * dwarf_getlocation.c (attr_ok): Also accept DW_AT_segment. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index 71a006fa..5fef2e18 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -87,7 +87,8 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_frame_info.c dwarf_frame_cfa.c dwarf_frame_register.c \ dwarf_cfi_addrframe.c \ dwarf_getcfi.c dwarf_getcfi_elf.c dwarf_cfi_end.c \ - dwarf_aggregate_size.c dwarf_getlocation_implicit_pointer.c + dwarf_aggregate_size.c dwarf_getlocation_implicit_pointer.c \ + dwarf_getlocation_die.c dwarf_getlocation_attr.c if MAINTAINER_MODE BUILT_SOURCES = $(srcdir)/known-dwarf.h diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index aab471c6..f7d64f41 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -95,13 +95,15 @@ 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 -store_implicit_value (Dwarf *dbg, void **cache, Dwarf_Op *op, - unsigned char *data) +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 *) op->number2; + Dwarf_Word blength; // Ignored, equal to op->number. + get_uleb128 (blength, data); block->addr = op; - block->data = data + op->number2; + block->data = (unsigned char *) data; block->length = op->number; (void) tsearch (block, cache, loc_compare); } @@ -412,11 +414,11 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, if (unlikely (dbg == NULL)) goto invalid; + newloc->number2 = (Dwarf_Word) data; /* start of block inc. len. */ /* 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; @@ -437,17 +439,20 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, break; case DW_OP_GNU_const_type: - /* XXX Check size. */ - get_uleb128 (newloc->number, data); - if (unlikely (data >= end_data)) - goto invalid; - newloc->number2 = *data++; /* Block length. */ - if (unlikely ((Dwarf_Word) (end_data - data) < newloc->number2)) - goto invalid; - /* The third operand is relative block offset: - newloc->number3 = data - block->data; - We don't support this at this point. */ - data += newloc->number2; /* Skip the block. */ + { + size_t size; + + /* XXX Check size. */ + get_uleb128 (newloc->number, data); + if (unlikely (data >= end_data)) + goto invalid; + + newloc->number2 = (Dwarf_Word) data; /* start of block inc. len. */ + size = *data++; + if (unlikely ((Dwarf_Word) (end_data - data) < size)) + goto invalid; + data += size; /* Skip the block. */ + } break; default: @@ -505,7 +510,7 @@ __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], block->data); + store_implicit_value (dbg, cache, &result[n]); loclist = loclist->next; } diff --git a/libdw/dwarf_getlocation_attr.c b/libdw/dwarf_getlocation_attr.c new file mode 100644 index 00000000..2d6084e9 --- /dev/null +++ b/libdw/dwarf_getlocation_attr.c @@ -0,0 +1,103 @@ +/* Return DWARF attribute associated with a location expression op. + Copyright (C) 2013 Red Hat, Inc. + This file is part of elfutils. + + 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 <libdwP.h> + + +int +dwarf_getlocation_attr (attr, op, result) + Dwarf_Attribute *attr; + const Dwarf_Op *op; + Dwarf_Attribute *result; +{ + if (attr == NULL) + return -1; + + result->cu = attr->cu; + + switch (op->atom) + { + case DW_OP_implicit_value: + result->code = DW_AT_const_value; + result->form = DW_FORM_block; + result->valp = (unsigned char *) op->number2; + break; + + case DW_OP_GNU_entry_value: + result->code = DW_AT_location; + result->form = DW_FORM_exprloc; + result->valp = (unsigned char *) op->number2; + break; + + case DW_OP_GNU_const_type: + result->code = DW_AT_const_value; + result->form = DW_FORM_block1; + result->valp = (unsigned char *) op->number2; + break; + + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_call_ref: + { + Dwarf_Die die; + if (INTUSE(dwarf_getlocation_die) (attr, op, &die) != 0) + return -1; + if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + } + break; + + case DW_OP_GNU_implicit_pointer: + { + Dwarf_Die die; + if (INTUSE(dwarf_getlocation_die) (attr, op, &die) != 0) + return -1; + if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL + && INTUSE(dwarf_attr) (&die, DW_AT_const_value, result) == NULL) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + } + break; + + default: + __libdw_seterrno (DWARF_E_INVALID_ACCESS); + return -1; + } + + return 0; +} diff --git a/libdw/dwarf_getlocation_die.c b/libdw/dwarf_getlocation_die.c new file mode 100644 index 00000000..fa03aac2 --- /dev/null +++ b/libdw/dwarf_getlocation_die.c @@ -0,0 +1,78 @@ +/* Return DIE associated with a location expression op. + Copyright (C) 2013 Red Hat, Inc. + This file is part of elfutils. + + 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 <libdwP.h> + +int +dwarf_getlocation_die (attr, op, result) + Dwarf_Attribute *attr; + const Dwarf_Op *op; + Dwarf_Die *result; +{ + if (attr == NULL) + return -1; + + Dwarf_Off dieoff; + switch (op->atom) + { + case DW_OP_GNU_implicit_pointer: + case DW_OP_call_ref: + dieoff = op->number; + break; + + case DW_OP_GNU_parameter_ref: + case DW_OP_GNU_convert: + case DW_OP_GNU_reinterpret: + case DW_OP_GNU_const_type: + case DW_OP_call2: + case DW_OP_call4: + dieoff = attr->cu->start + op->number; + break; + + case DW_OP_GNU_regval_type: + case DW_OP_GNU_deref_type: + dieoff = attr->cu->start + op->number2; + break; + + default: + __libdw_seterrno (DWARF_E_INVALID_ACCESS); + return -1; + } + + if (__libdw_offdie (attr->cu->dbg, dieoff, result, + attr->cu->type_offset != 0) == NULL) + return -1; + + return 0; +} +INTDEF(dwarf_getlocation_die); diff --git a/libdw/libdw.h b/libdw/libdw.h index 898aa74c..d1cd1775 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -664,6 +664,29 @@ extern int dwarf_getlocation_implicit_pointer (Dwarf_Attribute *attr, Dwarf_Attribute *result) __nonnull_attribute__ (2, 3); +/* Return the DIE associated with an operation such as + DW_OP_GNU_implicit_pointer, DW_OP_GNU_parameter_ref, DW_OP_GNU_convert, + DW_OP_GNU_reinterpret, DW_OP_GNU_const_type, DW_OP_GNU_regval_type or + DW_OP_GNU_deref_type. The OP pointer must point into an expression that + dwarf_getlocation or dwarf_getlocation_addr has returned given the same + ATTR. The RESULT is a DIE that expresses a type or value needed by the + given OP. */ +extern int dwarf_getlocation_die (Dwarf_Attribute *attr, + const Dwarf_Op *op, + Dwarf_Die *result) + __nonnull_attribute__ (2, 3); + +/* Return the attribute expressing a value associated with an operation such + as DW_OP_implicit_value, DW_OP_GNU_entry_value or DW_OP_GNU_const_type. + The OP pointer must point into an expression that dwarf_getlocation + or dwarf_getlocation_addr has returned given the same ATTR. + The RESULT is a value expressed by an attribute such as DW_AT_location + or DW_AT_const_value. */ +extern int dwarf_getlocation_attr (Dwarf_Attribute *attr, + const Dwarf_Op *op, + Dwarf_Attribute *result) + __nonnull_attribute__ (2, 3); + /* Compute the byte-size of a type DIE according to DWARF rules. For most types, this is just DW_AT_byte_size. diff --git a/libdw/libdw.map b/libdw/libdw.map index 2d2d37cc..09eae6a7 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -264,4 +264,6 @@ ELFUTILS_0.156 { ELFUTILS_0.157 { global: dwarf_getlocations; + dwarf_getlocation_die; + dwarf_getlocation_attr; } ELFUTILS_0.156; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 76bddffa..f02a5bf2 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -1,5 +1,5 @@ /* Internal definitions for libdwarf. - Copyright (C) 2002-2011 Red Hat, Inc. + Copyright (C) 2002-2011, 2013 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2002. @@ -654,6 +654,7 @@ INTDECL (dwarf_formudata) INTDECL (dwarf_getarange_addr) INTDECL (dwarf_getarangeinfo) INTDECL (dwarf_getaranges) +INTDECL (dwarf_getlocation_die) INTDECL (dwarf_getsrcfiles) INTDECL (dwarf_getsrclines) INTDECL (dwarf_hasattr) |