public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/3] Split vDSO range lookup to a gdbarch hook
  2014-09-26 12:25 [PATCH 0/3] Work around PR libc/13097 "linux-vdso.so.1" Pedro Alves
  2014-09-26 12:25 ` [PATCH 3/3] Cache the vsyscall/vDSO range per-inferior Pedro Alves
  2014-09-26 12:25 ` [PATCH 2/3] Work around PR libc/13097 "linux-vdso.so.1" Pedro Alves
@ 2014-09-26 12:25 ` Pedro Alves
  2014-10-10 16:14 ` [PATCH 0/3] Work around PR libc/13097 "linux-vdso.so.1" Pedro Alves
  3 siblings, 0 replies; 5+ messages in thread
From: Pedro Alves @ 2014-09-26 12:25 UTC (permalink / raw)
  To: gdb-patches

We have a case in solib-svr4.c where we could reuse symfile-mem.c's
vDSO range lookup.  Since symfile-mem.c is not present in all
configurations solib-svr4.c is, move that lookup to a gdbarch hook.

This has the minor (good) side effect that we stop even trying the
target_auxv_search lookup against targets that don't have a concept of
a vDSO, in case symfile-mem.c happens to be linked in the build
(--enable-targets=all).

Tested on x86_64 Fedora 20.

gdb/
2014-09-26  Pedro Alves  <palves@redhat.com>

	* arch-utils.c (default_vsyscall_range): New function.
	* arch-utils.h (default_vsyscall_range): New declaration.
	* gdbarch.sh (vsyscall_range): New hook.
	* gdbarch.h, gdbarch.c: Regenerate.
	* linux-tdep.c (linux_vsyscall_range): New function.
	(linux_init_abi): Install linux_vsyscall_range as
	vsyscall_range gdbarch hook.
	* memrange.c (address_in_mem_range): New function.
	* memrange.h (address_in_mem_range): New declaration.
	* symfile-mem.c (find_vdso_size): Delete function.
	(add_vsyscall_page): Use gdbarch_vsyscall_range.
---
 gdb/arch-utils.c  |  8 ++++++++
 gdb/arch-utils.h  |  4 ++++
 gdb/gdbarch.c     | 23 +++++++++++++++++++++++
 gdb/gdbarch.h     | 10 ++++++++++
 gdb/gdbarch.sh    |  7 +++++++
 gdb/linux-tdep.c  | 35 +++++++++++++++++++++++++++++++++++
 gdb/memrange.c    |  9 +++++++++
 gdb/memrange.h    |  5 +++++
 gdb/symfile-mem.c | 31 +++++--------------------------
 9 files changed, 106 insertions(+), 26 deletions(-)

diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index 5ae9fb3..41cf828 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, struct mem_range *range)
+{
+  return 0;
+}
+
 \f
 /* Functions to manipulate the endianness of the target.  */
 
diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h
index 46d1573..a609662 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_range.  Returns false.  */
+
+extern int default_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range);
 #endif
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index b0ee79d..b061f81 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, 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;
+}
+
 
 /* Keep a registry of per-architecture data-pointers required by GDB
    modules.  */
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 0303b2e..1215154 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -59,6 +59,7 @@ struct axs_value;
 struct stap_parse_info;
 struct ravenscar_arch_ops;
 struct elf_internal_linux_prpsinfo;
+struct mem_range;
 
 /* The architecture associated with the inferior through the
    connection to the target.
@@ -1317,6 +1318,15 @@ 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 *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);
+
 /* 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..be5f2de 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1029,6 +1029,12 @@ 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 *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
 EOF
 }
 
@@ -1148,6 +1154,7 @@ struct axs_value;
 struct stap_parse_info;
 struct ravenscar_arch_ops;
 struct elf_internal_linux_prpsinfo;
+struct mem_range;
 
 /* The architecture associated with the inferior through the
    connection to the target.
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index dae59c5..2a5e57d 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -1782,6 +1782,40 @@ linux_gdb_signal_to_target (struct gdbarch *gdbarch,
   return -1;
 }
 
+/* Rummage through mappings to find a mapping's size.  */
+
+static int
+find_mapping_size (CORE_ADDR vaddr, unsigned long size,
+		   int read, int write, int exec, int modified,
+		   void *data)
+{
+  struct mem_range *range = data;
+
+  if (vaddr == range->start)
+    {
+      range->length = size;
+      return 1;
+    }
+  return 0;
+}
+
+/* Implementation of the "vsyscall_range" gdbarch hook.  */
+
+static int
+linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
+{
+  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 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 ()));
+
+  range->length = 0;
+  gdbarch_find_memory_regions (gdbarch, find_mapping_size, range);
+  return 1;
+}
+
 /* To be called from the various GDB_OSABI_LINUX handlers for the
    various GNU/Linux architectures and machine types.  */
 
@@ -1799,6 +1833,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/memrange.c b/gdb/memrange.c
index 2895ff2..485550b 100644
--- a/gdb/memrange.c
+++ b/gdb/memrange.c
@@ -31,6 +31,15 @@ mem_ranges_overlap (CORE_ADDR start1, int len1,
   return (l < h);
 }
 
+/* See memrange.h.  */
+
+int
+address_in_mem_range (CORE_ADDR address, const struct mem_range *r)
+{
+  return (r->start <= address
+	  && (address - r->start) < r->length);
+}
+
 /* qsort comparison function, that compares mem_ranges.  Ranges are
    sorted in ascending START order.  */
 
diff --git a/gdb/memrange.h b/gdb/memrange.h
index 8c25d0a..2591789 100644
--- a/gdb/memrange.h
+++ b/gdb/memrange.h
@@ -43,6 +43,11 @@ DEF_VEC_O(mem_range_s);
 extern int mem_ranges_overlap (CORE_ADDR start1, int len1,
 			       CORE_ADDR start2, int len2);
 
+/* Returns true if ADDR is in RANGE.  */
+
+extern int address_in_mem_range (CORE_ADDR addr,
+				 const struct mem_range *range);
+
 /* Sort ranges by start address, then coalesce contiguous or
    overlapping ranges.  */
 
diff --git a/gdb/symfile-mem.c b/gdb/symfile-mem.c
index ef48f7d..2872e15 100644
--- a/gdb/symfile-mem.c
+++ b/gdb/symfile-mem.c
@@ -188,33 +188,15 @@ 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;
+  struct mem_range vsyscall_range;
 
-  if (target_auxv_search (target, AT_SYSINFO_EHDR, &sysinfo_ehdr) > 0
-      && sysinfo_ehdr != (CORE_ADDR) 0)
+  if (gdbarch_vsyscall_range (target_gdbarch (), &vsyscall_range))
     {
       struct bfd *bfd;
       struct symbol_file_add_from_memory_args args;
@@ -237,14 +219,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_range.start;
+      args.size = vsyscall_range.length;
 
       args.name = xstrprintf ("system-supplied DSO at %s",
-			      paddress (target_gdbarch (), sysinfo_ehdr));
+			      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.  */
-- 
1.9.3

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

* [PATCH 3/3] Cache the vsyscall/vDSO range per-inferior
  2014-09-26 12:25 [PATCH 0/3] Work around PR libc/13097 "linux-vdso.so.1" Pedro Alves
@ 2014-09-26 12:25 ` Pedro Alves
  2014-09-26 12:25 ` [PATCH 2/3] Work around PR libc/13097 "linux-vdso.so.1" Pedro Alves
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Pedro Alves @ 2014-09-26 12:25 UTC (permalink / raw)
  To: gdb-patches

We're now doing a vsyscall/vDSO address range lookup whenever we fetch
shared libraries, either through an explicit "info shared", or when
the target reports new libraries have been loaded, in order to filter
out the vDSO from glibc's DSO list.  Before we started doing that, GDB
would only ever lookup the vsyscall's address range once in the
process's lifetime.

Looking up the vDSO address range requires an auxv lookup (which is
already cached, so no problem), but also reading the process's
mappings from /proc to find out the vDSO's mapping's size.  That
generates extra RSP traffic when remote debugging.  Particularly
annoying when the process's mappings grow linearly as more libraries
are mapped in, and we went through the trouble of making incremental
DSO list updates work against gdbserver (when the probes-based dynamic
linker interface is available).

The vsyscall/vDSO is mapped by the kernel when the process is
initially mapped in, and doesn't change throughout the process's
lifetime, so we can cache its address range.

Caching at this level brings GDB back to one and only one vsyscall
address range lookup per process.

Tested on x86_64 Fedora 20.

gdb/
2014-09-26  Pedro Alves  <palves@redhat.com>

	* linux-tdep.c: Include observer.h.
	(linux_inferior_data): New global.
	(struct linux_info): New structure.
	(invalidate_linux_cache_inf, linux_inferior_data_cleanup)
	(get_linux_inferior_data): New functions.
	(linux_vsyscall_range): Rename to ...
	(linux_vsyscall_range_raw): ... this.
	(linux_vsyscall_range): New function; handles caching.
	(_initialize_linux_tdep): Register linux_inferior_data.  Install
	inferior_exit and inferior_appeared observers.
---
 gdb/linux-tdep.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 100 insertions(+), 2 deletions(-)

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 2a5e57d..550c97d 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -32,6 +32,7 @@
 #include "cli/cli-utils.h"
 #include "arch-utils.h"
 #include "gdb_obstack.h"
+#include "observer.h"
 
 #include <ctype.h>
 
@@ -119,6 +120,72 @@ get_linux_gdbarch_data (struct gdbarch *gdbarch)
   return gdbarch_data (gdbarch, linux_gdbarch_data_handle);
 }
 
+/* Per-inferior data key.  */
+static const struct inferior_data *linux_inferior_data;
+
+/* Linux-specific cached data.  This is used by GDB for caching
+   purposes for each inferior.  This helps reduce the overhead of
+   transfering data from a remote target to the local host.  */
+struct linux_info
+{
+  /* Cache of the inferior's vsyscall/vDSO mapping 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
+     throughout execution and therefore cannot be cached).  */
+  struct mem_range vsyscall_range;
+
+  /* Zero if we haven't tried looking up the vsyscall's range before
+     yet.  Positive if we tried looking it up, and found it.  Negative
+     if we tried looking it up but failed.  */
+  int vsyscall_range_p;
+};
+
+/* Frees whatever allocated space there is to be freed and sets INF's
+   linux cache data pointer to NULL.  */
+
+static void
+invalidate_linux_cache_inf (struct inferior *inf)
+{
+  struct linux_info *info;
+
+  info = inferior_data (inf, linux_inferior_data);
+  if (info != NULL)
+    {
+      xfree (info);
+      set_inferior_data (inf, linux_inferior_data, NULL);
+    }
+}
+
+/* Handles the cleanup of the linux cache for inferior INF.  ARG is
+   ignored.  Callback for the inferior_appeared and inferior_exit
+   events.  */
+
+static void
+linux_inferior_data_cleanup (struct inferior *inf, void *arg)
+{
+  invalidate_linux_cache_inf (inf);
+}
+
+/* Fetch the linux cache info for INF.  This function always returns a
+   valid INFO pointer.  */
+
+static struct linux_info *
+get_linux_inferior_data (void)
+{
+  struct linux_info *info;
+  struct inferior *inf = current_inferior ();
+
+  info = inferior_data (inf, linux_inferior_data);
+  if (info == NULL)
+    {
+      info = XCNEW (struct linux_info);
+      set_inferior_data (inf, linux_inferior_data, info);
+    }
+
+  return info;
+}
+
 /* This function is suitable for architectures that don't
    extend/override the standard siginfo structure.  */
 
@@ -1799,10 +1866,11 @@ find_mapping_size (CORE_ADDR vaddr, unsigned long size,
   return 0;
 }
 
-/* Implementation of the "vsyscall_range" gdbarch hook.  */
+/* Helper for linux_vsyscall_range that does the real work of finding
+   the vsyscall's address range.  */
 
 static int
-linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
+linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
 {
   if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
     return 0;
@@ -1816,6 +1884,29 @@ linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
   return 1;
 }
 
+/* Implementation of the "vsyscall_range" gdbarch hook.  Handles
+   caching, and defers the real work to linux_vsyscall_range_raw.  */
+
+static int
+linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
+{
+  struct linux_info *info = get_linux_inferior_data ();
+
+  if (info->vsyscall_range_p == 0)
+    {
+      if (linux_vsyscall_range_raw (gdbarch, &info->vsyscall_range))
+	info->vsyscall_range_p = 1;
+      else
+	info->vsyscall_range_p = -1;
+    }
+
+  if (info->vsyscall_range_p < 0)
+    return 0;
+
+  *range = info->vsyscall_range;
+  return 1;
+}
+
 /* To be called from the various GDB_OSABI_LINUX handlers for the
    various GNU/Linux architectures and machine types.  */
 
@@ -1844,4 +1935,11 @@ _initialize_linux_tdep (void)
 {
   linux_gdbarch_data_handle =
     gdbarch_data_register_post_init (init_linux_gdbarch_data);
+
+  /* Set a cache per-inferior.  */
+  linux_inferior_data
+    = register_inferior_data_with_cleanup (NULL, linux_inferior_data_cleanup);
+  /* Observers used to invalidate the cache when needed.  */
+  observer_attach_inferior_exit (invalidate_linux_cache_inf);
+  observer_attach_inferior_appeared (invalidate_linux_cache_inf);
 }
-- 
1.9.3

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

* [PATCH 0/3] Work around PR libc/13097 "linux-vdso.so.1"
@ 2014-09-26 12:25 Pedro Alves
  2014-09-26 12:25 ` [PATCH 3/3] Cache the vsyscall/vDSO range per-inferior Pedro Alves
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Pedro Alves @ 2014-09-26 12:25 UTC (permalink / raw)
  To: gdb-patches

Following up to:
  https://sourceware.org/ml/gdb-patches/2014-09/msg00777.html

This series works around PR libc/13097 in GDB.  See patch 2/3 for
details.

Pedro Alves (3):
  Split vDSO range lookup to a gdbarch hook
  Work around PR libc/13097 "linux-vdso.so.1"
  Cache the vsyscall/vDSO range per-inferior

 gdb/arch-utils.c                        |   8 ++
 gdb/arch-utils.h                        |   4 +
 gdb/gdbarch.c                           |  23 ++++++
 gdb/gdbarch.h                           |  10 +++
 gdb/gdbarch.sh                          |   7 ++
 gdb/linux-tdep.c                        | 133 ++++++++++++++++++++++++++++++++
 gdb/memrange.c                          |   9 +++
 gdb/memrange.h                          |   5 ++
 gdb/solib-svr4.c                        |  81 ++++++++++++++++++-
 gdb/symfile-mem.c                       |  31 ++------
 gdb/testsuite/gdb.base/vdso-warning.c   |  22 ++++++
 gdb/testsuite/gdb.base/vdso-warning.exp |  54 +++++++++++++
 12 files changed, 359 insertions(+), 28 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/vdso-warning.c
 create mode 100644 gdb/testsuite/gdb.base/vdso-warning.exp

-- 
1.9.3

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

* [PATCH 2/3] Work around PR libc/13097 "linux-vdso.so.1"
  2014-09-26 12:25 [PATCH 0/3] Work around PR libc/13097 "linux-vdso.so.1" Pedro Alves
  2014-09-26 12:25 ` [PATCH 3/3] Cache the vsyscall/vDSO range per-inferior Pedro Alves
@ 2014-09-26 12:25 ` Pedro Alves
  2014-09-26 12:25 ` [PATCH 1/3] Split vDSO range lookup to a gdbarch hook Pedro Alves
  2014-10-10 16:14 ` [PATCH 0/3] Work around PR libc/13097 "linux-vdso.so.1" Pedro Alves
  3 siblings, 0 replies; 5+ messages in thread
From: Pedro Alves @ 2014-09-26 12:25 UTC (permalink / raw)
  To: gdb-patches

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) 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 range with the addresses found iterating
the dynamic linker list, to tell which dynamic linker entry is the
vDSO.

Tested on x86_64 Fedora 20.

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

	PR 14466
	* solib-svr4.c (svr4_read_so_list): Rename to ...
	(svr4_current_sos_1): ... this and change the function comment.
	(svr4_current_sos): New function.

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/solib-svr4.c                        | 81 ++++++++++++++++++++++++++++++++-
 gdb/testsuite/gdb.base/vdso-warning.c   | 22 +++++++++
 gdb/testsuite/gdb.base/vdso-warning.exp | 54 ++++++++++++++++++++++
 3 files changed, 155 insertions(+), 2 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/vdso-warning.c
 create mode 100644 gdb/testsuite/gdb.base/vdso-warning.exp

diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 3deef20..c723873 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,82 @@ 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 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
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
+    }
+}
-- 
1.9.3

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

* Re: [PATCH 0/3] Work around PR libc/13097 "linux-vdso.so.1"
  2014-09-26 12:25 [PATCH 0/3] Work around PR libc/13097 "linux-vdso.so.1" Pedro Alves
                   ` (2 preceding siblings ...)
  2014-09-26 12:25 ` [PATCH 1/3] Split vDSO range lookup to a gdbarch hook Pedro Alves
@ 2014-10-10 16:14 ` Pedro Alves
  3 siblings, 0 replies; 5+ messages in thread
From: Pedro Alves @ 2014-10-10 16:14 UTC (permalink / raw)
  To: gdb-patches

On 09/26/2014 01:25 PM, Pedro Alves wrote:
> Following up to:
>   https://sourceware.org/ml/gdb-patches/2014-09/msg00777.html
> 
> This series works around PR libc/13097 in GDB.  See patch 2/3 for
> details.

I've pushed this to master now.

Thanks,
Pedro Alves

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

end of thread, other threads:[~2014-10-10 16:14 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-09-26 12:25 [PATCH 0/3] Work around PR libc/13097 "linux-vdso.so.1" Pedro Alves
2014-09-26 12:25 ` [PATCH 3/3] Cache the vsyscall/vDSO range per-inferior Pedro Alves
2014-09-26 12:25 ` [PATCH 2/3] Work around PR libc/13097 "linux-vdso.so.1" Pedro Alves
2014-09-26 12:25 ` [PATCH 1/3] Split vDSO range lookup to a gdbarch hook Pedro Alves
2014-10-10 16:14 ` [PATCH 0/3] Work around PR libc/13097 "linux-vdso.so.1" 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).