]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdwfl: find_dynsym don't assume dynamic linker has adjusted DYNAMIC entries.
authorMark Wielaard <mjw@redhat.com>
Mon, 10 Nov 2014 13:49:20 +0000 (14:49 +0100)
committerMark Wielaard <mjw@redhat.com>
Thu, 13 Nov 2014 14:46:31 +0000 (15:46 +0100)
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
libdwfl/dwfl_module_getdwarf.c
tests/ChangeLog
tests/Makefile.am
tests/vdsosyms.c [new file with mode: 0644]

index f788f3345616f3f41cfaa095edc4a0dbf509f7f4..78314c4a3a607463e850f106f412f51ebfa4d67a 100644 (file)
@@ -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.
index e705f57e8e68a3d3cbafffaa615e3a0e00f87964..adb94b4c591092a5c7e0ffe41b7f703ac02f974c 100644 (file)
@@ -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;
        }
     }
 }
index 277876b72b623aaba3185062e9814fd3ab5ed8f4..798bb7aee8d04da596dfe2025331011ea4e0a773 100644 (file)
@@ -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-09-10  Petr Machata  <pmachata@redhat.com>
 
        * dwarf-getmacros.c: Update to use the new macro iteration
index 09909d28360b2e03a8b51c5c2b96d220296f37ba..dcaf4f65fa6a642add540b6cc7eb7b75ee3e1818 100644 (file)
@@ -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 (file)
index 0000000..831caf4
--- /dev/null
@@ -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__ */