diff options
author | Markus Metzger <markus.t.metzger@intel.com> | 2021-10-04 10:24:35 +0200 |
---|---|---|
committer | Markus Metzger <markus.t.metzger@intel.com> | 2022-10-18 14:16:08 +0200 |
commit | 8d56636a0ecbe6c38bf52b0683326ee21693c548 (patch) | |
tree | d31ea425eab963a5849039435427ea2880dba1c2 /gdb/testsuite | |
parent | 60d09f0a0d8000359b8f1dd14b51e7f013ea9e5c (diff) | |
download | binutils-8d56636a0ecbe6c38bf52b0683326ee21693c548.zip binutils-8d56636a0ecbe6c38bf52b0683326ee21693c548.tar.gz binutils-8d56636a0ecbe6c38bf52b0683326ee21693c548.tar.bz2 |
gdb, gdbserver: support dlmopen()
In glibc, the r_debug structure contains (amongst others) the following
fields:
int r_version:
Version number for this protocol. It should be greater than 0.
If r_version is 2, struct r_debug is extended to struct r_debug_extended
with one additional field:
struct r_debug_extended *r_next;
Link to the next r_debug_extended structure. Each r_debug_extended
structure represents a different namespace. The first r_debug_extended
structure is for the default namespace.
1. Change solib_svr4_r_map argument to take the debug base.
2. Add solib_svr4_r_next to find the link map in the next namespace from
the r_next field.
3. Update svr4_current_sos_direct to get the link map in the next namespace
from the r_next field.
4. Don't check shared libraries in other namespaces when updating shared
libraries in a new namespace.
5. Update svr4_same to check the load offset in addition to the name
6. Update svr4_default_sos to also set l_addr_inferior
7. Change the flat solib_list into a per-namespace list using the
namespace's r_debug address to identify the namespace.
Add gdb.base/dlmopen.exp to test this.
To remain backwards compatible with older gdbserver, we reserve the
namespace zero for a flat list of solibs from all namespaces. Subsequent
patches will extend RSP to allow listing libraries grouped by namespace.
This fixes PR 11839.
Co-authored-by: Lu, Hongjiu <hongjiu.lu@intel.com>
Diffstat (limited to 'gdb/testsuite')
-rw-r--r-- | gdb/testsuite/gdb.base/dlmopen-lib.c | 25 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/dlmopen.c | 65 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/dlmopen.exp | 150 | ||||
-rw-r--r-- | gdb/testsuite/lib/gdb.exp | 96 |
4 files changed, 336 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.base/dlmopen-lib.c b/gdb/testsuite/gdb.base/dlmopen-lib.c new file mode 100644 index 0000000..616bf97 --- /dev/null +++ b/gdb/testsuite/gdb.base/dlmopen-lib.c @@ -0,0 +1,25 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2021-2022 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/>. + +*/ + +__attribute__((visibility ("default"))) +int +inc (int n) +{ + return n + 1; /* bp.inc. */ +} diff --git a/gdb/testsuite/gdb.base/dlmopen.c b/gdb/testsuite/gdb.base/dlmopen.c new file mode 100644 index 0000000..2dc2f2e --- /dev/null +++ b/gdb/testsuite/gdb.base/dlmopen.c @@ -0,0 +1,65 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2021 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/>. + +*/ + +#define _GNU_SOURCE +#include <dlfcn.h> +#include <stddef.h> +#include <assert.h> +#include <unistd.h> + +volatile int wait_for_gdb = 1; + +int +main (void) +{ + void *handle[4]; + int (*fun) (int); + Lmid_t lmid; + int dl; + + handle[0] = dlmopen (LM_ID_NEWLM, DSO1_NAME, RTLD_LAZY | RTLD_LOCAL); + assert (handle[0] != NULL); + + dlinfo (handle[0], RTLD_DI_LMID, &lmid); + + handle[1] = dlopen (DSO1_NAME, RTLD_LAZY | RTLD_LOCAL); + assert (handle[1] != NULL); + + handle[2] = dlmopen (LM_ID_NEWLM, DSO1_NAME, RTLD_LAZY | RTLD_LOCAL); + assert (handle[2] != NULL); + + handle[3] = dlmopen (lmid, DSO2_NAME, RTLD_LAZY | RTLD_LOCAL); + assert (handle[3] != NULL); + + alarm (20); + while (wait_for_gdb != 0) + usleep (1); + + for (dl = 0; dl < 4; ++dl) + { + fun = dlsym (handle[dl], "inc"); + assert (fun != NULL); + + fun (42); + + dlclose (handle[dl]); + } + + return 0; /* bp.main */ +} diff --git a/gdb/testsuite/gdb.base/dlmopen.exp b/gdb/testsuite/gdb.base/dlmopen.exp new file mode 100644 index 0000000..8e86d5d --- /dev/null +++ b/gdb/testsuite/gdb.base/dlmopen.exp @@ -0,0 +1,150 @@ +# This testcase is part of GDB, the GNU debugger. +# +# Copyright 2021-2022 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/>. +# +# +# Test shared libraries loaded into different namespaces with dlmopen(). +# +# We test that GDB shows the correct number of instances of the libraries +# the test loaded while unloading them one-by-one. + +if { [skip_dlmopen_tests] } { + unsupported "target does not support dlmopen debugging" + return -1 +} + +standard_testfile + +set basename_lib dlmopen-lib +set srcfile_lib $srcdir/$subdir/$basename_lib.c +set binfile_lib1 [standard_output_file $basename_lib.1.so] +set binfile_lib2 [standard_output_file $basename_lib.2.so] + +if { [gdb_compile_shlib $srcfile_lib $binfile_lib1 {debug}] != "" } { + untested "failed to prepare shlib" + return -1 +} + +if { [gdb_compile_shlib $srcfile_lib $binfile_lib2 {debug}] != "" } { + untested "failed to prepare shlib" + return -1 +} + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ + [list additional_flags=-DDSO1_NAME=\"$binfile_lib1\" \ + additional_flags=-DDSO2_NAME=\"$binfile_lib2\" \ + libs=-ldl debug]] } { + return -1 +} + +if { ![runto_main] } { + return -1 +} + +# Check that 'info shared' show NUM occurrences of DSO. +proc check_dso_count { dso num } { + global gdb_prompt hex + + set count 0 + gdb_test_multiple "info shared" "info shared" { + -re "$hex $hex Yes \[^\r\n\]*$dso\r\n" { + # use longer form so debug remote does not interfere + set count [expr $count + 1] + exp_continue + } + -re "$gdb_prompt " { + verbose -log "library: $dso, expected: $num, found: $count" + gdb_assert {$count == $num} "$gdb_test_name" + } + } +} + +# The DSO part of the test. We run it once per DSO call. +proc test_dlmopen_one { ndso1 ndso2 } { + global srcfile_lib srcfile_lib basename_lib bp_inc + + # Try to reach the breakpoint in the dynamically loaded library. + gdb_continue_to_breakpoint "cont to bp.inc" \ + ".*$srcfile_lib:$bp_inc\r\n.*" + + # We opened all DSOs initially and close them one by one. + with_test_prefix "dso 1" { check_dso_count $basename_lib.1.so $ndso1 } + with_test_prefix "dso 2" { check_dso_count $basename_lib.2.so $ndso2 } + + # This might help debugging. + gdb_test "info breakpoints" ".*" + gdb_test "print \$pc" ".*" +} + +# The actual test. We run it twice. +proc test_dlmopen {} { + global srcfile basename_lib bp_main + + with_test_prefix "dlmopen 1" { test_dlmopen_one 3 1 } + with_test_prefix "dlmopen 2" { test_dlmopen_one 2 1 } + with_test_prefix "dlmopen 3" { test_dlmopen_one 1 1 } + with_test_prefix "dlmopen 4" { test_dlmopen_one 0 1 } + + with_test_prefix "main" { + # Try to reach the breakpoint in the dynamically loaded library. + gdb_continue_to_breakpoint "cont to bp.main" \ + ".*$srcfile:$bp_main\r\n.*" + + # The library should not be listed. + with_test_prefix "dso 1" { check_dso_count $basename_lib.1.so 0 } + with_test_prefix "dso 2" { check_dso_count $basename_lib.2.so 0 } + } +} + +# Remove the pause. We only need it for the attach test. +gdb_test "print wait_for_gdb = 0" "\\\$1 = 0" + +# Break in the to-be-loaded library and at the end of main. +set bp_inc [gdb_get_line_number "bp.inc" $srcfile_lib] +set bp_main [gdb_get_line_number "bp.main" $srcfile] + +delete_breakpoints +gdb_breakpoint $srcfile_lib:$bp_inc allow-pending +gdb_breakpoint $srcfile:$bp_main + +test_dlmopen + +# Try the same again when attaching after dlmopen(). +if { ![can_spawn_for_attach] } { + unsupported "target does not support attach" + return -1 +} + +clean_restart $binfile + +# Start the test program. +set test_spawn_id [spawn_wait_for_attach $binfile] +set testpid [spawn_id_get_pid $test_spawn_id] + +# Attach. +gdb_test "attach $testpid" "Attaching to program.*, process $testpid.*" + +with_test_prefix "attach" { + # Remove the pause. We no longer need it. + gdb_test "print wait_for_gdb = 0" "\\\$1 = 0" + + # Set the same breakpoints again. This time, however, we do not allow the + # breakpoint to be pending since the library has already been loaded. + gdb_breakpoint $srcfile_lib:$bp_inc + gdb_breakpoint $srcfile:$bp_main + + test_dlmopen +} diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 36bcfac..c510ab2 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -2512,6 +2512,102 @@ proc skip_shlib_tests {} { return 1 } +# Return 1 if we should skip dlmopen tests, 0 if we should not. + +gdb_caching_proc skip_dlmopen_tests { + global srcdir subdir gdb_prompt inferior_exited_re + + # We need shared library support. + if { [skip_shlib_tests] } { + return 1 + } + + set me "skip_dlmopen_tests" + set lib { + int foo (void) { + return 42; + } + } + set src { + #define _GNU_SOURCE + #include <dlfcn.h> + #include <link.h> + #include <stdio.h> + #include <errno.h> + + int main (void) { + struct r_debug *r_debug; + ElfW(Dyn) *dyn; + void *handle; + + /* The version is kept at 1 until we create a new namespace. */ + handle = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL); + if (!handle) { + printf ("dlmopen failed: %s.\n", dlerror ()); + return 1; + } + + r_debug = 0; + /* Taken from /usr/include/link.h. */ + for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn) + if (dyn->d_tag == DT_DEBUG) + r_debug = (struct r_debug *) dyn->d_un.d_ptr; + + if (!r_debug) { + printf ("r_debug not found.\n"); + return 1; + } + if (r_debug->r_version < 2) { + printf ("dlmopen debug not supported.\n"); + return 1; + } + printf ("dlmopen debug supported.\n"); + return 0; + } + } + + set libsrc [standard_temp_file "libfoo.c"] + set libout [standard_temp_file "libfoo.so"] + gdb_produce_source $libsrc $lib + + if { [gdb_compile_shlib $libsrc $libout {debug}] != "" } { + verbose -log "failed to build library" + return 1 + } + if { ![gdb_simple_compile $me $src executable \ + [list shlib_load debug \ + additional_flags=-DDSO_NAME=\"$libout\"]] } { + verbose -log "failed to build executable" + return 1 + } + + gdb_exit + gdb_start + gdb_reinitialize_dir $srcdir/$subdir + gdb_load $obj + + if { [gdb_run_cmd] != 0 } { + verbose -log "failed to start skip test" + return 1 + } + gdb_expect { + -re "$inferior_exited_re normally.*${gdb_prompt} $" { + set skip_dlmopen_tests 0 + } + -re "$inferior_exited_re with code.*${gdb_prompt} $" { + set skip_dlmopen_tests 1 + } + default { + warning "\n$me: default case taken" + set skip_dlmopen_tests 1 + } + } + gdb_exit + + verbose "$me: returning $skip_dlmopen_tests" 2 + return $skip_dlmopen_tests +} + # Return 1 if we should skip tui related tests. proc skip_tui_tests {} { |