time to workaround libc/13097 in fsf gdb?

Message ID 541F2B1E.4030909@redhat.com
State Changes Requested, archived
Headers

Commit Message

Pedro Alves Sept. 21, 2014, 7:46 p.m. UTC
  On 09/21/2014 08:12 PM, Pedro Alves wrote:
> On 09/20/2014 10:30 PM, Jan Kratochvil wrote:
>> On Fri, 19 Sep 2014 16:41:34 +0200, Pedro Alves wrote:
>>> Here it is.  WDYT?
>> [...]
>>> --- a/gdb/solib-svr4.c
>>> +++ b/gdb/solib-svr4.c
>> [...]
>>> @@ -1478,6 +1479,42 @@ svr4_current_sos (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 objfile *objfile;
>>> +  struct obj_section *osect;
>>> +  CORE_ADDR vsyscall_addr = gdbarch_vsyscall_address (target_gdbarch ());
>>> +
>>> +  /* 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 (vsyscall_addr != 0)
>>> +    {
>>> +      struct so_list **sop;
>>> +
>>> +      sop = &so_head;
>>> +      while (*sop != NULL)
>>> +	{
>>> +	  struct so_list *so = *sop;
>>> +
>>> +	  if (so->lm_info->l_addr_inferior == vsyscall_addr)
>>
>> This won't work as l_addr_inferior (and also l_addr) do not necessarily
>> represent the real starting address of the ELF if the ELF itself is
>> "prelinked".  For some reason vDSOs on some kernels look like prelinked.
>>
>> kernel-3.16.2-200.fc20.x86_64 appears sane, vDSO is 0-based.
>>
>> But for example kernel-2.6.32-220.el6.x86_64 is "prelinked", see below.
> 
> Ah, didn't know that.  That's the sort of thing we should have in
> comments in the code, or at least in the commit log.
> 
>> That's the pain of solib-svr4.c which is OS-agnostic and so it cannot find out
>> start of the ELF file just from link map.  gdbserver can find it as it can
>> depend on /proc/PID/{s,}maps as its linux-low.c is Linux-specific.
> 
> Is it really a pain though?  We can just put things behind gdbarch hooks,
> like my patch was doing.  In fact, symfile-mem.c is already looking
> at /proc/PID/maps to find the vdso mapping size.  That's exactly done
> behind a gdbarch hook:
> 
>       args.size = 0;
>       if (gdbarch_find_memory_regions_p (target_gdbarch ()))
> 	(void) gdbarch_find_memory_regions (target_gdbarch (),
> 					    find_vdso_size, &args);
> 
>>
>> kernel-2.6.32-220.el6.x86_64
>> (gdb) p *_r_debug.r_map.l_next
>> $4 = {l_addr = 140737363566592, l_name = 0x3deba1ade4 "", l_ld = 0x7ffff7ffe580, l_next = 0x7ffff7ffd658, l_prev = 0x3debc21188}
>> (gdb) p/x *_r_debug.r_map.l_next
>> $5 = {l_addr = 0x7ffff88fe000, l_name = 0x3deba1ade4, l_ld = 0x7ffff7ffe580, l_next = 0x7ffff7ffd658, l_prev = 0x3debc21188}
>> # (gdb) p/x *new.lm_info
>> # $5 = {l_addr = 0x0, l_addr_inferior = 0x7ffff88fe000, l_addr_p = 0x0, lm_addr = 0x3debc21718, l_ld = 0x7ffff7ffe580, l_next = 0x7ffff7ffd658, l_prev = 0x3debc21188, l_name = 0x3deba1ade4}
>> (gdb) info auxv
>> 33   AT_SYSINFO_EHDR      System-supplied DSO's ELF header 0x7ffff7ffe000
>> (gdb) info proc mappings
>>       0x7ffff7ffe000     0x7ffff7fff000     0x1000          0                           [vdso]
> 
> Sounds like a predicate like this would work then?
> 
> 	  if (vsyscall_start <= so->lm_info->l_ld && so->lm_info->l_ld < vsyscall_end)
> 
> We would move the bit that finds the vdso size to the gdbarch_vsyscall_address hook,
> and make that new hook return the vsyscall's size too in addition to
> the starting address.  We can also cache the result somewhere instead
> of constantly reopening /proc/PID/maps if necessary.
> 
> We can also add a custom linux-specific target_so_ops implementation that
> extends svr4's if we want.  The target_so_ops to use is also registered
> in the gdbarch.
> 
>   CORE_ADDR vsyscall_start;
>   CORE_ADDR vsyscall_end;
> 
>   vsyscall_start = gdbarch_vsyscall_address (target_gdbarch (), &vsyscall_end);
> 
>   /* 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 (vsyscall_start != 0)
>     {
>        struct so_list **sop;
> 
>        sop = &so_head;
>        while (*sop != NULL)
>          {
>            struct so_list *so = *sop;
> 
>     	   if (vsyscall_start <= so->lm_info->l_ld
>                && so->lm_info->l_ld < vsyscall_end)
>              {
>                ... found vdso ...


Here's a quick update to the patch that does (just) that, for
testing.  Doesn't update comments, etc.  It works on f20 here.

WDYT ?

-------
From f64fc9d4bf3909570c6047ba0c6e78ce8ea8d8c6 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Fri, 19 Sep 2014 15:08:42 +0100
Subject: [PATCH] Subject: Work around PR libc/13097 "linux-vdso.so.1"

With upstream glibc, GDB prints:

	warning: Could not load shared library symbols for linux-vdso.so.1.
	Do you need "set solib-search-path" or "set sysroot"?

A bug's been filed for glibc a few years back:

  http://sourceware.org/bugzilla/show_bug.cgi?id=13097

but it's still not resolved.  It's not clear whether there's even
consensus that this is indeed a glibc bug.  It would actually be nice
if GDB also listed the vdso in the shared library list, but there are
some design considerations with that:

 - the vDSO is mapped by the kernel, not userspace, therefore we
   should load its symbols right from the process's start of life,
   even before glibc / the userspace loader sets up the initial DSO
   list.  The program might even be using a custom loader or no
   loader.

 - that kind of hints at that solib.c should handle retrieving shared
   library lists from more than one source, and that symfile-mem.c's
   loading of the vdso would be converted to load and relocate the
   vdso's bfd behind the target_so_ops interface.

 - and then, once glibc links in the vdso to its DSO list, we'd need
   to either:

    a) somehow hand over the vdso from one target_so_ops to the
      other
    b) or simply keep hiding glibc's entry.

And then b) seems the simplest.

With that in mind, this patch simply discards the vDSO from glibc's
reported shared library list.

We can match the vdso address found through AT_SYSINFO_EHDR with the
addresses found iterating the dynamic linker list, to know which
dynamic linker entry is the vdso.

Note that symfile-mem.c is not present in every configuration that
uses solib-svr4.c.  It actually probably should always be linked in
GDB (added to COMMON_OBS), so that the "add-symbol-file-from-memory"
command is always available for all targets.  But even if we did that,
even though harmless, it's unnecessary or a bit wrong in principle
even, to try the target_auxv_search lookup against all targets (even
though harmless; at least currently).

So solve that, this moves the target_auxv_search lookup to a gdbarch
hook, registered for all Linux architectures, and then both
solib-svr4.c and symfile-mem.c can both use it.

Tested on x86_64 Fedora 20.

gdb/
2014-09-19  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	PR 14466
	* arch-utils.c (default_vsyscall_address): New function.
	* arch-utils.h (default_vsyscall_address): New declaration.
	* gdbarch.sh (vsyscall_address): New hook.
	* gdbarch.h, gdbarch.c: Regenerate.
	* linux-tdep.c (linux_vsyscall_address): New function.
	(linux_init_abi): Install linux_vsyscall_address as
	vsyscall_address gdbarch hook.
	* solib-svr4.c (svr4_read_so_list): Rename to ...
	(svr4_current_sos_1): ... this and change the function comment.
	(svr4_current_sos): New function.
	* symfile-mem.c (add_vsyscall_page): Use gdbarch_vsyscall_address.

gdb/testsuite/
2014-09-19  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	PR 14466
	* gdb.base/vdso-warning.c: New file.
	* gdb.base/vdso-warning.exp: New file.
---
 gdb/arch-utils.c                        |  8 +++++
 gdb/arch-utils.h                        |  4 +++
 gdb/gdbarch.c                           | 23 ++++++++++++++
 gdb/gdbarch.h                           |  7 +++++
 gdb/gdbarch.sh                          |  4 +++
 gdb/linux-tdep.c                        | 49 ++++++++++++++++++++++++++++++
 gdb/solib-svr4.c                        | 43 ++++++++++++++++++++++++--
 gdb/symfile-mem.c                       | 32 ++++---------------
 gdb/testsuite/gdb.base/vdso-warning.c   | 22 ++++++++++++++
 gdb/testsuite/gdb.base/vdso-warning.exp | 54 +++++++++++++++++++++++++++++++++
 10 files changed, 218 insertions(+), 28 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/vdso-warning.c
 create mode 100644 gdb/testsuite/gdb.base/vdso-warning.exp
  

Comments

Doug Evans Sept. 23, 2014, 11:05 p.m. UTC | #1
On Sun, Sep 21, 2014 at 12:46 PM, Pedro Alves <palves@redhat.com> wrote:
>[...]
> @@ -1478,6 +1479,44 @@ svr4_current_sos (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 objfile *objfile;
> +  struct obj_section *osect;
> +  CORE_ADDR vsyscall_start, vsyscall_end;
> +
> +  /* 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_start, &vsyscall_end))
> +    {
> +      struct so_list **sop;
> +
> +      sop = &so_head;
> +      while (*sop != NULL)
> +       {
> +         struct so_list *so = *sop;
> +
> +         if (vsyscall_start <= so->lm_info->l_ld
> +             && so->lm_info->l_ld < vsyscall_end)

This test is subtle enough that I'd appreciate a full explanation here
of what's going on.
Thanks.

> +           {
> +             *sop = so->next;
> +             free_so (so);
> +             break;
> +           }
> +
> +         sop = &so->next;
> +       }
> +    }
> +
> +  return so_head;
> +}
> +

I may have a few more comments, but I think Jan has covered them, so
I'm going to wait until the next version for further review.
  
Pedro Alves Sept. 26, 2014, 12:09 p.m. UTC | #2
On 09/24/2014 12:05 AM, Doug Evans wrote:
> On Sun, Sep 21, 2014 at 12:46 PM, Pedro Alves <palves@redhat.com> wrote:
>> [...]
>> Here's a quick update to the patch that does (just) that, for
>> testing.  Doesn't update comments, etc.  It works on f20 here.

>> +         if (vsyscall_start <= so->lm_info->l_ld
>> +             && so->lm_info->l_ld < vsyscall_end)
> 
> This test is subtle enough that I'd appreciate a full explanation here
> of what's going on.
> Thanks.
> 

Yeah, as I mentioned, that was just a quick patch for testing.

I'll be sending out the next version in a new thread, which adds
a big comment here.

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index 5ae9fb3..ecaa773 100644
--- a/gdb/arch-utils.c
+++ b/gdb/arch-utils.c
@@ -243,6 +243,14 @@  default_remote_register_number (struct gdbarch *gdbarch,
   return regno;
 }

+/* See arch-utils.h.  */
+
+int
+default_vsyscall_range (struct gdbarch *gdbarch, CORE_ADDR *start, CORE_ADDR *end)
+{
+  return 0;
+}
+
 
 /* Functions to manipulate the endianness of the target.  */

diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h
index 46d1573..d9f49b9 100644
--- a/gdb/arch-utils.h
+++ b/gdb/arch-utils.h
@@ -174,4 +174,8 @@  extern int default_return_in_first_hidden_param_p (struct gdbarch *,
 extern int default_insn_is_call (struct gdbarch *, CORE_ADDR);
 extern int default_insn_is_ret (struct gdbarch *, CORE_ADDR);
 extern int default_insn_is_jump (struct gdbarch *, CORE_ADDR);
+
+/* Do-nothing version of vsyscall_address.  Returns 0.  */
+
+extern int default_vsyscall_range (struct gdbarch *, CORE_ADDR *, CORE_ADDR *);
 #endif
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index b0ee79d..54b2434 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -316,6 +316,7 @@  struct gdbarch
   gdbarch_insn_is_ret_ftype *insn_is_ret;
   gdbarch_insn_is_jump_ftype *insn_is_jump;
   gdbarch_auxv_parse_ftype *auxv_parse;
+  gdbarch_vsyscall_range_ftype *vsyscall_range;
 };

 /* Create a new ``struct gdbarch'' based on information provided by
@@ -408,6 +409,7 @@  gdbarch_alloc (const struct gdbarch_info *info,
   gdbarch->insn_is_call = default_insn_is_call;
   gdbarch->insn_is_ret = default_insn_is_ret;
   gdbarch->insn_is_jump = default_insn_is_jump;
+  gdbarch->vsyscall_range = default_vsyscall_range;
   /* gdbarch_alloc() */

   return gdbarch;
@@ -627,6 +629,7 @@  verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of insn_is_ret, invalid_p == 0 */
   /* Skip verify of insn_is_jump, invalid_p == 0 */
   /* Skip verify of auxv_parse, has predicate.  */
+  /* Skip verify of vsyscall_range, invalid_p == 0 */
   buf = ui_file_xstrdup (log, &length);
   make_cleanup (xfree, buf);
   if (length > 0)
@@ -1296,6 +1299,9 @@  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,
@@ -4403,6 +4409,23 @@  set_gdbarch_auxv_parse (struct gdbarch *gdbarch,
   gdbarch->auxv_parse = auxv_parse;
 }

+int
+gdbarch_vsyscall_range (struct gdbarch *gdbarch, CORE_ADDR *start, CORE_ADDR *end)
+{
+  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, start, end);
+}
+
+void
+set_gdbarch_vsyscall_range (struct gdbarch *gdbarch,
+                            gdbarch_vsyscall_range_ftype vsyscall_range)
+{
+  gdbarch->vsyscall_range = vsyscall_range;
+}
+

 /* Keep a registry of per-architecture data-pointers required by GDB
    modules.  */
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 0303b2e..5613ce4 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1317,6 +1317,13 @@  typedef int (gdbarch_auxv_parse_ftype) (struct gdbarch *gdbarch, gdb_byte **read
 extern int gdbarch_auxv_parse (struct gdbarch *gdbarch, gdb_byte **readptr, gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp);
 extern void set_gdbarch_auxv_parse (struct gdbarch *gdbarch, gdbarch_auxv_parse_ftype *auxv_parse);

+/* Find the address range of the current inferior's vsyscall/vDSO, and
+   write it to *START, *END.  Returns true if found, false otherwise. */
+
+typedef int (gdbarch_vsyscall_range_ftype) (struct gdbarch *gdbarch, CORE_ADDR *start, CORE_ADDR *end);
+extern int gdbarch_vsyscall_range (struct gdbarch *gdbarch, CORE_ADDR *start, CORE_ADDR *end);
+extern void set_gdbarch_vsyscall_range (struct gdbarch *gdbarch, gdbarch_vsyscall_range_ftype *vsyscall_range);
+
 /* Definition for an unknown syscall, used basically in error-cases.  */
 #define UNKNOWN_SYSCALL (-1)

diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 2a8bca8..e3bbf82 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1029,6 +1029,10 @@  m:int:insn_is_jump:CORE_ADDR addr:addr::default_insn_is_jump::0
 # Return -1 if there is insufficient buffer for a whole entry.
 # Return 1 if an entry was read into *TYPEP and *VALP.
 M:int:auxv_parse:gdb_byte **readptr, gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp:readptr, endptr, typep, valp
+
+# Find the address range of the current inferior's vsyscall/vDSO, and
+# write it to *START, *END.  Returns true if found, false otherwise.
+m:int:vsyscall_range:CORE_ADDR *start, CORE_ADDR *end:start, end::default_vsyscall_range::0
 EOF
 }

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index dae59c5..3f28fc9 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -1782,6 +1782,54 @@  linux_gdb_signal_to_target (struct gdbarch *gdbarch,
   return -1;
 }

+/* Arguments for symbol_file_add_from_memory_wrapper.  */
+
+struct find_mapping_size_args
+{
+  CORE_ADDR vaddr;
+  size_t size;
+};
+
+/* Rummage through mappings to find a mapping size.  */
+
+static int
+find_mapping_size (CORE_ADDR vaddr, unsigned long size,
+		   int read, int write, int exec, int modified,
+		   void *data)
+{
+  struct find_mapping_size_args *args = data;
+
+  if (vaddr == args->vaddr)
+    {
+      args->size = size;
+      return 1;
+    }
+  return 0;
+}
+
+/* Implementation of the "vsyscall_range" gdbarch hook.  */
+
+static int
+linux_vsyscall_range (struct gdbarch *gdbarch, CORE_ADDR *start, CORE_ADDR *end)
+{
+  struct find_mapping_size_args args;
+
+  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &args.vaddr) <= 0)
+    return 0;
+
+  /* This is installed by linux_init_abi below, so should always be
+     available.  */
+  gdb_assert (gdbarch_find_memory_regions_p (target_gdbarch ()));
+
+  args.size = 0;
+  gdbarch_find_memory_regions (target_gdbarch (),
+			       find_mapping_size, &args);
+
+  *start = args.vaddr;
+  *end = *start + args.size;
+  return 1;
+}
+
 /* To be called from the various GDB_OSABI_LINUX handlers for the
    various GNU/Linux architectures and machine types.  */

@@ -1799,6 +1847,7 @@  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);
 }

 /* Provide a prototype to silence -Wmissing-prototypes.  */
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 3deef20..cc38979 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -1462,10 +1462,11 @@  svr4_current_sos_direct (struct svr4_info *info)
   return head;
 }

-/* Implement the "current_sos" target_so_ops method.  */
+/* Implement the main part of the "current_sos" target_so_ops
+   method.  */

 static struct so_list *
-svr4_current_sos (void)
+svr4_current_sos_1 (void)
 {
   struct svr4_info *info = get_svr4_info ();

@@ -1478,6 +1479,44 @@  svr4_current_sos (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 objfile *objfile;
+  struct obj_section *osect;
+  CORE_ADDR vsyscall_start, vsyscall_end;
+
+  /* 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_start, &vsyscall_end))
+    {
+      struct so_list **sop;
+
+      sop = &so_head;
+      while (*sop != NULL)
+	{
+	  struct so_list *so = *sop;
+
+	  if (vsyscall_start <= so->lm_info->l_ld
+	      && so->lm_info->l_ld < vsyscall_end)
+	    {
+	      *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
diff --git a/gdb/symfile-mem.c b/gdb/symfile-mem.c
index ef48f7d..6b5fa7a 100644
--- a/gdb/symfile-mem.c
+++ b/gdb/symfile-mem.c
@@ -188,33 +188,16 @@  symbol_file_add_from_memory_wrapper (struct ui_out *uiout, void *data)
   return 0;
 }

-/* Rummage through mappings to find the vsyscall page size.  */
-
-static int
-find_vdso_size (CORE_ADDR vaddr, unsigned long size,
-		int read, int write, int exec, int modified,
-		void *data)
-{
-  struct symbol_file_add_from_memory_args *args = data;
-
-  if (vaddr == args->sysinfo_ehdr)
-    {
-      args->size = size;
-      return 1;
-    }
-  return 0;
-}
-
 /* Try to add the symbols for the vsyscall page, if there is one.
    This function is called via the inferior_created observer.  */

 static void
 add_vsyscall_page (struct target_ops *target, int from_tty)
 {
-  CORE_ADDR sysinfo_ehdr;
+  CORE_ADDR vsyscall_start, vsyscall_end;

-  if (target_auxv_search (target, AT_SYSINFO_EHDR, &sysinfo_ehdr) > 0
-      && sysinfo_ehdr != (CORE_ADDR) 0)
+  if (gdbarch_vsyscall_range (target_gdbarch (),
+			      &vsyscall_start, &vsyscall_end))
     {
       struct bfd *bfd;
       struct symbol_file_add_from_memory_args args;
@@ -237,14 +220,11 @@  add_vsyscall_page (struct target_ops *target, int from_tty)
 	  return;
 	}
       args.bfd = bfd;
-      args.sysinfo_ehdr = sysinfo_ehdr;
-      args.size = 0;
-      if (gdbarch_find_memory_regions_p (target_gdbarch ()))
-	(void) gdbarch_find_memory_regions (target_gdbarch (),
-					    find_vdso_size, &args);
+      args.sysinfo_ehdr = vsyscall_start;
+      args.size = vsyscall_end - vsyscall_start;

       args.name = xstrprintf ("system-supplied DSO at %s",
-			      paddress (target_gdbarch (), sysinfo_ehdr));
+			      paddress (target_gdbarch (), vsyscall_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.  */
diff --git a/gdb/testsuite/gdb.base/vdso-warning.c b/gdb/testsuite/gdb.base/vdso-warning.c
new file mode 100644
index 0000000..4b94803
--- /dev/null
+++ b/gdb/testsuite/gdb.base/vdso-warning.c
@@ -0,0 +1,22 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2014 Free Software Foundation, Inc.
+
+   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/>.  */
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/vdso-warning.exp b/gdb/testsuite/gdb.base/vdso-warning.exp
new file mode 100644
index 0000000..1f538fa
--- /dev/null
+++ b/gdb/testsuite/gdb.base/vdso-warning.exp
@@ -0,0 +1,54 @@ 
+# Copyright 2012-2014 Free Software Foundation, Inc.
+
+# 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/>.
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} $srcfile] } {
+    return -1
+}
+
+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"
+
+gdb_run_cmd
+
+set test "stop without warning"
+gdb_test_multiple "" $test {
+    -re "Could not load shared library symbols .*\r\n$gdb_prompt $" {
+	fail $test
+    }
+    -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
+    }
+}