From 8b9a549d3a9176f92b4cac5b388eb473919f80e6 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Fri, 10 Oct 2014 15:57:13 +0100 Subject: [PATCH] PR symtab/14466: 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) 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-10-10 Jan Kratochvil Pedro Alves PR symtab/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-10-10 Jan Kratochvil Pedro Alves PR symtab/14466 * gdb.base/vdso-warning.c: New file. * gdb.base/vdso-warning.exp: New file. --- gdb/ChangeLog | 8 +++ gdb/solib-svr4.c | 81 ++++++++++++++++++++++++- gdb/testsuite/ChangeLog | 7 +++ gdb/testsuite/gdb.base/vdso-warning.c | 22 +++++++ gdb/testsuite/gdb.base/vdso-warning.exp | 54 +++++++++++++++++ 5 files changed, 170 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/ChangeLog b/gdb/ChangeLog index 89236986d76..685419a0917 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,11 @@ +2014-10-10 Jan Kratochvil + Pedro Alves + + PR symtab/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. + 2014-10-10 Pedro Alves * arch-utils.c (default_vsyscall_range): New function. diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 5c87019c7c2..5313bf06112 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -1461,10 +1461,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 (); @@ -1477,6 +1478,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/ChangeLog b/gdb/testsuite/ChangeLog index fac6e076bdb..5578060c8c1 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2014-10-10 Jan Kratochvil + Pedro Alves + + PR symtab/14466 + * gdb.base/vdso-warning.c: New file. + * gdb.base/vdso-warning.exp: New file. + 2014-10-02 Doug Evans * gdb.base/structs.c (main): Don't run forever. diff --git a/gdb/testsuite/gdb.base/vdso-warning.c b/gdb/testsuite/gdb.base/vdso-warning.c new file mode 100644 index 00000000000..4b948035d16 --- /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 . */ + +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 00000000000..1f538fa1c32 --- /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 . + +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 + } +} -- 2.30.2