]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdw: Add new function dwarf_getlocations.
authorMark Wielaard <mjw@redhat.com>
Fri, 23 Aug 2013 14:12:37 +0000 (16:12 +0200)
committerMark Wielaard <mjw@redhat.com>
Fri, 6 Sep 2013 10:09:45 +0000 (12:09 +0200)
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
libdw/dwarf_getlocation.c
libdw/libdw.h
libdw/libdw.map

index aa1e2ccf369a039aefe06f6c3e5d354c1b99176c..153ea746e9942d9678b3a1d3c8e3357c51319ebd 100644 (file)
@@ -1,3 +1,15 @@
+2013-08-23  Mark Wielaard  <mjw@redhat.com>
+
+       * dwarf_getlocation.c (attr_ok): Also accept DW_AT_segment.
+       (attr_base_address): New function.
+       (initial_offset_base): New function.
+       (getlocations_addr): New function. Taken from...
+       (dwarf_getlocation_addr): here. Use new initial_offset_base and
+       getlocations_addr.
+       (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
index c3f3f477d0b037251d668a781a8dbd956ab82f34..aab471c6d8bc1e4266ceef798c10dd0a244572ca 100644 (file)
@@ -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,121 @@ 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;
+}
+
+static int
+initial_offset_base (attr, offset, basep)
+     Dwarf_Attribute *attr;
+     ptrdiff_t *offset;
+     Dwarf_Addr *basep;
+{
+  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;
+  return 0;
+}
+
+static ptrdiff_t
+getlocations_addr (attr, offset, basep, startp, endp, address,
+                  locs, expr, exprlen)
+     Dwarf_Attribute *attr;
+     ptrdiff_t offset;
+     Dwarf_Addr *basep;
+     Dwarf_Addr *startp;
+     Dwarf_Addr *endp;
+     Dwarf_Addr address;
+     Elf_Data *locs;
+     Dwarf_Op **expr;
+     size_t *exprlen;
+{
+  unsigned char *readp = locs->d_buf + offset;
+  unsigned char *readendp = locs->d_buf + locs->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;
+
+  *startp = *basep + begin;
+  *endp = *basep + end;
+
+  /* If address is minus one we want them all, otherwise only matching.  */
+  if (address != (Dwarf_Word) -1 && (address < *startp || address >= *endp))
+    goto next;
+
+  if (getlocation (attr->cu, &block, expr, exprlen, IDX_debug_loc) != 0)
+    return -1;
+
+  return readp - (unsigned char *) locs->d_buf;
+}
+
 int
 dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
      Dwarf_Attribute *attr;
@@ -605,85 +721,109 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
   if (result != 1)
     return result ?: 1;
 
-  unsigned char *endp;
-  unsigned char *readp = __libdw_formptr (attr, IDX_debug_loc,
-                                         DWARF_E_NO_LOCLIST, &endp, NULL);
-  if (readp == NULL)
+  Dwarf_Addr base, start, end;
+  Dwarf_Op *expr;
+  size_t expr_len;
+  ptrdiff_t off = 0;
+  size_t got = 0;
+
+  /* This is a true loclistptr, fetch the initial base address and offset.  */
+  if (initial_offset_base (attr, &off, &base) != 0)
     return -1;
 
-  Dwarf_Addr base = (Dwarf_Addr) -1;
-  size_t got = 0;
-  while (got < maxlocs)
+  const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
+  if (d == NULL)
     {
-      if (endp - readp < attr->cu->address_size * 2)
+      __libdw_seterrno (DWARF_E_NO_LOCLIST);
+      return -1;
+    }
+
+  while (got < maxlocs
+         && (off = getlocations_addr (attr, off, &base, &start, &end,
+                                  address, d, &expr, &expr_len)) > 0)
+    {
+      /* This one matches the address.  */
+      if (llbufs != NULL)
        {
-       invalid:
-         __libdw_seterrno (DWARF_E_INVALID_DWARF);
-         return -1;
+         llbufs[got] = expr;
+         listlens[got] = expr_len;
        }
+      ++got;
+    }
 
-      Dwarf_Addr begin;
-      Dwarf_Addr end;
+  /* We might stop early, so off can be zero or positive on success.  */
+  if (off < 0)
+    return -1;
 
-      int status
-       = __libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc,
-                                          &readp, attr->cu->address_size,
-                                          &begin, &end, &base);
-      if (status == 2) /* End of list entry.  */
-       break;
-      else if (status == 1) /* Base address selected.  */
-       continue;
-      else if (status < 0)
-       return status;
-
-      if (endp - readp < 2)
-       goto invalid;
-
-      /* We have a location expression.  */
-      block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
-      block.data = readp;
-      if (endp - readp < (ptrdiff_t) block.length)
-       goto invalid;
-      readp += block.length;
-
-      if (base == (Dwarf_Addr) -1)
+  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)
        {
-         /* 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;
+         if (getlocation (attr->cu, &block, expr, exprlen,
+                          cu_sec_idx (attr->cu)) != 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;
-           }
+         /* This is the one and only location covering everything. */
+         *startp = 0;
+         *endp = -1;
+         return 1;
        }
 
-      if (address >= base + begin && address < base + end)
+      int error = INTUSE(dwarf_errno) ();
+      if (unlikely (error != DWARF_E_NO_BLOCK))
        {
-         /* This one matches the address.  */
-         if (llbufs != NULL
-             && unlikely (getlocation (attr->cu, &block,
-                                       &llbufs[got], &listlens[got],
-                                       IDX_debug_loc) != 0))
-           return -1;
-         ++got;
+         __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 (initial_offset_base (attr, &offset, basep) != 0)
+       return -1;
     }
 
-  return got;
+  const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
+  if (d == NULL)
+    {
+      __libdw_seterrno (DWARF_E_NO_LOCLIST);
+      return -1;
+    }
+
+  return getlocations_addr (attr, offset, basep, startp, endp,
+                           (Dwarf_Word) -1, d, expr, exprlen);
 }
index f5fc4e2389b44c7673032b8bb4b90800b7141a16..898aa74c65e7166a9b734e017981162d74e55299 100644 (file)
@@ -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.  */
index d38a8ef6d63719e0772fbb6ab5501da9e4b83b40..2d2d37cc6f3ca4b6b6992e11a01b894d07cc3fdb 100644 (file)
@@ -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;