Re: time to workaround libc/13097 in fsf gdb?

Pedro Alves
2014-09-19T14:41:42Z

On 09/19/2014 03:38 PM, Pedro Alves wrote:
> OK, I've investigated this further. I think that we're a couple steps
> away from being able to list the vdso without causing other issues.
> I'll reply again with a patch based on your "new" patch, but that matches
> the vdso by address independently of add-symbol-file-from-memory.

Here it is. WDYT?

-------------
[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 | 4 +++
gdb/gdbarch.sh | 3 ++
gdb/linux-tdep.c | 13 ++++++++
gdb/solib-svr4.c | 41 +++++++++++++++++++++++--
gdb/symfile-mem.c | 5 ++-
gdb/testsuite/gdb.base/vdso-warning.c | 22 ++++++++++++++
gdb/testsuite/gdb.base/vdso-warning.exp | 54 +++++++++++++++++++++++++++++++++
10 files changed, 172 insertions(+), 5 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/arch-utils.c b/gdb/arch-utils.c
index 5ae9fb3..b7ccb32 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. */
+
+CORE_ADDR
+default_vsyscall_address (struct gdbarch *gdbarch)
+{
+ 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..fc0ce53 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 CORE_ADDR default_vsyscall_address (struct gdbarch *);
#endif
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index b0ee79d..68b7a3e 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_address_ftype *vsyscall_address;
};

/* 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_address = default_vsyscall_address;
/* 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_address, 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_address = <%s>\n",
+ host_address_to_string (gdbarch->vsyscall_address));
+ 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;
}

+CORE_ADDR
+gdbarch_vsyscall_address (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->vsyscall_address != NULL);
+ if (gdbarch_debug >= 2)
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_vsyscall_address called\n");
+ return gdbarch->vsyscall_address (gdbarch);
+}
+
+void
+set_gdbarch_vsyscall_address (struct gdbarch *gdbarch,
+ gdbarch_vsyscall_address_ftype vsyscall_address)
+{
+ gdbarch->vsyscall_address = vsyscall_address;
+}
+

/* Keep a registry of per-architecture data-pointers required by GDB
modules. */
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 0303b2e..8f54405 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1317,6 +1317,10 @@ 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);

+typedef CORE_ADDR (gdbarch_vsyscall_address_ftype) (struct gdbarch *gdbarch);
+extern CORE_ADDR gdbarch_vsyscall_address (struct gdbarch *gdbarch);
+extern void set_gdbarch_vsyscall_address (struct gdbarch *gdbarch, gdbarch_vsyscall_address_ftype *vsyscall_address);
+
/* 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..b6f0550 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1029,6 +1029,9 @@ 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
+
+# Return the address of the current inferior's vsyscall/vDSO.
+m:CORE_ADDR:vsyscall_address:void:::default_vsyscall_address::0
EOF
}

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

+/* Implementation of the "vsyscall_address" gdbarch hook. */
+
+static CORE_ADDR
+linux_vsyscall_address (struct gdbarch *gdbarch)
+{
+ CORE_ADDR sysinfo_ehdr;
+
+ if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &sysinfo_ehdr) > 0)
+ return sysinfo_ehdr;
+ return 0;
+}
+
/* To be called from the various GDB_OSABI_LINUX handlers for the
various GNU/Linux architectures and machine types. */

@@ -1799,6 +1811,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_address (gdbarch, linux_vsyscall_address);
}

/* Provide a prototype to silence -Wmissing-prototypes. */
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 3deef20..af9a4ca 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,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)
+ {
+ *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..073dec3 100644
--- a/gdb/symfile-mem.c
+++ b/gdb/symfile-mem.c
@@ -211,10 +211,9 @@ find_vdso_size (CORE_ADDR vaddr, unsigned long size,
static void
add_vsyscall_page (struct target_ops *target, int from_tty)
{
- CORE_ADDR sysinfo_ehdr;
+ CORE_ADDR sysinfo_ehdr = gdbarch_vsyscall_address (target_gdbarch ());

- if (target_auxv_search (target, AT_SYSINFO_EHDR, &sysinfo_ehdr) > 0
- && sysinfo_ehdr != (CORE_ADDR) 0)
+ if (sysinfo_ehdr != 0)
{
struct bfd *bfd;
struct symbol_file_add_from_memory_args args;
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