public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* warning: Could not load shared library symbols for linux-vdso.so.1.
@ 2016-08-11 11:46 Yao Qi
  2016-08-11 15:57 ` Pedro Alves
  0 siblings, 1 reply; 10+ messages in thread
From: Yao Qi @ 2016-08-11 11:46 UTC (permalink / raw)
  To: gdb-patches


Hi,
When I test gdb master/7.12 with glibc mainline on aarch64, I got the
following fail,

(gdb) core-file build-gdb/gdb/testsuite/outputs/gdb.base/corefile/corefile.core^M
[New LWP 2362]^M
warning: Could not load shared library symbols for linux-vdso.so.1.^M
Do you need "set solib-search-path" or "set sysroot"?^M
Core was generated by `build-gdb/gdb/testsuite/outputs/gdb.base/corefile/'.^M
Program terminated with signal SIGABRT, Aborted.^M
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:58^M
58      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.^M
(gdb) FAIL: gdb.base/corefile.exp: core-file warning-free

Looks the warning "Could not load shared library symbols for
linux-vdso.so.1." makes the trouble.  It was discussed and fixed in this
thread https://sourceware.org/ml/gdb-patches/2014-09/msg00361.html  In
the fix, we filter out the vDSO module if l_ld is in the range of vDSO
module.  However, it only works for native live debugging.  We can
know the starting address of vDSO by AT_SYSINFO_EHDR, but we don't know
size of vDSO when target is corefile.  In my observation, vDSO is _not_
dumped in corefile at all.

One version of Pedro's patch uses "(so->lm_info->l_addr_inferior
== vsyscall_addr)" to check whether "so" is vDSO (it works for me in my
fail here), but we changed it to range checking in order to handle
"prelinked" vDSO.  I go through the mail thread above, but I don't know
how vDSO is "prelinked".

Alternatively, we can filter vDSO by name matching, like "",
"linux-vdso.so.1" and "linux-gate.so.1", which was proposed by Doug
too.  Is it a good approach to fix this problem?

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: warning: Could not load shared library symbols for linux-vdso.so.1.
  2016-08-11 11:46 warning: Could not load shared library symbols for linux-vdso.so.1 Yao Qi
@ 2016-08-11 15:57 ` Pedro Alves
  2016-08-12 10:28   ` Yao Qi
  0 siblings, 1 reply; 10+ messages in thread
From: Pedro Alves @ 2016-08-11 15:57 UTC (permalink / raw)
  To: Yao Qi, gdb-patches

On 08/11/2016 12:46 PM, Yao Qi wrote:
> 
> Hi,
> When I test gdb master/7.12 with glibc mainline on aarch64, I got the
> following fail,
> 
> (gdb) core-file build-gdb/gdb/testsuite/outputs/gdb.base/corefile/corefile.core^M
> [New LWP 2362]^M
> warning: Could not load shared library symbols for linux-vdso.so.1.^M
> Do you need "set solib-search-path" or "set sysroot"?^M
> Core was generated by `build-gdb/gdb/testsuite/outputs/gdb.base/corefile/'.^M
> Program terminated with signal SIGABRT, Aborted.^M
> #0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:58^M
> 58      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.^M
> (gdb) FAIL: gdb.base/corefile.exp: core-file warning-free

Hmm.  gdb.base/vdso-warning.exp was written to expose this even
on Fedora/RHEL, but it isn't simply because it doesn't try loading
a core dump.

> 
> Looks the warning "Could not load shared library symbols for
> linux-vdso.so.1." makes the trouble.  It was discussed and fixed in this
> thread https://sourceware.org/ml/gdb-patches/2014-09/msg00361.html  In
> the fix, we filter out the vDSO module if l_ld is in the range of vDSO
> module.  However, it only works for native live debugging.  We can
> know the starting address of vDSO by AT_SYSINFO_EHDR, but we don't know
> size of vDSO when target is corefile.  In my observation, vDSO is _not_
> dumped in corefile at all.
> 
> One version of Pedro's patch uses "(so->lm_info->l_addr_inferior
> == vsyscall_addr)" to check whether "so" is vDSO (it works for me in my
> fail here), but we changed it to range checking in order to handle
> "prelinked" vDSO.  I go through the mail thread above, but I don't know
> how vDSO is "prelinked".
> 
> Alternatively, we can filter vDSO by name matching, like "",
> "linux-vdso.so.1" and "linux-gate.so.1", which was proposed by Doug
> too.  Is it a good approach to fix this problem?
> 

How about something around this?

From a49ac46860c9770dd57812a13f36105361825b01 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 11 Aug 2016 16:04:37 +0100
Subject: [PATCH] Make vDSO detection work with core files

- Make gdb.base/vdso-warning.exp test loading a core.  With
  LD_DEBUG=unused, we see the warning on systems with local glibc
  patches as well (Fedora/RHEL).

- When debugging a core, we can only tell the starting address of the
  vDSO.  Make that a valid result out of linux_vsyscall_range.

- When we can only tell the starting address, do the simpler
  lm_info->l_addr_inferior check.

Older kernels lose, but I don't think older kernels should hold us
back.

Is there an easy way to check whether we're in the vdso prelinked
situation just from doing some address comparisions?  I feel like that
should be possible, but I didn't think it through.  It is is indeed
possible, we could alwayy skip the /proc/pid/maps parsing entirely
even against live processes, on modern kernels.
---
 gdb/linux-tdep.c                        | 22 ++++++----
 gdb/solib-svr4.c                        | 33 +++++++++-----
 gdb/testsuite/gdb.base/vdso-warning.exp | 76 ++++++++++++++++++++++-----------
 3 files changed, 87 insertions(+), 44 deletions(-)

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index ab110b0..6bc7a0d 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -2287,16 +2287,21 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
   long pid;
   char *data;
 
-  /* Can't access /proc if debugging a core file.  */
-  if (!target_has_execution)
+  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
     return 0;
 
+  /* Alright, we know the starting address.  Let's see if we can find
+     the end address.  */
+  range->length = 0;
+
+  /* Can't access /proc if debugging a core file, and NT_FILE notes
+     don't include the vDSO mapping.  */
+  if (!target_has_execution)
+    return 1;
+
   /* We need to know the real target PID to access /proc.  */
   if (current_inferior ()->fake_pid_p)
-    return 0;
-
-  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
-    return 0;
+    return 1;
 
   pid = current_inferior ()->pid;
 
@@ -2330,8 +2335,7 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
 		p++;
 	      endaddr = strtoulst (p, &p, 16);
 	      range->length = endaddr - addr;
-	      do_cleanups (cleanup);
-	      return 1;
+	      break;
 	    }
 	}
 
@@ -2340,7 +2344,7 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
   else
     warning (_("unable to open /proc file '%s'"), filename);
 
-  return 0;
+  return 1;
 }
 
 /* Implementation of the "vsyscall_range" gdbarch hook.  Handles
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index fe36d45..bd97bd6 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -1539,8 +1539,7 @@ svr4_current_sos (void)
   /* Filter out the vDSO module, if present.  Its symbol file would
      not be found on disk.  The vDSO/vsyscall's OBJFILE is instead
      managed by symfile-mem.c:add_vsyscall_page.  */
-  if (gdbarch_vsyscall_range (target_gdbarch (), &vsyscall_range)
-      && vsyscall_range.length != 0)
+  if (gdbarch_vsyscall_range (target_gdbarch (), &vsyscall_range))
     {
       struct so_list **sop;
 
@@ -1549,14 +1548,14 @@ svr4_current_sos (void)
 	{
 	  struct so_list *so = *sop;
 
-	  /* We can't simply match the vDSO by starting address alone,
-	     because lm_info->l_addr_inferior (and also l_addr) do not
-	     necessarily represent the real starting address of the
-	     ELF if the vDSO's ELF itself is "prelinked".  The l_ld
-	     field (the ".dynamic" section of the shared object)
-	     always points at the absolute/resolved address though.
-	     So check whether that address is inside the vDSO's
-	     mapping instead.
+	  /* Simply matching the vDSO by starting address alone might
+	     not work, because lm_info->l_addr_inferior (and also
+	     l_addr) do not necessarily represent the real starting
+	     address of the ELF if the vDSO's ELF itself is
+	     "prelinked".  The l_ld field (the ".dynamic" section of
+	     the shared object) always points at the absolute/resolved
+	     address though.  So if we know the range, check whether
+	     that address is inside the vDSO's mapping instead.
 
 	     E.g., on Linux 3.16 (x86_64) the vDSO is a regular
 	     0-based ELF, and we see:
@@ -1590,7 +1589,19 @@ svr4_current_sos (void)
 	      [...]
 		[ 9] .dynamic DYNAMIC ffffffffff700580 000580 0000f0
 	  */
-	  if (address_in_mem_range (so->lm_info->l_ld, &vsyscall_range))
+	  if (vsyscall_range.length != 0
+	      && address_in_mem_range (so->lm_info->l_ld, &vsyscall_range))
+	    {
+	      *sop = so->next;
+	      free_so (so);
+	      break;
+	    }
+
+	  /* However, if we only know the starting address address,
+	     try a simple match.  XXX: Is there an easy "SO is
+	     prelinked" check we could do here?  */
+	  if (vsyscall_range.length == 0
+	      && so->lm_info->l_addr_inferior == vsyscall_range.start)
 	    {
 	      *sop = so->next;
 	      free_so (so);
diff --git a/gdb/testsuite/gdb.base/vdso-warning.exp b/gdb/testsuite/gdb.base/vdso-warning.exp
index af2b2b0..aeb85a2 100644
--- a/gdb/testsuite/gdb.base/vdso-warning.exp
+++ b/gdb/testsuite/gdb.base/vdso-warning.exp
@@ -13,42 +13,70 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+# Test that on Linux, we don't warn about not finding the vDSO.  E.g.:
+#
+#   warning: Could not load shared library symbols for linux-vdso.so.1.
+
 standard_testfile
 
 if { [prepare_for_testing "failed to prepare" ${testfile} $srcfile] } {
     return -1
 }
 
-gdb_breakpoint "main"
+with_test_prefix "setup" {
+    gdb_breakpoint "main"
 
-# At least some versions of Fedora/RHEL glibc have local patches that
-# hide the vDSO.  This lines re-exposes it.  See PR libc/13097,
-# comment 2.  There's no support for passing environment variables in
-# the remote protocol, but that's OK -- if we're testing against a
-# glibc that doesn't list the vDSO without this, the test should still
-# pass.
-gdb_test_no_output "set environment LD_DEBUG=unused"
+    # At least some versions of Fedora/RHEL glibc have local patches that
+    # hide the vDSO.  This lines re-exposes it.  See PR libc/13097,
+    # comment 2.  There's no support for passing environment variables in
+    # the remote protocol, but that's OK -- if we're testing against a
+    # glibc that doesn't list the vDSO without this, the test should still
+    # pass.
+    gdb_test_no_output "set environment LD_DEBUG=unused"
+}
 
-gdb_run_cmd
+proc test_no_vdso {command} {
+    global srcfile
+    global gdb_prompt
 
-set test "stop without warning"
-gdb_test_multiple "" $test {
-    -re "Could not load shared library symbols .*\r\n$gdb_prompt $" {
-	fail $test
+    set message "startup"
+    gdb_test_multiple "$command" $message {
+	-re "Could not load shared library symbols .*\r\n$gdb_prompt $" {
+	    fail $message
+	}
+	-re "main \\(\\) at .*$srcfile.*\r\n$gdb_prompt $" {
+	    pass $message
+	}
     }
-    -re "\r\nBreakpoint \[0-9\]+, main .*\r\n$gdb_prompt $" {
-	pass $test
+
+    # Extra testing in case the warning changes and we miss updating
+    # the above.
+    set test "no vdso without symbols is listed"
+    gdb_test_multiple "info shared" $test {
+	-re "No\[^\r\n\]+linux-(vdso|gate).*$gdb_prompt $" {
+	    fail $test
+	}
+	-re "$gdb_prompt $" {
+	    pass $test
+	}
     }
 }
 
-# Extra testing in case the warning changes and we miss updating the
-# above.
-set test "no vdso without symbols is listed"
-gdb_test_multiple "info shared" $test {
-    -re "No\[^\r\n\]+linux-(vdso|gate).*$gdb_prompt $" {
-	fail $test
-    }
-    -re "$gdb_prompt $" {
-	pass $test
+# First, try a live process.
+with_test_prefix "run" {
+    gdb_run_cmd
+    test_no_vdso ""
+}
+
+# Now, dump a core, and reload it.
+with_test_prefix "core" {
+    set corefile [standard_output_file $testfile.core]
+    set core_supported [gdb_gcore_cmd "$corefile" "save a corefile"]
+    if {!$core_supported} {
+	return -1
     }
+
+    clean_restart ${testfile}
+
+    test_no_vdso "core-file $corefile"
 }
-- 
2.5.5


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: warning: Could not load shared library symbols for linux-vdso.so.1.
  2016-08-11 15:57 ` Pedro Alves
@ 2016-08-12 10:28   ` Yao Qi
  2016-08-19  1:05     ` Pedro Alves
  0 siblings, 1 reply; 10+ messages in thread
From: Yao Qi @ 2016-08-12 10:28 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Yao Qi, gdb-patches

Pedro Alves <palves@redhat.com> writes:

> Is there an easy way to check whether we're in the vdso prelinked
> situation just from doing some address comparisions?  I feel like that
> should be possible, but I didn't think it through.  It is is indeed
> possible, we could alwayy skip the /proc/pid/maps parsing entirely
> even against live processes, on modern kernels.

I don't know either.  However, looks vDSO prelink is done only on x86
and was already removed from kernel in 2014, as far as I can see.  The
base address of vDSO in kernel is VDSO_PRELINK, which is hard-coded.  I
am not sure we can rely on this hard-coded constant.

> -	  if (address_in_mem_range (so->lm_info->l_ld, &vsyscall_range))
> +	  if (vsyscall_range.length != 0
> +	      && address_in_mem_range (so->lm_info->l_ld, &vsyscall_range))
> +	    {
> +	      *sop = so->next;
> +	      free_so (so);
> +	      break;
> +	    }
> +
> +	  /* However, if we only know the starting address address,
> +	     try a simple match.  XXX: Is there an easy "SO is
> +	     prelinked" check we could do here?  */
> +	  if (vsyscall_range.length == 0
> +	      && so->lm_info->l_addr_inferior == vsyscall_range.start)
>  	    {
>  	      *sop = so->next;
>  	      free_so (so);

Patch is good to me.  One nit, we can merge these two condition checks,
so we can delete SO in the list in one place.

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: warning: Could not load shared library symbols for linux-vdso.so.1.
  2016-08-12 10:28   ` Yao Qi
@ 2016-08-19  1:05     ` Pedro Alves
  2016-08-19  9:59       ` Yao Qi
  0 siblings, 1 reply; 10+ messages in thread
From: Pedro Alves @ 2016-08-19  1:05 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 2365 bytes --]

On 08/12/2016 11:28 AM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> Is there an easy way to check whether we're in the vdso prelinked
>> situation just from doing some address comparisions?  I feel like that
>> should be possible, but I didn't think it through.  It is is indeed
>> possible, we could alwayy skip the /proc/pid/maps parsing entirely
>> even against live processes, on modern kernels.
> 
> I don't know either.  However, looks vDSO prelink is done only on x86
> and was already removed from kernel in 2014, as far as I can see.  The
> base address of vDSO in kernel is VDSO_PRELINK, which is hard-coded.  I
> am not sure we can rely on this hard-coded constant.

I tried to come up with some clean way to detect this, and failed.

This made me have second thoughts on the approach, since there's a remote
chance that we'll hide the wrong DSO, on older kernels.

I think I found an alternative fix.  

The idea is simply to read the vDSO bfd out of memory anyway even
when we weren't able to find its size from the mappings.  When 
elf_bfd_from_remote_memory is passed '0' for size, it figures
out the size of the elf in memory from the elf structure.  If we use
that as another source for the vdso address range, then
svr4_current_sos doesn't have to change at all.

Now, in order to do this, we need to move the add_vsyscall_page
call earlier, before svr4_current_dsos is ever called, in order
to read the vdso bfd out of memory before we ever first need to
filter out the vdso.  The cleanest I could do with the current
gdbarch_vsyscall_range-based design was to do the add_vsyscall_page
call from within gdbarch_vsyscall_range.  But that looks very ugly
to me, for reading symbols from a quite innocent looking gdbarch hook.

So I bit the bullet and made a custom Linux-specific
struct solib_ops that inherits svr4_so_ops, and overrides a couple
methods for vDSO awareness.  I think the end result is clearer
and probably more extensible if/when we decide to do the work necessary
to be able to show the vdso in "info sharedlibrary", without causing
could-not-find-file warnings.

I'm attaching two patches; the first implements the new solib_ops
instance, and then the second uses the vDSO's bfd size, fixing the
bug.

Passes testing on x86_64 Fedora 23 here, as well as the new test.

Let me know what you think.

Pedro Alves


[-- Attachment #2: 0001-Introduce-linux_solib_ops.patch --]
[-- Type: text/x-patch, Size: 36696 bytes --]

From f830ed7d1b3289c2a7d9da4fd92dd667ff3bb3f2 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Fri, 19 Aug 2016 01:11:16 +0100
Subject: [PATCH 1/2] Introduce linux_solib_ops

Add a custom solib_ops for Linux-based targets that extends
solib-svr4.c and then adds vDSO awareness to a couple solib_ops
methods.  This replaces the current scheme of including a bit of
vDSO-awareness in solib-svr4.c directly, but hidden (though not very
well) behind the gdbarch_vsyscall_range gdbarch hook.

The idea is to read the vDSO out of memory before current_sos() is
ever called, and the scheme doesn't allow that in a clean way.

This change allows centralizing all vDSO handling in linux-tdep.c, and
completely getting rid of that gdbarch hook.
symfile-mem.c:add_vsyscall_page could move to linux-tdep.c too, but I
have not done that yet.  I'd like to do that though in the future, and
also do a global s/vsyscall/vDSO/g throughout, since we're really
handling the vDSO, not the old vsyscall.  All the vsyscall references
are probably making all this code quite confusing to newcomers, I bet.
---
 gdb/gdbarch.c         |  23 ------
 gdb/gdbarch.h         |   9 ---
 gdb/gdbarch.sh        |   6 --
 gdb/linux-tdep.c      | 134 +++++++++++++++++++++++++++++++++--
 gdb/linux-tdep.h      |   4 ++
 gdb/mips-linux-tdep.c |   2 +-
 gdb/ppc-linux-tdep.c  |   2 +-
 gdb/solib-spu.c       |  18 ++---
 gdb/solib-svr4.c      | 190 +++++++++++++++-----------------------------------
 gdb/solib-svr4.h      |  21 ++++++
 gdb/solib.c           |   2 +-
 gdb/solib.h           |   4 ++
 gdb/symfile-mem.c     |  38 ++++------
 gdb/symfile-mem.h     |  30 ++++++++
 14 files changed, 269 insertions(+), 214 deletions(-)
 create mode 100644 gdb/symfile-mem.h

diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index af7359e..a07029e 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -330,7 +330,6 @@ struct gdbarch
   gdbarch_insn_is_jump_ftype *insn_is_jump;
   gdbarch_auxv_parse_ftype *auxv_parse;
   gdbarch_print_auxv_entry_ftype *print_auxv_entry;
-  gdbarch_vsyscall_range_ftype *vsyscall_range;
   gdbarch_infcall_mmap_ftype *infcall_mmap;
   gdbarch_infcall_munmap_ftype *infcall_munmap;
   gdbarch_gcc_target_options_ftype *gcc_target_options;
@@ -435,7 +434,6 @@ gdbarch_alloc (const struct gdbarch_info *info,
   gdbarch->insn_is_ret = default_insn_is_ret;
   gdbarch->insn_is_jump = default_insn_is_jump;
   gdbarch->print_auxv_entry = default_print_auxv_entry;
-  gdbarch->vsyscall_range = default_vsyscall_range;
   gdbarch->infcall_mmap = default_infcall_mmap;
   gdbarch->infcall_munmap = default_infcall_munmap;
   gdbarch->gcc_target_options = default_gcc_target_options;
@@ -682,7 +680,6 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of insn_is_jump, invalid_p == 0 */
   /* Skip verify of auxv_parse, has predicate.  */
   /* Skip verify of print_auxv_entry, invalid_p == 0 */
-  /* Skip verify of vsyscall_range, invalid_p == 0 */
   /* Skip verify of infcall_mmap, invalid_p == 0 */
   /* Skip verify of infcall_munmap, invalid_p == 0 */
   /* Skip verify of gcc_target_options, invalid_p == 0 */
@@ -1423,9 +1420,6 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: virtual_frame_pointer = <%s>\n",
                       host_address_to_string (gdbarch->virtual_frame_pointer));
   fprintf_unfiltered (file,
-                      "gdbarch_dump: vsyscall_range = <%s>\n",
-                      host_address_to_string (gdbarch->vsyscall_range));
-  fprintf_unfiltered (file,
                       "gdbarch_dump: vtable_function_descriptors = %s\n",
                       plongest (gdbarch->vtable_function_descriptors));
   fprintf_unfiltered (file,
@@ -4793,23 +4787,6 @@ set_gdbarch_print_auxv_entry (struct gdbarch *gdbarch,
   gdbarch->print_auxv_entry = print_auxv_entry;
 }
 
-int
-gdbarch_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
-{
-  gdb_assert (gdbarch != NULL);
-  gdb_assert (gdbarch->vsyscall_range != NULL);
-  if (gdbarch_debug >= 2)
-    fprintf_unfiltered (gdb_stdlog, "gdbarch_vsyscall_range called\n");
-  return gdbarch->vsyscall_range (gdbarch, range);
-}
-
-void
-set_gdbarch_vsyscall_range (struct gdbarch *gdbarch,
-                            gdbarch_vsyscall_range_ftype vsyscall_range)
-{
-  gdbarch->vsyscall_range = vsyscall_range;
-}
-
 CORE_ADDR
 gdbarch_infcall_mmap (struct gdbarch *gdbarch, CORE_ADDR size, unsigned prot)
 {
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index bc0f692..8c7a37b 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1471,15 +1471,6 @@ typedef void (gdbarch_print_auxv_entry_ftype) (struct gdbarch *gdbarch, struct u
 extern void gdbarch_print_auxv_entry (struct gdbarch *gdbarch, struct ui_file *file, CORE_ADDR type, CORE_ADDR val);
 extern void set_gdbarch_print_auxv_entry (struct gdbarch *gdbarch, gdbarch_print_auxv_entry_ftype *print_auxv_entry);
 
-/* Find the address range of the current inferior's vsyscall/vDSO, and
-   write it to *RANGE.  If the vsyscall's length can't be determined, a
-   range with zero length is returned.  Returns true if the vsyscall is
-   found, false otherwise. */
-
-typedef int (gdbarch_vsyscall_range_ftype) (struct gdbarch *gdbarch, struct mem_range *range);
-extern int gdbarch_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range);
-extern void set_gdbarch_vsyscall_range (struct gdbarch *gdbarch, gdbarch_vsyscall_range_ftype *vsyscall_range);
-
 /* Allocate SIZE bytes of PROT protected page aligned memory in inferior.
    PROT has GDB_MMAP_PROT_* bitmask format.
    Throw an error if it is not possible.  Returned address is always valid. */
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index d8e0eeb..8f5a893 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1114,12 +1114,6 @@ M:int:auxv_parse:gdb_byte **readptr, gdb_byte *endptr, CORE_ADDR *typep, CORE_AD
 # to FILE.
 m:void:print_auxv_entry:struct ui_file *file, CORE_ADDR type, CORE_ADDR val:file, type, val::default_print_auxv_entry::0
 
-# Find the address range of the current inferior's vsyscall/vDSO, and
-# write it to *RANGE.  If the vsyscall's length can't be determined, a
-# range with zero length is returned.  Returns true if the vsyscall is
-# found, false otherwise.
-m:int:vsyscall_range:struct mem_range *range:range::default_vsyscall_range::0
-
 # Allocate SIZE bytes of PROT protected page aligned memory in inferior.
 # PROT has GDB_MMAP_PROT_* bitmask format.
 # Throw an error if it is not possible.  Returned address is always valid.
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index ab110b0..91931eb 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -38,9 +38,17 @@
 #include "gdbcmd.h"
 #include "gdb_regex.h"
 #include "common/enum-flags.h"
+#include "solib.h"
+#include "solist.h"
+#include "solib-svr4.h"
+#include "symfile-mem.h"
 
 #include <ctype.h>
 
+/* Shared library operations for Linux.  This inherits svr4, and adds
+   support for the vDSO.  */
+struct target_so_ops linux_so_ops;
+
 /* This enum represents the values that the user can choose when
    informing the Linux kernel about which memory mappings will be
    dumped in a corefile.  They are described in the file
@@ -2343,11 +2351,15 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
   return 0;
 }
 
-/* Implementation of the "vsyscall_range" gdbarch hook.  Handles
-   caching, and defers the real work to linux_vsyscall_range_raw.  */
+/* Find the address range of the current inferior's vDSO.  If the
+   vDSO's length can't be determined, a range with zero length is
+   recorded.  Returns true if the vDSO is found, false otherwise.
+
+   Handles caching in struct linux_info, and defers the real work to
+   linux_vsyscall_range_raw.  */
 
 static int
-linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
+linux_vsyscall_range (struct gdbarch *gdbarch)
 {
   struct linux_info *info = get_linux_inferior_data ();
 
@@ -2361,9 +2373,8 @@ linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
 
   if (info->vsyscall_range_p < 0)
     return 0;
-
-  *range = info->vsyscall_range;
-  return 1;
+  else
+    return 1;
 }
 
 /* Symbols for linux_infcall_mmap's ARG_FLAGS; their Linux MAP_* system
@@ -2484,6 +2495,107 @@ show_use_coredump_filter (struct ui_file *file, int from_tty,
 			    " corefiles is %s.\n"), value);
 }
 
+/* Implement the "create_inferior_hook" target_so_ops method.  */
+
+static void
+linux_solib_create_inferior_hook (int from_tty)
+{
+  struct linux_info *info = get_linux_inferior_data ();
+
+  if (linux_vsyscall_range (target_gdbarch ()))
+    {
+      /* Add the vDSO symbols.  We need to do this before calling the
+	 svr4 version, because that refreshes the DSO list.  In some
+	 cases, such as when debugging core dumps, the information
+	 necessary for identifying the vDSO in order to filter it out
+	 can only be found by reading the vDSO out of memory.
+
+	 Note we don't pass SYMFILE_VERBOSE (i.e., pass FROM_TTY==0),
+	 because the action of loading the vDSO was not triggered by
+	 the user, even if the user typed "run" at the TTY.  */
+      add_vsyscall_page (&info->vsyscall_range, SYMFILE_DEFER_BP_RESET);
+    }
+
+  svr4_so_ops.solib_create_inferior_hook (from_tty);
+}
+
+/* Implement the "current_sos" target_so_ops method.  */
+
+static struct so_list *
+linux_current_sos (void)
+{
+  struct so_list *so_head = svr4_so_ops.current_sos ();
+  struct linux_info *info = get_linux_inferior_data ();
+
+  /* Filter out the vDSO module, if present.  Its symbol file would
+     not be found on disk.  The vDSO/vsyscall's OBJFILE is instead
+     managed by symfile-mem.c:add_vsyscall_page.  */
+  if (linux_vsyscall_range (target_gdbarch ())
+      && info->vsyscall_range.length != 0)
+    {
+      struct so_list **sop;
+
+      sop = &so_head;
+      while (*sop != NULL)
+	{
+	  struct so_list *so = *sop;
+	  struct svr4_lm_info *lm_info = (struct svr4_lm_info *) so->lm_info;
+
+	  /* We can't simply match the vDSO by starting address alone,
+	     because lm_info->l_addr_inferior (and also l_addr) do not
+	     necessarily represent the real starting address of the
+	     ELF if the vDSO's ELF itself is "prelinked".  The l_ld
+	     field (the ".dynamic" section of the shared object)
+	     always points at the absolute/resolved address though.
+	     So check whether that address is inside the vDSO's
+	     mapping instead.
+
+	     E.g., on Linux 3.16 (x86_64) the vDSO is a regular
+	     0-based ELF, and we see:
+
+	      (gdb) info auxv
+	      33  AT_SYSINFO_EHDR  System-supplied DSO's ELF header 0x7ffff7ffb000
+	      (gdb)  p/x *_r_debug.r_map.l_next
+	      $1 = {l_addr = 0x7ffff7ffb000, ..., l_ld = 0x7ffff7ffb318, ...}
+
+	     And on Linux 2.6.32 (x86_64) we see:
+
+	      (gdb) info auxv
+	      33  AT_SYSINFO_EHDR  System-supplied DSO's ELF header 0x7ffff7ffe000
+	      (gdb) p/x *_r_debug.r_map.l_next
+	      $5 = {l_addr = 0x7ffff88fe000, ..., l_ld = 0x7ffff7ffe580, ... }
+
+	     Dumping that vDSO shows:
+
+	      (gdb) info proc mappings
+	      0x7ffff7ffe000  0x7ffff7fff000  0x1000  0  [vdso]
+	      (gdb) dump memory vdso.bin 0x7ffff7ffe000 0x7ffff7fff000
+	      # readelf -Wa vdso.bin
+	      [...]
+		Entry point address: 0xffffffffff700700
+	      [...]
+	      Section Headers:
+		[Nr] Name     Type    Address	       Off    Size
+		[ 0]	      NULL    0000000000000000 000000 000000
+		[ 1] .hash    HASH    ffffffffff700120 000120 000038
+		[ 2] .dynsym  DYNSYM  ffffffffff700158 000158 0000d8
+	      [...]
+		[ 9] .dynamic DYNAMIC ffffffffff700580 000580 0000f0
+	  */
+	  if (address_in_mem_range (lm_info->l_ld, &info->vsyscall_range))
+	    {
+	      *sop = so->next;
+	      free_so (so);
+	      break;
+	    }
+
+	  sop = &so->next;
+	}
+    }
+
+  return so_head;
+}
+
 /* To be called from the various GDB_OSABI_LINUX handlers for the
    various GNU/Linux architectures and machine types.  */
 
@@ -2501,10 +2613,18 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 				      linux_gdb_signal_from_target);
   set_gdbarch_gdb_signal_to_target (gdbarch,
 				    linux_gdb_signal_to_target);
-  set_gdbarch_vsyscall_range (gdbarch, linux_vsyscall_range);
   set_gdbarch_infcall_mmap (gdbarch, linux_infcall_mmap);
   set_gdbarch_infcall_munmap (gdbarch, linux_infcall_munmap);
   set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
+
+  /* Inherit svr4 and override a few functions.  */
+  if (linux_so_ops.current_sos == NULL)
+    {
+      linux_so_ops = svr4_so_ops;
+      linux_so_ops.current_sos = linux_current_sos;
+      linux_so_ops.solib_create_inferior_hook = linux_solib_create_inferior_hook;
+  }
+  set_solib_ops (gdbarch, &linux_so_ops);
 }
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h
index 2da7de4..da08097 100644
--- a/gdb/linux-tdep.h
+++ b/gdb/linux-tdep.h
@@ -22,6 +22,10 @@
 
 #include "bfd.h"
 
+struct target_so_ops;
+
+extern struct target_so_ops linux_so_ops;
+
 struct regcache;
 
 /* Enum used to define the extra fields of the siginfo type used by an
diff --git a/gdb/mips-linux-tdep.c b/gdb/mips-linux-tdep.c
index 8dc0566..78eb856 100644
--- a/gdb/mips-linux-tdep.c
+++ b/gdb/mips-linux-tdep.c
@@ -1716,7 +1716,7 @@ mips_linux_init_abi (struct gdbarch_info info,
      dependency on solib-svr4.c's _initialize routine.  */
   if (mips_svr4_so_ops.in_dynsym_resolve_code == NULL)
     {
-      mips_svr4_so_ops = svr4_so_ops;
+      mips_svr4_so_ops = linux_so_ops;
       mips_svr4_so_ops.in_dynsym_resolve_code
 	= mips_linux_in_dynsym_resolve_code;
     }
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index cde4f2e..e50a83a 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -1704,7 +1704,7 @@ ppc_linux_init_abi (struct gdbarch_info info,
 
       if (powerpc_so_ops.in_dynsym_resolve_code == NULL)
 	{
-	  powerpc_so_ops = svr4_so_ops;
+	  powerpc_so_ops = linux_so_ops;
 	  /* Override dynamic resolve function.  */
 	  powerpc_so_ops.in_dynsym_resolve_code =
 	    powerpc_linux_in_dynsym_resolve_code;
diff --git a/gdb/solib-spu.c b/gdb/solib-spu.c
index fa2977e..6952f32 100644
--- a/gdb/solib-spu.c
+++ b/gdb/solib-spu.c
@@ -34,7 +34,7 @@
 #include "breakpoint.h"
 #include "gdbthread.h"
 #include "gdb_bfd.h"
-
+#include "linux-tdep.h"
 #include "spu-tdep.h"
 
 /* Highest SPE id (file handle) the inferior may have.  */
@@ -164,7 +164,7 @@ spu_current_sos (void)
   int i, size;
 
   /* First, retrieve the SVR4 shared library list.  */
-  head = svr4_so_ops.current_sos ();
+  head = linux_so_ops.current_sos ();
 
   /* Append our libraries to the end of the list.  */
   for (link_ptr = &head; *link_ptr; link_ptr = &(*link_ptr)->next)
@@ -238,7 +238,7 @@ static void
 spu_free_so (struct so_list *so)
 {
   if (so->so_original_name[0] != '@')
-    svr4_so_ops.free_so (so);
+    linux_so_ops.free_so (so);
 }
 
 /* Relocate section addresses.  */
@@ -247,7 +247,7 @@ spu_relocate_section_addresses (struct so_list *so,
 				struct target_section *sec)
 {
   if (so->so_original_name[0] != '@')
-    svr4_so_ops.relocate_section_addresses (so, sec);
+    linux_so_ops.relocate_section_addresses (so, sec);
   else
     {
       unsigned long long addr;
@@ -355,7 +355,7 @@ spu_bfd_open (char *pathname)
 
   /* Handle regular SVR4 libraries.  */
   if (!original_name)
-    return svr4_so_ops.bfd_open (pathname);
+    return linux_so_ops.bfd_open (pathname);
 
   /* Decode object ID.  */
   if (sscanf (original_name, "@0x%llx <%d>", &addr, &fd) != 2)
@@ -399,8 +399,8 @@ spu_lookup_lib_symbol (struct objfile *objfile,
   if (bfd_get_arch (objfile->obfd) == bfd_arch_spu)
     return lookup_global_symbol_from_objfile (objfile, name, domain);
 
-  if (svr4_so_ops.lookup_lib_global_symbol != NULL)
-    return svr4_so_ops.lookup_lib_global_symbol (objfile, name, domain);
+  if (linux_so_ops.lookup_lib_global_symbol != NULL)
+    return linux_so_ops.lookup_lib_global_symbol (objfile, name, domain);
   return (struct block_symbol) {NULL, NULL};
 }
 
@@ -495,7 +495,7 @@ spu_solib_create_inferior_hook (int from_tty)
     }
 
   /* Call SVR4 hook -- this will re-insert the SVR4 solib breakpoints.  */
-  svr4_so_ops.solib_create_inferior_hook (from_tty);
+  linux_so_ops.solib_create_inferior_hook (from_tty);
 
   /* If the inferior is statically linked against libspe, we need to install
      our own solib breakpoint right now.  Otherwise, it will be installed by
@@ -514,7 +514,7 @@ set_spu_solib_ops (struct gdbarch *gdbarch)
      dependency on solib-svr4.c's _initialize routine.  */
   if (spu_so_ops.current_sos == NULL)
     {
-      spu_so_ops = svr4_so_ops;
+      spu_so_ops = linux_so_ops;
       spu_so_ops.solib_create_inferior_hook = spu_solib_create_inferior_hook;
       spu_so_ops.relocate_section_addresses = spu_relocate_section_addresses;
       spu_so_ops.free_so = spu_free_so;
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index fe36d45..d578683 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -51,27 +51,6 @@ static int svr4_have_link_map_offsets (void);
 static void svr4_relocate_main_executable (void);
 static void svr4_free_library_list (void *p_list);
 
-/* Link map info to include in an allocated so_list entry.  */
-
-struct lm_info
-  {
-    /* Amount by which addresses in the binary should be relocated to
-       match the inferior.  The direct inferior value is L_ADDR_INFERIOR.
-       When prelinking is involved and the prelink base address changes,
-       we may need a different offset - the recomputed offset is in L_ADDR.
-       It is commonly the same value.  It is cached as we want to warn about
-       the difference and compute it only once.  L_ADDR is valid
-       iff L_ADDR_P.  */
-    CORE_ADDR l_addr, l_addr_inferior;
-    unsigned int l_addr_p : 1;
-
-    /* The target location of lm.  */
-    CORE_ADDR lm_addr;
-
-    /* Values read in from inferior's fields of the same name.  */
-    CORE_ADDR l_ld, l_next, l_prev, l_name;
-  };
-
 /* On SVR4 systems, a list of symbols in the dynamic linker where
    GDB can try to place a breakpoint to monitor shared library
    events.
@@ -189,12 +168,12 @@ svr4_same (struct so_list *gdb, struct so_list *inferior)
   return (svr4_same_1 (gdb->so_original_name, inferior->so_original_name));
 }
 
-static struct lm_info *
+static struct svr4_lm_info *
 lm_info_read (CORE_ADDR lm_addr)
 {
   struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
   gdb_byte *lm;
-  struct lm_info *lm_info;
+  struct svr4_lm_info *lm_info;
   struct cleanup *back_to;
 
   lm = (gdb_byte *) xmalloc (lmo->link_map_size);
@@ -210,7 +189,7 @@ lm_info_read (CORE_ADDR lm_addr)
     {
       struct type *ptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr;
 
-      lm_info = XCNEW (struct lm_info);
+      lm_info = XCNEW (struct svr4_lm_info);
       lm_info->lm_addr = lm_addr;
 
       lm_info->l_addr_inferior = extract_typed_address (&lm[lmo->l_addr_offset],
@@ -237,20 +216,28 @@ has_lm_dynamic_from_link_map (void)
   return lmo->l_ld_offset >= 0;
 }
 
+static struct svr4_lm_info *
+svr4_lm_info (const struct so_list *so)
+{
+  return (struct svr4_lm_info *) so->lm_info;
+}
+
 static CORE_ADDR
 lm_addr_check (const struct so_list *so, bfd *abfd)
 {
-  if (!so->lm_info->l_addr_p)
+  struct svr4_lm_info *lm_info = svr4_lm_info (so);
+
+  if (!lm_info->l_addr_p)
     {
       struct bfd_section *dyninfo_sect;
       CORE_ADDR l_addr, l_dynaddr, dynaddr;
 
-      l_addr = so->lm_info->l_addr_inferior;
+      l_addr = lm_info->l_addr_inferior;
 
       if (! abfd || ! has_lm_dynamic_from_link_map ())
 	goto set_addr;
 
-      l_dynaddr = so->lm_info->l_ld;
+      l_dynaddr = lm_info->l_ld;
 
       dyninfo_sect = bfd_get_section_by_name (abfd, ".dynamic");
       if (dyninfo_sect == NULL)
@@ -333,11 +320,11 @@ lm_addr_check (const struct so_list *so, bfd *abfd)
 	}
 
     set_addr:
-      so->lm_info->l_addr = l_addr;
-      so->lm_info->l_addr_p = 1;
+      lm_info->l_addr = l_addr;
+      lm_info->l_addr_p = 1;
     }
 
-  return so->lm_info->l_addr;
+  return lm_info->l_addr;
 }
 
 /* Per pspace SVR4 specific data.  */
@@ -978,6 +965,7 @@ svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
   struct svr4_info *info;
   CORE_ADDR ldsomap;
   struct so_list *newobj;
+  struct svr4_lm_info *lm_info;
   struct cleanup *old_chain;
   CORE_ADDR name_lm;
 
@@ -994,9 +982,10 @@ svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
 
   newobj = XCNEW (struct so_list);
   old_chain = make_cleanup (xfree, newobj);
-  newobj->lm_info = lm_info_read (ldsomap);
+  lm_info = lm_info_read (ldsomap);
+  newobj->lm_info = (struct lm_info *) lm_info;
   make_cleanup (xfree, newobj->lm_info);
-  name_lm = newobj->lm_info ? newobj->lm_info->l_name : 0;
+  name_lm = lm_info ? lm_info->l_name : 0;
   do_cleanups (old_chain);
 
   return (name_lm >= vaddr && name_lm < vaddr + size);
@@ -1102,8 +1091,10 @@ svr4_free_so (struct so_list *so)
 static void
 svr4_clear_so (struct so_list *so)
 {
-  if (so->lm_info != NULL)
-    so->lm_info->l_addr_p = 0;
+  struct svr4_lm_info *lm_info = svr4_lm_info (so);
+
+  if (lm_info != NULL)
+    lm_info->l_addr_p = 0;
 }
 
 /* Free so_list built so far (called via cleanup).  */
@@ -1133,12 +1124,14 @@ svr4_copy_library_list (struct so_list *src)
   while (src != NULL)
     {
       struct so_list *newobj;
+      struct svr4_lm_info *lm_info;
 
       newobj = XNEW (struct so_list);
       memcpy (newobj, src, sizeof (struct so_list));
 
-      newobj->lm_info = XNEW (struct lm_info);
-      memcpy (newobj->lm_info, src->lm_info, sizeof (struct lm_info));
+      lm_info = XNEW (struct svr4_lm_info);
+      newobj->lm_info = (struct lm_info *) lm_info;
+      memcpy (newobj->lm_info, src->lm_info, sizeof (struct svr4_lm_info));
 
       newobj->next = NULL;
       *link = newobj;
@@ -1172,12 +1165,14 @@ library_list_start_library (struct gdb_xml_parser *parser,
   ULONGEST *l_ldp
     = (ULONGEST *) xml_find_attribute (attributes, "l_ld")->value;
   struct so_list *new_elem;
+  struct svr4_lm_info *lm_info;
 
   new_elem = XCNEW (struct so_list);
-  new_elem->lm_info = XCNEW (struct lm_info);
-  new_elem->lm_info->lm_addr = *lmp;
-  new_elem->lm_info->l_addr_inferior = *l_addrp;
-  new_elem->lm_info->l_ld = *l_ldp;
+  lm_info = XCNEW (struct svr4_lm_info);
+  lm_info->lm_addr = *lmp;
+  lm_info->l_addr_inferior = *l_addrp;
+  lm_info->l_ld = *l_ldp;
+  new_elem->lm_info = (struct lm_info *) lm_info;
 
   strncpy (new_elem->so_name, name, sizeof (new_elem->so_name) - 1);
   new_elem->so_name[sizeof (new_elem->so_name) - 1] = 0;
@@ -1323,17 +1318,19 @@ svr4_default_sos (void)
 {
   struct svr4_info *info = get_svr4_info ();
   struct so_list *newobj;
+  struct svr4_lm_info *lm_info;
 
   if (!info->debug_loader_offset_p)
     return NULL;
 
   newobj = XCNEW (struct so_list);
 
-  newobj->lm_info = XCNEW (struct lm_info);
+  lm_info = XCNEW (struct svr4_lm_info);
+  newobj->lm_info = (struct lm_info *) lm_info;
 
   /* Nothing will ever check the other fields if we set l_addr_p.  */
-  newobj->lm_info->l_addr = info->debug_loader_offset;
-  newobj->lm_info->l_addr_p = 1;
+  lm_info->l_addr = info->debug_loader_offset;
+  lm_info->l_addr_p = 1;
 
   strncpy (newobj->so_name, info->debug_loader_name, SO_NAME_MAX_PATH_SIZE - 1);
   newobj->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0';
@@ -1363,24 +1360,26 @@ svr4_read_so_list (CORE_ADDR lm, CORE_ADDR prev_lm,
       struct cleanup *old_chain;
       int errcode;
       char *buffer;
+      struct svr4_lm_info *lm_info;
 
       newobj = XCNEW (struct so_list);
       old_chain = make_cleanup_free_so (newobj);
 
-      newobj->lm_info = lm_info_read (lm);
-      if (newobj->lm_info == NULL)
+      lm_info = lm_info_read (lm);
+      if (lm_info == NULL)
 	{
 	  do_cleanups (old_chain);
 	  return 0;
 	}
 
-      next_lm = newobj->lm_info->l_next;
+      newobj->lm_info = (struct lm_info *) lm_info;
+      next_lm = lm_info->l_next;
 
-      if (newobj->lm_info->l_prev != prev_lm)
+      if (lm_info->l_prev != prev_lm)
 	{
 	  warning (_("Corrupted shared library list: %s != %s"),
 		   paddress (target_gdbarch (), prev_lm),
-		   paddress (target_gdbarch (), newobj->lm_info->l_prev));
+		   paddress (target_gdbarch (), lm_info->l_prev));
 	  do_cleanups (old_chain);
 	  return 0;
 	}
@@ -1390,18 +1389,18 @@ svr4_read_so_list (CORE_ADDR lm, CORE_ADDR prev_lm,
          SVR4, it has no name.  For others (Solaris 2.3 for example), it
          does have a name, so we can no longer use a missing name to
          decide when to ignore it.  */
-      if (ignore_first && newobj->lm_info->l_prev == 0)
+      if (ignore_first && lm_info->l_prev == 0)
 	{
 	  struct svr4_info *info = get_svr4_info ();
 
-	  first_l_name = newobj->lm_info->l_name;
-	  info->main_lm_addr = newobj->lm_info->lm_addr;
+	  first_l_name = lm_info->l_name;
+	  info->main_lm_addr = lm_info->lm_addr;
 	  do_cleanups (old_chain);
 	  continue;
 	}
 
       /* Extract this shared object's name.  */
-      target_read_string (newobj->lm_info->l_name, &buffer,
+      target_read_string (lm_info->l_name, &buffer,
 			  SO_NAME_MAX_PATH_SIZE - 1, &errcode);
       if (errcode != 0)
 	{
@@ -1409,7 +1408,7 @@ svr4_read_so_list (CORE_ADDR lm, CORE_ADDR prev_lm,
 	     inferior executable, then this is not a normal shared
 	     object, but (most likely) a vDSO.  In this case, silently
 	     skip it; otherwise emit a warning. */
-	  if (first_l_name == 0 || newobj->lm_info->l_name != first_l_name)
+	  if (first_l_name == 0 || lm_info->l_name != first_l_name)
 	    warning (_("Can't read pathname for load map: %s."),
 		     safe_strerror (errcode));
 	  do_cleanups (old_chain);
@@ -1515,7 +1514,7 @@ svr4_current_sos_direct (struct svr4_info *info)
    method.  */
 
 static struct so_list *
-svr4_current_sos_1 (void)
+svr4_current_sos (void)
 {
   struct svr4_info *info = get_svr4_info ();
 
@@ -1528,82 +1527,6 @@ svr4_current_sos_1 (void)
   return svr4_current_sos_direct (info);
 }
 
-/* Implement the "current_sos" target_so_ops method.  */
-
-static struct so_list *
-svr4_current_sos (void)
-{
-  struct so_list *so_head = svr4_current_sos_1 ();
-  struct mem_range vsyscall_range;
-
-  /* Filter out the vDSO module, if present.  Its symbol file would
-     not be found on disk.  The vDSO/vsyscall's OBJFILE is instead
-     managed by symfile-mem.c:add_vsyscall_page.  */
-  if (gdbarch_vsyscall_range (target_gdbarch (), &vsyscall_range)
-      && vsyscall_range.length != 0)
-    {
-      struct so_list **sop;
-
-      sop = &so_head;
-      while (*sop != NULL)
-	{
-	  struct so_list *so = *sop;
-
-	  /* We can't simply match the vDSO by starting address alone,
-	     because lm_info->l_addr_inferior (and also l_addr) do not
-	     necessarily represent the real starting address of the
-	     ELF if the vDSO's ELF itself is "prelinked".  The l_ld
-	     field (the ".dynamic" section of the shared object)
-	     always points at the absolute/resolved address though.
-	     So check whether that address is inside the vDSO's
-	     mapping instead.
-
-	     E.g., on Linux 3.16 (x86_64) the vDSO is a regular
-	     0-based ELF, and we see:
-
-	      (gdb) info auxv
-	      33  AT_SYSINFO_EHDR  System-supplied DSO's ELF header 0x7ffff7ffb000
-	      (gdb)  p/x *_r_debug.r_map.l_next
-	      $1 = {l_addr = 0x7ffff7ffb000, ..., l_ld = 0x7ffff7ffb318, ...}
-
-	     And on Linux 2.6.32 (x86_64) we see:
-
-	      (gdb) info auxv
-	      33  AT_SYSINFO_EHDR  System-supplied DSO's ELF header 0x7ffff7ffe000
-	      (gdb) p/x *_r_debug.r_map.l_next
-	      $5 = {l_addr = 0x7ffff88fe000, ..., l_ld = 0x7ffff7ffe580, ... }
-
-	     Dumping that vDSO shows:
-
-	      (gdb) info proc mappings
-	      0x7ffff7ffe000  0x7ffff7fff000  0x1000  0  [vdso]
-	      (gdb) dump memory vdso.bin 0x7ffff7ffe000 0x7ffff7fff000
-	      # readelf -Wa vdso.bin
-	      [...]
-		Entry point address: 0xffffffffff700700
-	      [...]
-	      Section Headers:
-		[Nr] Name     Type    Address	       Off    Size
-		[ 0]	      NULL    0000000000000000 000000 000000
-		[ 1] .hash    HASH    ffffffffff700120 000120 000038
-		[ 2] .dynsym  DYNSYM  ffffffffff700158 000158 0000d8
-	      [...]
-		[ 9] .dynamic DYNAMIC ffffffffff700580 000580 0000f0
-	  */
-	  if (address_in_mem_range (so->lm_info->l_ld, &vsyscall_range))
-	    {
-	      *sop = so->next;
-	      free_so (so);
-	      break;
-	    }
-
-	  sop = &so->next;
-	}
-    }
-
-  return so_head;
-}
-
 /* Get the address of the link_map for a given OBJFILE.  */
 
 CORE_ADDR
@@ -1624,7 +1547,7 @@ svr4_fetch_objfile_link_map (struct objfile *objfile)
      of shared libraries.  */
   for (so = master_so_list (); so; so = so->next)
     if (so->objfile == objfile)
-      return so->lm_info->lm_addr;
+      return svr4_lm_info (so)->lm_addr;
 
   /* Not found!  */
   return 0;
@@ -1856,7 +1779,7 @@ solist_update_incremental (struct svr4_info *info, CORE_ADDR lm)
   /* Walk to the end of the list.  */
   for (tail = info->solib_list; tail->next != NULL; tail = tail->next)
     /* Nothing.  */;
-  prev_lm = tail->lm_info->lm_addr;
+  prev_lm = svr4_lm_info (tail)->lm_addr;
 
   /* Read the new objects.  */
   if (info->using_xfer)
@@ -3187,7 +3110,8 @@ set_solib_svr4_fetch_link_map_offsets (struct gdbarch *gdbarch,
 
   ops->fetch_link_map_offsets = flmo;
 
-  set_solib_ops (gdbarch, &svr4_so_ops);
+  if (solib_ops (gdbarch) == NULL)
+    set_solib_ops (gdbarch, &svr4_so_ops);
 }
 
 /* Fetch a link_map_offsets structure using the architecture-specific
diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h
index d541136..87650d1 100644
--- a/gdb/solib-svr4.h
+++ b/gdb/solib-svr4.h
@@ -63,6 +63,27 @@ struct link_map_offsets
     int l_name_offset;
   };
 
+/* Link map info to include in an allocated so_list entry.  */
+
+struct svr4_lm_info
+  {
+    /* Amount by which addresses in the binary should be relocated to
+       match the inferior.  The direct inferior value is L_ADDR_INFERIOR.
+       When prelinking is involved and the prelink base address changes,
+       we may need a different offset - the recomputed offset is in L_ADDR.
+       It is commonly the same value.  It is cached as we want to warn about
+       the difference and compute it only once.  L_ADDR is valid
+       iff L_ADDR_P.  */
+    CORE_ADDR l_addr, l_addr_inferior;
+    unsigned int l_addr_p : 1;
+
+    /* The target location of lm.  */
+    CORE_ADDR lm_addr;
+
+    /* Values read in from inferior's fields of the same name.  */
+    CORE_ADDR l_ld, l_next, l_prev, l_name;
+  };
+
 /* set_solib_svr4_fetch_link_map_offsets() is intended to be called by
    a <arch>_gdbarch_init() function.  It is used to establish an
    architecture specific link_map_offsets fetcher for the architecture
diff --git a/gdb/solib.c b/gdb/solib.c
index 2235505..84ed485 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -62,7 +62,7 @@ solib_init (struct obstack *obstack)
   return ops;
 }
 
-static const struct target_so_ops *
+const struct target_so_ops *
 solib_ops (struct gdbarch *gdbarch)
 {
   const struct target_so_ops **ops
diff --git a/gdb/solib.h b/gdb/solib.h
index 00fd6cb..d594ea2 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -68,6 +68,10 @@ extern int in_solib_dynsym_resolve_code (CORE_ADDR);
 
 extern void no_shared_libraries (char *ignored, int from_tty);
 
+/* Get the solib operations for GDBARCH.  */
+
+const struct target_so_ops *solib_ops (struct gdbarch *gdbarch);
+
 /* Set the solib operations for GDBARCH to NEW_OPS.  */
 
 extern void set_solib_ops (struct gdbarch *gdbarch,
diff --git a/gdb/symfile-mem.c b/gdb/symfile-mem.c
index 79739a6..00d20a2 100644
--- a/gdb/symfile-mem.c
+++ b/gdb/symfile-mem.c
@@ -82,7 +82,7 @@ target_read_memory_bfd (bfd_vma memaddr, bfd_byte *myaddr, bfd_size_type len)
    which will be attached to the BFD.  */
 static struct objfile *
 symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr,
-			     size_t size, char *name, int from_tty)
+			     size_t size, char *name, int add_flags)
 {
   struct objfile *objf;
   struct bfd *nbfd;
@@ -127,8 +127,7 @@ symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr,
   sai->num_sections = i;
 
   objf = symbol_file_add_from_bfd (nbfd, bfd_get_filename (nbfd),
-				   from_tty ? SYMFILE_VERBOSE : 0,
-                                   sai, OBJF_SHARED, NULL);
+				   add_flags, sai, OBJF_SHARED, NULL);
 
   add_target_sections_of_objfile (objf);
 
@@ -145,6 +144,7 @@ add_symbol_file_from_memory_command (char *args, int from_tty)
 {
   CORE_ADDR addr;
   struct bfd *templ;
+  int add_flags = from_tty ? SYMFILE_VERBOSE : 0;
 
   if (args == NULL)
     error (_("add-symbol-file-from-memory requires an expression argument"));
@@ -160,7 +160,7 @@ add_symbol_file_from_memory_command (char *args, int from_tty)
     error (_("Must use symbol-file or exec-file "
 	     "before add-symbol-file-from-memory."));
 
-  symbol_file_add_from_memory (templ, addr, 0, NULL, from_tty);
+  symbol_file_add_from_memory (templ, addr, 0, NULL, add_flags);
 }
 
 /* Arguments for symbol_file_add_from_memory_wrapper.  */
@@ -171,7 +171,7 @@ struct symbol_file_add_from_memory_args
   CORE_ADDR sysinfo_ehdr;
   size_t size;
   char *name;
-  int from_tty;
+  int add_flags;
 };
 
 /* Wrapper function for symbol_file_add_from_memory, for
@@ -184,19 +184,15 @@ symbol_file_add_from_memory_wrapper (struct ui_out *uiout, void *data)
     = (struct symbol_file_add_from_memory_args *) data;
 
   symbol_file_add_from_memory (args->bfd, args->sysinfo_ehdr, args->size,
-			       args->name, args->from_tty);
+			       args->name, args->add_flags);
   return 0;
 }
 
-/* Try to add the symbols for the vsyscall page, if there is one.
-   This function is called via the inferior_created observer.  */
+/* See symfile-mem.h.  */
 
-static void
-add_vsyscall_page (struct target_ops *target, int from_tty)
+void
+add_vsyscall_page (struct mem_range *vsyscall_range, int add_flags)
 {
-  struct mem_range vsyscall_range;
-
-  if (gdbarch_vsyscall_range (target_gdbarch (), &vsyscall_range))
     {
       struct bfd *bfd;
       struct symbol_file_add_from_memory_args args;
@@ -218,15 +214,13 @@ add_vsyscall_page (struct target_ops *target, int from_tty)
 	  return;
 	}
       args.bfd = bfd;
-      args.sysinfo_ehdr = vsyscall_range.start;
-      args.size = vsyscall_range.length;
+      args.sysinfo_ehdr = vsyscall_range->start;
+      args.size = vsyscall_range->length;
 
       args.name = xstrprintf ("system-supplied DSO at %s",
-			      paddress (target_gdbarch (), vsyscall_range.start));
-      /* Pass zero for FROM_TTY, because the action of loading the
-	 vsyscall DSO was not triggered by the user, even if the user
-	 typed "run" at the TTY.  */
-      args.from_tty = 0;
+			      paddress (target_gdbarch (),
+					vsyscall_range->start));
+      args.add_flags = add_flags;
       catch_exceptions (current_uiout, symbol_file_add_from_memory_wrapper,
 			&args, RETURN_MASK_ALL);
     }
@@ -247,8 +241,4 @@ _initialize_symfile_mem (void)
 	     "Give an expression for the address "
 	     "of the file's shared object file header."),
            &cmdlist);
-
-  /* Want to know of each new inferior so that its vsyscall info can
-     be extracted.  */
-  observer_attach_inferior_created (add_vsyscall_page);
 }
diff --git a/gdb/symfile-mem.h b/gdb/symfile-mem.h
new file mode 100644
index 0000000..df0e879
--- /dev/null
+++ b/gdb/symfile-mem.h
@@ -0,0 +1,30 @@
+/* Definitions for reading symbol files from memory into GDB.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program 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.
+
+   This program 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/>.  */
+
+#if !defined (SYMFILE_MEM_H)
+#define SYMFILE_MEM_H
+
+struct mem_range;
+
+/* Try to add the symbols for the vsyscall page, if there is one.  */
+
+extern void add_vsyscall_page (struct mem_range *vsyscall_range,
+			       int add_flags);
+
+#endif /* !defined(SYMFILE_MEM_H) */
-- 
2.5.5


[-- Attachment #3: 0002-Make-vDSO-detection-work-with-core-files.patch --]
[-- Type: text/x-patch, Size: 8577 bytes --]

From 17811ce925279d3a64eb04541591cb5c567ef3cc Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 18 Aug 2016 23:00:52 +0100
Subject: [PATCH 2/2] Make vDSO detection work with core files

- Make gdb.base/vdso-warning.exp test loading a core.  With
  LD_DEBUG=unused, we see the warning on systems with local glibc
  patches as well (Fedora/RHEL).

- When debugging a core, we can't find the size of the vDSO from the
  mappings, because they're nowhere to be found.  However, we can
  still read the vDSO out of memory when we don't know its size.  BFD
  will figure it out from the ELF structure.  In fact, this is what we
  always used to do before 5979d6b69b20 ("Handle VDSO section headers
  past end of page").

  So the fix is to read the vDSO out of memory before we ever fetch
  the current list of DSOs, and extract the vDSO size out of the size
  of the bfd read in.
---
 gdb/linux-tdep.c                        | 27 +++++++-----
 gdb/symfile-mem.c                       | 24 ++++++++++-
 gdb/testsuite/gdb.base/vdso-warning.exp | 76 ++++++++++++++++++++++-----------
 3 files changed, 90 insertions(+), 37 deletions(-)

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 91931eb..7d1d547 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -193,7 +193,7 @@ static const struct inferior_data *linux_inferior_data;
    transfering data from a remote target to the local host.  */
 struct linux_info
 {
-  /* Cache of the inferior's vsyscall/vDSO mapping range.  Only valid
+  /* Cache of the inferior's vsyscall/vDSO address range.  Only valid
      if VSYSCALL_RANGE_P is positive.  This is cached because getting
      at this info requires an auxv lookup (which is itself cached),
      and looking through the inferior's mappings (which change
@@ -2286,7 +2286,8 @@ linux_gdb_signal_to_target (struct gdbarch *gdbarch,
 }
 
 /* Helper for linux_vsyscall_range that does the real work of finding
-   the vsyscall's address range.  */
+   the vsyscall's address range based on auxv info and page
+   mappings.  */
 
 static int
 linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
@@ -2295,16 +2296,21 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
   long pid;
   char *data;
 
-  /* Can't access /proc if debugging a core file.  */
-  if (!target_has_execution)
+  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
     return 0;
 
+  /* Alright, we know the starting address.  Let's see if we can find
+     the end address.  */
+  range->length = 0;
+
+  /* Can't access /proc if debugging a core file, and NT_FILE notes
+     don't include the vDSO mapping.  */
+  if (!target_has_execution)
+    return 1;
+
   /* We need to know the real target PID to access /proc.  */
   if (current_inferior ()->fake_pid_p)
-    return 0;
-
-  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
-    return 0;
+    return 1;
 
   pid = current_inferior ()->pid;
 
@@ -2338,8 +2344,7 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
 		p++;
 	      endaddr = strtoulst (p, &p, 16);
 	      range->length = endaddr - addr;
-	      do_cleanups (cleanup);
-	      return 1;
+	      break;
 	    }
 	}
 
@@ -2348,7 +2353,7 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
   else
     warning (_("unable to open /proc file '%s'"), filename);
 
-  return 0;
+  return 1;
 }
 
 /* Find the address range of the current inferior's vDSO.  If the
diff --git a/gdb/symfile-mem.c b/gdb/symfile-mem.c
index 00d20a2..e172404 100644
--- a/gdb/symfile-mem.c
+++ b/gdb/symfile-mem.c
@@ -167,11 +167,15 @@ add_symbol_file_from_memory_command (char *args, int from_tty)
 
 struct symbol_file_add_from_memory_args
 {
+  /* Inputs.  */
   struct bfd *bfd;
   CORE_ADDR sysinfo_ehdr;
   size_t size;
   char *name;
   int add_flags;
+
+  /* Outputs.  */
+  struct objfile *objf_added;
 };
 
 /* Wrapper function for symbol_file_add_from_memory, for
@@ -183,8 +187,10 @@ symbol_file_add_from_memory_wrapper (struct ui_out *uiout, void *data)
   struct symbol_file_add_from_memory_args *args
     = (struct symbol_file_add_from_memory_args *) data;
 
-  symbol_file_add_from_memory (args->bfd, args->sysinfo_ehdr, args->size,
-			       args->name, args->add_flags);
+  args->objf_added = symbol_file_add_from_memory (args->bfd,
+						  args->sysinfo_ehdr,
+						  args->size, args->name,
+						  args->add_flags);
   return 0;
 }
 
@@ -196,6 +202,7 @@ add_vsyscall_page (struct mem_range *vsyscall_range, int add_flags)
     {
       struct bfd *bfd;
       struct symbol_file_add_from_memory_args args;
+      struct stat statbuf;
 
       if (core_bfd != NULL)
 	bfd = core_bfd;
@@ -223,6 +230,19 @@ add_vsyscall_page (struct mem_range *vsyscall_range, int add_flags)
       args.add_flags = add_flags;
       catch_exceptions (current_uiout, symbol_file_add_from_memory_wrapper,
 			&args, RETURN_MASK_ALL);
+
+      /* If we couldn't find the size of the vDSO from the mappings,
+	 then set it from the size of the bfd that we just read
+	 in.  */
+      if (vsyscall_range->length == 0)
+	{
+	  if (bfd_stat (args.objf_added->obfd, &statbuf) < 0)
+	    {
+	      internal_error (__FILE__, __LINE__,
+			      _("bfd_stat on memfile bfd failed"));
+	    }
+	  vsyscall_range->length = statbuf.st_size;
+	}
     }
 }
 
diff --git a/gdb/testsuite/gdb.base/vdso-warning.exp b/gdb/testsuite/gdb.base/vdso-warning.exp
index af2b2b0..aeb85a2 100644
--- a/gdb/testsuite/gdb.base/vdso-warning.exp
+++ b/gdb/testsuite/gdb.base/vdso-warning.exp
@@ -13,42 +13,70 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+# Test that on Linux, we don't warn about not finding the vDSO.  E.g.:
+#
+#   warning: Could not load shared library symbols for linux-vdso.so.1.
+
 standard_testfile
 
 if { [prepare_for_testing "failed to prepare" ${testfile} $srcfile] } {
     return -1
 }
 
-gdb_breakpoint "main"
+with_test_prefix "setup" {
+    gdb_breakpoint "main"
 
-# At least some versions of Fedora/RHEL glibc have local patches that
-# hide the vDSO.  This lines re-exposes it.  See PR libc/13097,
-# comment 2.  There's no support for passing environment variables in
-# the remote protocol, but that's OK -- if we're testing against a
-# glibc that doesn't list the vDSO without this, the test should still
-# pass.
-gdb_test_no_output "set environment LD_DEBUG=unused"
+    # At least some versions of Fedora/RHEL glibc have local patches that
+    # hide the vDSO.  This lines re-exposes it.  See PR libc/13097,
+    # comment 2.  There's no support for passing environment variables in
+    # the remote protocol, but that's OK -- if we're testing against a
+    # glibc that doesn't list the vDSO without this, the test should still
+    # pass.
+    gdb_test_no_output "set environment LD_DEBUG=unused"
+}
 
-gdb_run_cmd
+proc test_no_vdso {command} {
+    global srcfile
+    global gdb_prompt
 
-set test "stop without warning"
-gdb_test_multiple "" $test {
-    -re "Could not load shared library symbols .*\r\n$gdb_prompt $" {
-	fail $test
+    set message "startup"
+    gdb_test_multiple "$command" $message {
+	-re "Could not load shared library symbols .*\r\n$gdb_prompt $" {
+	    fail $message
+	}
+	-re "main \\(\\) at .*$srcfile.*\r\n$gdb_prompt $" {
+	    pass $message
+	}
     }
-    -re "\r\nBreakpoint \[0-9\]+, main .*\r\n$gdb_prompt $" {
-	pass $test
+
+    # Extra testing in case the warning changes and we miss updating
+    # the above.
+    set test "no vdso without symbols is listed"
+    gdb_test_multiple "info shared" $test {
+	-re "No\[^\r\n\]+linux-(vdso|gate).*$gdb_prompt $" {
+	    fail $test
+	}
+	-re "$gdb_prompt $" {
+	    pass $test
+	}
     }
 }
 
-# Extra testing in case the warning changes and we miss updating the
-# above.
-set test "no vdso without symbols is listed"
-gdb_test_multiple "info shared" $test {
-    -re "No\[^\r\n\]+linux-(vdso|gate).*$gdb_prompt $" {
-	fail $test
-    }
-    -re "$gdb_prompt $" {
-	pass $test
+# First, try a live process.
+with_test_prefix "run" {
+    gdb_run_cmd
+    test_no_vdso ""
+}
+
+# Now, dump a core, and reload it.
+with_test_prefix "core" {
+    set corefile [standard_output_file $testfile.core]
+    set core_supported [gdb_gcore_cmd "$corefile" "save a corefile"]
+    if {!$core_supported} {
+	return -1
     }
+
+    clean_restart ${testfile}
+
+    test_no_vdso "core-file $corefile"
 }
-- 
2.5.5


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: warning: Could not load shared library symbols for linux-vdso.so.1.
  2016-08-19  1:05     ` Pedro Alves
@ 2016-08-19  9:59       ` Yao Qi
  2016-08-19 10:32         ` Pedro Alves
  0 siblings, 1 reply; 10+ messages in thread
From: Yao Qi @ 2016-08-19  9:59 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Yao Qi, gdb-patches

Pedro Alves <palves@redhat.com> writes:

> So I bit the bullet and made a custom Linux-specific
> struct solib_ops that inherits svr4_so_ops, and overrides a couple
> methods for vDSO awareness.  I think the end result is clearer
> and probably more extensible if/when we decide to do the work necessary
> to be able to show the vdso in "info sharedlibrary", without causing
> could-not-find-file warnings.
>
> I'm attaching two patches; the first implements the new solib_ops
> instance, and then the second uses the vDSO's bfd size, fixing the
> bug.
>
> Passes testing on x86_64 Fedora 23 here, as well as the new test.
>
> Let me know what you think.

They are good to me.  Do you plan to push them to 7.12 branch?  I'd like
to keep these patches in master only, because the problem they fix is
minor while the patches are not small changes.  What do you think?

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: warning: Could not load shared library symbols for linux-vdso.so.1.
  2016-08-19  9:59       ` Yao Qi
@ 2016-08-19 10:32         ` Pedro Alves
  2016-08-19 11:41           ` Yao Qi
  0 siblings, 1 reply; 10+ messages in thread
From: Pedro Alves @ 2016-08-19 10:32 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 08/19/2016 10:59 AM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> So I bit the bullet and made a custom Linux-specific
>> struct solib_ops that inherits svr4_so_ops, and overrides a couple
>> methods for vDSO awareness.  I think the end result is clearer
>> and probably more extensible if/when we decide to do the work necessary
>> to be able to show the vdso in "info sharedlibrary", without causing
>> could-not-find-file warnings.
>>
>> I'm attaching two patches; the first implements the new solib_ops
>> instance, and then the second uses the vDSO's bfd size, fixing the
>> bug.
>>
>> Passes testing on x86_64 Fedora 23 here, as well as the new test.
>>
>> Let me know what you think.
> 
> They are good to me.  Do you plan to push them to 7.12 branch?

I was working under the assumption you wanted it in 7.12, given
you set the milestone.  :-)

> I'd like
> to keep these patches in master only, because the problem they fix is
> minor while the patches are not small changes.  What do you think?

Yeah, the patch ended up larger than I originally thought.  Keeping
it master-only is certainly fine with me.

However, if you want this on 7.12, then there's another option.  We
can start with the hacky version that moves the add_vsyscall_page
call to gdbarch_vsyscall_range, as mentioned in:

> Now, in order to do this, we need to move the add_vsyscall_page
> call earlier, before svr4_current_dsos is ever called, in order
> to read the vdso bfd out of memory before we ever first need to
> filter out the vdso.  The cleanest I could do with the current
> gdbarch_vsyscall_range-based design was to do the add_vsyscall_page
> call from within gdbarch_vsyscall_range.  But that looks very ugly
> to me, for reading symbols from a quite innocent looking gdbarch hook.

and then clean this up with the new linux_so_ops, on master, only.

So below's the "ugly" patch.  It's functionally exactly the same as
the larger one.  The switch to linux_so_ops was mainly "cosmetic".

Let me know what you prefer.

From 1036e36a71623fa84f93a43cad2636ef3dd51da6 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 18 Aug 2016 20:16:44 +0100
Subject: [PATCH] Make vDSO detection work with core files

- Make gdb.base/vdso-warning.exp test loading a core.  With
  LD_DEBUG=unused, we see the warning on systems with local glibc
  patches as well (Fedora/RHEL).

- When debugging a core, we can't find the size of the vDSO from the
  mappings, because they're nowhere to be found.  However, we can
  still read the vDSO out of memory when we don't know its size.  BFD
  will figure it out from the ELF structure.  In fact, this is what we
  always used to do before 5979d6b69b20 ("Handle VDSO section headers
  past end of page").

  So the fix is to read the vDSO out of memory and extract the vDSO
  size out of the size of the bfd read in, before we decide to filter
  the vDSO out of the svr4 DSO list.  In order to do that without much
  churn, we hack it in gdbarch_vsyscall_range.
---
 gdb/linux-tdep.c                        | 41 +++++++++++++-----
 gdb/symfile-mem.c                       | 58 ++++++++++++++-----------
 gdb/symfile-mem.h                       | 30 +++++++++++++
 gdb/testsuite/gdb.base/vdso-warning.exp | 76 ++++++++++++++++++++++-----------
 4 files changed, 145 insertions(+), 60 deletions(-)
 create mode 100644 gdb/symfile-mem.h

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index ab110b0..2094ee9 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -38,6 +38,7 @@
 #include "gdbcmd.h"
 #include "gdb_regex.h"
 #include "common/enum-flags.h"
+#include "symfile-mem.h"
 
 #include <ctype.h>
 
@@ -185,7 +186,7 @@ static const struct inferior_data *linux_inferior_data;
    transfering data from a remote target to the local host.  */
 struct linux_info
 {
-  /* Cache of the inferior's vsyscall/vDSO mapping range.  Only valid
+  /* Cache of the inferior's vsyscall/vDSO address range.  Only valid
      if VSYSCALL_RANGE_P is positive.  This is cached because getting
      at this info requires an auxv lookup (which is itself cached),
      and looking through the inferior's mappings (which change
@@ -196,6 +197,9 @@ struct linux_info
      yet.  Positive if we tried looking it up, and found it.  Negative
      if we tried looking it up but failed.  */
   int vsyscall_range_p;
+
+  /* True if we've already called add_vsyscall_page.  */
+  int vdso_syms_added_p;
 };
 
 /* Frees whatever allocated space there is to be freed and sets INF's
@@ -2278,7 +2282,8 @@ linux_gdb_signal_to_target (struct gdbarch *gdbarch,
 }
 
 /* Helper for linux_vsyscall_range that does the real work of finding
-   the vsyscall's address range.  */
+   the vsyscall's address range based on auxv info and page
+   mappings.  */
 
 static int
 linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
@@ -2287,16 +2292,21 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
   long pid;
   char *data;
 
-  /* Can't access /proc if debugging a core file.  */
-  if (!target_has_execution)
+  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
     return 0;
 
+  /* Alright, we know the starting address.  Let's see if we can find
+     the end address.  */
+  range->length = 0;
+
+  /* Can't access /proc if debugging a core file, and NT_FILE notes
+     don't include the vDSO mapping.  */
+  if (!target_has_execution)
+    return 1;
+
   /* We need to know the real target PID to access /proc.  */
   if (current_inferior ()->fake_pid_p)
-    return 0;
-
-  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
-    return 0;
+    return 1;
 
   pid = current_inferior ()->pid;
 
@@ -2330,8 +2340,7 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
 		p++;
 	      endaddr = strtoulst (p, &p, 16);
 	      range->length = endaddr - addr;
-	      do_cleanups (cleanup);
-	      return 1;
+	      break;
 	    }
 	}
 
@@ -2340,7 +2349,7 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
   else
     warning (_("unable to open /proc file '%s'"), filename);
 
-  return 0;
+  return 1;
 }
 
 /* Implementation of the "vsyscall_range" gdbarch hook.  Handles
@@ -2362,6 +2371,16 @@ linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
   if (info->vsyscall_range_p < 0)
     return 0;
 
+  /* If we couldn't figure out the vDSO size from the mappings, then
+     open it using BFD in order to figure out the size from the ELF
+     structures.  Need to do this before svr4_current_sos need the
+     range info in order to filter out the vDSO.  */
+  if (!info->vdso_syms_added_p)
+    {
+      info->vdso_syms_added_p = 1;
+      add_vsyscall_page (&info->vsyscall_range, SYMFILE_DEFER_BP_RESET);
+    }
+
   *range = info->vsyscall_range;
   return 1;
 }
diff --git a/gdb/symfile-mem.c b/gdb/symfile-mem.c
index 79739a6..3a5ce3a 100644
--- a/gdb/symfile-mem.c
+++ b/gdb/symfile-mem.c
@@ -82,7 +82,7 @@ target_read_memory_bfd (bfd_vma memaddr, bfd_byte *myaddr, bfd_size_type len)
    which will be attached to the BFD.  */
 static struct objfile *
 symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr,
-			     size_t size, char *name, int from_tty)
+			     size_t size, char *name, int add_flags)
 {
   struct objfile *objf;
   struct bfd *nbfd;
@@ -127,8 +127,7 @@ symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr,
   sai->num_sections = i;
 
   objf = symbol_file_add_from_bfd (nbfd, bfd_get_filename (nbfd),
-				   from_tty ? SYMFILE_VERBOSE : 0,
-                                   sai, OBJF_SHARED, NULL);
+				   add_flags, sai, OBJF_SHARED, NULL);
 
   add_target_sections_of_objfile (objf);
 
@@ -145,6 +144,7 @@ add_symbol_file_from_memory_command (char *args, int from_tty)
 {
   CORE_ADDR addr;
   struct bfd *templ;
+  int add_flags = from_tty ? SYMFILE_VERBOSE : 0;
 
   if (args == NULL)
     error (_("add-symbol-file-from-memory requires an expression argument"));
@@ -160,18 +160,22 @@ add_symbol_file_from_memory_command (char *args, int from_tty)
     error (_("Must use symbol-file or exec-file "
 	     "before add-symbol-file-from-memory."));
 
-  symbol_file_add_from_memory (templ, addr, 0, NULL, from_tty);
+  symbol_file_add_from_memory (templ, addr, 0, NULL, add_flags);
 }
 
 /* Arguments for symbol_file_add_from_memory_wrapper.  */
 
 struct symbol_file_add_from_memory_args
 {
+  /* Inputs.  */
   struct bfd *bfd;
   CORE_ADDR sysinfo_ehdr;
   size_t size;
   char *name;
-  int from_tty;
+  int add_flags;
+
+  /* Outputs.  */
+  struct objfile *objf_added;
 };
 
 /* Wrapper function for symbol_file_add_from_memory, for
@@ -183,23 +187,22 @@ symbol_file_add_from_memory_wrapper (struct ui_out *uiout, void *data)
   struct symbol_file_add_from_memory_args *args
     = (struct symbol_file_add_from_memory_args *) data;
 
-  symbol_file_add_from_memory (args->bfd, args->sysinfo_ehdr, args->size,
-			       args->name, args->from_tty);
+  args->objf_added = symbol_file_add_from_memory (args->bfd,
+						  args->sysinfo_ehdr,
+						  args->size, args->name,
+						  args->add_flags);
   return 0;
 }
 
-/* Try to add the symbols for the vsyscall page, if there is one.
-   This function is called via the inferior_created observer.  */
+/* See symfile-mem.h.  */
 
-static void
-add_vsyscall_page (struct target_ops *target, int from_tty)
+void
+add_vsyscall_page (struct mem_range *vsyscall_range, int add_flags)
 {
-  struct mem_range vsyscall_range;
-
-  if (gdbarch_vsyscall_range (target_gdbarch (), &vsyscall_range))
     {
       struct bfd *bfd;
       struct symbol_file_add_from_memory_args args;
+      struct stat statbuf;
 
       if (core_bfd != NULL)
 	bfd = core_bfd;
@@ -218,17 +221,26 @@ add_vsyscall_page (struct target_ops *target, int from_tty)
 	  return;
 	}
       args.bfd = bfd;
-      args.sysinfo_ehdr = vsyscall_range.start;
-      args.size = vsyscall_range.length;
+      args.sysinfo_ehdr = vsyscall_range->start;
+      args.size = vsyscall_range->length;
 
       args.name = xstrprintf ("system-supplied DSO at %s",
-			      paddress (target_gdbarch (), vsyscall_range.start));
-      /* Pass zero for FROM_TTY, because the action of loading the
-	 vsyscall DSO was not triggered by the user, even if the user
-	 typed "run" at the TTY.  */
-      args.from_tty = 0;
+			      paddress (target_gdbarch (), vsyscall_range->start));
+      args.add_flags = add_flags;
       catch_exceptions (current_uiout, symbol_file_add_from_memory_wrapper,
 			&args, RETURN_MASK_ALL);
+
+      /* If we couldn't find the end address of the vDSO pages, then set
+	 it from the size of the bfd that we just read in.  */
+      if (vsyscall_range->length == 0)
+	{
+	  if (bfd_stat (args.objf_added->obfd, &statbuf) < 0)
+	    {
+	      internal_error (__FILE__, __LINE__,
+			      _("bfd_stat on memfile bfd failed"));
+	    }
+	  vsyscall_range->length = statbuf.st_size;
+	}
     }
 }
 
@@ -247,8 +259,4 @@ _initialize_symfile_mem (void)
 	     "Give an expression for the address "
 	     "of the file's shared object file header."),
            &cmdlist);
-
-  /* Want to know of each new inferior so that its vsyscall info can
-     be extracted.  */
-  observer_attach_inferior_created (add_vsyscall_page);
 }
diff --git a/gdb/symfile-mem.h b/gdb/symfile-mem.h
new file mode 100644
index 0000000..df0e879
--- /dev/null
+++ b/gdb/symfile-mem.h
@@ -0,0 +1,30 @@
+/* Definitions for reading symbol files from memory into GDB.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program 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.
+
+   This program 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/>.  */
+
+#if !defined (SYMFILE_MEM_H)
+#define SYMFILE_MEM_H
+
+struct mem_range;
+
+/* Try to add the symbols for the vsyscall page, if there is one.  */
+
+extern void add_vsyscall_page (struct mem_range *vsyscall_range,
+			       int add_flags);
+
+#endif /* !defined(SYMFILE_MEM_H) */
diff --git a/gdb/testsuite/gdb.base/vdso-warning.exp b/gdb/testsuite/gdb.base/vdso-warning.exp
index af2b2b0..aeb85a2 100644
--- a/gdb/testsuite/gdb.base/vdso-warning.exp
+++ b/gdb/testsuite/gdb.base/vdso-warning.exp
@@ -13,42 +13,70 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+# Test that on Linux, we don't warn about not finding the vDSO.  E.g.:
+#
+#   warning: Could not load shared library symbols for linux-vdso.so.1.
+
 standard_testfile
 
 if { [prepare_for_testing "failed to prepare" ${testfile} $srcfile] } {
     return -1
 }
 
-gdb_breakpoint "main"
+with_test_prefix "setup" {
+    gdb_breakpoint "main"
 
-# At least some versions of Fedora/RHEL glibc have local patches that
-# hide the vDSO.  This lines re-exposes it.  See PR libc/13097,
-# comment 2.  There's no support for passing environment variables in
-# the remote protocol, but that's OK -- if we're testing against a
-# glibc that doesn't list the vDSO without this, the test should still
-# pass.
-gdb_test_no_output "set environment LD_DEBUG=unused"
+    # At least some versions of Fedora/RHEL glibc have local patches that
+    # hide the vDSO.  This lines re-exposes it.  See PR libc/13097,
+    # comment 2.  There's no support for passing environment variables in
+    # the remote protocol, but that's OK -- if we're testing against a
+    # glibc that doesn't list the vDSO without this, the test should still
+    # pass.
+    gdb_test_no_output "set environment LD_DEBUG=unused"
+}
 
-gdb_run_cmd
+proc test_no_vdso {command} {
+    global srcfile
+    global gdb_prompt
 
-set test "stop without warning"
-gdb_test_multiple "" $test {
-    -re "Could not load shared library symbols .*\r\n$gdb_prompt $" {
-	fail $test
+    set message "startup"
+    gdb_test_multiple "$command" $message {
+	-re "Could not load shared library symbols .*\r\n$gdb_prompt $" {
+	    fail $message
+	}
+	-re "main \\(\\) at .*$srcfile.*\r\n$gdb_prompt $" {
+	    pass $message
+	}
     }
-    -re "\r\nBreakpoint \[0-9\]+, main .*\r\n$gdb_prompt $" {
-	pass $test
+
+    # Extra testing in case the warning changes and we miss updating
+    # the above.
+    set test "no vdso without symbols is listed"
+    gdb_test_multiple "info shared" $test {
+	-re "No\[^\r\n\]+linux-(vdso|gate).*$gdb_prompt $" {
+	    fail $test
+	}
+	-re "$gdb_prompt $" {
+	    pass $test
+	}
     }
 }
 
-# Extra testing in case the warning changes and we miss updating the
-# above.
-set test "no vdso without symbols is listed"
-gdb_test_multiple "info shared" $test {
-    -re "No\[^\r\n\]+linux-(vdso|gate).*$gdb_prompt $" {
-	fail $test
-    }
-    -re "$gdb_prompt $" {
-	pass $test
+# First, try a live process.
+with_test_prefix "run" {
+    gdb_run_cmd
+    test_no_vdso ""
+}
+
+# Now, dump a core, and reload it.
+with_test_prefix "core" {
+    set corefile [standard_output_file $testfile.core]
+    set core_supported [gdb_gcore_cmd "$corefile" "save a corefile"]
+    if {!$core_supported} {
+	return -1
     }
+
+    clean_restart ${testfile}
+
+    test_no_vdso "core-file $corefile"
 }
-- 
2.5.5


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: warning: Could not load shared library symbols for linux-vdso.so.1.
  2016-08-19 10:32         ` Pedro Alves
@ 2016-08-19 11:41           ` Yao Qi
  2016-08-19 14:02             ` Pedro Alves
  0 siblings, 1 reply; 10+ messages in thread
From: Yao Qi @ 2016-08-19 11:41 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

On Fri, Aug 19, 2016 at 11:32 AM, Pedro Alves <palves@redhat.com> wrote:
>>
>> They are good to me.  Do you plan to push them to 7.12 branch?
>
> I was working under the assumption you wanted it in 7.12, given
> you set the milestone.  :-)
>

I put this issue there to make sure we don't forget this one.  At that moment,
I didn't expect a big change. :)

>
> However, if you want this on 7.12, then there's another option.  We
> can start with the hacky version that moves the add_vsyscall_page
> call to gdbarch_vsyscall_range, as mentioned in:
>
>> Now, in order to do this, we need to move the add_vsyscall_page
>> call earlier, before svr4_current_dsos is ever called, in order
>> to read the vdso bfd out of memory before we ever first need to
>> filter out the vdso.  The cleanest I could do with the current
>> gdbarch_vsyscall_range-based design was to do the add_vsyscall_page
>> call from within gdbarch_vsyscall_range.  But that looks very ugly
>> to me, for reading symbols from a quite innocent looking gdbarch hook.
>
> and then clean this up with the new linux_so_ops, on master, only.

OK, that is fine.

>
> So below's the "ugly" patch.  It's functionally exactly the same as
> the larger one.  The switch to linux_so_ops was mainly "cosmetic".
>
> Let me know what you prefer.
>

This patch is good to master and 7.12.

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: warning: Could not load shared library symbols for linux-vdso.so.1.
  2016-08-19 11:41           ` Yao Qi
@ 2016-08-19 14:02             ` Pedro Alves
  2016-08-22  9:36               ` Yao Qi
  0 siblings, 1 reply; 10+ messages in thread
From: Pedro Alves @ 2016-08-19 14:02 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 08/19/2016 12:40 PM, Yao Qi wrote:

>> So below's the "ugly" patch.  It's functionally exactly the same as
>> the larger one.  The switch to linux_so_ops was mainly "cosmetic".
>>
>> Let me know what you prefer.
>>
> 
> This patch is good to master and 7.12.

Hmm, wait, wait, wait...  I think we can do simpler and better even.

If reading the elf out of core memory works, then that means that
there must be a load segment that covers it.  So that means we should
be able to find a PT_LOAD segment for the vDSO, in the core file.  E.g.,:

$ readelf -l testsuite/outputs/gdb.base/vdso-warning/vdso-warning.core  

Elf file type is CORE (Core file)
Entry point 0x0
There are 18 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  NOTE           0x0000000000000430 0x0000000000000000 0x0000000000000000
                 0x0000000000000c18 0x0000000000000000  R      1

...
  LOAD           0x0000000000036048 0x00007ffff7ffa000 0x0000000000000000
                 0x0000000000002000 0x0000000000002000  R E    1
...

Hence the patch below.  If this doesn't work, then I think the ones
based on updating the vsyscall range from the memory bfd's size wouldn't
either.  And I think this one should work even for the cases that led to
commit 5979d6b69b20 ("Handle VDSO section headers past end of page")
too, thus making it even better.

I even wrote ChangeLog entries this time... :-)

WDYT?

From bdf1cb2aa3ef3b22fb80ce24c3b2d19ac5be9438 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Fri, 19 Aug 2016 14:09:06 +0100
Subject: [PATCH] Make vDSO detection work with core files

Loading a core dump that was either generated on a system running
pristine glibc master, or on a Fedora/RHEL system with LD_DEBUG=unused
set in the environment, solib-svr4.c:svr4_current_sos fails to filter
out the vDSO, resulting in:

  (gdb) core-file corefile.core^M
  [New LWP 2362]^M
  warning: Could not load shared library symbols for linux-vdso.so.1.^M
  Do you need "set solib-search-path" or "set sysroot"?^M
  Core was generated by `build-gdb/gdb/testsuite/outputs/gdb.base/corefile/'.^M
  ...

The problem is that gdbarch_vsyscall_range does not support core
inferiors at all.

When live debugging, we're finding the vDSO's start address with
auxv/AT_SYSINFO_EHDR, and then we find the vDSO's size by look for the
corresponding mapping, by parsing /proc/PID/maps.  When debugging a
core dump, we can also determine the starting address from
auxv/AT_SYSINFO_EHDR.  However, we obviously can't read the core
mappings out of the host's /proc.  But we can instead look for a
corresponding load segment in the core's bfd.

gdb/ChangeLog:
2016-08-19  Pedro Alves  <palves@redhat.com>

	* linux-tdep.c (linux_vsyscall_range_raw): For core inferiors,
	find the vDSO's start address with AT_SYSINFO_EHDR too, and
	determine the vDSO's size by finding the PT_LOAD segment that
	matches AT_SYSINFO_EHDR.

gdb/testsuite/ChangeLog:
2016-08-19  Pedro Alves  <palves@redhat.com>

	* gdb.base/vdso-warning.exp: Test core dumps too.  Use
	with_test_prefix.  Factor out bits to ...
	(test_no_vdso): ... this new procedure.
---
 gdb/linux-tdep.c                        | 35 ++++++++++++---
 gdb/testsuite/gdb.base/vdso-warning.exp | 76 ++++++++++++++++++++++-----------
 2 files changed, 82 insertions(+), 29 deletions(-)

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index ab110b0..718dc1a 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -2287,17 +2287,42 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
   long pid;
   char *data;
 
-  /* Can't access /proc if debugging a core file.  */
-  if (!target_has_execution)
+  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
     return 0;
 
+  /* It doesn't make sense to access the host's /proc when debugging a
+     core file.  Instead, look for the PT_LOAD segment that matches
+     the vDSO.  */
+  if (!target_has_execution)
+    {
+      Elf_Internal_Phdr *phdrs;
+      long phdrs_size;
+      int num_phdrs, i;
+
+      phdrs_size = bfd_get_elf_phdr_upper_bound (core_bfd);
+      if (phdrs_size == -1)
+	return 0;
+
+      phdrs = (Elf_Internal_Phdr *) alloca (phdrs_size);
+      num_phdrs = bfd_get_elf_phdrs (core_bfd, phdrs);
+      if (num_phdrs == -1)
+	return 0;
+
+      for (i = 0; i < num_phdrs; i++)
+	if (phdrs[i].p_type == PT_LOAD
+	    && phdrs[i].p_vaddr == range->start)
+	  {
+	    range->length = phdrs[i].p_memsz;
+	    return 1;
+	  }
+
+      return 0;
+    }
+
   /* We need to know the real target PID to access /proc.  */
   if (current_inferior ()->fake_pid_p)
     return 0;
 
-  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
-    return 0;
-
   pid = current_inferior ()->pid;
 
   /* Note that reading /proc/PID/task/PID/maps (1) is much faster than
diff --git a/gdb/testsuite/gdb.base/vdso-warning.exp b/gdb/testsuite/gdb.base/vdso-warning.exp
index af2b2b0..aeb85a2 100644
--- a/gdb/testsuite/gdb.base/vdso-warning.exp
+++ b/gdb/testsuite/gdb.base/vdso-warning.exp
@@ -13,42 +13,70 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+# Test that on Linux, we don't warn about not finding the vDSO.  E.g.:
+#
+#   warning: Could not load shared library symbols for linux-vdso.so.1.
+
 standard_testfile
 
 if { [prepare_for_testing "failed to prepare" ${testfile} $srcfile] } {
     return -1
 }
 
-gdb_breakpoint "main"
+with_test_prefix "setup" {
+    gdb_breakpoint "main"
 
-# At least some versions of Fedora/RHEL glibc have local patches that
-# hide the vDSO.  This lines re-exposes it.  See PR libc/13097,
-# comment 2.  There's no support for passing environment variables in
-# the remote protocol, but that's OK -- if we're testing against a
-# glibc that doesn't list the vDSO without this, the test should still
-# pass.
-gdb_test_no_output "set environment LD_DEBUG=unused"
+    # At least some versions of Fedora/RHEL glibc have local patches that
+    # hide the vDSO.  This lines re-exposes it.  See PR libc/13097,
+    # comment 2.  There's no support for passing environment variables in
+    # the remote protocol, but that's OK -- if we're testing against a
+    # glibc that doesn't list the vDSO without this, the test should still
+    # pass.
+    gdb_test_no_output "set environment LD_DEBUG=unused"
+}
 
-gdb_run_cmd
+proc test_no_vdso {command} {
+    global srcfile
+    global gdb_prompt
 
-set test "stop without warning"
-gdb_test_multiple "" $test {
-    -re "Could not load shared library symbols .*\r\n$gdb_prompt $" {
-	fail $test
+    set message "startup"
+    gdb_test_multiple "$command" $message {
+	-re "Could not load shared library symbols .*\r\n$gdb_prompt $" {
+	    fail $message
+	}
+	-re "main \\(\\) at .*$srcfile.*\r\n$gdb_prompt $" {
+	    pass $message
+	}
     }
-    -re "\r\nBreakpoint \[0-9\]+, main .*\r\n$gdb_prompt $" {
-	pass $test
+
+    # Extra testing in case the warning changes and we miss updating
+    # the above.
+    set test "no vdso without symbols is listed"
+    gdb_test_multiple "info shared" $test {
+	-re "No\[^\r\n\]+linux-(vdso|gate).*$gdb_prompt $" {
+	    fail $test
+	}
+	-re "$gdb_prompt $" {
+	    pass $test
+	}
     }
 }
 
-# Extra testing in case the warning changes and we miss updating the
-# above.
-set test "no vdso without symbols is listed"
-gdb_test_multiple "info shared" $test {
-    -re "No\[^\r\n\]+linux-(vdso|gate).*$gdb_prompt $" {
-	fail $test
-    }
-    -re "$gdb_prompt $" {
-	pass $test
+# First, try a live process.
+with_test_prefix "run" {
+    gdb_run_cmd
+    test_no_vdso ""
+}
+
+# Now, dump a core, and reload it.
+with_test_prefix "core" {
+    set corefile [standard_output_file $testfile.core]
+    set core_supported [gdb_gcore_cmd "$corefile" "save a corefile"]
+    if {!$core_supported} {
+	return -1
     }
+
+    clean_restart ${testfile}
+
+    test_no_vdso "core-file $corefile"
 }
-- 
2.5.5


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: warning: Could not load shared library symbols for linux-vdso.so.1.
  2016-08-19 14:02             ` Pedro Alves
@ 2016-08-22  9:36               ` Yao Qi
  2016-08-22 19:47                 ` Pedro Alves
  0 siblings, 1 reply; 10+ messages in thread
From: Yao Qi @ 2016-08-22  9:36 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Yao Qi, gdb-patches

Pedro Alves <palves@redhat.com> writes:

>
> If reading the elf out of core memory works, then that means that
> there must be a load segment that covers it.  So that means we should
> be able to find a PT_LOAD segment for the vDSO, in the core file.  E.g.,:
>

It is a good idea to tell the size of vDSO by a PT_LOAD segment.  I
can't figure out a case this doesn't work.  Patch is good me.

Note that I am unable to run the tests on the aarch64 machine where I
reported the problem, because it is stuck in the kernel upgrade.
However, I run your patch on other aarch64 machine.  Your patch does
fixes the fails in vdso-warning.exp.

Thanks a lot for fixing it in different ways.

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: warning: Could not load shared library symbols for linux-vdso.so.1.
  2016-08-22  9:36               ` Yao Qi
@ 2016-08-22 19:47                 ` Pedro Alves
  0 siblings, 0 replies; 10+ messages in thread
From: Pedro Alves @ 2016-08-22 19:47 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 08/22/2016 10:36 AM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>>
>> If reading the elf out of core memory works, then that means that
>> there must be a load segment that covers it.  So that means we should
>> be able to find a PT_LOAD segment for the vDSO, in the core file.  E.g.,:
>>
> 
> It is a good idea to tell the size of vDSO by a PT_LOAD segment.  I
> can't figure out a case this doesn't work.  Patch is good me.
> 
> Note that I am unable to run the tests on the aarch64 machine where I
> reported the problem, because it is stuck in the kernel upgrade.
> However, I run your patch on other aarch64 machine.  Your patch does
> fixes the fails in vdso-warning.exp.
> 
> Thanks a lot for fixing it in different ways.
> 

Alright, I've filed PR 20515 and then pushed to both master and 7.12.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2016-08-22 19:47 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-11 11:46 warning: Could not load shared library symbols for linux-vdso.so.1 Yao Qi
2016-08-11 15:57 ` Pedro Alves
2016-08-12 10:28   ` Yao Qi
2016-08-19  1:05     ` Pedro Alves
2016-08-19  9:59       ` Yao Qi
2016-08-19 10:32         ` Pedro Alves
2016-08-19 11:41           ` Yao Qi
2016-08-19 14:02             ` Pedro Alves
2016-08-22  9:36               ` Yao Qi
2016-08-22 19:47                 ` Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).