>From 819c349f6339512d6961a6172c539fdf2c2f1328 Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mjw@redhat.com>
Date: Wed, 27 Nov 2013 16:45:44 +0100
Subject: [PATCH] libdwfl: Add dwfl_module_addrsym_elf and dwfl_module_getsym_elf.

Introduce two new functions that also return the elf associated with a
symbol to make symbol section indexing work for non-special sections.
Simplify code by removing dwfl_file where appropriate and just track Elf
directly. Document limitations of shndx with existing dwfl_module_addrsym
and dwfl_module_getsym. Extend dwflsyms testcase to check some more symbol
and section (index) properties.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
---
 libdw/ChangeLog               |    5 ++
 libdw/libdw.map               |    3 +
 libdwfl/ChangeLog             |   22 ++++++++
 libdwfl/dwfl_module_addrsym.c |   49 +++++++++++++-----
 libdwfl/dwfl_module_getsym.c  |   33 ++++++------
 libdwfl/dwfl_module_info.c    |    2 +-
 libdwfl/libdwfl.h             |   25 +++++++++-
 libdwfl/libdwflP.h            |   24 +++------
 libdwfl/relocate.c            |    2 +-
 tests/ChangeLog               |   10 ++++
 tests/dwflsyms.c              |   66 +++++++++++++++++++++++--
 tests/run-dwflsyms.sh         |  110 ++++++++++++++++++++--------------------
 12 files changed, 243 insertions(+), 108 deletions(-)

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 33885ac..91e1083 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,8 @@
+2013-11-27  Mark Wielaard  <mjw@redhat.com>
+
+	* libdw.map (ELFUTILS_0.158): Add dwfl_module_addrsym_elf and
+	dwfl_module_getsym_elf.
+
 2013-11-26  Mark Wielaard  <mjw@redhat.com>
 
 	* libdw.map (ELFUTILS_0.156): Move dwfl_attach_state, dwfl_pid,
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 060c3df..0438e24 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -283,4 +283,7 @@ ELFUTILS_0.158 {
     dwfl_getthreads;
     dwfl_thread_getframes;
     dwfl_frame_pc;
+
+    dwfl_module_addrsym_elf;
+    dwfl_module_getsym_elf;
 } ELFUTILS_0.157;
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index deb5014..9937bbb 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,25 @@
+2013-11-27  Mark Wielaard  <mjw@redhat.com>
+
+	* dwfl_module_addrsym.c (dwfl_module_addrsym): Rename to and call...
+	(dwfl_module_addrsym_elf): this. Add elfp and biasp arguments,
+	keep track of symelf, addr_symelf, closest_elf and sizeless_elf
+	instead of tracking dwfl_files.
+	* dwfl_module_getsym.c (__libdwfl_module_getsym): Renamed to...
+	(dwfl_module_getsym_elf): ...this. Remove dwfl_file argument, add
+	new elfp and biasp arguments. Track elf instead of file.
+	(dwfl_module_getsym): Call dwfl_module_getsym_elf.
+	dwfl_module_info.c (dwfl_module_info): Pass elf to
+	dwfl_adjusted_st_value.
+	* libdwfl.h (dwfl_module_getsym): Document limitations of shndx.
+	(dwfl_module_getsym_elf): New function declaration.
+	(dwfl_module_addrsym_elf): Likewise.
+	* libdwflP.h (dwfl_module_addrsym_elf): INTDECL.
+	(dwfl_module_getsym_elf): Likewise.
+	(dwfl_adjusted_st_value): Take and check elf not dwfl_file.
+	(dwfl_deadjust_st_value): Likewise.
+	(__libdwfl_module_getsym): Removed.
+	* relocate.c (resolve_symbol): Pass elf to dwfl_adjusted_st_value.
+
 2013-11-21  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
 	Fix non-build-id core files on build-id system.
diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c
index d9eb0a2..320d41f 100644
--- a/libdwfl/dwfl_module_addrsym.c
+++ b/libdwfl/dwfl_module_addrsym.c
@@ -32,8 +32,9 @@
    Never returns symbols at addresses above ADDR.  */
 
 const char *
-dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
-		     GElf_Sym *closest_sym, GElf_Word *shndxp)
+dwfl_module_addrsym_elf (Dwfl_Module *mod, GElf_Addr addr,
+			 GElf_Sym *closest_sym, GElf_Word *shndxp,
+			 Elf **elfp, Dwarf_Addr *biasp)
 {
   int syments = INTUSE(dwfl_module_getsymtab) (mod);
   if (syments < 0)
@@ -41,22 +42,21 @@ dwfl_module_addrsym (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;
-  struct dwfl_file *addr_symfile = NULL;
-  inline bool same_section (const GElf_Sym *sym, struct dwfl_file *symfile,
-			    GElf_Word shndx)
+  Elf *addr_symelf = NULL;
+  inline bool same_section (const GElf_Sym *sym, Elf *symelf, GElf_Word shndx)
     {
       /* For absolute symbols and the like, only match exactly.  */
       if (shndx >= SHN_LORESERVE)
 	return sym->st_value == addr;
 
       /* Figure out what section ADDR lies in.  */
-      if (addr_shndx == SHN_UNDEF || addr_symfile != symfile)
+      if (addr_shndx == SHN_UNDEF || addr_symelf != symelf)
 	{
-	  GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, symfile, addr);
+	  GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, symelf, addr);
 	  Elf_Scn *scn = NULL;
 	  addr_shndx = SHN_ABS;
-	  addr_symfile = symfile;
-	  while ((scn = elf_nextscn (symfile->elf, scn)) != NULL)
+	  addr_symelf = symelf;
+	  while ((scn = elf_nextscn (symelf, scn)) != NULL)
 	    {
 	      GElf_Shdr shdr_mem;
 	      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
@@ -70,18 +70,20 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
 	    }
 	}
 
-      return shndx == addr_shndx && addr_symfile == symfile;
+      return shndx == addr_shndx && addr_symelf == 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_Word closest_shndx = SHN_UNDEF;
+  Elf *closest_elf = NULL;
 
   /* 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_Word sizeless_shndx = SHN_UNDEF;
+  Elf *sizeless_elf = NULL;
 
   /* Keep track of the lowest address a relevant sizeless symbol could have.  */
   GElf_Addr min_label = 0;
@@ -93,9 +95,10 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
 	{
 	  GElf_Sym sym;
 	  GElf_Word shndx;
-	  struct dwfl_file *file;
-	  const char *name = __libdwfl_module_getsym (mod, i, &sym, &shndx,
-						      &file);
+	  Elf *elf;
+	  const char *name = INTUSE(dwfl_module_getsym_elf) (mod, i, &sym,
+							     &shndx, &elf,
+							     NULL);
 	  if (name != NULL && name[0] != '\0'
 	      && sym.st_shndx != SHN_UNDEF
 	      && sym.st_value <= addr
@@ -136,11 +139,12 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
 			{
 			  *closest_sym = sym;
 			  closest_shndx = shndx;
+			  closest_elf = elf;
 			  closest_name = name;
 			}
 		      else if (closest_name == NULL
 			       && sym.st_value >= min_label
-			       && same_section (&sym, file, shndx))
+			       && same_section (&sym, elf, shndx))
 			{
 			  /* Handwritten assembly symbols sometimes have no
 			     st_size.  If no symbol with proper size includes
@@ -148,6 +152,7 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
 			     the same section as ADDR.  */
 			  sizeless_sym = sym;
 			  sizeless_shndx = shndx;
+			  sizeless_elf = elf;
 			  sizeless_name = name;
 			}
 		    }
@@ -166,6 +171,7 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
 		    {
 		      *closest_sym = sym;
 		      closest_shndx = shndx;
+		      closest_elf = elf;
 		      closest_name = name;
 		    }
 		}
@@ -200,11 +206,26 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
     {
       *closest_sym = sizeless_sym;
       closest_shndx = sizeless_shndx;
+      closest_elf = sizeless_elf;
       closest_name = sizeless_name;
     }
 
   if (shndxp != NULL)
     *shndxp = closest_shndx;
+  if (elfp != NULL)
+    *elfp = closest_elf;
+  if (biasp != NULL)
+    *biasp = dwfl_adjusted_st_value (mod, closest_elf, 0);
   return closest_name;
 }
+INTDEF (dwfl_module_addrsym_elf)
+
+
+const char *
+dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
+		     GElf_Sym *closest_sym, GElf_Word *shndxp)
+{
+  return INTUSE(dwfl_module_addrsym_elf) (mod, addr, closest_sym, shndxp,
+					  NULL, NULL);
+}
 INTDEF (dwfl_module_addrsym)
diff --git a/libdwfl/dwfl_module_getsym.c b/libdwfl/dwfl_module_getsym.c
index 0f5dd37..319f975 100644
--- a/libdwfl/dwfl_module_getsym.c
+++ b/libdwfl/dwfl_module_getsym.c
@@ -29,10 +29,9 @@
 #include "libdwflP.h"
 
 const char *
-internal_function
-__libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
-			 GElf_Sym *sym, GElf_Word *shndxp,
-			 struct dwfl_file **filep)
+dwfl_module_getsym_elf (Dwfl_Module *mod, int ndx,
+			GElf_Sym *sym, GElf_Word *shndxp,
+			Elf **elfp, Dwarf_Addr *biasp)
 {
   if (unlikely (mod == NULL))
     return NULL;
@@ -51,7 +50,7 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
   GElf_Word shndx;
   int tndx = ndx;
   int skip_aux_zero = (mod->syments > 0 && mod->aux_syments > 0) ? 1 : 0;
-  struct dwfl_file *file;
+  Elf *elf;
   Elf_Data *symdata;
   Elf_Data *symxndxdata;
   Elf_Data *symstrdata;
@@ -60,7 +59,7 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
     {
       /* main symbol table (locals).  */
       tndx = ndx;
-      file = mod->symfile;
+      elf = mod->symfile->elf;
       symdata = mod->symdata;
       symxndxdata = mod->symxndxdata;
       symstrdata = mod->symstrdata;
@@ -69,7 +68,7 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
     {
       /* aux symbol table (locals).  */
       tndx = ndx - mod->first_global + skip_aux_zero;
-      file = &mod->aux_sym;
+      elf = mod->aux_sym.elf;
       symdata = mod->aux_symdata;
       symxndxdata = mod->aux_symxndxdata;
       symstrdata = mod->aux_symstrdata;
@@ -78,7 +77,7 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
     {
       /* main symbol table (globals).  */
       tndx = ndx - mod->aux_first_global + skip_aux_zero;
-      file = mod->symfile;
+      elf = mod->symfile->elf;
       symdata = mod->symdata;
       symxndxdata = mod->symxndxdata;
       symstrdata = mod->symstrdata;
@@ -87,7 +86,7 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
     {
       /* aux symbol table (globals).  */
       tndx = ndx - mod->syments + skip_aux_zero;
-      file = &mod->aux_sym;
+      elf = mod->aux_sym.elf;
       symdata = mod->aux_symdata;
       symxndxdata = mod->aux_symxndxdata;
       symstrdata = mod->aux_symstrdata;
@@ -110,8 +109,7 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
 	  || (sym->st_shndx < SHN_LORESERVE && sym->st_shndx != SHN_UNDEF)))
     {
       GElf_Shdr shdr_mem;
-      GElf_Shdr *shdr = gelf_getshdr (elf_getscn (file->elf, shndx),
-				      &shdr_mem);
+      GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, shndx), &shdr_mem);
       alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC);
     }
 
@@ -132,7 +130,7 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
 	  /* In an ET_REL file, the symbol table values are relative
 	     to the section, not to the module's load base.  */
 	  size_t symshstrndx = SHN_UNDEF;
-	  Dwfl_Error result = __libdwfl_relocate_value (mod, file->elf,
+	  Dwfl_Error result = __libdwfl_relocate_value (mod, elf,
 							&symshstrndx,
 							shndx, &sym->st_value);
 	  if (unlikely (result != DWFL_E_NOERROR))
@@ -143,7 +141,7 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
 	}
       else if (alloc)
 	/* Apply the bias to the symbol value.  */
-	sym->st_value = dwfl_adjusted_st_value (mod, file, sym->st_value);
+	sym->st_value = dwfl_adjusted_st_value (mod, elf, sym->st_value);
       break;
     }
 
@@ -152,15 +150,18 @@ __libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
       __libdwfl_seterrno (DWFL_E_BADSTROFF);
       return NULL;
     }
-  if (filep)
-    *filep = file;
+  if (elfp)
+    *elfp = elf;
+  if (biasp)
+    *biasp = dwfl_adjusted_st_value (mod, elf, 0);
   return (const char *) symstrdata->d_buf + sym->st_name;
 }
+INTDEF (dwfl_module_getsym_elf)
 
 const char *
 dwfl_module_getsym (Dwfl_Module *mod, int ndx,
 		    GElf_Sym *sym, GElf_Word *shndxp)
 {
-  return __libdwfl_module_getsym (mod, ndx, sym, shndxp, NULL);
+  return dwfl_module_getsym_elf (mod, ndx, sym, shndxp, NULL, NULL);
 }
 INTDEF (dwfl_module_getsym)
diff --git a/libdwfl/dwfl_module_info.c b/libdwfl/dwfl_module_info.c
index fdb4202..df16be4 100644
--- a/libdwfl/dwfl_module_info.c
+++ b/libdwfl/dwfl_module_info.c
@@ -49,7 +49,7 @@ dwfl_module_info (Dwfl_Module *mod, void ***userdata,
 	       : dwfl_adjusted_dwarf_addr (mod, 0));
   if (symbias)
     *symbias = (mod->symfile == NULL ? (Dwarf_Addr) -1
-		: dwfl_adjusted_st_value (mod, mod->symfile, 0));
+		: dwfl_adjusted_st_value (mod, mod->symfile->elf, 0));
 
   if (mainfile)
     *mainfile = mod->main.name;
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index c1a0fb9..3d5bede 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -436,11 +436,24 @@ extern int dwfl_module_getsymtab (Dwfl_Module *mod);
    an absolute value based on the module's location, when the symbol is in
    an SHF_ALLOC section.  If SHNDXP is non-null, it's set with the section
    index (whether from st_shndx or extended index table); in case of a
-   symbol in a non-allocated section, *SHNDXP is instead set to -1.  */
+   symbol in a non-allocated section, *SHNDXP is instead set to -1.
+   Note that since symbols can come from either the main, debug or auxiliary
+   ELF symbol file (either dynsym or symtab) the section index can only
+   be reliably used to compare against special section constants like
+   SHN_UNDEF or SHN_ABS.  */
 extern const char *dwfl_module_getsym (Dwfl_Module *mod, int ndx,
 				       GElf_Sym *sym, GElf_Word *shndxp)
   __nonnull_attribute__ (3);
 
+/* Same as dwfl_module_getsym but also returns the ELF file, if not NULL,
+   that the symbol came from so the section index can be reliably used.
+   Fills in *BIAS, if not NULL, with the difference between addresses
+   within the loaded module and those in symbol tables of the ELF file. */
+extern const char *dwfl_module_getsym_elf (Dwfl_Module *mod, int ndx,
+				           GElf_Sym *sym, GElf_Word *shndxp,
+					   Elf **elfp, Dwarf_Addr *bias)
+  __nonnull_attribute__ (3);
+
 /* Find the symbol that ADDRESS lies inside, and return its name.  */
 extern const char *dwfl_module_addrname (Dwfl_Module *mod, GElf_Addr address);
 
@@ -450,6 +463,16 @@ extern const char *dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr address,
 					GElf_Sym *sym, GElf_Word *shndxp)
   __nonnull_attribute__ (3);
 
+/* Same as dwfl_module_addrsym but also returns the ELF file, if not NULL,
+   that the symbol came from so the section index can be reliably used.
+   Fills in *BIAS, if not NULL, with the difference between addresses
+   within the loaded module and those in symbol tables of the ELF file. */
+extern const char *dwfl_module_addrsym_elf (Dwfl_Module *mod,
+					    GElf_Addr address, GElf_Sym *sym,
+					    GElf_Word *shndxp, Elf **elfp,
+					    Dwarf_Addr *bias)
+  __nonnull_attribute__ (3);
+
 /* Find the ELF section that *ADDRESS lies inside and return it.
    On success, adjusts *ADDRESS to be relative to the section,
    and sets *BIAS to the difference between addresses used in
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index b8a64d8..b73f7b1 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -359,23 +359,21 @@ dwfl_deadjust_aux_sym_addr (Dwfl_Module *mod, Dwarf_Addr addr)
 }
 
 static inline GElf_Addr
-dwfl_adjusted_st_value (Dwfl_Module *mod, struct dwfl_file *symfile,
-			GElf_Addr addr)
+dwfl_adjusted_st_value (Dwfl_Module *mod, Elf *symelf, GElf_Addr addr)
 {
-  if (symfile == &mod->main)
+  if (symelf == mod->main.elf)
     return dwfl_adjusted_address (mod, addr);
-  if (symfile == &mod->debug)
+  if (symelf == mod->debug.elf)
     return dwfl_adjusted_dwarf_addr (mod, addr);
   return dwfl_adjusted_aux_sym_addr (mod, addr);
 }
 
 static inline GElf_Addr
-dwfl_deadjust_st_value (Dwfl_Module *mod, struct dwfl_file *symfile,
-			GElf_Addr addr)
+dwfl_deadjust_st_value (Dwfl_Module *mod, Elf *symelf, GElf_Addr addr)
 {
-  if (symfile == &mod->main)
+  if (symelf == mod->main.elf)
     return dwfl_deadjust_address (mod, addr);
-  if (symfile == &mod->debug)
+  if (symelf == mod->debug.elf)
     return dwfl_deadjust_dwarf_addr (mod, addr);
   return dwfl_deadjust_aux_sym_addr (mod, addr);
 }
@@ -421,14 +419,6 @@ extern Dwfl_Error __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf,
 					    GElf_Addr *value)
      internal_function;
 
-/* See dwfl_module_getsym.  *FILEP will be set to the file of *SYM.
-   FILEP can be NULL.  */
-extern const char *__libdwfl_module_getsym (Dwfl_Module *mod, int ndx,
-					    GElf_Sym *sym, GElf_Word *shndxp,
-					    struct dwfl_file **filep)
-     internal_function;
-
-
 /* Ensure that MOD->ebl is set up.  */
 extern Dwfl_Error __libdwfl_module_getebl (Dwfl_Module *mod) internal_function;
 
@@ -648,10 +638,12 @@ INTDECL (dwfl_getmodules)
 INTDECL (dwfl_module_addrdie)
 INTDECL (dwfl_module_address_section)
 INTDECL (dwfl_module_addrsym)
+INTDECL (dwfl_module_addrsym_elf)
 INTDECL (dwfl_module_build_id)
 INTDECL (dwfl_module_getdwarf)
 INTDECL (dwfl_module_getelf)
 INTDECL (dwfl_module_getsym)
+INTDECL (dwfl_module_getsym_elf)
 INTDECL (dwfl_module_getsymtab)
 INTDECL (dwfl_module_getsrc)
 INTDECL (dwfl_module_report_build_id)
diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c
index bd51ad6..f8a5fcf 100644
--- a/libdwfl/relocate.c
+++ b/libdwfl/relocate.c
@@ -252,7 +252,7 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
 
 		if (m->e_type != ET_REL)
 		  {
-		    sym->st_value = dwfl_adjusted_st_value (m, m->symfile,
+		    sym->st_value = dwfl_adjusted_st_value (m, m->symfile->elf,
 							    sym->st_value);
 		    return DWFL_E_NOERROR;
 		  }
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 8a6c4d2..6c0ec36 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,13 @@
+2013-11-27  Mark Wielaard  <mjw@redhat.com>
+
+	* dwflsyms.c (gelf_bind_order): New function.
+	(elf_section_name): Likewise.
+	(addr_in_section): Likewise.
+	(list_syms): Use dwfl_module_getsym_elf and dwfl_module_addrsym_elf.
+	Refine assert using gelf_bind_order. Print elf_section_name. Check
+	bias with addr_in_section.
+	* run-dwflsyms.sh: Add section names to expected output.
+
 2013-11-26  Mark Wielaard  <mjw@redhat.com>
 
 	* Makefile.am (EXTRA_DIST): Add run-funcretval.sh.
diff --git a/tests/dwflsyms.c b/tests/dwflsyms.c
index 55f2653..10c01f1 100644
--- a/tests/dwflsyms.c
+++ b/tests/dwflsyms.c
@@ -69,6 +69,42 @@ gelf_bind (GElf_Sym *sym)
 }
 
 static int
+gelf_bind_order (GElf_Sym *sym)
+{
+  switch (GELF_ST_BIND (sym->st_info))
+    {
+    case STB_LOCAL:
+      return 1;
+    case STB_WEAK:
+      return 2;
+    case STB_GLOBAL:
+      return 3;
+    default:
+      return 0;
+    }
+}
+
+static const char *
+elf_section_name (Elf *elf, GElf_Word shndx)
+{
+  GElf_Ehdr ehdr;
+  GElf_Shdr shdr;
+  Elf_Scn *scn = elf_getscn (elf, shndx);
+  gelf_getshdr (scn, &shdr);
+  gelf_getehdr (elf, &ehdr);
+  return elf_strptr (elf, ehdr.e_shstrndx, shdr.sh_name);
+}
+
+bool
+addr_in_section (Elf *elf, GElf_Word shndx, GElf_Addr addr)
+{
+  GElf_Shdr shdr;
+  Elf_Scn *scn = elf_getscn (elf, shndx);
+  gelf_getshdr (scn, &shdr);
+  return addr >= shdr.sh_addr && addr < shdr.sh_addr + shdr.sh_size;
+}
+
+static int
 list_syms (struct Dwfl_Module *mod,
 	   void **user __attribute__ ((unused)),
 	   const char *mod_name __attribute__ ((unused)),
@@ -82,7 +118,10 @@ list_syms (struct Dwfl_Module *mod,
     {
       GElf_Sym sym;
       GElf_Word shndxp;
-      const char *name = dwfl_module_getsym (mod, ndx, &sym, &shndxp);
+      Elf *elf;
+      Dwarf_Addr bias;
+      const char *name = dwfl_module_getsym_elf (mod, ndx, &sym, &shndxp,
+						 &elf, &bias);
       printf("%4d: %s\t%s\t%s (%" PRIu64 ") %#" PRIx64,
 	     ndx, gelf_type (&sym), gelf_bind (&sym), name,
 	     sym.st_size, sym.st_value);
@@ -92,15 +131,34 @@ list_syms (struct Dwfl_Module *mod,
 	 dwfl_module_getsym ().  */
       if (GELF_ST_TYPE (sym.st_info) == STT_FUNC && shndxp != SHN_UNDEF)
 	{
+	  /* Make sure the adjusted value really falls in the elf section. */
+          assert (addr_in_section (elf, shndxp, sym.st_value - bias));
+
 	  GElf_Addr addr = sym.st_value;
 	  GElf_Sym asym;
 	  GElf_Word ashndxp;
-	  const char *aname = dwfl_module_addrsym (mod, addr, &asym, &ashndxp);
-	  assert (strcmp (name, aname) == 0);
+	  Elf *aelf;
+	  Dwarf_Addr abias;
+	  const char *aname = dwfl_module_addrsym_elf (mod, addr, &asym,
+						       &ashndxp, &aelf, &abias);
+
+	  /* Make sure the adjusted value really falls in the elf section. */
+          assert (addr_in_section (aelf, ashndxp, asym.st_value - abias));
+
+	  /* Either they are the same symbol (name), the binding of
+	     asym is "stronger" (or equal) to sym or asym is more specific
+	     (has a lower address) than sym.  */
+	  assert ((strcmp (name, aname) == 0
+		   || gelf_bind_order (&asym) >= gelf_bind_order (&sym))
+		  && asym.st_value <= sym.st_value);
 
 	  int res = dwfl_module_relocate_address (mod, &addr);
 	  assert (res != -1);
-	  printf(", rel: %#" PRIx64 "", addr);
+	  if (shndxp < SHN_LORESERVE)
+	    printf(", rel: %#" PRIx64 " (%s)", addr,
+		   elf_section_name (elf, shndxp));
+	  else
+	    printf(", rel: %#" PRIx64 "", addr);
 	}
       printf ("\n");
     }
diff --git a/tests/run-dwflsyms.sh b/tests/run-dwflsyms.sh
index 2adec5a..3cd7bf3 100755
--- a/tests/run-dwflsyms.sh
+++ b/tests/run-dwflsyms.sh
@@ -72,17 +72,17 @@ cat > testfile.symtab.in <<\EOF
   32: SECTION	LOCAL	 (0) 0
   33: FILE	LOCAL	crtstuff.c (0) 0
   34: OBJECT	LOCAL	__JCR_LIST__ (0) 0x200de0
-  35: FUNC	LOCAL	deregister_tm_clones (0) 0x710, rel: 0x710
-  36: FUNC	LOCAL	register_tm_clones (0) 0x740, rel: 0x740
-  37: FUNC	LOCAL	__do_global_dtors_aux (0) 0x780, rel: 0x780
+  35: FUNC	LOCAL	deregister_tm_clones (0) 0x710, rel: 0x710 (.text)
+  36: FUNC	LOCAL	register_tm_clones (0) 0x740, rel: 0x740 (.text)
+  37: FUNC	LOCAL	__do_global_dtors_aux (0) 0x780, rel: 0x780 (.text)
   38: OBJECT	LOCAL	completed.6137 (1) 0x20103c
   39: OBJECT	LOCAL	__do_global_dtors_aux_fini_array_entry (0) 0x200dd8
-  40: FUNC	LOCAL	frame_dummy (0) 0x7c0, rel: 0x7c0
+  40: FUNC	LOCAL	frame_dummy (0) 0x7c0, rel: 0x7c0 (.text)
   41: OBJECT	LOCAL	__frame_dummy_init_array_entry (0) 0x200dd0
   42: FILE	LOCAL	foo.c (0) 0
   43: FILE	LOCAL	bar.c (0) 0
   44: OBJECT	LOCAL	b1 (4) 0x201034
-  45: FUNC	LOCAL	foo (20) 0x814, rel: 0x814
+  45: FUNC	LOCAL	foo (20) 0x814, rel: 0x814 (.text)
   46: FILE	LOCAL	crtstuff.c (0) 0
   47: OBJECT	LOCAL	__FRAME_END__ (0) 0xa58
   48: OBJECT	LOCAL	__JCR_END__ (0) 0x200de0
@@ -91,28 +91,28 @@ cat > testfile.symtab.in <<\EOF
   51: OBJECT	LOCAL	_DYNAMIC (0) 0x200df0
   52: NOTYPE	LOCAL	__init_array_start (0) 0x200dd0
   53: OBJECT	LOCAL	_GLOBAL_OFFSET_TABLE_ (0) 0x201000
-  54: FUNC	GLOBAL	__libc_csu_fini (2) 0x8f0, rel: 0x8f0
+  54: FUNC	GLOBAL	__libc_csu_fini (2) 0x8f0, rel: 0x8f0 (.text)
   55: NOTYPE	WEAK	_ITM_deregisterTMCloneTable (0) 0
   56: NOTYPE	WEAK	data_start (0) 0x201030
   57: NOTYPE	GLOBAL	_edata (0) 0x20103c
-  58: FUNC	GLOBAL	bar (44) 0x828, rel: 0x828
-  59: FUNC	GLOBAL	_fini (0) 0x8f4, rel: 0x8f4
+  58: FUNC	GLOBAL	bar (44) 0x828, rel: 0x828 (.text)
+  59: FUNC	GLOBAL	_fini (0) 0x8f4, rel: 0x8f4 (.fini)
   60: FUNC	GLOBAL	__libc_start_main@@GLIBC_2.2.5 (0) 0
   61: NOTYPE	GLOBAL	__data_start (0) 0x201030
   62: NOTYPE	WEAK	__gmon_start__ (0) 0
   63: OBJECT	GLOBAL	__dso_handle (0) 0x200de8
   64: OBJECT	GLOBAL	_IO_stdin_used (4) 0x900
   65: OBJECT	GLOBAL	b2 (4) 0x201038
-  66: FUNC	GLOBAL	__libc_csu_init (137) 0x860, rel: 0x860
+  66: FUNC	GLOBAL	__libc_csu_init (137) 0x860, rel: 0x860 (.text)
   67: NOTYPE	GLOBAL	_end (0) 0x201040
-  68: FUNC	GLOBAL	_start (0) 0x6e0, rel: 0x6e0
+  68: FUNC	GLOBAL	_start (0) 0x6e0, rel: 0x6e0 (.text)
   69: NOTYPE	GLOBAL	__bss_start (0) 0x20103c
-  70: FUNC	GLOBAL	main (35) 0x7f0, rel: 0x7f0
+  70: FUNC	GLOBAL	main (35) 0x7f0, rel: 0x7f0 (.text)
   71: NOTYPE	WEAK	_Jv_RegisterClasses (0) 0
   72: OBJECT	GLOBAL	__TMC_END__ (0) 0x201040
   73: NOTYPE	WEAK	_ITM_registerTMCloneTable (0) 0
   74: FUNC	WEAK	__cxa_finalize@@GLIBC_2.2.5 (0) 0
-  75: FUNC	GLOBAL	_init (0) 0x680, rel: 0x680
+  75: FUNC	GLOBAL	_init (0) 0x680, rel: 0x680 (.init)
 EOF
 
 cat > testfile.symtab_pl.in <<\EOF
@@ -151,17 +151,17 @@ cat > testfile.symtab_pl.in <<\EOF
   32: SECTION	LOCAL	 (0) 0
   33: FILE	LOCAL	crtstuff.c (0) 0
   34: OBJECT	LOCAL	__JCR_LIST__ (0) 0x3000200de0
-  35: FUNC	LOCAL	deregister_tm_clones (0) 0x3000000710, rel: 0x710
-  36: FUNC	LOCAL	register_tm_clones (0) 0x3000000740, rel: 0x740
-  37: FUNC	LOCAL	__do_global_dtors_aux (0) 0x3000000780, rel: 0x780
+  35: FUNC	LOCAL	deregister_tm_clones (0) 0x3000000710, rel: 0x710 (.text)
+  36: FUNC	LOCAL	register_tm_clones (0) 0x3000000740, rel: 0x740 (.text)
+  37: FUNC	LOCAL	__do_global_dtors_aux (0) 0x3000000780, rel: 0x780 (.text)
   38: OBJECT	LOCAL	completed.6137 (1) 0x300020103c
   39: OBJECT	LOCAL	__do_global_dtors_aux_fini_array_entry (0) 0x3000200dd8
-  40: FUNC	LOCAL	frame_dummy (0) 0x30000007c0, rel: 0x7c0
+  40: FUNC	LOCAL	frame_dummy (0) 0x30000007c0, rel: 0x7c0 (.text)
   41: OBJECT	LOCAL	__frame_dummy_init_array_entry (0) 0x3000200dd0
   42: FILE	LOCAL	foo.c (0) 0
   43: FILE	LOCAL	bar.c (0) 0
   44: OBJECT	LOCAL	b1 (4) 0x3000201034
-  45: FUNC	LOCAL	foo (20) 0x3000000814, rel: 0x814
+  45: FUNC	LOCAL	foo (20) 0x3000000814, rel: 0x814 (.text)
   46: FILE	LOCAL	crtstuff.c (0) 0
   47: OBJECT	LOCAL	__FRAME_END__ (0) 0x3000000a58
   48: OBJECT	LOCAL	__JCR_END__ (0) 0x3000200de0
@@ -170,28 +170,28 @@ cat > testfile.symtab_pl.in <<\EOF
   51: OBJECT	LOCAL	_DYNAMIC (0) 0x3000200df0
   52: NOTYPE	LOCAL	__init_array_start (0) 0x3000200dd0
   53: OBJECT	LOCAL	_GLOBAL_OFFSET_TABLE_ (0) 0x3000201000
-  54: FUNC	GLOBAL	__libc_csu_fini (2) 0x30000008f0, rel: 0x8f0
+  54: FUNC	GLOBAL	__libc_csu_fini (2) 0x30000008f0, rel: 0x8f0 (.text)
   55: NOTYPE	WEAK	_ITM_deregisterTMCloneTable (0) 0
   56: NOTYPE	WEAK	data_start (0) 0x3000201030
   57: NOTYPE	GLOBAL	_edata (0) 0x300020103c
-  58: FUNC	GLOBAL	bar (44) 0x3000000828, rel: 0x828
-  59: FUNC	GLOBAL	_fini (0) 0x30000008f4, rel: 0x8f4
+  58: FUNC	GLOBAL	bar (44) 0x3000000828, rel: 0x828 (.text)
+  59: FUNC	GLOBAL	_fini (0) 0x30000008f4, rel: 0x8f4 (.fini)
   60: FUNC	GLOBAL	__libc_start_main@@GLIBC_2.2.5 (0) 0
   61: NOTYPE	GLOBAL	__data_start (0) 0x3000201030
   62: NOTYPE	WEAK	__gmon_start__ (0) 0
   63: OBJECT	GLOBAL	__dso_handle (0) 0x3000200de8
   64: OBJECT	GLOBAL	_IO_stdin_used (4) 0x3000000900
   65: OBJECT	GLOBAL	b2 (4) 0x3000201038
-  66: FUNC	GLOBAL	__libc_csu_init (137) 0x3000000860, rel: 0x860
+  66: FUNC	GLOBAL	__libc_csu_init (137) 0x3000000860, rel: 0x860 (.text)
   67: NOTYPE	GLOBAL	_end (0) 0x3000201040
-  68: FUNC	GLOBAL	_start (0) 0x30000006e0, rel: 0x6e0
+  68: FUNC	GLOBAL	_start (0) 0x30000006e0, rel: 0x6e0 (.text)
   69: NOTYPE	GLOBAL	__bss_start (0) 0x300020103c
-  70: FUNC	GLOBAL	main (35) 0x30000007f0, rel: 0x7f0
+  70: FUNC	GLOBAL	main (35) 0x30000007f0, rel: 0x7f0 (.text)
   71: NOTYPE	WEAK	_Jv_RegisterClasses (0) 0
   72: OBJECT	GLOBAL	__TMC_END__ (0) 0x3000201040
   73: NOTYPE	WEAK	_ITM_registerTMCloneTable (0) 0
   74: FUNC	WEAK	__cxa_finalize@@GLIBC_2.2.5 (0) 0
-  75: FUNC	GLOBAL	_init (0) 0x3000000680, rel: 0x680
+  75: FUNC	GLOBAL	_init (0) 0x3000000680, rel: 0x680 (.init)
 EOF
 
 cat > testfile.dynsym.in <<\EOF
@@ -205,22 +205,22 @@ cat > testfile.dynsym.in <<\EOF
    7: FUNC	WEAK	__cxa_finalize (0) 0
    8: NOTYPE	GLOBAL	_edata (0) 0x20103c
    9: NOTYPE	GLOBAL	_end (0) 0x201040
-  10: FUNC	GLOBAL	__libc_csu_init (137) 0x860, rel: 0x860
+  10: FUNC	GLOBAL	__libc_csu_init (137) 0x860, rel: 0x860 (.text)
   11: NOTYPE	GLOBAL	__bss_start (0) 0x20103c
-  12: FUNC	GLOBAL	main (35) 0x7f0, rel: 0x7f0
-  13: FUNC	GLOBAL	__libc_csu_fini (2) 0x8f0, rel: 0x8f0
+  12: FUNC	GLOBAL	main (35) 0x7f0, rel: 0x7f0 (.text)
+  13: FUNC	GLOBAL	__libc_csu_fini (2) 0x8f0, rel: 0x8f0 (.text)
 EOF
 
 cat > testfile.minsym.in <<\EOF
    0: NOTYPE	LOCAL	 (0) 0
    1: SECTION	LOCAL	 (0) 0x238
-   2: FUNC	LOCAL	deregister_tm_clones (0) 0x710, rel: 0x710
-   3: FUNC	LOCAL	register_tm_clones (0) 0x740, rel: 0x740
-   4: FUNC	LOCAL	__do_global_dtors_aux (0) 0x780, rel: 0x780
+   2: FUNC	LOCAL	deregister_tm_clones (0) 0x710, rel: 0x710 (.text)
+   3: FUNC	LOCAL	register_tm_clones (0) 0x740, rel: 0x740 (.text)
+   4: FUNC	LOCAL	__do_global_dtors_aux (0) 0x780, rel: 0x780 (.text)
    5: OBJECT	LOCAL	__do_global_dtors_aux_fini_array_entry (0) 0x200dd8
-   6: FUNC	LOCAL	frame_dummy (0) 0x7c0, rel: 0x7c0
+   6: FUNC	LOCAL	frame_dummy (0) 0x7c0, rel: 0x7c0 (.text)
    7: OBJECT	LOCAL	__frame_dummy_init_array_entry (0) 0x200dd0
-   8: FUNC	LOCAL	foo (20) 0x814, rel: 0x814
+   8: FUNC	LOCAL	foo (20) 0x814, rel: 0x814 (.text)
    9: NOTYPE	LOCAL	__init_array_end (0) 0x200dd8
   10: NOTYPE	LOCAL	__init_array_start (0) 0x200dd0
   11: SECTION	LOCAL	 (0) 0x238
@@ -257,26 +257,26 @@ cat > testfile.minsym.in <<\EOF
   42: FUNC	WEAK	__cxa_finalize (0) 0
   43: NOTYPE	GLOBAL	_edata (0) 0x20103c
   44: NOTYPE	GLOBAL	_end (0) 0x201040
-  45: FUNC	GLOBAL	__libc_csu_init (137) 0x860, rel: 0x860
+  45: FUNC	GLOBAL	__libc_csu_init (137) 0x860, rel: 0x860 (.text)
   46: NOTYPE	GLOBAL	__bss_start (0) 0x20103c
-  47: FUNC	GLOBAL	main (35) 0x7f0, rel: 0x7f0
-  48: FUNC	GLOBAL	__libc_csu_fini (2) 0x8f0, rel: 0x8f0
-  49: FUNC	GLOBAL	bar (44) 0x828, rel: 0x828
-  50: FUNC	GLOBAL	_fini (0) 0x8f4, rel: 0x8f4
-  51: FUNC	GLOBAL	_start (0) 0x6e0, rel: 0x6e0
-  52: FUNC	GLOBAL	_init (0) 0x680, rel: 0x680
+  47: FUNC	GLOBAL	main (35) 0x7f0, rel: 0x7f0 (.text)
+  48: FUNC	GLOBAL	__libc_csu_fini (2) 0x8f0, rel: 0x8f0 (.text)
+  49: FUNC	GLOBAL	bar (44) 0x828, rel: 0x828 (.text)
+  50: FUNC	GLOBAL	_fini (0) 0x8f4, rel: 0x8f4 (.fini)
+  51: FUNC	GLOBAL	_start (0) 0x6e0, rel: 0x6e0 (.text)
+  52: FUNC	GLOBAL	_init (0) 0x680, rel: 0x680 (.init)
 EOF
 
 cat > testfile.minsym_pl.in <<\EOF
    0: NOTYPE	LOCAL	 (0) 0
    1: SECTION	LOCAL	 (0) 0x3000000238
-   2: FUNC	LOCAL	deregister_tm_clones (0) 0x3000000710, rel: 0x710
-   3: FUNC	LOCAL	register_tm_clones (0) 0x3000000740, rel: 0x740
-   4: FUNC	LOCAL	__do_global_dtors_aux (0) 0x3000000780, rel: 0x780
+   2: FUNC	LOCAL	deregister_tm_clones (0) 0x3000000710, rel: 0x710 (.text)
+   3: FUNC	LOCAL	register_tm_clones (0) 0x3000000740, rel: 0x740 (.text)
+   4: FUNC	LOCAL	__do_global_dtors_aux (0) 0x3000000780, rel: 0x780 (.text)
    5: OBJECT	LOCAL	__do_global_dtors_aux_fini_array_entry (0) 0x3000200dd8
-   6: FUNC	LOCAL	frame_dummy (0) 0x30000007c0, rel: 0x7c0
+   6: FUNC	LOCAL	frame_dummy (0) 0x30000007c0, rel: 0x7c0 (.text)
    7: OBJECT	LOCAL	__frame_dummy_init_array_entry (0) 0x3000200dd0
-   8: FUNC	LOCAL	foo (20) 0x3000000814, rel: 0x814
+   8: FUNC	LOCAL	foo (20) 0x3000000814, rel: 0x814 (.text)
    9: NOTYPE	LOCAL	__init_array_end (0) 0x3000200dd8
   10: NOTYPE	LOCAL	__init_array_start (0) 0x3000200dd0
   11: SECTION	LOCAL	 (0) 0x3000000238
@@ -313,14 +313,14 @@ cat > testfile.minsym_pl.in <<\EOF
   42: FUNC	WEAK	__cxa_finalize (0) 0
   43: NOTYPE	GLOBAL	_edata (0) 0x300020103c
   44: NOTYPE	GLOBAL	_end (0) 0x3000201040
-  45: FUNC	GLOBAL	__libc_csu_init (137) 0x3000000860, rel: 0x860
+  45: FUNC	GLOBAL	__libc_csu_init (137) 0x3000000860, rel: 0x860 (.text)
   46: NOTYPE	GLOBAL	__bss_start (0) 0x300020103c
-  47: FUNC	GLOBAL	main (35) 0x30000007f0, rel: 0x7f0
-  48: FUNC	GLOBAL	__libc_csu_fini (2) 0x30000008f0, rel: 0x8f0
-  49: FUNC	GLOBAL	bar (44) 0x3000000828, rel: 0x828
-  50: FUNC	GLOBAL	_fini (0) 0x30000008f4, rel: 0x8f4
-  51: FUNC	GLOBAL	_start (0) 0x30000006e0, rel: 0x6e0
-  52: FUNC	GLOBAL	_init (0) 0x3000000680, rel: 0x680
+  47: FUNC	GLOBAL	main (35) 0x30000007f0, rel: 0x7f0 (.text)
+  48: FUNC	GLOBAL	__libc_csu_fini (2) 0x30000008f0, rel: 0x8f0 (.text)
+  49: FUNC	GLOBAL	bar (44) 0x3000000828, rel: 0x828 (.text)
+  50: FUNC	GLOBAL	_fini (0) 0x30000008f4, rel: 0x8f4 (.fini)
+  51: FUNC	GLOBAL	_start (0) 0x30000006e0, rel: 0x6e0 (.text)
+  52: FUNC	GLOBAL	_init (0) 0x3000000680, rel: 0x680 (.init)
 EOF
 
 cat testfile.symtab.in \
@@ -352,14 +352,14 @@ sed s/0x3000/0x4200/g testfile.minsym_pl.in \
 
 testrun_compare ${abs_builddir}/dwflsyms -e testfilebasmin <<\EOF
    0: NOTYPE	LOCAL	 (0) 0
-   1: FUNC	LOCAL	foo (18) 0x400168, rel: 0x400168
+   1: FUNC	LOCAL	foo (18) 0x400168, rel: 0x400168 (.text)
    2: SECTION	LOCAL	 (0) 0x400120
    3: SECTION	LOCAL	 (0) 0x400144
    4: SECTION	LOCAL	 (0) 0x4001c0
    5: SECTION	LOCAL	 (0) 0x600258
-   6: FUNC	GLOBAL	_start (21) 0x4001a8, rel: 0x4001a8
-   7: FUNC	GLOBAL	main (33) 0x400144, rel: 0x400144
-   8: FUNC	GLOBAL	bar (44) 0x40017a, rel: 0x40017a
+   6: FUNC	GLOBAL	_start (21) 0x4001a8, rel: 0x4001a8 (.text)
+   7: FUNC	GLOBAL	main (33) 0x400144, rel: 0x400144 (.text)
+   8: FUNC	GLOBAL	bar (44) 0x40017a, rel: 0x40017a (.text)
 EOF
 
 exit 0
-- 
1.7.1

