This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] 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>
---
 libdw/ChangeLog                |  19 ++++++++
 libdw/Makefile.am              |   3 +-
 libdw/dwarf_getlocation.c      |  37 ++++++++-------
 libdw/dwarf_getlocation_attr.c | 103 +++++++++++++++++++++++++++++++++++++++++
 libdw/dwarf_getlocation_die.c  |  78 +++++++++++++++++++++++++++++++
 libdw/libdw.h                  |  23 +++++++++
 libdw/libdw.map                |   2 +
 libdw/libdwP.h                 |   3 +-
 8 files changed, 250 insertions(+), 18 deletions(-)
 create mode 100644 libdw/dwarf_getlocation_attr.c
 create mode 100644 libdw/dwarf_getlocation_die.c

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 18ddd9e..78dcf69 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 71a006f..5fef2e1 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 2af0df7..428299f 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 0000000..2d6084e
--- /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 0000000..fa03aac
--- /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 898aa74..d1cd177 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 2d2d37c..09eae6a 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 76bddff..f02a5bf 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)
-- 
1.8.3.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]