]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdw: Add new functions dwarf_getlocation_attr and dwarf_getlocation_die.
authorMark Wielaard <mjw@redhat.com>
Fri, 30 Aug 2013 21:55:12 +0000 (23:55 +0200)
committerMark Wielaard <mjw@redhat.com>
Fri, 6 Sep 2013 10:09:45 +0000 (12:09 +0200)
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
libdw/Makefile.am
libdw/dwarf_getlocation.c
libdw/dwarf_getlocation_attr.c [new file with mode: 0644]
libdw/dwarf_getlocation_die.c [new file with mode: 0644]
libdw/libdw.h
libdw/libdw.map
libdw/libdwP.h

index 153ea746e9942d9678b3a1d3c8e3357c51319ebd..c8398b2312102413794c5c397d0201719f9bd677 100644 (file)
@@ -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.
index 71a006fab1f98894663613942096c1c825a9288e..5fef2e1857e83a79948d4f69a75c82a5b8bec963 100644 (file)
@@ -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
index aab471c6d8bc1e4266ceef798c10dd0a244572ca..f7d64f4176ce60bc2fb3a702590f28e364b860d6 100644 (file)
@@ -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 (file)
index 0000000..2d6084e
--- /dev/null
@@ -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 (file)
index 0000000..fa03aac
--- /dev/null
@@ -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);
index 898aa74c65e7166a9b734e017981162d74e55299..d1cd1775c371d644d1bd3d876d2f2555292f185f 100644 (file)
@@ -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.
index 2d2d37cc6f3ca4b6b6992e11a01b894d07cc3fdb..09eae6a7bea8f6cd9a933082b82fab23c7211f4a 100644 (file)
@@ -264,4 +264,6 @@ ELFUTILS_0.156 {
 ELFUTILS_0.157 {
   global:
     dwarf_getlocations;
+    dwarf_getlocation_die;
+    dwarf_getlocation_attr;
 } ELFUTILS_0.156;
index 76bddffa587222118b94db198f4aafe284fed9a5..f02a5bf2d9532724c29e3039a9ac128b69ef7902 100644 (file)
@@ -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)