Merge "Merge upstream SHA '9d1e236'"
diff --git a/backends/ChangeLog b/backends/ChangeLog
index 7f0d1c0..4b604fd 100644
--- a/backends/ChangeLog
+++ b/backends/ChangeLog
@@ -1,3 +1,19 @@
+2015-12-08  Jose E. Marchesi  <jose.marchesi@oracle.com>
+
+	* sparc_init.c (sparc_init): Hook sparc_set_initial_registers_tid.
+	* sparc_initreg.c: New file.
+	* Makefile.am (sparc_SRCS): Added sparc_initreg.c.
+
+2015-12-08  Jose E. Marchesi  <jose.marchesi@oracle.com>
+
+	* sparc_corenote.c: Header comment typo fixed.
+	(PRSTATUS_REGSET_ITEMS): Defined, so the PC can be fetched from
+	core files.
+	* Makefile.am (sparc_SRCS): Added sparc_cfi.c
+	* sparc_cfi.c: New file.
+	* sparc_init.c (sparc_init): Set eh->frame_nregs, eh->ra_offset
+	and hook sparc_abi_cfi.
+
 2015-10-21  Chih-Hung Hsieh  <chh@google.com>
 
 	* ia64_retval.c (hfa_type): Move nested function 'hfa' to file scope.
diff --git a/backends/Makefile.am b/backends/Makefile.am
index f7002fb..b16f948 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -84,7 +84,8 @@
 am_libebl_aarch64_pic_a_OBJECTS = $(aarch64_SRCS:.c=.os)
 
 sparc_SRCS = sparc_init.c sparc_symbol.c sparc_regs.c sparc_retval.c \
-	     sparc_corenote.c sparc64_corenote.c sparc_auxv.c sparc_attrs.c
+	     sparc_corenote.c sparc64_corenote.c sparc_auxv.c sparc_attrs.c \
+             sparc_cfi.c sparc_initreg.c
 libebl_sparc_pic_a_SOURCES = $(sparc_SRCS)
 am_libebl_sparc_pic_a_OBJECTS = $(sparc_SRCS:.c=.os)
 
diff --git a/backends/sparc_cfi.c b/backends/sparc_cfi.c
new file mode 100644
index 0000000..dcc17bd
--- /dev/null
+++ b/backends/sparc_cfi.c
@@ -0,0 +1,83 @@
+/* SPARC defaults for DWARF CFI.
+   Copyright (C) 2015 Oracle 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>
+
+#define BACKEND sparc_
+#include "libebl_CPU.h"
+
+int
+sparc_abi_cfi (Ebl *ebl __attribute__ ((unused)), Dwarf_CIE *abi_info)
+{
+  static const uint8_t abi_cfi[] =
+    {
+#define SV(n) DW_CFA_same_value, ULEB128_7 (n)
+      /* %g0 .. %g7 */
+      SV (0), SV (1), SV (2), SV (3), SV (4), SV (5), SV (6), SV (7),
+      /* %o0 .. %o7 */
+      SV (8), SV (9), SV (10), SV (11), SV (12), SV (13), SV (14), SV (15),
+      /* %l0 .. %l7 */
+      SV (16), SV (17), SV (18), SV (19), SV (20), SV (21), SV (22), SV (23),
+      /* %i0 .. %i7 */
+      SV (24), SV (25), SV (26), SV (27), SV (28), SV (29), SV (30), SV (31),
+      /* %f0 .. %f32 */
+      SV (32), SV (33), SV (34), SV (35), SV (36), SV (37), SV (38), SV (39),
+      SV (40), SV (41), SV (42), SV (43), SV (44), SV (45), SV (46), SV (47),
+      SV (48), SV (49), SV (50), SV (51), SV (52), SV (53), SV (54), SV (55),
+      SV (56), SV (57), SV (58), SV (59), SV (60), SV (61), SV (52), SV (63),
+      /* %f33 .. %63
+         Note that there are DWARF columns for the odd registers, even
+         if they don't exist in hardware) */
+      SV (64), SV (65), SV (66), SV (67), SV (68), SV (69), SV (70), SV (71),
+      SV (72), SV (73), SV (74), SV (75), SV (76), SV (77), SV (78), SV (79),
+      SV (80), SV (81), SV (82), SV (83), SV (84), SV (85), SV (86), SV (87),
+      SV (88), SV (89), SV (90), SV (91), SV (92), SV (93), SV (94), SV (95),
+      /* %fcc[0123] */
+      SV (96), SV (97), SV (98), SV (99),
+      /* %icc/%xcc */
+      SV (100),
+      /* Soft frame-pointer */
+      SV (101),
+      /* %gsr */
+      SV (102)
+#undef SV
+    };
+
+  abi_info->initial_instructions = abi_cfi;
+  abi_info->initial_instructions_end = &abi_cfi[sizeof abi_cfi];
+  abi_info->data_alignment_factor = 4;
+
+  abi_info->return_address_register = 31; /* %i7 */
+
+  return 0;
+}
+
diff --git a/backends/sparc_corenote.c b/backends/sparc_corenote.c
index 7912539..c9b6ff3 100644
--- a/backends/sparc_corenote.c
+++ b/backends/sparc_corenote.c
@@ -1,5 +1,6 @@
-/* PowerPC specific core note handling.
+/* SPARC specific core note handling.
    Copyright (C) 2007 Red Hat, Inc.
+   Copyright (C) 2015 Oracle, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -109,4 +110,11 @@
 #define ALIGN_PID_T		4
 #define TYPE_PID_T		ELF_T_SWORD
 
+#define PRSTATUS_REGSET_ITEMS						      \
+  {									      \
+    .name = "pc", .type = ELF_T_ADDR, .format = 'x',			      \
+    .offset = offsetof (struct EBLHOOK(prstatus), pr_reg[33]),		      \
+    .group = "register", .pc_register = true				      \
+  }
+
 #include "linux-core-note.c"
diff --git a/backends/sparc_init.c b/backends/sparc_init.c
index f8a7cfb..8e946fb 100644
--- a/backends/sparc_init.c
+++ b/backends/sparc_init.c
@@ -76,6 +76,14 @@
   HOOK (eh, register_info);
   HOOK (eh, return_value_location);
   HOOK (eh, check_object_attribute);
+  HOOK (eh, abi_cfi);
+  /* gcc/config/sparc.h define FIRST_PSEUDO_REGISTER  */
+  eh->frame_nregs = 103;
+  /* The CFI Dwarf register with the "return address" in sparc
+     actually contains the call address.  The return address is
+     located 8 bytes after it.  */
+  eh->ra_offset = 8;
+  HOOK (eh, set_initial_registers_tid);
 
   return MODVERSION;
 }
diff --git a/backends/sparc_initreg.c b/backends/sparc_initreg.c
new file mode 100644
index 0000000..c2a9b32
--- /dev/null
+++ b/backends/sparc_initreg.c
@@ -0,0 +1,129 @@
+/* Fetch live process registers from TID.
+   Copyright (C) 2015 Oracle, In
+   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 "system.h"
+#include <stdlib.h>
+#ifdef __sparc__
+# include <asm/ptrace.h>
+# include <sys/ptrace.h>
+#endif
+
+#define BACKEND sparc_
+#include "libebl_CPU.h"
+
+bool
+EBLHOOK (set_initial_registers_tid) (pid_t tid __attribute__ ((unused)),
+                                     ebl_tid_registers_t *setfunc __attribute__ ((unused)),
+                                     void *arg __attribute__ ((unused)))
+{
+#ifndef __sparc__
+  return false;
+#else /* __sparc__ */
+
+
+  /* The pt_regs structure filled in by PTRACE_GETREGS provides the
+     PC, the global registers and the output registers.  Note how the
+     %g0 register is not explicitly provided in the structure (it's
+     value is always 0) and the resulting weird packing in the u_regs
+     array: the last element is not used.  */
+  
+  struct pt_regs regs;
+  if (ptrace (PTRACE_GETREGS, tid, &regs, 0) == -1)
+    return false;
+
+  /* PC: no DWARF number  */
+  if (!setfunc (-1, 1, (Dwarf_Word *) &regs.tpc, arg))
+    return false;
+  
+  /* Global registers: DWARF 0 .. 7  */
+  Dwarf_Word zero = 0;
+  if (!setfunc (0, 1, &zero, arg))
+    return false;
+  if (!setfunc (1, 7, (Dwarf_Word *) &regs.u_regs[0], arg))
+    return false;
+
+  /* Output registers: DWARF  8 .. 15  */
+  if (!setfunc (8, 8, (Dwarf_Word *) &regs.u_regs[7], arg))
+    return false;
+
+  /* Local and input registers must be read from the stack.  They are
+     saved in the previous stack frame.  The stack pointer is %o6,
+     read above.  */
+
+  Dwarf_Word locals_outs[16];
+  Dwarf_Word sp = regs.u_regs[13];
+
+  if (sp & 1)
+    {
+      /* Registers are 64 bits, and we need to apply the 2047 stack
+         bias in order to get the real stack pointer.  */
+
+      sp += 2047;
+
+      for (unsigned i = 0; i < 16; i++)
+        {
+          locals_outs[i] = ptrace (PTRACE_PEEKDATA, tid,
+                                   (void *) (uintptr_t) (sp + (i * 8)),
+                                   NULL);
+          if (errno != 0)
+            return false;
+        }
+    }
+  else
+    {
+      /* Registers are 32 bits.  */
+
+      for (unsigned i = 0; i < 8; i++)
+        {
+          Dwarf_Word tuple = ptrace (PTRACE_PEEKDATA, tid,
+                                     (void *) (uintptr_t) (sp + (i * 8)),
+                                     NULL);
+          if (errno != 0)
+            return false;
+
+          locals_outs[2*i] = (tuple >> 32) & 0xffffffff;
+          locals_outs[2*i+1] = tuple & 0xffffffff;
+        }
+    }
+
+  
+  /* Local registers:  DWARF 16 .. 23 */
+  if (!setfunc (16, 8, &locals_outs[0], arg))
+    return false;
+  
+  /* Input registers: DWARF 24 .. 31 */
+  if (!setfunc (24, 8, &locals_outs[8], arg))
+    return false;
+
+  return true;
+#endif
+}
diff --git a/libdw/Android.mk b/libdw/Android.mk
index 55a04dc..5558a1a 100755
--- a/libdw/Android.mk
+++ b/libdw/Android.mk
@@ -143,9 +143,6 @@
 
 include $(CLEAR_VARS)
 
-# Clang has no nested functions.
-LOCAL_CLANG := false
-
 LOCAL_SRC_FILES := $(LIBDW_SRC_FILES)
 
 LOCAL_C_INCLUDES := \
@@ -175,9 +172,6 @@
 
 include $(CLEAR_VARS)
 
-# Clang has no nested functions.
-LOCAL_CLANG := false
-
 # b/25642296, local __thread variable does not work with arm64 clang/llvm.
 LOCAL_CLANG_arm64 := false
 
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 5218145..d0e97f3 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,32 @@
+2015-12-02  Mark Wielaard  <mjw@redhat.com>
+
+	* fde.c (intern_fde): Don't leak duplicate FDEs.
+
+2015-12-01  Mark Wielaard  <mjw@redhat.com>
+
+	* fde.c (intern_fde): Don't intern an fde that doesn't cover a
+	valid code range.
+
+2015-12-01  Mark Wielaard  <mjw@redhat.com>
+
+	* dwarf_end.c (dwarf_end): Call cu_free on fake_loc_cu if it exists.
+
+2015-10-14  Chih-Hung Hsieh  <chh@google.com>
+
+	* dwarf_entry_breakpoints.c (dwarf_entry_breakpoints): Move recursive
+	functions 'add_bkpt', 'entrypc_bkpt', and 'search_range' to file scope.
+
+2015-10-14  Chih-Hung Hsieh  <chh@google.com>
+
+	* libdw_visit_scopes.c (__libdw_visit_scopes): Move recursive nested
+	function 'walk_children' to file scope; inline 'recurse' at its call
+	site.
+
+2015-10-19  Mark Wielaard  <mjw@redhat.com>
+
+	* frame-cache.c (__libdw_destroy_frame_cache): Call ebl_closebackend
+	if necessary.
+
 2015-10-16  Dmitry V. Levin  <ldv@altlinux.org>
 
 	* dwarf_getsrclines.c (read_srclines): Initialize state early.
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index 32b551d..2108063 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -117,7 +117,11 @@
 	elf_end (dwarf->elf);
 
       /* Free the fake location list CU.  */
-      free (dwarf->fake_loc_cu);
+      if (dwarf->fake_loc_cu != NULL)
+	{
+	  cu_free (dwarf->fake_loc_cu);
+	  free (dwarf->fake_loc_cu);
+	}
 
       /* Free the context descriptor.  */
       free (dwarf);
diff --git a/libdw/dwarf_entry_breakpoints.c b/libdw/dwarf_entry_breakpoints.c
index abfee73..c3c0f39 100644
--- a/libdw/dwarf_entry_breakpoints.c
+++ b/libdw/dwarf_entry_breakpoints.c
@@ -34,54 +34,40 @@
 #include <stdlib.h>
 
 
-int
-dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts)
+/* Add one breakpoint location to the result vector.  */
+static inline int
+add_bkpt (Dwarf_Addr pc, Dwarf_Addr **bkpts, int *pnbkpts)
 {
-  int nbkpts = 0;
-  *bkpts = NULL;
-
-  /* Add one breakpoint location to the result vector.  */
-  inline int add_bkpt (Dwarf_Addr pc)
+  Dwarf_Addr *newlist = realloc (*bkpts, ++(*pnbkpts) * sizeof newlist[0]);
+  if (newlist == NULL)
     {
-      Dwarf_Addr *newlist = realloc (*bkpts, ++nbkpts * sizeof newlist[0]);
-      if (newlist == NULL)
-	{
-	  free (*bkpts);
-	  *bkpts = NULL;
-	  __libdw_seterrno (DWARF_E_NOMEM);
-	  return -1;
-	}
-      newlist[nbkpts - 1] = pc;
-      *bkpts = newlist;
-      return nbkpts;
-    }
-
-  /* Fallback result, break at the entrypc/lowpc value.  */
-  inline int entrypc_bkpt (void)
-    {
-      Dwarf_Addr pc;
-      return INTUSE(dwarf_entrypc) (die, &pc) < 0 ? -1 : add_bkpt (pc);
-    }
-
-  /* Fetch the CU's line records to look for this DIE's addresses.  */
-  Dwarf_Die cudie = CUDIE (die->cu);
-  Dwarf_Lines *lines;
-  size_t nlines;
-  if (INTUSE(dwarf_getsrclines) (&cudie, &lines, &nlines) < 0)
-    {
-      int error = INTUSE (dwarf_errno) ();
-      if (error == 0)		/* CU has no DW_AT_stmt_list.  */
-	return entrypc_bkpt ();
-      __libdw_seterrno (error);
+      free (*bkpts);
+      *bkpts = NULL;
+      __libdw_seterrno (DWARF_E_NOMEM);
       return -1;
     }
+  newlist[*pnbkpts - 1] = pc;
+  *bkpts = newlist;
+  return *pnbkpts;
+}
 
-  /* Search a contiguous PC range for prologue-end markers.
-     If DWARF, look for proper markers.
-     Failing that, if ADHOC, look for the ad hoc convention.  */
-  inline int search_range (Dwarf_Addr low, Dwarf_Addr high,
-			   bool dwarf, bool adhoc)
-    {
+/* Fallback result, break at the entrypc/lowpc value.  */
+static inline int
+entrypc_bkpt (Dwarf_Die *die, Dwarf_Addr **bkpts, int *pnbkpts)
+{
+  Dwarf_Addr pc;
+  return INTUSE(dwarf_entrypc) (die, &pc) < 0 ? -1 : add_bkpt (pc, bkpts, pnbkpts);
+}
+
+/* Search a contiguous PC range for prologue-end markers.
+   If DWARF, look for proper markers.
+   Failing that, if ADHOC, look for the ad hoc convention.  */
+static inline int
+search_range (Dwarf_Addr low, Dwarf_Addr high,
+	      bool dwarf, bool adhoc,
+              Dwarf_Lines *lines, size_t nlines,
+              Dwarf_Addr **bkpts, int *pnbkpts)
+{
       size_t l = 0, u = nlines;
       while (l < u)
 	{
@@ -103,16 +89,35 @@
 	  if (dwarf)
 	    for (size_t i = l; i < u && lines->info[i].addr < high; ++i)
 	      if (lines->info[i].prologue_end
-		  && add_bkpt (lines->info[i].addr) < 0)
+		  && add_bkpt (lines->info[i].addr, bkpts, pnbkpts) < 0)
 		return -1;
-	  if (adhoc && nbkpts == 0)
+	  if (adhoc && *pnbkpts == 0)
 	    while (++l < nlines && lines->info[l].addr < high)
 	      if (!lines->info[l].end_sequence)
-		return add_bkpt (lines->info[l].addr);
-	  return nbkpts;
+		return add_bkpt (lines->info[l].addr, bkpts, pnbkpts);
+	  return *pnbkpts;
 	}
       __libdw_seterrno (DWARF_E_INVALID_DWARF);
       return -1;
+}
+
+int
+dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts)
+{
+  int nbkpts = 0;
+  *bkpts = NULL;
+
+  /* Fetch the CU's line records to look for this DIE's addresses.  */
+  Dwarf_Die cudie = CUDIE (die->cu);
+  Dwarf_Lines *lines;
+  size_t nlines;
+  if (INTUSE(dwarf_getsrclines) (&cudie, &lines, &nlines) < 0)
+    {
+      int error = INTUSE (dwarf_errno) ();
+      if (error == 0)		/* CU has no DW_AT_stmt_list.  */
+	return entrypc_bkpt (die, bkpts, &nbkpts);
+      __libdw_seterrno (error);
+      return -1;
     }
 
   /* Search each contiguous address range for DWARF prologue_end markers.  */
@@ -126,14 +131,16 @@
 
   /* Most often there is a single contiguous PC range for the DIE.  */
   if (offset == 1)
-    return search_range (begin, end, true, true) ?: entrypc_bkpt ();
+    return search_range (begin, end, true, true, lines, nlines, bkpts, &nbkpts)
+        ?: entrypc_bkpt (die, bkpts, &nbkpts);
 
   Dwarf_Addr lowpc = (Dwarf_Addr) -1l;
   Dwarf_Addr highpc = (Dwarf_Addr) -1l;
   while (offset > 0)
     {
       /* We have an address range entry.  */
-      if (search_range (begin, end, true, false) < 0)
+      if (search_range (begin, end, true, false,
+                        lines, nlines, bkpts, &nbkpts) < 0)
 	return -1;
 
       if (begin < lowpc)
@@ -150,6 +157,7 @@
      fall back to just using the entrypc value.  */
   return (nbkpts
 	  ?: (lowpc == (Dwarf_Addr) -1l ? 0
-	      : search_range (lowpc, highpc, false, true))
-	  ?: entrypc_bkpt ());
+	      : search_range (lowpc, highpc, false, true,
+	                      lines, nlines, bkpts, &nbkpts))
+	  ?: entrypc_bkpt (die, bkpts, &nbkpts));
 }
diff --git a/libdw/fde.c b/libdw/fde.c
index c8475f3..f5f6fbe 100644
--- a/libdw/fde.c
+++ b/libdw/fde.c
@@ -90,6 +90,13 @@
     }
   fde->end += fde->start;
 
+  /* Make sure the fde actually covers a real code range.  */
+  if (fde->start >= fde->end)
+    {
+      free (fde);
+      return (void *) -1;
+    }
+
   fde->cie = cie;
 
   if (cie->sized_augmentation_data)
@@ -112,12 +119,21 @@
     fde->instructions += cie->fde_augmentation_data_size;
 
   /* Add the new entry to the search tree.  */
-  if (tsearch (fde, &cache->fde_tree, &compare_fde) == NULL)
+  struct dwarf_fde **tres = tsearch (fde, &cache->fde_tree, &compare_fde);
+  if (tres == NULL)
     {
       free (fde);
       __libdw_seterrno (DWARF_E_NOMEM);
       return NULL;
     }
+  else if (*tres != fde)
+    {
+      /* There is already an FDE in the cache that covers the same
+	 address range.  That is odd.  Ignore this FDE.  And just use
+	 the one in the cache for consistency.  */
+      free (fde);
+      return *tres;
+    }
 
   return fde;
 }
diff --git a/libdw/frame-cache.c b/libdw/frame-cache.c
index 54a1cc9..5b6afb5 100644
--- a/libdw/frame-cache.c
+++ b/libdw/frame-cache.c
@@ -1,5 +1,5 @@
 /* Frame cache handling.
-   Copyright (C) 2009 Red Hat, Inc.
+   Copyright (C) 2009, 2015 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -30,6 +30,7 @@
 # include <config.h>
 #endif
 
+#include "../libebl/libebl.h"
 #include "cfi.h"
 #include <search.h>
 #include <stdlib.h>
@@ -63,4 +64,7 @@
   tdestroy (cache->fde_tree, free_fde);
   tdestroy (cache->cie_tree, free_cie);
   tdestroy (cache->expr_tree, free_expr);
+
+  if (cache->ebl != NULL && cache->ebl != (void *) -1l)
+    ebl_closebackend (cache->ebl);
 }
diff --git a/libdw/libdw_visit_scopes.c b/libdw/libdw_visit_scopes.c
index 5e5c26f..eb892e1 100644
--- a/libdw/libdw_visit_scopes.c
+++ b/libdw/libdw_visit_scopes.c
@@ -64,6 +64,21 @@
   return false;
 }
 
+struct walk_children_state
+{
+  /* Parameters of __libdw_visit_scopes. */
+  unsigned int depth;
+  struct Dwarf_Die_Chain *imports;
+  int (*previsit) (unsigned int depth, struct Dwarf_Die_Chain *, void *);
+  int (*postvisit) (unsigned int depth, struct Dwarf_Die_Chain *, void *);
+  void *arg;
+  /* Extra local variables for the walker. */
+  struct Dwarf_Die_Chain child;
+};
+
+static inline int
+walk_children (struct walk_children_state *state);
+
 int
 internal_function
 __libdw_visit_scopes (unsigned int depth, struct Dwarf_Die_Chain *root,
@@ -76,95 +91,98 @@
 					void *),
 		      void *arg)
 {
-  struct Dwarf_Die_Chain child;
-  int ret;
+  struct walk_children_state state =
+    {
+      .depth = depth,
+      .imports = imports,
+      .previsit = previsit,
+      .postvisit = postvisit,
+      .arg = arg
+    };
 
-  child.parent = root;
-  if ((ret = INTUSE(dwarf_child) (&root->die, &child.die)) != 0)
+  state.child.parent = root;
+  int ret;
+  if ((ret = INTUSE(dwarf_child) (&root->die, &state.child.die)) != 0)
     return ret < 0 ? -1 : 0; // Having zero children is legal.
 
-  inline int recurse (void)
-    {
-      return __libdw_visit_scopes (depth + 1, &child, imports,
-				   previsit, postvisit, arg);
-    }
+  return walk_children (&state);
+}
 
-  /* Checks the given DIE hasn't been imported yet to prevent cycles.  */
-  inline bool imports_contains (Dwarf_Die *die)
-  {
-    for (struct Dwarf_Die_Chain *import = imports; import != NULL;
-	 import = import->parent)
-      if (import->die.addr == die->addr)
-	return true;
-
-    return false;
-  }
-
-  inline int walk_children (void)
+static inline int
+walk_children (struct walk_children_state *state)
 {
-    do
-      {
-	/* For an imported unit, it is logically as if the children of
-	   that unit are siblings of the other children.  So don't do
-	   a full recursion into the imported unit, but just walk the
-	   children in place before moving to the next real child.  */
-	while (INTUSE(dwarf_tag) (&child.die) == DW_TAG_imported_unit)
-	  {
-	    Dwarf_Die orig_child_die = child.die;
-	    Dwarf_Attribute attr_mem;
-	    Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&child.die,
-							DW_AT_import,
-							&attr_mem);
-	    if (INTUSE(dwarf_formref_die) (attr, &child.die) != NULL
-                && INTUSE(dwarf_child) (&child.die, &child.die) == 0)
-	      {
-		if (imports_contains (&orig_child_die))
-		  {
-		    __libdw_seterrno (DWARF_E_INVALID_DWARF);
-		    return -1;
-		  }
-		struct Dwarf_Die_Chain *orig_imports = imports;
-		struct Dwarf_Die_Chain import = { .die = orig_child_die,
-						  .parent = orig_imports };
-		imports = &import;
-		int result = walk_children ();
-		imports = orig_imports;
-		if (result != DWARF_CB_OK)
-		  return result;
-	      }
+  int ret;
+  do
+    {
+      /* For an imported unit, it is logically as if the children of
+	 that unit are siblings of the other children.  So don't do
+	 a full recursion into the imported unit, but just walk the
+	 children in place before moving to the next real child.  */
+      while (INTUSE(dwarf_tag) (&state->child.die) == DW_TAG_imported_unit)
+	{
+	  Dwarf_Die orig_child_die = state->child.die;
+	  Dwarf_Attribute attr_mem;
+	  Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&state->child.die,
+						      DW_AT_import,
+						      &attr_mem);
+	  if (INTUSE(dwarf_formref_die) (attr, &state->child.die) != NULL
+	      && INTUSE(dwarf_child) (&state->child.die, &state->child.die) == 0)
+	    {
+	      /* Checks the given DIE hasn't been imported yet
+	         to prevent cycles.  */
+	      bool imported = false;
+	      for (struct Dwarf_Die_Chain *import = state->imports; import != NULL;
+	        import = import->parent)
+	        if (import->die.addr == orig_child_die.addr)
+	          {
+	            imported = true;
+	            break;
+	          }
+	      if (imported)
+		{
+		  __libdw_seterrno (DWARF_E_INVALID_DWARF);
+		  return -1;
+		}
+	      struct Dwarf_Die_Chain *orig_imports = state->imports;
+	      struct Dwarf_Die_Chain import = { .die = orig_child_die,
+					        .parent = orig_imports };
+	      state->imports = &import;
+	      int result = walk_children (state);
+	      state->imports = orig_imports;
+	      if (result != DWARF_CB_OK)
+		return result;
+	    }
 
-	    /* Any "real" children left?  */
-	    if ((ret = INTUSE(dwarf_siblingof) (&orig_child_die,
-						&child.die)) != 0)
-	      return ret < 0 ? -1 : 0;
-	  };
+	  /* Any "real" children left?  */
+	  if ((ret = INTUSE(dwarf_siblingof) (&orig_child_die,
+					      &state->child.die)) != 0)
+	    return ret < 0 ? -1 : 0;
+	};
 
-	child.prune = false;
+	state->child.prune = false;
 
 	/* previsit is declared NN */
-	int result = (*previsit) (depth + 1, &child, arg);
+	int result = (*state->previsit) (state->depth + 1, &state->child, state->arg);
 	if (result != DWARF_CB_OK)
 	  return result;
 
-	if (!child.prune && may_have_scopes (&child.die)
-	    && INTUSE(dwarf_haschildren) (&child.die))
+	if (!state->child.prune && may_have_scopes (&state->child.die)
+	    && INTUSE(dwarf_haschildren) (&state->child.die))
 	  {
-	    result = recurse ();
+	    result = __libdw_visit_scopes (state->depth + 1, &state->child, state->imports,
+				           state->previsit, state->postvisit, state->arg);
 	    if (result != DWARF_CB_OK)
 	      return result;
 	  }
 
-	if (postvisit != NULL)
+	if (state->postvisit != NULL)
 	  {
-	    result = (*postvisit) (depth + 1, &child, arg);
+	    result = (*state->postvisit) (state->depth + 1, &state->child, state->arg);
 	    if (result != DWARF_CB_OK)
 	      return result;
 	  }
-      }
-    while ((ret = INTUSE(dwarf_siblingof) (&child.die, &child.die)) == 0);
+    }
+  while ((ret = INTUSE(dwarf_siblingof) (&state->child.die, &state->child.die)) == 0);
 
-    return ret < 0 ? -1 : 0;
-  }
-
-  return walk_children ();
+  return ret < 0 ? -1 : 0;
 }
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 163a6f1..06b8469 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,65 @@
+2015-11-18  Chih-Hung Hsieh <chh@google.com>
+
+	* linux-proc-maps.c (proc_maps_report): Move nested function
+	'report' to file scope.
+
+2015-11-18  Chih-Hung Hsieh <chh@google.com>
+
+	* core-file.c (elf_begin_rand): Move nested function 'fail' to file
+	scope.
+	* core-file.c (dwfl_elf_phdr_memory_callback): Move nested functions
+	'update_end' and 'more' to file scope.
+
+2015-11-17  Chih-Hung Hsieh <chh@google.com>
+
+	* link_map.c (auxv_format_probe): Move nested functions
+	check64 and check32 to file scope.
+
+2015-12-08  Jose E. Marchesi  <jose.marchesi@oracle.com>
+
+	* dwfl_frame.c (state_fetch_pc): Add a backend-defined offset to
+	the value of the return address register as defined by the CFI
+	abi.
+	* frame_unwind.c (handle_cfi): Likewise.
+
+2015-12-01  Mark Wielaard  <mjw@redhat.com>
+
+	* link_map.c (dwfl_link_map_report): Track whether in.d_buf comes
+	from exec or memory_callback, free as appropriate.
+
+2015-12-01  Mark Wielaard  <mjw@redhat.com>
+
+	* libdwflP.h (struct Dwfl_User_Core): New.
+	(struct DWfl): Replace executable_for_core with user_core.
+	* argp-std.c (parse_opt): Store core and fd in Dwfl user_core.
+	* core-file.c (dwfl_core_file_report): Check and store
+	executable_for_core in Dwfl user_core.
+	* dwfl_build_id_find_elf.c (dwfl_build_id_find_elf): Check and use
+	executable_for_core in Dwfl user_core.
+	* dwfl_end.c (dwfl_end): Release resources held in Dwfl user_core.
+	* link-map.c (report_r_debug): Check executable_for_core in Dwfl
+	user_core.
+	(dwfl_link_map_report): Likewise.
+
+2015-11-16  Chih-Hung Hsieh <chh@google.com>
+
+	* dwfl_module_getdwarf.c (find_prelink_address_sync): Move nested
+	function 'consider_shdr' to file scope.
+	* dwfl_module_getdwarf.c (find_dynsym): Move nested function
+	'translate_offs' to file scope.
+
+2015-11-16  Chih-Hung Hsieh <chh@google.com>
+
+	* dwfl_module_addrsym.c (__libdwfl_addrsym): Move nested functions
+	'same_section', 'binding_value', 'try_sym_value', and 'search_table'
+	to file scope.
+
+2015-11-19  Mark Wielaard  <mjw@redhat.com>
+
+	* dwfl_module.c (__libdwfl_module_free): Remove Dwfl_Module Ebl from
+	eh_cfi and dwarf_cfi cache if necessary before calling dwarf_end and
+	dwarf_cfi_end.
+
 2015-11-13  Chih-Hung Hsieh <chh@google.com>
 
 	* gzip.c (unzip): Move nested functions to file scope.
diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c
index 2bbf74f..501530a 100644
--- a/libdwfl/argp-std.c
+++ b/libdwfl/argp-std.c
@@ -1,5 +1,5 @@
 /* Standard argp argument parsers for tools using libdwfl.
-   Copyright (C) 2005-2010, 2012 Red Hat, Inc.
+   Copyright (C) 2005-2010, 2012, 2015 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -303,7 +303,19 @@
 	    /* Non-fatal to not be able to attach to core, ignore error.  */
 	    INTUSE(dwfl_core_file_attach) (dwfl, core);
 
-	    /* From now we leak FD and CORE.  */
+	    /* Store core Elf and fd in Dwfl to expose with dwfl_end.  */
+	    if (dwfl->user_core == NULL)
+	      {
+		dwfl->user_core = calloc (1, sizeof (struct Dwfl_User_Core));
+		if (dwfl->user_core == NULL)
+		  {
+		    argp_failure (state, EXIT_FAILURE, 0,
+				  _("Not enough memory"));
+		    return ENOMEM;
+		  }
+	      }
+	    dwfl->user_core->core = core;
+	    dwfl->user_core->fd = fd;
 
 	    if (result == 0)
 	      {
diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
index b317eca..67684c9 100644
--- a/libdwfl/core-file.c
+++ b/libdwfl/core-file.c
@@ -39,6 +39,19 @@
 #include "system.h"
 
 
+/* On failure return, we update *NEXT to point back at OFFSET.  */
+static inline Elf *
+do_fail (int error, off_t *next, off_t offset)
+{
+    if (next != NULL)
+      *next = offset;
+    //__libelf_seterrno (error);
+    __libdwfl_seterrno (DWFL_E (LIBELF, error));
+    return NULL;
+}
+
+#define fail(error) do_fail (error, next, offset)
+
 /* This is a prototype of what a new libelf interface might be.
    This implementation is pessimal for non-mmap cases and should
    be replaced by more diddling inside libelf internals.  */
@@ -48,16 +61,6 @@
   if (parent == NULL)
     return NULL;
 
-  /* On failure return, we update *NEXT to point back at OFFSET.  */
-  inline Elf *fail (int error)
-  {
-    if (next != NULL)
-      *next = offset;
-    //__libelf_seterrno (error);
-    __libdwfl_seterrno (DWFL_E (LIBELF, error));
-    return NULL;
-  }
-
   off_t min = (parent->kind == ELF_K_ELF ?
 		(parent->class == ELFCLASS32
 		 ? sizeof (Elf32_Ehdr) : sizeof (Elf64_Ehdr))
@@ -238,6 +241,44 @@
   return cost <= MAX_EAGER_COST;
 }
 
+static inline void
+update_end (GElf_Phdr *pphdr, const GElf_Off align,
+            GElf_Off *pend, GElf_Addr *pend_vaddr)
+{
+  *pend = (pphdr->p_offset + pphdr->p_filesz + align - 1) & -align;
+  *pend_vaddr = (pphdr->p_vaddr + pphdr->p_memsz + align - 1) & -align;
+}
+
+/* Use following contiguous segments to get towards SIZE.  */
+static inline bool
+do_more (size_t size, GElf_Phdr *pphdr, const GElf_Off align,
+         Elf *elf, GElf_Off start, int *pndx,
+         GElf_Off *pend, GElf_Addr *pend_vaddr)
+{
+  while (*pend <= start || *pend - start < size)
+    {
+      if (pphdr->p_filesz < pphdr->p_memsz)
+	/* This segment is truncated, so no following one helps us.  */
+	return false;
+
+      if (unlikely (gelf_getphdr (elf, (*pndx)++, pphdr) == NULL))
+	return false;
+
+      if (pphdr->p_type == PT_LOAD)
+	{
+	  if (pphdr->p_offset > *pend
+	      || pphdr->p_vaddr > *pend_vaddr)
+	    /* It's discontiguous!  */
+	    return false;
+
+	  update_end (pphdr, align, pend, pend_vaddr);
+	}
+    }
+  return true;
+}
+
+#define more(size) do_more (size, &phdr, align, elf, start, &ndx, &end, &end_vaddr)
+
 bool
 dwfl_elf_phdr_memory_callback (Dwfl *dwfl, int ndx,
 			       void **buffer, size_t *buffer_available,
@@ -270,38 +311,7 @@
   GElf_Off end;
   GElf_Addr end_vaddr;
 
-  inline void update_end (void)
-{
-    end = (phdr.p_offset + phdr.p_filesz + align - 1) & -align;
-    end_vaddr = (phdr.p_vaddr + phdr.p_memsz + align - 1) & -align;
-  }
-
-  update_end ();
-
-  /* Use following contiguous segments to get towards SIZE.  */
-  inline bool more (size_t size)
-  {
-    while (end <= start || end - start < size)
-      {
-	if (phdr.p_filesz < phdr.p_memsz)
-	  /* This segment is truncated, so no following one helps us.  */
-	  return false;
-
-	if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL))
-	  return false;
-
-	if (phdr.p_type == PT_LOAD)
-	  {
-	    if (phdr.p_offset > end
-		|| phdr.p_vaddr > end_vaddr)
-	      /* It's discontiguous!  */
-	      return false;
-
-	    update_end ();
-	  }
-      }
-    return true;
-  }
+  update_end (&phdr, align, &end, &end_vaddr);
 
   /* We need at least this much.  */
   if (! more (minread))
@@ -441,13 +451,27 @@
       return -1;
     }
 
-  free (dwfl->executable_for_core);
+  if (dwfl->user_core != NULL)
+    free (dwfl->user_core->executable_for_core);
   if (executable == NULL)
-    dwfl->executable_for_core = NULL;
+    {
+      if (dwfl->user_core != NULL)
+	dwfl->user_core->executable_for_core = NULL;
+    }
   else
     {
-      dwfl->executable_for_core = strdup (executable);
-      if (dwfl->executable_for_core == NULL)
+      if (dwfl->user_core == NULL)
+	{
+	  dwfl->user_core = calloc (1, sizeof (struct Dwfl_User_Core));
+	  if (dwfl->user_core == NULL)
+	    {
+	      __libdwfl_seterrno (DWFL_E_NOMEM);
+	      return -1;
+	    }
+	  dwfl->user_core->fd = -1;
+	}
+      dwfl->user_core->executable_for_core = strdup (executable);
+      if (dwfl->user_core->executable_for_core == NULL)
 	{
 	  __libdwfl_seterrno (DWFL_E_NOMEM);
 	  return -1;
diff --git a/libdwfl/dwfl_build_id_find_elf.c b/libdwfl/dwfl_build_id_find_elf.c
index 2e30b7a..903e193 100644
--- a/libdwfl/dwfl_build_id_find_elf.c
+++ b/libdwfl/dwfl_build_id_find_elf.c
@@ -140,16 +140,19 @@
 			char **file_name, Elf **elfp)
 {
   *elfp = NULL;
-  if (mod->is_executable && mod->dwfl->executable_for_core != NULL)
+  if (mod->is_executable
+      && mod->dwfl->user_core != NULL
+      && mod->dwfl->user_core->executable_for_core != NULL)
     {
       /* When dwfl_core_file_report was called with a non-NULL executable file
 	 name this callback will replace the Dwfl_Module main.name with the
 	 recorded executable file when MOD was identified as main executable
 	 (which then triggers opening and reporting of the executable).  */
-      int fd = open (mod->dwfl->executable_for_core, O_RDONLY);
+      const char *executable = mod->dwfl->user_core->executable_for_core;
+      int fd = open (executable, O_RDONLY);
       if (fd >= 0)
 	{
-	  *file_name = strdup (mod->dwfl->executable_for_core);
+	  *file_name = strdup (executable);
 	  if (*file_name != NULL)
 	    return fd;
 	  else
diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c
index 33cae48..0b35bd2 100644
--- a/libdwfl/dwfl_end.c
+++ b/libdwfl/dwfl_end.c
@@ -1,5 +1,5 @@
 /* Finish a session using libdwfl.
-   Copyright (C) 2005, 2008, 2012-2013 Red Hat, Inc.
+   Copyright (C) 2005, 2008, 2012-2013, 2015 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -27,6 +27,7 @@
    not, see <http://www.gnu.org/licenses/>.  */
 
 #include "libdwflP.h"
+#include <unistd.h>
 
 void
 dwfl_end (Dwfl *dwfl)
@@ -49,6 +50,13 @@
       __libdwfl_module_free (dead);
     }
 
-  free (dwfl->executable_for_core);
+  if (dwfl->user_core != NULL)
+    {
+      free (dwfl->user_core->executable_for_core);
+      elf_end (dwfl->user_core->core);
+      if (dwfl->user_core->fd != -1)
+	close (dwfl->user_core->fd);
+      free (dwfl->user_core);
+    }
   free (dwfl);
 }
diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c
index a91a1d6..d639939 100644
--- a/libdwfl/dwfl_frame.c
+++ b/libdwfl/dwfl_frame.c
@@ -57,7 +57,7 @@
 	    __libdwfl_seterrno (DWFL_E_LIBEBL_BAD);
 	    return false;
 	  }
-	state->pc = state->regs[ra];
+	state->pc = state->regs[ra] + ebl_ra_offset (ebl);
 	state->pc_state = DWFL_FRAME_STATE_PC_SET;
       }
       return true;
diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c
index 76d45a8..515092f 100644
--- a/libdwfl/dwfl_module.c
+++ b/libdwfl/dwfl_module.c
@@ -1,5 +1,5 @@
 /* Maintenance of module list in libdwfl.
-   Copyright (C) 2005, 2006, 2007, 2008, 2014 Red Hat, Inc.
+   Copyright (C) 2005, 2006, 2007, 2008, 2014, 2015 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -27,6 +27,7 @@
    not, see <http://www.gnu.org/licenses/>.  */
 
 #include "libdwflP.h"
+#include "../libdw/cfi.h"
 #include <search.h>
 #include <unistd.h>
 
@@ -70,6 +71,23 @@
       free (mod->cu);
     }
 
+  /* We might have primed the Dwarf_CFI ebl cache with our own ebl
+     in __libdwfl_set_cfi. Make sure we don't free it twice.  */
+  if (mod->eh_cfi != NULL)
+    {
+      if (mod->eh_cfi->ebl != NULL && mod->eh_cfi->ebl == mod->ebl)
+	mod->eh_cfi->ebl = NULL;
+      dwarf_cfi_end (mod->eh_cfi);
+    }
+
+  if (mod->dwarf_cfi != NULL)
+    {
+      if (mod->dwarf_cfi->ebl != NULL && mod->dwarf_cfi->ebl == mod->ebl)
+	mod->dwarf_cfi->ebl = NULL;
+      /* We don't need to explicitly destroy the dwarf_cfi.
+	 That will be done by dwarf_end.  */
+    }
+
   if (mod->dw != NULL)
     {
       INTUSE(dwarf_end) (mod->dw);
@@ -97,9 +115,6 @@
   if (mod->reloc_info != NULL)
     free (mod->reloc_info);
 
-  if (mod->eh_cfi != NULL)
-    dwarf_cfi_end (mod->eh_cfi);
-
   free (mod->name);
   free (mod);
 }
diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c
index d205832..5a7bf71 100644
--- a/libdwfl/dwfl_module_addrsym.c
+++ b/libdwfl/dwfl_module_addrsym.c
@@ -28,141 +28,144 @@
 
 #include "libdwflP.h"
 
-/* Returns the name of the symbol "closest" to ADDR.
-   Never returns symbols at addresses above ADDR.  */
-
-const char *
-internal_function
-__libdwfl_addrsym (Dwfl_Module *mod, GElf_Addr addr, GElf_Off *off,
-		   GElf_Sym *closest_sym, GElf_Word *shndxp,
-		   Elf **elfp, Dwarf_Addr *biasp, bool adjust_st_value)
+struct search_state
 {
-  int syments = INTUSE(dwfl_module_getsymtab) (mod);
-  if (syments < 0)
-    return NULL;
+  Dwfl_Module *mod;
+  GElf_Addr addr;
 
-  /* Return true iff we consider ADDR to lie in the same section as SYM.  */
-  GElf_Word addr_shndx = SHN_UNDEF;
-  Elf *addr_symelf = NULL;
-  inline bool same_section (GElf_Addr value, Elf *symelf, GElf_Word shndx)
-    {
-      /* For absolute symbols and the like, only match exactly.  */
-      if (shndx >= SHN_LORESERVE)
-	return value == addr;
-
-      /* If value might not be st_value, the shndx of the symbol might
-	 not match the section of the value. Explicitly look both up.  */
-      if (! adjust_st_value)
-	{
-	  Dwarf_Addr v;
-	  if (addr_shndx == SHN_UNDEF)
-	    {
-	      v = addr;
-	      addr_shndx = __libdwfl_find_section_ndx (mod, &v);
-	    }
-
-	  v = value;
-	  return addr_shndx == __libdwfl_find_section_ndx (mod, &v);
-	}
-
-      /* Figure out what section ADDR lies in.  */
-      if (addr_shndx == SHN_UNDEF || addr_symelf != symelf)
-	{
-	  GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, symelf, addr);
-	  Elf_Scn *scn = NULL;
-	  addr_shndx = SHN_ABS;
-	  addr_symelf = symelf;
-	  while ((scn = elf_nextscn (symelf, scn)) != NULL)
-	    {
-	      GElf_Shdr shdr_mem;
-	      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
-	      if (likely (shdr != NULL)
-		  && mod_addr >= shdr->sh_addr
-		  && mod_addr < shdr->sh_addr + shdr->sh_size)
-		{
-		  addr_shndx = elf_ndxscn (scn);
-		  break;
-		}
-	    }
-	}
-
-      return shndx == addr_shndx && addr_symelf == symelf;
-    }
+  GElf_Sym *closest_sym;
+  bool adjust_st_value;
+  GElf_Word addr_shndx;
+  Elf *addr_symelf;
 
   /* Keep track of the closest symbol we have seen so far.
      Here we store only symbols with nonzero st_size.  */
-  const char *closest_name = NULL;
-  GElf_Addr closest_value = 0;
-  GElf_Word closest_shndx = SHN_UNDEF;
-  Elf *closest_elf = NULL;
+  const char *closest_name;
+  GElf_Addr closest_value;
+  GElf_Word closest_shndx;
+  Elf *closest_elf;
 
   /* Keep track of an eligible symbol with st_size == 0 as a fallback.  */
-  const char *sizeless_name = NULL;
-  GElf_Sym sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF };
-  GElf_Addr sizeless_value = 0;
-  GElf_Word sizeless_shndx = SHN_UNDEF;
-  Elf *sizeless_elf = NULL;
+  const char *sizeless_name;
+  GElf_Sym sizeless_sym;
+  GElf_Addr sizeless_value;
+  GElf_Word sizeless_shndx;
+  Elf *sizeless_elf;
 
   /* Keep track of the lowest address a relevant sizeless symbol could have.  */
-  GElf_Addr min_label = 0;
+  GElf_Addr min_label;
+};
 
-  /* Try one symbol and associated value from the search table.  */
-  inline void try_sym_value (GElf_Addr value, GElf_Sym *sym,
-			     const char *name, GElf_Word shndx,
-			     Elf *elf, bool resolved)
-  {
+/* Return true iff we consider ADDR to lie in the same section as SYM.  */
+static inline bool
+same_section (struct search_state *state,
+	      GElf_Addr value, Elf *symelf, GElf_Word shndx)
+{
+  /* For absolute symbols and the like, only match exactly.  */
+  if (shndx >= SHN_LORESERVE)
+    return value == state->addr;
+
+  /* If value might not be st_value, the shndx of the symbol might
+      not match the section of the value. Explicitly look both up.  */
+  if (! state->adjust_st_value)
+    {
+      Dwarf_Addr v;
+      if (state->addr_shndx == SHN_UNDEF)
+        {
+          v = state->addr;
+          state->addr_shndx = __libdwfl_find_section_ndx (state->mod, &v);
+        }
+
+      v = value;
+      return state->addr_shndx == __libdwfl_find_section_ndx (state->mod, &v);
+    }
+
+  /* Figure out what section ADDR lies in.  */
+  if (state->addr_shndx == SHN_UNDEF || state->addr_symelf != symelf)
+    {
+      GElf_Addr mod_addr = dwfl_deadjust_st_value (state->mod, symelf,
+						   state->addr);
+      Elf_Scn *scn = NULL;
+      state->addr_shndx = SHN_ABS;
+      state->addr_symelf = symelf;
+      while ((scn = elf_nextscn (symelf, scn)) != NULL)
+        {
+          GElf_Shdr shdr_mem;
+          GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+          if (likely (shdr != NULL)
+              && mod_addr >= shdr->sh_addr
+              && mod_addr < shdr->sh_addr + shdr->sh_size)
+            {
+              state->addr_shndx = elf_ndxscn (scn);
+              break;
+            }
+        }
+    }
+
+  return shndx == state->addr_shndx && state->addr_symelf == symelf;
+}
+
+/* Return GELF_ST_BIND as higher-is-better integer.  */
+static inline int
+binding_value (const GElf_Sym *symp)
+{
+  switch (GELF_ST_BIND (symp->st_info))
+    {
+    case STB_GLOBAL:
+      return 3;
+    case STB_WEAK:
+      return 2;
+    case STB_LOCAL:
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+/* Try one symbol and associated value from the search table.  */
+static inline void
+try_sym_value (struct search_state *state,
+               GElf_Addr value, GElf_Sym *sym,
+               const char *name, GElf_Word shndx,
+               Elf *elf, bool resolved)
+{
     /* Even if we don't choose this symbol, its existence excludes
        any sizeless symbol (assembly label) that is below its upper
        bound.  */
-    if (value + sym->st_size > min_label)
-      min_label = value + sym->st_size;
+    if (value + sym->st_size > state->min_label)
+      state->min_label = value + sym->st_size;
 
-    if (sym->st_size == 0 || addr - value < sym->st_size)
+    if (sym->st_size == 0 || state->addr - value < sym->st_size)
       {
-	/* Return GELF_ST_BIND as higher-is-better integer.  */
-	inline int binding_value (const GElf_Sym *symp)
-	{
-	  switch (GELF_ST_BIND (symp->st_info))
-	    {
-	    case STB_GLOBAL:
-	      return 3;
-	    case STB_WEAK:
-	      return 2;
-	    case STB_LOCAL:
-	      return 1;
-	    default:
-	      return 0;
-	    }
-	}
-
 	/* This symbol is a better candidate than the current one
 	   if it's closer to ADDR or is global when it was local.  */
-	if (closest_name == NULL
-	    || closest_value < value
-	    || binding_value (closest_sym) < binding_value (sym))
+	if (state->closest_name == NULL
+	    || state->closest_value < value
+	    || binding_value (state->closest_sym) < binding_value (sym))
 	  {
 	    if (sym->st_size != 0)
 	      {
-		*closest_sym = *sym;
-		closest_value = value;
-		closest_shndx = shndx;
-		closest_elf = elf;
-		closest_name = name;
+		*state->closest_sym = *sym;
+		state->closest_value = value;
+		state->closest_shndx = shndx;
+		state->closest_elf = elf;
+		state->closest_name = name;
 	      }
-	    else if (closest_name == NULL
-		     && value >= min_label
-		     && same_section (value,
-				      resolved ? mod->main.elf : elf, shndx))
+	    else if (state->closest_name == NULL
+		     && value >= state->min_label
+		     && same_section (state, value,
+				      resolved ? state->mod->main.elf : elf,
+				      shndx))
 	      {
 		/* Handwritten assembly symbols sometimes have no
 		   st_size.  If no symbol with proper size includes
 		   the address, we'll use the closest one that is in
 		   the same section as ADDR.  */
-		sizeless_sym = *sym;
-		sizeless_value = value;
-		sizeless_shndx = shndx;
-		sizeless_elf = elf;
-		sizeless_name = name;
+		state->sizeless_sym = *sym;
+		state->sizeless_value = value;
+		state->sizeless_shndx = shndx;
+		state->sizeless_elf = elf;
+		state->sizeless_name = name;
 	      }
 	  }
 	/* When the beginning of its range is no closer,
@@ -170,26 +173,27 @@
 	   GELF_ST_BIND preference.  If all are equal prefer
 	   the first symbol found.  */
 	else if (sym->st_size != 0
-		 && closest_value == value
-		 && ((closest_sym->st_size > sym->st_size
-		      && (binding_value (closest_sym)
+		 && state->closest_value == value
+		 && ((state->closest_sym->st_size > sym->st_size
+		      && (binding_value (state->closest_sym)
 			  <= binding_value (sym)))
-		     || (closest_sym->st_size >= sym->st_size
-			 && (binding_value (closest_sym)
+		     || (state->closest_sym->st_size >= sym->st_size
+			 && (binding_value (state->closest_sym)
 			     < binding_value (sym)))))
 	  {
-	    *closest_sym = *sym;
-	    closest_value = value;
-	    closest_shndx = shndx;
-	    closest_elf = elf;
-	    closest_name = name;
+	    *state->closest_sym = *sym;
+	    state->closest_value = value;
+	    state->closest_shndx = shndx;
+	    state->closest_elf = elf;
+	    state->closest_name = name;
 	  }
       }
-  }
+}
 
-  /* Look through the symbol table for a matching symbol.  */
-  inline void search_table (int start, int end)
-    {
+/* Look through the symbol table for a matching symbol.  */
+static inline void
+search_table (struct search_state *state, int start, int end)
+{
       for (int i = start; i < end; ++i)
 	{
 	  GElf_Sym sym;
@@ -197,32 +201,66 @@
 	  GElf_Word shndx;
 	  Elf *elf;
 	  bool resolved;
-	  const char *name = __libdwfl_getsym (mod, i, &sym, &value,
+	  const char *name = __libdwfl_getsym (state->mod, i, &sym, &value,
 					       &shndx, &elf, NULL,
-					       &resolved, adjust_st_value);
+					       &resolved,
+					       state->adjust_st_value);
 	  if (name != NULL && name[0] != '\0'
 	      && sym.st_shndx != SHN_UNDEF
-	      && value <= addr
+	      && value <= state->addr
 	      && GELF_ST_TYPE (sym.st_info) != STT_SECTION
 	      && GELF_ST_TYPE (sym.st_info) != STT_FILE
 	      && GELF_ST_TYPE (sym.st_info) != STT_TLS)
 	    {
-	      try_sym_value (value, &sym, name, shndx, elf, resolved);
+	      try_sym_value (state, value, &sym, name, shndx, elf, resolved);
 
 	      /* If this is an addrinfo variant and the value could be
 		 resolved then also try matching the (adjusted) st_value.  */
-	      if (resolved && mod->e_type != ET_REL)
+	      if (resolved && state->mod->e_type != ET_REL)
 		{
 		  GElf_Addr adjusted_st_value;
-		  adjusted_st_value = dwfl_adjusted_st_value (mod, elf,
+		  adjusted_st_value = dwfl_adjusted_st_value (state->mod, elf,
 							      sym.st_value);
-		  if (value != adjusted_st_value && adjusted_st_value <= addr)
-		    try_sym_value (adjusted_st_value, &sym, name, shndx,
+		  if (value != adjusted_st_value
+		      && adjusted_st_value <= state->addr)
+		    try_sym_value (state, adjusted_st_value, &sym, name, shndx,
 				   elf, false);
 		}
 	    }
 	}
-    }
+}
+
+/* Returns the name of the symbol "closest" to ADDR.
+   Never returns symbols at addresses above ADDR.  */
+const char *
+internal_function
+__libdwfl_addrsym (Dwfl_Module *_mod, GElf_Addr _addr, GElf_Off *off,
+		   GElf_Sym *_closest_sym, GElf_Word *shndxp,
+		   Elf **elfp, Dwarf_Addr *biasp, bool _adjust_st_value)
+{
+  int syments = INTUSE(dwfl_module_getsymtab) (_mod);
+  if (syments < 0)
+    return NULL;
+
+  struct search_state state =
+    {
+      .addr = _addr,
+      .mod = _mod,
+      .closest_sym = _closest_sym,
+      .adjust_st_value = _adjust_st_value,
+      .addr_shndx = SHN_UNDEF,
+      .addr_symelf = NULL,
+      .closest_name = NULL,
+      .closest_value = 0,
+      .closest_shndx = SHN_UNDEF,
+      .closest_elf = NULL,
+      .sizeless_name = NULL,
+      .sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF },
+      .sizeless_value = 0,
+      .sizeless_shndx = SHN_UNDEF,
+      .sizeless_elf = NULL,
+      .min_label = 0
+    };
 
   /* First go through global symbols.  mod->first_global and
      mod->aux_first_global are setup by dwfl_module_getsymtab to the
@@ -233,38 +271,39 @@
      come first in the symbol table, then all globals.  The zeroth,
      null entry, in the auxiliary table is skipped if there is a main
      table.  */
-  int first_global = INTUSE (dwfl_module_getsymtab_first_global) (mod);
+  int first_global = INTUSE (dwfl_module_getsymtab_first_global) (state.mod);
   if (first_global < 0)
     return NULL;
-  search_table (first_global == 0 ? 1 : first_global, syments);
+  search_table (&state, first_global == 0 ? 1 : first_global, syments);
 
   /* If we found nothing searching the global symbols, then try the locals.
      Unless we have a global sizeless symbol that matches exactly.  */
-  if (closest_name == NULL && first_global > 1
-      && (sizeless_name == NULL || sizeless_value != addr))
-    search_table (1, first_global);
+  if (state.closest_name == NULL && first_global > 1
+      && (state.sizeless_name == NULL || state.sizeless_value != state.addr))
+    search_table (&state, 1, first_global);
 
   /* If we found no proper sized symbol to use, fall back to the best
      candidate sizeless symbol we found, if any.  */
-  if (closest_name == NULL
-      && sizeless_name != NULL && sizeless_value >= min_label)
+  if (state.closest_name == NULL
+      && state.sizeless_name != NULL
+      && state.sizeless_value >= state.min_label)
     {
-      *closest_sym = sizeless_sym;
-      closest_value = sizeless_value;
-      closest_shndx = sizeless_shndx;
-      closest_elf = sizeless_elf;
-      closest_name = sizeless_name;
+      *state.closest_sym = state.sizeless_sym;
+      state.closest_value = state.sizeless_value;
+      state.closest_shndx = state.sizeless_shndx;
+      state.closest_elf = state.sizeless_elf;
+      state.closest_name = state.sizeless_name;
     }
 
-  *off = addr - closest_value;
+  *off = state.addr - state.closest_value;
 
   if (shndxp != NULL)
-    *shndxp = closest_shndx;
+    *shndxp = state.closest_shndx;
   if (elfp != NULL)
-    *elfp = closest_elf;
+    *elfp = state.closest_elf;
   if (biasp != NULL)
-    *biasp = dwfl_adjusted_st_value (mod, closest_elf, 0);
-  return closest_name;
+    *biasp = dwfl_adjusted_st_value (state.mod, state.closest_elf, 0);
+  return state.closest_name;
 }
 
 
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index 8483fa2..e9589b3 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -231,6 +231,24 @@
   mod->main_bias = mod->e_type == ET_REL ? 0 : mod->low_addr - mod->main.vaddr;
 }
 
+static inline void
+consider_shdr (GElf_Addr interp,
+               GElf_Word sh_type,
+               GElf_Xword sh_flags,
+               GElf_Addr sh_addr,
+               GElf_Xword sh_size,
+               GElf_Addr *phighest)
+{
+  if ((sh_flags & SHF_ALLOC)
+      && ((sh_type == SHT_PROGBITS && sh_addr != interp)
+          || sh_type == SHT_NOBITS))
+    {
+      const GElf_Addr sh_end = sh_addr + sh_size;
+      if (sh_end > *phighest)
+        *phighest = sh_end;
+    }
+}
+
 /* If the main file might have been prelinked, then we need to
    discover the correct synchronization address between the main and
    debug files.  Because of prelink's section juggling, we cannot rely
@@ -448,22 +466,6 @@
 
   GElf_Addr highest;
 
-  inline void consider_shdr (GElf_Addr interp,
-			     GElf_Word sh_type,
-			     GElf_Xword sh_flags,
-			     GElf_Addr sh_addr,
-			     GElf_Xword sh_size)
-  {
-    if ((sh_flags & SHF_ALLOC)
-	&& ((sh_type == SHT_PROGBITS && sh_addr != interp)
-	    || sh_type == SHT_NOBITS))
-      {
-	const GElf_Addr sh_end = sh_addr + sh_size;
-	if (sh_end > highest)
-	  highest = sh_end;
-      }
-  }
-
   highest = 0;
   scn = NULL;
   while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
@@ -476,7 +478,7 @@
 	  return DWFL_E_LIBELF;
 	}
       consider_shdr (main_interp, sh->sh_type, sh->sh_flags,
-		     sh->sh_addr, sh->sh_size);
+		     sh->sh_addr, sh->sh_size, &highest);
     }
   if (highest > mod->main.vaddr)
     {
@@ -489,7 +491,7 @@
 	  for (size_t i = 0; i < shnum - 1; ++i)
 	    consider_shdr (undo_interp, (*s32)[i].sh_type,
 			   (*s32)[i].sh_flags, (*s32)[i].sh_addr,
-			   (*s32)[i].sh_size);
+			   (*s32)[i].sh_size, &highest);
 	}
       else
 	{
@@ -497,7 +499,7 @@
 	  for (size_t i = 0; i < shnum - 1; ++i)
 	    consider_shdr (undo_interp, (*s64)[i].sh_type,
 			   (*s64)[i].sh_flags, (*s64)[i].sh_addr,
-			   (*s64)[i].sh_size);
+			   (*s64)[i].sh_size, &highest);
 	}
 
       if (highest > file->vaddr)
@@ -676,6 +678,141 @@
     }
 }
 
+/* Various addresses we might want to pull from the dynamic segment.  */
+enum
+{
+  i_symtab,
+  i_strtab,
+  i_hash,
+  i_gnu_hash,
+  i_max
+};
+
+/* Translate pointers into file offsets.  ADJUST is either zero
+   in case the dynamic segment wasn't adjusted or mod->main_bias.
+   Will set mod->symfile if the translated offsets can be used as
+   symbol table.  */
+static void
+translate_offs (GElf_Addr adjust,
+                Dwfl_Module *mod, size_t phnum,
+                GElf_Addr addrs[i_max], GElf_Xword strsz,
+                GElf_Ehdr *ehdr)
+{
+  GElf_Off offs[i_max] = { 0, };
+  find_offsets (mod->main.elf, adjust, phnum, i_max, addrs, offs);
+
+  /* Figure out the size of the symbol table.  */
+  if (offs[i_hash] != 0)
+    {
+      /* In the original format, .hash says the size of .dynsym.  */
+
+      size_t entsz = SH_ENTSIZE_HASH (ehdr);
+      Elf_Data *data = elf_getdata_rawchunk (mod->main.elf,
+					     offs[i_hash] + entsz, entsz,
+					     (entsz == 4
+					      ? ELF_T_WORD : ELF_T_XWORD));
+      if (data != NULL)
+	mod->syments = (entsz == 4
+			? *(const GElf_Word *) data->d_buf
+			: *(const GElf_Xword *) data->d_buf);
+    }
+  if (offs[i_gnu_hash] != 0 && mod->syments == 0)
+    {
+      /* In the new format, we can derive it with some work.  */
+
+      const struct
+      {
+        Elf32_Word nbuckets;
+        Elf32_Word symndx;
+        Elf32_Word maskwords;
+        Elf32_Word shift2;
+      } *header;
+
+      Elf_Data *data = elf_getdata_rawchunk (mod->main.elf, offs[i_gnu_hash],
+					     sizeof *header, ELF_T_WORD);
+      if (data != NULL)
+        {
+          header = data->d_buf;
+          Elf32_Word nbuckets = header->nbuckets;
+          Elf32_Word symndx = header->symndx;
+          GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header
+				 + (gelf_getclass (mod->main.elf)
+				    * sizeof (Elf32_Word)
+				    * header->maskwords));
+
+          // elf_getdata_rawchunk takes a size_t, make sure it
+          // doesn't overflow.
+#if SIZE_MAX <= UINT32_MAX
+          if (nbuckets > SIZE_MAX / sizeof (Elf32_Word))
+            data = NULL;
+          else
+#endif
+            data = elf_getdata_rawchunk (mod->main.elf, buckets_at,
+					   nbuckets * sizeof (Elf32_Word),
+					   ELF_T_WORD);
+	  if (data != NULL && symndx < nbuckets)
+	    {
+	      const Elf32_Word *const buckets = data->d_buf;
+	      Elf32_Word maxndx = symndx;
+	      for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket)
+		if (buckets[bucket] > maxndx)
+		  maxndx = buckets[bucket];
+
+	      GElf_Off hasharr_at = (buckets_at
+				     + nbuckets * sizeof (Elf32_Word));
+	      hasharr_at += (maxndx - symndx) * sizeof (Elf32_Word);
+	      do
+		{
+		  data = elf_getdata_rawchunk (mod->main.elf,
+					       hasharr_at,
+					       sizeof (Elf32_Word),
+					       ELF_T_WORD);
+		  if (data != NULL
+		      && (*(const Elf32_Word *) data->d_buf & 1u))
+		    {
+		      mod->syments = maxndx + 1;
+		      break;
+		    }
+		  ++maxndx;
+		  hasharr_at += sizeof (Elf32_Word);
+		}
+	      while (data != NULL);
+	    }
+	}
+    }
+  if (offs[i_strtab] > offs[i_symtab] && mod->syments == 0)
+    mod->syments = ((offs[i_strtab] - offs[i_symtab])
+		    / gelf_fsize (mod->main.elf,
+				  ELF_T_SYM, 1, EV_CURRENT));
+
+  if (mod->syments > 0)
+    {
+      mod->symdata = elf_getdata_rawchunk (mod->main.elf,
+					   offs[i_symtab],
+					   gelf_fsize (mod->main.elf,
+						       ELF_T_SYM,
+						       mod->syments,
+						       EV_CURRENT),
+						       ELF_T_SYM);
+      if (mod->symdata != NULL)
+	{
+	  mod->symstrdata = elf_getdata_rawchunk (mod->main.elf,
+						  offs[i_strtab],
+						  strsz,
+						  ELF_T_BYTE);
+	  if (mod->symstrdata == NULL)
+	    mod->symdata = NULL;
+	}
+      if (mod->symdata == NULL)
+	mod->symerr = DWFL_E (LIBELF, elf_errno ());
+      else
+	{
+	  mod->symfile = &mod->main;
+	  mod->symerr = DWFL_E_NOERROR;
+	}
+    }
+}
+
 /* Try to find a dynamic symbol table via phdrs.  */
 static void
 find_dynsym (Dwfl_Module *mod)
@@ -704,14 +841,6 @@
 	  if (data == NULL)
 	    continue;
 
-	  enum
-	    {
-	      i_symtab,
-	      i_strtab,
-	      i_hash,
-	      i_gnu_hash,
-	      i_max
-	    };
 	  GElf_Addr addrs[i_max] = { 0, };
 	  GElf_Xword strsz = 0;
 	  size_t n = data->d_size / gelf_fsize (mod->main.elf,
@@ -752,131 +881,12 @@
 	      break;
 	    }
 
-	  /* Translate pointers into file offsets.  ADJUST is either zero
-	     in case the dynamic segment wasn't adjusted or mod->main_bias.  */
-	  void translate_offs (GElf_Addr adjust)
-	  {
-	    GElf_Off offs[i_max] = { 0, };
-	    find_offsets (mod->main.elf, adjust, phnum, i_max, addrs, offs);
-
-	    /* Figure out the size of the symbol table.  */
-	    if (offs[i_hash] != 0)
-	      {
-		/* In the original format, .hash says the size of .dynsym.  */
-
-		size_t entsz = SH_ENTSIZE_HASH (ehdr);
-		data = elf_getdata_rawchunk (mod->main.elf,
-					     offs[i_hash] + entsz, entsz,
-					     entsz == 4 ? ELF_T_WORD
-					     : ELF_T_XWORD);
-		if (data != NULL)
-		  mod->syments = (entsz == 4
-				  ? *(const GElf_Word *) data->d_buf
-				  : *(const GElf_Xword *) data->d_buf);
-	      }
-	    if (offs[i_gnu_hash] != 0 && mod->syments == 0)
-	      {
-		/* In the new format, we can derive it with some work.  */
-
-		const struct
-		{
-		  Elf32_Word nbuckets;
-		  Elf32_Word symndx;
-		  Elf32_Word maskwords;
-		  Elf32_Word shift2;
-		} *header;
-
-		data = elf_getdata_rawchunk (mod->main.elf, offs[i_gnu_hash],
-					     sizeof *header, ELF_T_WORD);
-		if (data != NULL)
-		  {
-		    header = data->d_buf;
-		    Elf32_Word nbuckets = header->nbuckets;
-		    Elf32_Word symndx = header->symndx;
-		    GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header
-					   + (gelf_getclass (mod->main.elf)
-					      * sizeof (Elf32_Word)
-					      * header->maskwords));
-
-		    // elf_getdata_rawchunk takes a size_t, make sure it
-		    // doesn't overflow.
-#if SIZE_MAX <= UINT32_MAX
-		    if (nbuckets > SIZE_MAX / sizeof (Elf32_Word))
-		      data = NULL;
-		    else
-#endif
-		      data
-			 = elf_getdata_rawchunk (mod->main.elf, buckets_at,
-						 nbuckets * sizeof (Elf32_Word),
-						 ELF_T_WORD);
-		    if (data != NULL && symndx < nbuckets)
-		      {
-			const Elf32_Word *const buckets = data->d_buf;
-			Elf32_Word maxndx = symndx;
-			for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket)
-			  if (buckets[bucket] > maxndx)
-			    maxndx = buckets[bucket];
-
-			GElf_Off hasharr_at = (buckets_at
-					       + nbuckets * sizeof (Elf32_Word));
-			hasharr_at += (maxndx - symndx) * sizeof (Elf32_Word);
-			do
-			  {
-			    data = elf_getdata_rawchunk (mod->main.elf,
-							 hasharr_at,
-							 sizeof (Elf32_Word),
-							 ELF_T_WORD);
-			    if (data != NULL
-				&& (*(const Elf32_Word *) data->d_buf & 1u))
-			      {
-				mod->syments = maxndx + 1;
-				break;
-			      }
-			    ++maxndx;
-			    hasharr_at += sizeof (Elf32_Word);
-			  } while (data != NULL);
-		      }
-		  }
-	      }
-	    if (offs[i_strtab] > offs[i_symtab] && mod->syments == 0)
-	      mod->syments = ((offs[i_strtab] - offs[i_symtab])
-			      / gelf_fsize (mod->main.elf,
-					    ELF_T_SYM, 1, EV_CURRENT));
-
-	    if (mod->syments > 0)
-	      {
-		mod->symdata = elf_getdata_rawchunk (mod->main.elf,
-						     offs[i_symtab],
-						     gelf_fsize (mod->main.elf,
-								 ELF_T_SYM,
-								 mod->syments,
-								 EV_CURRENT),
-						     ELF_T_SYM);
-		if (mod->symdata != NULL)
-		  {
-		    mod->symstrdata = elf_getdata_rawchunk (mod->main.elf,
-							    offs[i_strtab],
-							    strsz,
-							    ELF_T_BYTE);
-		    if (mod->symstrdata == NULL)
-		      mod->symdata = NULL;
-		  }
-		if (mod->symdata == NULL)
-		  mod->symerr = DWFL_E (LIBELF, elf_errno ());
-		else
-		  {
-		    mod->symfile = &mod->main;
-		    mod->symerr = DWFL_E_NOERROR;
-		  }
-	      }
-	  }
-
 	  /* First try unadjusted, like ELF files from disk, vdso.
 	     Then try for already adjusted dynamic section, like ELF
 	     from remote memory.  */
-	  translate_offs (0);
+	  translate_offs (0, mod, phnum, addrs, strsz, ehdr);
 	  if (mod->symfile == NULL)
-	    translate_offs (mod->main_bias);
+	    translate_offs (mod->main_bias, mod, phnum, addrs, strsz, ehdr);
 
 	  return;
 	}
diff --git a/libdwfl/frame_unwind.c b/libdwfl/frame_unwind.c
index 39509b7..0e470b9 100644
--- a/libdwfl/frame_unwind.c
+++ b/libdwfl/frame_unwind.c
@@ -637,7 +637,14 @@
       if (unwound->pc == 0)
 	unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
       else
-	unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+        {
+          unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+          /* In SPARC the return address register actually contains
+             the address of the call instruction instead of the return
+             address.  Therefore we add here an offset defined by the
+             backend.  Most likely 0.  */
+          unwound->pc += ebl_ra_offset (ebl);
+        }
     }
   free (frame);
 }
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 63556d5..2a83646 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -1,5 +1,5 @@
 /* Internal definitions for libdwfl.
-   Copyright (C) 2005-2014 Red Hat, Inc.
+   Copyright (C) 2005-2015 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -104,6 +104,16 @@
 extern int __libdwfl_canon_error (Dwfl_Error) internal_function;
 extern void __libdwfl_seterrno (Dwfl_Error) internal_function;
 
+/* Resources we might keep for the user about the core file that the
+   Dwfl might have been created from.  Can currently only be set
+   through std-argp.  */
+struct Dwfl_User_Core
+{
+  char *executable_for_core;	/* --executable if --core was specified.  */
+  Elf *core;                    /* non-NULL if we need to free it.  */
+  int fd;                       /* close if >= 0.  */
+};
+
 struct Dwfl
 {
   const Dwfl_Callbacks *callbacks;
@@ -130,7 +140,7 @@
   GElf_Off lookup_tail_offset;
   int lookup_tail_ndx;
 
-  char *executable_for_core;	/* --executable if --core was specified.  */
+  struct Dwfl_User_Core *user_core;
 };
 
 #define OFFLINE_REDZONE		0x10000
diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c
index 13cac52..28d7382 100644
--- a/libdwfl/link_map.c
+++ b/libdwfl/link_map.c
@@ -42,6 +42,62 @@
 #define PROBE_VAL64	sizeof (Elf64_Phdr)
 
 
+static inline bool
+do_check64 (size_t i, const Elf64_auxv_t (*a64)[], uint_fast8_t *elfdata)
+{
+  /* The AUXV pointer might not even be naturally aligned for 64-bit
+     data, because note payloads in a core file are not aligned.  */
+
+  uint64_t type = read_8ubyte_unaligned_noncvt (&(*a64)[i].a_type);
+  uint64_t val = read_8ubyte_unaligned_noncvt (&(*a64)[i].a_un.a_val);
+
+  if (type == BE64 (PROBE_TYPE)
+      && val == BE64 (PROBE_VAL64))
+    {
+      *elfdata = ELFDATA2MSB;
+      return true;
+    }
+
+  if (type == LE64 (PROBE_TYPE)
+      && val == LE64 (PROBE_VAL64))
+    {
+      *elfdata = ELFDATA2LSB;
+      return true;
+    }
+
+  return false;
+}
+
+#define check64(n) do_check64 (n, a64, elfdata)
+
+static inline bool
+do_check32 (size_t i, const Elf32_auxv_t (*a32)[], uint_fast8_t *elfdata)
+{
+  /* The AUXV pointer might not even be naturally aligned for 32-bit
+     data, because note payloads in a core file are not aligned.  */
+
+  uint32_t type = read_4ubyte_unaligned_noncvt (&(*a32)[i].a_type);
+  uint32_t val = read_4ubyte_unaligned_noncvt (&(*a32)[i].a_un.a_val);
+
+  if (type == BE32 (PROBE_TYPE)
+      && val == BE32 (PROBE_VAL32))
+    {
+      *elfdata = ELFDATA2MSB;
+      return true;
+    }
+
+  if (type == LE32 (PROBE_TYPE)
+      && val == LE32 (PROBE_VAL32))
+    {
+      *elfdata = ELFDATA2LSB;
+      return true;
+    }
+
+  return false;
+}
+
+#define check32(n) do_check32 (n, a32, elfdata)
+
 /* Examine an auxv data block and determine its format.
    Return true iff we figured it out.  */
 static bool
@@ -51,56 +107,6 @@
   const Elf32_auxv_t (*a32)[size / sizeof (Elf32_auxv_t)] = (void *) auxv;
   const Elf64_auxv_t (*a64)[size / sizeof (Elf64_auxv_t)] = (void *) auxv;
 
-  inline bool check64 (size_t i)
-  {
-    /* The AUXV pointer might not even be naturally aligned for 64-bit
-       data, because note payloads in a core file are not aligned.  */
-
-    uint64_t type = read_8ubyte_unaligned_noncvt (&(*a64)[i].a_type);
-    uint64_t val = read_8ubyte_unaligned_noncvt (&(*a64)[i].a_un.a_val);
-
-    if (type == BE64 (PROBE_TYPE)
-	&& val == BE64 (PROBE_VAL64))
-      {
-	*elfdata = ELFDATA2MSB;
-	return true;
-      }
-
-    if (type == LE64 (PROBE_TYPE)
-	&& val == LE64 (PROBE_VAL64))
-      {
-	*elfdata = ELFDATA2LSB;
-	return true;
-      }
-
-    return false;
-  }
-
-  inline bool check32 (size_t i)
-  {
-    /* The AUXV pointer might not even be naturally aligned for 32-bit
-       data, because note payloads in a core file are not aligned.  */
-
-    uint32_t type = read_4ubyte_unaligned_noncvt (&(*a32)[i].a_type);
-    uint32_t val = read_4ubyte_unaligned_noncvt (&(*a32)[i].a_un.a_val);
-
-    if (type == BE32 (PROBE_TYPE)
-	&& val == BE32 (PROBE_VAL32))
-      {
-	*elfdata = ELFDATA2MSB;
-	return true;
-      }
-
-    if (type == LE32 (PROBE_TYPE)
-	&& val == LE32 (PROBE_VAL32))
-      {
-	*elfdata = ELFDATA2LSB;
-	return true;
-      }
-
-    return false;
-  }
-
   for (size_t i = 0; i < size / sizeof (Elf64_auxv_t); ++i)
     {
       if (check64 (i))
@@ -356,8 +362,10 @@
       if (name != NULL && name[0] == '\0')
 	name = NULL;
 
-      if (iterations == 1 && dwfl->executable_for_core != NULL)
-	name = dwfl->executable_for_core;
+      if (iterations == 1
+	  && dwfl->user_core != NULL
+	  && dwfl->user_core->executable_for_core != NULL)
+	name = dwfl->user_core->executable_for_core;
 
       struct r_debug_info_module *r_debug_info_module = NULL;
       if (r_debug_info != NULL)
@@ -789,7 +797,10 @@
 	  bool in_ok = (*memory_callback) (dwfl, phdr_segndx, &in.d_buf,
 					   &in.d_size, phdr, phnum * phent,
 					   memory_callback_arg);
-	  if (! in_ok && dwfl->executable_for_core != NULL)
+	  bool in_from_exec = false;
+	  if (! in_ok
+	      && dwfl->user_core != NULL
+	      && dwfl->user_core->executable_for_core != NULL)
 	    {
 	      /* AUXV -> PHDR -> DYNAMIC
 		 Both AUXV and DYNAMIC should be always present in a core file.
@@ -797,7 +808,7 @@
 		 EXECUTABLE_FOR_CORE to find where DYNAMIC is located in the
 		 core file.  */
 
-	      int fd = open (dwfl->executable_for_core, O_RDONLY);
+	      int fd = open (dwfl->user_core->executable_for_core, O_RDONLY);
 	      Elf *elf;
 	      Dwfl_Error error = DWFL_E_ERRNO;
 	      if (fd != -1)
@@ -851,6 +862,7 @@
 		  return false;
 		}
 	      in_ok = true;
+	      in_from_exec = true;
 	    }
 	  if (in_ok)
 	    {
@@ -899,8 +911,11 @@
 		    }
 		}
 
-	      (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
-				  memory_callback_arg);
+	      if (in_from_exec)
+		free (in.d_buf);
+	      else
+		(*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
+				    memory_callback_arg);
 	      free (buf);
 	    }
 	  else
diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c
index 2e2c8f9..9e7b2a2 100644
--- a/libdwfl/linux-proc-maps.c
+++ b/libdwfl/linux-proc-maps.c
@@ -175,6 +175,23 @@
   return ENOEXEC;
 }
 
+static inline bool
+do_report (Dwfl *dwfl, char **plast_file, Dwarf_Addr low, Dwarf_Addr high)
+{
+  if (*plast_file != NULL)
+    {
+      Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, *plast_file,
+						     low, high);
+      free (*plast_file);
+      *plast_file = NULL;
+      if (unlikely (mod == NULL))
+        return true;
+    }
+  return false;
+}
+
+#define report() do_report(dwfl, &last_file, low, high)
+
 static int
 proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
 {
@@ -183,20 +200,6 @@
   char *last_file = NULL;
   Dwarf_Addr low = 0, high = 0;
 
-  inline bool report (void)
-    {
-      if (last_file != NULL)
-	{
-	  Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, last_file,
-							 low, high);
-	  free (last_file);
-	  last_file = NULL;
-	  if (unlikely (mod == NULL))
-	    return true;
-	}
-      return false;
-    }
-
   char *line = NULL;
   size_t linesz;
   ssize_t len;
diff --git a/libebl/ChangeLog b/libebl/ChangeLog
index d040c08..a1a1022 100644
--- a/libebl/ChangeLog
+++ b/libebl/ChangeLog
@@ -1,3 +1,9 @@
+2015-12-08  Jose E. Marchesi  <jose.marchesi@oracle.com>
+
+	* libebl.h: Prototype for ebl_ra_offset.
+	* eblabicfi.c (ebl_ra_offset): New function.
+	* libeblP.h (struct ebl): new field ra_offset;
+
 2015-09-24  Jose E. Marchesi  <jose.marchesi@oracle.com>
 
 	* Makefile.am (AM_CFLAGS): Use -fPIC instead of -fpic to avoid
diff --git a/libebl/eblabicfi.c b/libebl/eblabicfi.c
index 6b0e18e..8bf189f 100644
--- a/libebl/eblabicfi.c
+++ b/libebl/eblabicfi.c
@@ -38,3 +38,9 @@
 {
   return ebl == NULL ? -1 : ebl->abi_cfi (ebl, abi_info);
 }
+
+int
+ebl_ra_offset (Ebl *ebl)
+{
+  return ebl->ra_offset;
+}
diff --git a/libebl/libebl.h b/libebl/libebl.h
index 7dbf460..efcb6d6 100644
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -422,6 +422,12 @@
 extern size_t ebl_frame_nregs (Ebl *ebl)
   __nonnull_attribute__ (1);
 
+/* Offset to apply to the value of the return_address_register, as
+   fetched from a Dwarf CFI.  This is used by some backends, where the
+   return_address_register actually contains the call address.  */
+extern int ebl_ra_offset (Ebl *ebl)
+  __nonnull_attribute__ (1);
+
 /* Mask to use for function symbol or unwind return addresses in case
    the architecture adds some extra non-address bits to it.  This is
    different from ebl_resolve_sym_value which only works for actual
diff --git a/libebl/libeblP.h b/libebl/libeblP.h
index dbd67f3..5b339b3 100644
--- a/libebl/libeblP.h
+++ b/libebl/libeblP.h
@@ -64,6 +64,12 @@
      Ebl architecture can unwind iff FRAME_NREGS > 0.  */
   size_t frame_nregs;
 
+  /* Offset to apply to the value of the return_address_register, as
+     fetched from a Dwarf CFI.  This is used by some backends, where
+     the return_address_register actually contains the call
+     address.  */
+  int ra_offset;
+
   /* Mask to use to turn a function value into a real function address
      in case the architecture adds some extra non-address bits to it.
      If not initialized (0) then ebl_func_addr_mask will return ~0,
diff --git a/src/ChangeLog b/src/ChangeLog
index d6d2936..b5b5e4a 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,31 @@
+2015-12-02  Mark Wielaard  <mjw@redhat.com>
+
+	* nm.c (process_file): Accept fd and pass it on.
+	(handle_elf): Likewise.
+	(find_no_debuginfo): New.
+	(struct getdbg): Likewise.
+	(getdbg_dwflmod): Likewise.
+	(show_symbols): Take fd. If the file is ET_REL use libdwfl to get
+	the relocated Dwarf.
+
+2015-12-02  Mark Wielaard  <mjw@redhat.com>
+
+	* nm.c (get_local_names): Check for duplicates in local_root tree.
+
+2015-12-02  Mark Wielaard  <mjw@redhat.com>
+
+	* unstrip.c (struct data_list): New.
+	(new_data_list): Likewise.
+	(record_new_data): Likewise.
+	(free_new_data): Likewise.
+	(adjust_relocs): Call record_new_data.
+	(add_new_section_symbols): Likewise.
+	(copy_elided_sections): Call free_new_data.
+
+2015-12-01  Mark Wielaard  <mjw@redhat.com>
+
+	* elfcmp.c (main): Close ebl1 and ebl2 backends.
+
 2015-10-16  Mark Wielaard  <mjw@redhat.com>
 
 	* Makefile.am [BUILD_STATIC](libdw): Add -lz.
diff --git a/src/elfcmp.c b/src/elfcmp.c
index 0250fbe..852b92f 100644
--- a/src/elfcmp.c
+++ b/src/elfcmp.c
@@ -655,6 +655,8 @@
  out:
   elf_end (elf1);
   elf_end (elf2);
+  ebl_closebackend (ebl1);
+  ebl_closebackend (ebl2);
   close (fd1);
   close (fd2);
 
diff --git a/src/nm.c b/src/nm.c
index 15d9da4..2911afa 100644
--- a/src/nm.c
+++ b/src/nm.c
@@ -45,6 +45,7 @@
 
 #include <system.h>
 #include "../libebl/libeblP.h"
+#include "../libdwfl/libdwflP.h"
 
 
 /* Name and version of program.  */
@@ -131,7 +132,7 @@
 		      const char *suffix);
 
 /* Handle ELF file.  */
-static int handle_elf (Elf *elf, const char *prefix, const char *fname,
+static int handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
 		       const char *suffix);
 
 
@@ -384,7 +385,7 @@
     {
       if (elf_kind (elf) == ELF_K_ELF)
 	{
-	  int result = handle_elf (elf, more_than_one ? "" : NULL,
+	  int result = handle_elf (fd, elf, more_than_one ? "" : NULL,
 				   fname, NULL);
 
 	  if (elf_end (elf) != 0)
@@ -493,7 +494,7 @@
 	  && strcmp (arhdr->ar_name, "/SYM64/") != 0)
 	{
 	  if (elf_kind (subelf) == ELF_K_ELF)
-	    result |= handle_elf (subelf, new_prefix, arhdr->ar_name,
+	    result |= handle_elf (fd, subelf, new_prefix, arhdr->ar_name,
 				  new_suffix);
 	  else if (elf_kind (subelf) == ELF_K_AR)
 	    result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name,
@@ -708,11 +709,16 @@
 	    newp->lowpc = lowpc;
 	    newp->highpc = highpc;
 
-	    /* Since we cannot deallocate individual memory we do not test
-	       for duplicates in the tree.  This should not happen anyway.  */
-	    if (tsearch (newp, &local_root, local_compare) == NULL)
-	      error (EXIT_FAILURE, errno,
-		     gettext ("cannot create search tree"));
+	   /* Check whether a similar local_name is already in the
+	      cache.  That should not happen.  But if it does, we
+	      don't want to leak memory.  */
+	    struct local_name **tres = tsearch (newp, &local_root,
+						local_compare);
+	    if (tres == NULL)
+              error (EXIT_FAILURE, errno,
+                     gettext ("cannot create search tree"));
+	    else if (*tres != newp)
+	      free (newp);
 	  }
 	while (dwarf_siblingof (die, die) == 0);
     }
@@ -1148,8 +1154,63 @@
   return reverse_sort ? -result : result;
 }
 
+/* Stub libdwfl callback, only the ELF handle already open is ever
+   used.  Only used for finding the alternate debug file if the Dwarf
+   comes from the main file.  We are not interested in separate
+   debuginfo.  */
+static int
+find_no_debuginfo (Dwfl_Module *mod,
+		   void **userdata,
+		   const char *modname,
+		   Dwarf_Addr base,
+		   const char *file_name,
+		   const char *debuglink_file,
+		   GElf_Word debuglink_crc,
+		   char **debuginfo_file_name)
+{
+  Dwarf_Addr dwbias;
+  dwfl_module_info (mod, NULL, NULL, NULL, &dwbias, NULL, NULL, NULL);
+
+  /* We are only interested if the Dwarf has been setup on the main
+     elf file but is only missing the alternate debug link.  If dwbias
+     hasn't even been setup, this is searching for separate debuginfo
+     for the main elf.  We don't care in that case.  */
+  if (dwbias == (Dwarf_Addr) -1)
+    return -1;
+
+  return dwfl_standard_find_debuginfo (mod, userdata, modname, base,
+				       file_name, debuglink_file,
+				       debuglink_crc, debuginfo_file_name);
+}
+
+/* Get the Dwarf for the module/file we want.  */
+struct getdbg
+{
+  const char *name;
+  Dwarf **dbg;
+};
+
+static int
+getdbg_dwflmod (Dwfl_Module *dwflmod,
+		void **userdata __attribute__ ((unused)),
+		const char *name,
+		Dwarf_Addr base __attribute__ ((unused)),
+		void *arg)
+{
+  struct getdbg *get = (struct getdbg *) arg;
+  if (get != NULL && get->name != NULL && strcmp (get->name, name) == 0)
+    {
+      Dwarf_Addr bias;
+      *get->dbg = dwfl_module_getdwarf (dwflmod, &bias);
+      return DWARF_CB_ABORT;
+    }
+
+  return DWARF_CB_OK;
+}
+
 static void
-show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
+show_symbols (int fd, Ebl *ebl, GElf_Ehdr *ehdr,
+	      Elf_Scn *scn, Elf_Scn *xndxscn,
 	      GElf_Shdr *shdr, const char *prefix, const char *fname,
 	      const char *fullname)
 {
@@ -1189,9 +1250,48 @@
   /* Get a DWARF debugging descriptor.  It's no problem if this isn't
      possible.  We just won't print any line number information.  */
   Dwarf *dbg = NULL;
+  Dwfl *dwfl = NULL;
   if (format == format_sysv)
     {
-      dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL);
+      if (ehdr->e_type != ET_REL)
+	dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL);
+      else
+	{
+	  /* Abuse libdwfl to do the relocations for us.  This is just
+	     for the ET_REL file containing Dwarf, so no need for
+	     fancy lookups.  */
+
+	  /* Duplicate an fd for dwfl_report_offline to swallow.  */
+	  int dwfl_fd = dup (fd);
+	  if (likely (dwfl_fd >= 0))
+	    {
+	      static const Dwfl_Callbacks callbacks =
+		{
+		  .section_address = dwfl_offline_section_address,
+		  .find_debuginfo = find_no_debuginfo
+		};
+	      dwfl = dwfl_begin (&callbacks);
+	      if (likely (dwfl != NULL))
+		{
+		  /* Let 0 be the logical address of the file (or
+		     first in archive).  */
+		  dwfl->offline_next_address = 0;
+		  if (dwfl_report_offline (dwfl, fname, fname, dwfl_fd)
+		      == NULL)
+		    {
+		      /* Consumed on success, not on failure.  */
+		      close (dwfl_fd);
+		    }
+		  else
+		    {
+		      dwfl_report_end (dwfl, NULL, NULL);
+
+		      struct getdbg get = { .name = fname, .dbg = &dbg };
+		      dwfl_getmodules (dwfl, &getdbg_dwflmod, &get, 0);
+		    }
+		}
+	    }
+	}
       if (dbg != NULL)
 	{
 	  (void) dwarf_getpubnames (dbg, get_global, NULL, 0);
@@ -1396,13 +1496,16 @@
       tdestroy (local_root, free);
       local_root = NULL;
 
-      (void) dwarf_end (dbg);
+      if (dwfl == NULL)
+	(void) dwarf_end (dbg);
     }
+  if (dwfl != NULL)
+    dwfl_end (dwfl);
 }
 
 
 static int
-handle_elf (Elf *elf, const char *prefix, const char *fname,
+handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
 	    const char *suffix)
 {
   size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
@@ -1481,7 +1584,7 @@
 		}
 	    }
 
-	  show_symbols (ebl, ehdr, scn, xndxscn, shdr, prefix, fname,
+	  show_symbols (fd, ebl, ehdr, scn, xndxscn, shdr, prefix, fname,
 			fullname);
 	}
     }
diff --git a/src/unstrip.c b/src/unstrip.c
index bc8ed50..85e0a1d 100644
--- a/src/unstrip.c
+++ b/src/unstrip.c
@@ -311,6 +311,38 @@
       error (EXIT_FAILURE, errno, _("cannot create directory '%s'"), dir);
 }
 
+/* Keep track of new section data we are creating, so we can free it
+   when done.  */
+struct data_list
+{
+  void *data;
+  struct data_list *next;
+};
+
+struct data_list *new_data_list;
+
+static void
+record_new_data (void *data)
+{
+  struct data_list *next = new_data_list;
+  new_data_list = xmalloc (sizeof (struct data_list));
+  new_data_list->data = data;
+  new_data_list->next = next;
+}
+
+static void
+free_new_data (void)
+{
+  struct data_list *list = new_data_list;
+  while (list != NULL)
+    {
+      struct data_list *next = list->next;
+      free (list->data);
+      free (list);
+      list = next;
+    }
+  new_data_list = NULL;
+}
 
 /* The binutils linker leaves gratuitous section symbols in .symtab
    that strip has to remove.  Older linkers likewise include a
@@ -472,6 +504,7 @@
 	    if (old_chain[i] != STN_UNDEF)				      \
 	      new_chain[map[i - 1]] = map[old_chain[i] - 1];		      \
 									      \
+	  record_new_data (new_hash);					\
 	  data->d_buf = new_hash;					      \
 	  data->d_size = nent * sizeof new_hash[0];			      \
 	}
@@ -514,6 +547,7 @@
 	    ELF_CHECK (v != NULL, _("cannot get symbol version: %s"));
 	  }
 
+	record_new_data (versym);
 	data->d_buf = versym;
 	data->d_size = nent * shdr->sh_entsize;
 	elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY);
@@ -571,6 +605,7 @@
 
   symdata->d_size = shdr->sh_size;
   symdata->d_buf = xmalloc (symdata->d_size);
+  record_new_data (symdata->d_buf);
 
   /* Copy the existing section symbols.  */
   Elf_Data *old_symdata = elf_getdata (old_symscn, NULL);
@@ -1762,6 +1797,7 @@
 
       shdr->sh_size = symdata->d_size = (1 + nsym) * shdr->sh_entsize;
       symdata->d_buf = xmalloc (symdata->d_size);
+      record_new_data (symdata->d_buf);
 
       GElf_Sym sym;
       memset (&sym, 0, sizeof sym);
@@ -1927,13 +1963,12 @@
       free (strtab_data->d_buf);
     }
 
-  if (symdata != NULL)
-    free (symdata->d_buf);
   if (symstrtab != NULL)
     {
       ebl_strtabfree (symstrtab);
       free (symstrdata->d_buf);
     }
+  free_new_data ();
 }
 
 /* Process one pair of files, already opened.  */
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 69a25fc..b8d1d95 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,31 @@
+2015-12-08  Jose E. Marchesi  <jose.marchesi@oracle.com>
+
+	* run-backtrace-core-sparc.sh: New file.
+	* backtrace.sparc.core.bz2: New file.
+	* backtrace.sparc.exec.bz2: New file.
+	* Makefile.am (EXTRA_DIST): ... and added all here.
+	(TESTS): Added run-backtrace-core-sparc.sh.
+
+2015-12-02  Mark Wielaard  <mjw@redhat.com>
+
+	* Makefile.am (valgrind_cmd): Use --leak-check=full.
+	* run-backtrace-demangle.sh: Disable valgrind.
+	* run-stack-demangled-test.sh: Likewise.
+	* run-stack-d-test.sh: Likewise.
+	* run-stack-i-test.sh: Likewise.
+
+2015-12-01  Mark Wielaard  <mjw@redhat.com>
+
+	* test-flag-nobits.c (main): Call elf_end.
+	* rerequest_tag.c (main): Call dwarf_end.
+	* funcscopes.c (handle_function): Free scopes.
+	* dwarf-getstring.c (main): Call dwarf_end.
+	* allregs.c (main): Free state.info.
+	* alldts.c (main): Free dyn.
+	* addrcfi.c (handle_address): Free stuff.frame between handle_cfi
+	calls.
+	* addrscopes.c (handle_address): Free scopes.
+
 2015-10-16  Mark Wielaard  <mjw@redhat.com>
 
 	* Makefile.am [BUILD_STATIC] (libdw): Add -lz.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 30cf137..8fca801 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -114,7 +114,7 @@
 	run-backtrace-core-x32.sh \
 	run-backtrace-core-i386.sh run-backtrace-core-ppc.sh \
 	run-backtrace-core-s390x.sh run-backtrace-core-s390.sh \
-	run-backtrace-core-aarch64.sh \
+	run-backtrace-core-aarch64.sh run-backtrace-core-sparc.sh \
 	run-backtrace-demangle.sh run-stack-d-test.sh run-stack-i-test.sh \
 	run-stack-demangled-test.sh \
 	run-readelf-dwz-multi.sh run-allfcts-multi.sh run-deleted.sh \
@@ -287,6 +287,8 @@
 	     run-backtrace-core-s390x.sh run-backtrace-core-s390.sh \
 	     run-backtrace-core-aarch64.sh \
 	     backtrace.aarch64.core.bz2 backtrace.aarch64.exec.bz2 \
+	     run-backtrace-core-sparc.sh \
+	     backtrace.sparc.core.bz2 backtrace.sparc.exec.bz2 \
 	     run-backtrace-demangle.sh testfile-backtrace-demangle.bz2 \
 	     testfile-backtrace-demangle.cc \
 	     testfile-backtrace-demangle.core.bz2 \
@@ -307,7 +309,7 @@
 	     run-lfs-symbols.sh lfs-symbols testfile-nolfs.bz2
 
 if USE_VALGRIND
-valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'
+valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1'
 endif
 
 
diff --git a/tests/addrcfi.c b/tests/addrcfi.c
index eb950c0..589b851 100644
--- a/tests/addrcfi.c
+++ b/tests/addrcfi.c
@@ -160,10 +160,19 @@
   Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc);
 
   struct stuff stuff;
-  return (handle_cfi (dwfl, ".eh_frame",
-		      dwfl_module_eh_cfi (mod, &stuff.bias), pc, &stuff)
-	  & handle_cfi (dwfl, ".debug_frame",
-			dwfl_module_dwarf_cfi (mod, &stuff.bias), pc, &stuff));
+  stuff.frame = NULL;
+  stuff.bias = 0;
+  int res = handle_cfi (dwfl, ".eh_frame",
+			dwfl_module_eh_cfi (mod, &stuff.bias), pc, &stuff);
+  free (stuff.frame);
+
+  stuff.frame = NULL;
+  stuff.bias = 0;
+  res &= handle_cfi (dwfl, ".debug_frame",
+		     dwfl_module_dwarf_cfi (mod, &stuff.bias), pc, &stuff);
+  free (stuff.frame);
+
+  return res;
 }
 
 int
diff --git a/tests/addrscopes.c b/tests/addrscopes.c
index 2285d2c..791569f 100644
--- a/tests/addrscopes.c
+++ b/tests/addrscopes.c
@@ -134,6 +134,7 @@
 
 	  print_vars (indent + INDENT, die);
 	}
+      free (scopes);
     }
 }
 
diff --git a/tests/alldts.c b/tests/alldts.c
index c39b8fb..eaecaf5 100644
--- a/tests/alldts.c
+++ b/tests/alldts.c
@@ -256,6 +256,9 @@
   /* And the data allocated in the .shstrtab section.  */
   free (data->d_buf);
 
+  /* And the dynamic entries.  */
+  free (dyn);
+
   /* All done.  */
   if (elf_end (elf) != 0)
     {
diff --git a/tests/allregs.c b/tests/allregs.c
index 901d4e8..286f7e3 100644
--- a/tests/allregs.c
+++ b/tests/allregs.c
@@ -184,6 +184,7 @@
 		    dwarf_encoding_string (state.info[i].type),
 		    state.info[i].bits);
 	  }
+      free (state.info);
     }
   else
     do
diff --git a/tests/backtrace.sparc.core.bz2 b/tests/backtrace.sparc.core.bz2
new file mode 100644
index 0000000..ad37f75
--- /dev/null
+++ b/tests/backtrace.sparc.core.bz2
Binary files differ
diff --git a/tests/backtrace.sparc.exec.bz2 b/tests/backtrace.sparc.exec.bz2
new file mode 100755
index 0000000..b049ec5
--- /dev/null
+++ b/tests/backtrace.sparc.exec.bz2
Binary files differ
diff --git a/tests/dwarf-getstring.c b/tests/dwarf-getstring.c
index 824edef..ffa3e37 100644
--- a/tests/dwarf-getstring.c
+++ b/tests/dwarf-getstring.c
@@ -70,6 +70,7 @@
 	  offset += len + 1;
 	}
 
+      dwarf_end (dbg);
       close (fd);
     }
 
diff --git a/tests/funcscopes.c b/tests/funcscopes.c
index 55cb4fa..9c90185 100644
--- a/tests/funcscopes.c
+++ b/tests/funcscopes.c
@@ -162,6 +162,7 @@
 
 	  print_vars (indent + INDENT, die);
 	}
+      free (scopes);
     }
 
   return 0;
diff --git a/tests/rerequest_tag.c b/tests/rerequest_tag.c
index d0bf5f2..b4d4627 100644
--- a/tests/rerequest_tag.c
+++ b/tests/rerequest_tag.c
@@ -42,5 +42,6 @@
   assert (die == &die_mem);
   assert (dwarf_tag (die) == 0);
 
+  dwarf_end (dw);
   return 0;
 }
diff --git a/tests/run-backtrace-core-sparc.sh b/tests/run-backtrace-core-sparc.sh
new file mode 100755
index 0000000..60399ba
--- /dev/null
+++ b/tests/run-backtrace-core-sparc.sh
@@ -0,0 +1,20 @@
+#! /bin/bash
+# Copyright (C) 2015 Oracle, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/backtrace-subr.sh
+
+check_core sparc
diff --git a/tests/run-backtrace-demangle.sh b/tests/run-backtrace-demangle.sh
index 71a7351..2d25324 100755
--- a/tests/run-backtrace-demangle.sh
+++ b/tests/run-backtrace-demangle.sh
@@ -26,6 +26,11 @@
 testfiles $child{,.core}
 tempfiles $child.{bt,err}
 
+# Disable valgrind while dumping because of a bug unmapping libc.so.
+# https://bugs.kde.org/show_bug.cgi?id=327427
+SAVED_VALGRIND_CMD="$VALGRIND_CMD"
+unset VALGRIND_CMD
+
 # There can be more than 3 frames, but depending on the system/installed
 # glibc we might not be able to unwind fully till the end.
 # cxxfunc -> f -> main
@@ -33,6 +38,12 @@
 # (exit code 1)
 testrun ${abs_top_builddir}/src/stack -n 2 -e $child --core $child.core >$child.bt 2>$child.err || exitcode=$?
 cat $child.{bt,err}
+
+if [ "x$SAVED_VALGRIND_CMD" != "x" ]; then
+  VALGRIND_CMD="$SAVED_VALGRIND_CMD"
+  export VALGRIND_CMD
+fi
+
 if test $exitcode != 1 || ! grep "shown max number of frames" $child.err; then
   echo >&2 $2: expected more than 2 frames
   false
diff --git a/tests/run-stack-d-test.sh b/tests/run-stack-d-test.sh
index 4198201..a9f0380 100755
--- a/tests/run-stack-d-test.sh
+++ b/tests/run-stack-d-test.sh
@@ -68,6 +68,11 @@
 STACKCMD=${abs_top_builddir}/src/stack
 fi
 
+# Disable valgrind while dumping because of a bug unmapping libc.so.
+# https://bugs.kde.org/show_bug.cgi?id=327427
+SAVED_VALGRIND_CMD="$VALGRIND_CMD"
+unset VALGRIND_CMD
+
 # Without -d the top function comes out as fu. Use --raw to not demangle.
 testrun_compare ${abs_top_builddir}/src/stack -r -n 2 -e testfiledwarfinlines --core testfiledwarfinlines.core<<EOF
 PID 13654 - core
@@ -109,4 +114,9 @@
 $STACKCMD: tid 13654: shown max number of frames (2, use -n 0 for unlimited)
 EOF
 
+if [ "x$SAVED_VALGRIND_CMD" != "x" ]; then
+  VALGRIND_CMD="$SAVED_VALGRIND_CMD"
+  export VALGRIND_CMD
+fi
+
 exit 0
diff --git a/tests/run-stack-demangled-test.sh b/tests/run-stack-demangled-test.sh
index 98f8ff8..c26918f 100755
--- a/tests/run-stack-demangled-test.sh
+++ b/tests/run-stack-demangled-test.sh
@@ -37,6 +37,11 @@
 STACKCMD=${abs_top_builddir}/src/stack
 fi
 
+# Disable valgrind while dumping because of a bug unmapping libc.so.
+# https://bugs.kde.org/show_bug.cgi?id=327427
+SAVED_VALGRIND_CMD="$VALGRIND_CMD"
+unset VALGRIND_CMD
+
 # Without -d the top function comes out as fu.
 testrun_compare ${abs_top_builddir}/src/stack -n 2 -e testfiledwarfinlines --core testfiledwarfinlines.core<<EOF
 PID 13654 - core
@@ -92,4 +97,9 @@
 $STACKCMD: tid 13654: shown max number of frames (6, use -n 0 for unlimited)
 EOF
 
+if [ "x$SAVED_VALGRIND_CMD" != "x" ]; then
+  VALGRIND_CMD="$SAVED_VALGRIND_CMD"
+  export VALGRIND_CMD
+fi
+
 exit 0
diff --git a/tests/run-stack-i-test.sh b/tests/run-stack-i-test.sh
index ffab85f..3722ab0 100755
--- a/tests/run-stack-i-test.sh
+++ b/tests/run-stack-i-test.sh
@@ -29,6 +29,11 @@
 STACKCMD=${abs_top_builddir}/src/stack
 fi
 
+# Disable valgrind while dumping because of a bug unmapping libc.so.
+# https://bugs.kde.org/show_bug.cgi?id=327427
+SAVED_VALGRIND_CMD="$VALGRIND_CMD"
+unset VALGRIND_CMD
+
 # Compare with run-stack-d-test.sh to see the output without --inlines.
 # Only two call frames are visible (there is a jump from main to fu or
 # fubar). Explicitly use --raw so demangler support being configured in
@@ -68,4 +73,9 @@
 $STACKCMD: tid 13654: shown max number of frames (6, use -n 0 for unlimited)
 EOF
 
+if [ "x$SAVED_VALGRIND_CMD" != "x" ]; then
+  VALGRIND_CMD="$SAVED_VALGRIND_CMD"
+  export VALGRIND_CMD
+fi
+
 exit 0
diff --git a/tests/test-flag-nobits.c b/tests/test-flag-nobits.c
index ff19ce2..15d44ea 100644
--- a/tests/test-flag-nobits.c
+++ b/tests/test-flag-nobits.c
@@ -36,4 +36,7 @@
   Elf_Scn *scn = NULL;
   while ((scn = elf_nextscn (stripped, scn)) != NULL)
     elf_flagdata (elf_getdata (scn, NULL), ELF_C_SET, ELF_F_DIRTY);
+
+  elf_end (stripped);
+  return 0;
 }