summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wielaard <mjw@redhat.com>2013-08-30 23:55:12 +0200
committerMark Wielaard <mjw@redhat.com>2013-09-06 12:09:45 +0200
commitb2535b6a6be7717cdd41834d76e5cb48cb446b83 (patch)
tree6f90ce8b4e754103beaf72cef9778fe744ddc65d
parent66eaae9bcc1608efad65e3aa0204afbb3cb1a83d (diff)
downloadelfutils-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/ChangeLog19
-rw-r--r--libdw/Makefile.am3
-rw-r--r--libdw/dwarf_getlocation.c37
-rw-r--r--libdw/dwarf_getlocation_attr.c103
-rw-r--r--libdw/dwarf_getlocation_die.c78
-rw-r--r--libdw/libdw.h23
-rw-r--r--libdw/libdw.map2
-rw-r--r--libdw/libdwP.h3
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)