This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] libdwfl: find_dynsym don't assume dynamic linker has adjusted DYNAMIC entries.


We had a regression on i686 where we failed to find the vdso symbol table
and so couldn't resolve any addresses pointing into the vdso to function
names. This was because at least on i686 we have to rely on the phdrs and
the DYNAMIC segment to locate the symbol table.

The fix look large, but ignoring whitespace it is actually pretty small:

diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index e705f57..adb94b4 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -718,10 +718,12 @@ find_dynsym (Dwfl_Module *mod)
              break;
            }
 
-         /* Translate pointers into file offsets.  */
+         /* 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, mod->main_bias, phnum, i_max, addrs,
-                       offs);
+           find_offsets (mod->main.elf, adjust, phnum, i_max, addrs, offs);
 
            /* Figure out the size of the symbol table.  */
            if (offs[i_hash] != 0)
@@ -824,9 +826,18 @@ find_dynsym (Dwfl_Module *mod)
                    mod->symfile = &mod->main;
                    mod->symerr = DWFL_E_NOERROR;
                  }
-             return;
              }
          }
+
+         /* First try unadjusted, like ELF files from disk, vdso.
+            Then try for already adjusted dynamic section, like ELF
+            from remote memory.  */
+         translate_offs (0);
+         if (mod->symfile == NULL)
+           translate_offs (mod->main_bias);
+
+         return;
+       }
     }
 }


<--->

commit 037505 "Fix resolving ELF symbols for live PIDs with deleted files"
changed find_dynsym to assume the PT_DYNAMIC entries had been adjusted by
the dynamic linker. That is often a correct assumption when the ELF image
comes from remote memory. But we cannot rely on that. In the case of the
vdso image the DYNAMIC segment has not been adjusted for example.

There is no good way to determine whether the DYNAMIC segment has or
hasn't been adjusted already to the load address by the dynamic linker.
So we just try twice. Once without and if the fails again with assuming
adjustments being applied.

Includes a new vdsosyms testcase that fails on i686 before and succeeds
after the fix.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
---
 libdwfl/ChangeLog              |   6 ++
 libdwfl/dwfl_module_getdwarf.c | 219 ++++++++++++++++++++++-------------------
 tests/ChangeLog                |   7 ++
 tests/Makefile.am              |   5 +-
 tests/vdsosyms.c               | 101 +++++++++++++++++++
 5 files changed, 232 insertions(+), 106 deletions(-)
 create mode 100644 tests/vdsosyms.c

diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index f788f33..78314c4 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,9 @@
+2014-11-10  Mark Wielaard  <mjw@redhat.com>
+
+	* dwfl_module_getdwarf.c (find_dynsym): New inner function
+	translate_offs that takes an adjust argument. Try finding
+	the symbol table with and without adjusting to main_bias.
+
 2014-09-26  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
 	Support NT_FILE for locating files.
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index e705f57..adb94b4 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -718,114 +718,125 @@ find_dynsym (Dwfl_Module *mod)
 	      break;
 	    }
 
-	  /* Translate pointers into file offsets.  */
-	  GElf_Off offs[i_max] = { 0, };
-	  find_offsets (mod->main.elf, mod->main_bias, 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.  */
+	  /* 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);
 
-	      const struct
+	    /* Figure out the size of the symbol table.  */
+	    if (offs[i_hash] != 0)
 	      {
-		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));
-
-		  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));
+		/* 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.  */
 
-	  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
+		const struct
 		{
-		  mod->symfile = &mod->main;
-		  mod->symerr = DWFL_E_NOERROR;
-		}
-	      return;
-	    }
+		  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));
+
+		    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);
+	  if (mod->symfile == NULL)
+	    translate_offs (mod->main_bias);
+
+	  return;
 	}
     }
 }
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 5c06d23..a048c71 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,10 @@
+2014-11-10  Mark Wielaard  <mjw@redhat.com>
+
+	* vdsosyms.c: New test.
+	* Makefile.am (check_PROGRAMS): Add vdsosyms.
+	(TESTS): Likewise.
+	(vdsosyms_LDADD): New variable.
+
 2014-10-06  Mark Wielaard  <mjw@redhat.com>
 
 	* run-aggregate-size.sh: Add testfile-sizes3.o test case.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 09909d2..dcaf4f6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -50,7 +50,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
 		  test-elf_cntl_gelf_getshdr dwflsyms dwfllines \
 		  dwfl-report-elf-align varlocs backtrace backtrace-child \
 		  backtrace-data backtrace-dwarf debuglink debugaltlink \
-		  buildid deleted deleted-lib.so aggregate_size
+		  buildid deleted deleted-lib.so aggregate_size vdsosyms
 
 asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
 	    asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -110,7 +110,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
 	run-backtrace-core-aarch64.sh \
 	run-backtrace-demangle.sh run-stack-d-test.sh run-stack-i-test.sh \
 	run-readelf-dwz-multi.sh run-allfcts-multi.sh run-deleted.sh \
-	run-linkmap-cut.sh run-aggregate-size.sh
+	run-linkmap-cut.sh run-aggregate-size.sh vdsosyms
 
 if !BIARCH
 export ELFUTILS_DISABLE_BIARCH = 1
@@ -415,6 +415,7 @@ deleted_LDADD = ./deleted-lib.so
 deleted_lib_so_LDFLAGS = -shared -rdynamic
 deleted_lib_so_CFLAGS = -fPIC
 aggregate_size_LDADD = $(libdw) $(libelf)
+vdsosyms_LDADD = $(libdw) $(libelf)
 
 if GCOV
 check: check-am coverage
diff --git a/tests/vdsosyms.c b/tests/vdsosyms.c
new file mode 100644
index 0000000..831caf4
--- /dev/null
+++ b/tests/vdsosyms.c
@@ -0,0 +1,101 @@
+/* Test program for getting symbol table from vdso module.
+   Copyright (C) 2014 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of 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/>.  */
+
+#include <config.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include ELFUTILS_HEADER(dwfl)
+
+#ifndef __linux__
+int
+main (int argc __attribute__ ((unused)), char **argv)
+{
+  printf ("Getting the vdso is unsupported.\n");
+  return 77;
+}
+#else /* __linux__ */
+static bool vdso_seen = false;
+
+static int
+module_callback (Dwfl_Module *mod, void **userdata __attribute__((unused)),
+		 const char *name, Dwarf_Addr start __attribute__((unused)),
+		 void *arg __attribute__((unused)))
+{
+  /* We can only recognize the vdso by inspecting the "magic name".  */
+  if (strncmp ("[vdso: ", name, 7) == 0)
+    {
+      int syms = dwfl_module_getsymtab (mod);
+      printf ("vdso syms: %d\n", syms);
+      if (syms < 0)
+	error (2, 0, "dwfl_module_getsymtab: %s", dwfl_errmsg (-1));
+      vdso_seen = true;
+
+      for (int i = 0; i < syms; i++)
+	{
+	  GElf_Sym sym;
+	  GElf_Addr addr;
+	  const char *sname = dwfl_module_getsym_info (mod, i, &sym, &addr,
+						       NULL, NULL, NULL);
+	  assert (sname != NULL);
+	  printf ("%d: '%s' %" PRIx64 " (%" PRIx64 ")\n",
+		  i, sname, sym.st_value, addr);
+	}
+    }
+
+  return DWARF_CB_OK;
+}
+
+int
+main (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused)))
+{
+  static char *debuginfo_path;
+  static const Dwfl_Callbacks proc_callbacks =
+    {
+      .find_debuginfo = dwfl_standard_find_debuginfo,
+      .debuginfo_path = &debuginfo_path,
+
+      .find_elf = dwfl_linux_proc_find_elf,
+    };
+  Dwfl *dwfl = dwfl_begin (&proc_callbacks);
+  if (dwfl == NULL)
+    error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+
+  /* Take our parent as "arbitrary" process to inspect.  */
+  pid_t pid = getppid();
+
+  int result = dwfl_linux_proc_report (dwfl, pid);
+  if (result < 0)
+    error (2, 0, "dwfl_linux_proc_report: %s", dwfl_errmsg (-1));
+  else if (result > 0)
+    error (2, result, "dwfl_linux_proc_report");
+
+  if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+    error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+
+  if (dwfl_getmodules (dwfl, module_callback, NULL, 0) != 0)
+    error (1, 0, "dwfl_getmodules: %s", dwfl_errmsg (-1));
+
+  return vdso_seen ? 0 : -1;
+}
+
+#endif /* ! __linux__ */
-- 
1.8.3.1


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