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 function dwarf_getlocations.


Using dwarf_getlocation it is possible to get single location
descriptions and with dwarf_getlocation_addr it is possible to
get a location list covering a specific address.  But sometimes
it is more convenient to get all ranges covered by a location
list.  For example when a specific address isn't covered and
you want to find alternative addresses where a location
description is defined.

dwarf_getlocations is modelled after dwarf_ranges. It enumerates
the location ranges and descriptions covered by the given
attribute.  In the first call OFFSET should be zero and *BASEP
need not be initialized.  Returns -1 for errors, zero when
there are no more locations to report, or a nonzero OFFSET
value to pass to the next call.  Each subsequent call must
preserve *BASEP from the prior call.  Successful calls fill in
*STARTP and *ENDP with a contiguous address range and *EXPR with
a pointer to an array of operations with length *EXPRLEN.  If
the attribute describes a single location description and not a
location list the first call (with OFFSET zero) will return the
location description in *EXPR with *STARTP set to zero and *ENDP
set to minus one.

ptrdiff_t dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset,
                              Dwarf_Addr *basep, Dwarf_Addr *startp,
                              Dwarf_Addr *endp, Dwarf_Op **expr,
                              size_t *exprlen);

Signed-off-by: Mark Wielaard <mjw@redhat.com>
---
 libdw/ChangeLog           |    9 ++
 libdw/dwarf_getlocation.c |  182 ++++++++++++++++++++++++++++++++++++++-------
 libdw/libdw.h             |   18 ++++-
 libdw/libdw.map           |    5 +
 4 files changed, 186 insertions(+), 28 deletions(-)

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index aa1e2cc..18ddd9e 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,12 @@
+2013-08-23  Mark Wielaard  <mjw@redhat.com>
+
+	* dwarf_getlocation.c (attr_ok): Also accept DW_AT_segment.
+	(attr_base_address): New function, taken from...
+	(dwarf_getlocation_addr): here. Use attr_base_address.
+	(dwarf_getlocations): New function.
+	* libdw.h (dwarf_getlocations): New function definition.
+	* libdw.map (ELFUTILS_0.157): New.
+
 2013-07-02  Mark Wielaard  <mjw@redhat.com>
 
 	* dwarf_getsrclines.c (dwarf_getsrclines): Add new stack allocation
diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c
index c3f3f47..2af0df7 100644
--- a/libdw/dwarf_getlocation.c
+++ b/libdw/dwarf_getlocation.c
@@ -1,5 +1,5 @@
 /* Return location expression list.
-   Copyright (C) 2000-2010 Red Hat, Inc.
+   Copyright (C) 2000-2010, 2013 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2000.
 
@@ -56,6 +56,7 @@ attr_ok (Dwarf_Attribute *attr)
     case DW_AT_frame_base:
     case DW_AT_return_addr:
     case DW_AT_static_link:
+    case DW_AT_segment:
       break;
 
     default:
@@ -567,6 +568,37 @@ dwarf_getlocation (attr, llbuf, listlen)
   return getlocation (attr->cu, &block, llbuf, listlen, cu_sec_idx (attr->cu));
 }
 
+static int
+attr_base_address (attr, basep)
+     Dwarf_Attribute *attr;
+     Dwarf_Addr *basep;
+{
+  /* Fetch the CU's base address.  */
+  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 (unlikely (INTUSE(dwarf_lowpc) (&cudie, basep) != 0)
+      && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
+						     DW_AT_entry_pc,
+						     &attr_mem),
+				 basep) != 0)
+    {
+      if (INTUSE(dwarf_errno) () != 0)
+	return -1;
+
+      /* The compiler provided no base address when it should
+	 have.  Buggy GCC does this when it used absolute
+	 addresses in the location list and no DW_AT_ranges.  */
+      *basep = 0;
+    }
+  return 0;
+}
+
 int
 dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
      Dwarf_Attribute *attr;
@@ -646,32 +678,8 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
 	goto invalid;
       readp += block.length;
 
-      if (base == (Dwarf_Addr) -1)
-	{
-	  /* Fetch the CU's base address.  */
-	  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 (unlikely (INTUSE(dwarf_lowpc) (&cudie, &base) != 0)
-	      && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
-							     DW_AT_entry_pc,
-							     &attr_mem),
-					 &base) != 0)
-	    {
-	      if (INTUSE(dwarf_errno) () != 0)
-		return -1;
-
-	      /* The compiler provided no base address when it should
-		 have.  Buggy GCC does this when it used absolute
-		 addresses in the location list and no DW_AT_ranges.  */
-	      base = 0;
-	    }
-	}
+      if (base == (Dwarf_Addr) -1 && attr_base_address (attr, &base) != 0)
+	return -1;
 
       if (address >= base + begin && address < base + end)
 	{
@@ -687,3 +695,123 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
 
   return got;
 }
+
+ptrdiff_t
+dwarf_getlocations (attr, offset, basep, startp, endp, expr, exprlen)
+     Dwarf_Attribute *attr;
+     ptrdiff_t offset;
+     Dwarf_Addr *basep;
+     Dwarf_Addr *startp;
+     Dwarf_Addr *endp;
+     Dwarf_Op **expr;
+     size_t *exprlen;
+{
+  if (! attr_ok (attr))
+    return -1;
+
+  /* 1 is an invalid offset, meaning no more locations. */
+  if (offset == 1)
+    return 0;
+
+  if (offset == 0)
+    {
+      /* If it has a block form, it's a single location expression.  */
+      Dwarf_Block block;
+      if (INTUSE(dwarf_formblock) (attr, &block) == 0)
+	{
+	  if (getlocation (attr->cu, &block, expr, exprlen,
+			   cu_sec_idx (attr->cu)) != 0)
+	    return -1;
+
+	  /* This is the one and only location covering everything. */
+	  *startp = 0;
+	  *endp = -1;
+	  return 1;
+	}
+
+      int error = INTUSE(dwarf_errno) ();
+      if (unlikely (error != DWARF_E_NO_BLOCK))
+	{
+	  __libdw_seterrno (error);
+	  return -1;
+	}
+
+      int result = check_constant_offset (attr, expr, exprlen);
+      if (result != 1)
+	{
+	  if (result == 0)
+	    {
+	      /* This is the one and only location covering everything. */
+	      *startp = 0;
+	      *endp = -1;
+	      return 1;
+	    }
+	  return result;
+	}
+
+      /* We must be looking at a true loclistptr, fetch the initial
+	 base address and offset.  */
+      if (attr_base_address (attr, basep) != 0)
+	return -1;
+
+      Dwarf_Word start_offset;
+      if (__libdw_formptr (attr, IDX_debug_loc,
+			   DWARF_E_NO_LOCLIST,
+			   NULL, &start_offset) == NULL)
+	return -1;
+      offset = start_offset;
+    }
+
+  const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
+  if (d == NULL)
+    {
+      __libdw_seterrno (DWARF_E_NO_LOCLIST);
+      return -1;
+    }
+
+  unsigned char *readp = d->d_buf + offset;
+  unsigned char *readendp = d->d_buf + d->d_size;
+
+ next:
+  if (readendp - readp < attr->cu->address_size * 2)
+    {
+    invalid:
+      __libdw_seterrno (DWARF_E_INVALID_DWARF);
+      return -1;
+    }
+
+  Dwarf_Addr begin;
+  Dwarf_Addr end;
+
+  switch (__libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc,
+					   &readp, attr->cu->address_size,
+					   &begin, &end, basep))
+    {
+    case 0: /* got location range. */
+      break;
+    case 1: /* base address setup. */
+      goto next;
+    case 2: /* end of loclist */
+      return 0;
+    default: /* error */
+      return -1;
+    }
+
+  if (readendp - readp < 2)
+    goto invalid;
+
+  /* We have a location expression.  */
+  Dwarf_Block block;
+  block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
+  block.data = readp;
+  if (readendp - readp < (ptrdiff_t) block.length)
+    goto invalid;
+  readp += block.length;
+
+  if (getlocation (attr->cu, &block, expr, exprlen, IDX_debug_loc) != 0)
+    return -1;
+
+  *startp = *basep + begin;
+  *endp = *basep + end;
+  return readp - (unsigned char *) d->d_buf;
+}
diff --git a/libdw/libdw.h b/libdw/libdw.h
index f5fc4e2..898aa74 100644
--- a/libdw/libdw.h
+++ b/libdw/libdw.h
@@ -1,5 +1,5 @@
 /* Interfaces for libdw.
-   Copyright (C) 2002-2010 Red Hat, Inc.
+   Copyright (C) 2002-2010, 2013 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -630,6 +630,22 @@ extern int dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address,
 				   Dwarf_Op **exprs, size_t *exprlens,
 				   size_t nlocs);
 
+/* Enumerate the locations ranges and descriptions covered by the
+   given attribute.  In the first call OFFSET should be zero and
+   *BASEP need not be initialized.  Returns -1 for errors, zero when
+   there are no more locations to report, or a nonzero OFFSET
+   value to pass to the next call.  Each subsequent call must preserve
+   *BASEP from the prior call.  Successful calls fill in *STARTP and
+   *ENDP with a contiguous address range and *EXPR with a pointer to
+   an array of operations with length *EXPRLEN.  If the attribute
+   describes a single location description and not a location list the
+   first call (with OFFSET zero) will return the location description
+   in *EXPR with *STARTP set to zero and *ENDP set to minus one.  */
+extern ptrdiff_t dwarf_getlocations (Dwarf_Attribute *attr,
+				     ptrdiff_t offset, Dwarf_Addr *basep,
+				     Dwarf_Addr *startp, Dwarf_Addr *endp,
+				     Dwarf_Op **expr, size_t *exprlen);
+
 /* Return the block associated with a DW_OP_implicit_value operation.
    The OP pointer must point into an expression that dwarf_getlocation
    or dwarf_getlocation_addr has returned given the same ATTR.  */
diff --git a/libdw/libdw.map b/libdw/libdw.map
index d38a8ef..2d2d37c 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -260,3 +260,8 @@ ELFUTILS_0.156 {
     # Replaced ELFUTILS_0.122 version, which has a wrapper without add_p_vaddr.
     dwfl_report_elf;
 } ELFUTILS_0.149;
+
+ELFUTILS_0.157 {
+  global:
+    dwarf_getlocations;
+} ELFUTILS_0.156;
-- 
1.7.1


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