aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite
diff options
context:
space:
mode:
authorMarkus Metzger <markus.t.metzger@intel.com>2021-10-04 10:24:35 +0200
committerMarkus Metzger <markus.t.metzger@intel.com>2022-10-18 14:16:08 +0200
commit8d56636a0ecbe6c38bf52b0683326ee21693c548 (patch)
treed31ea425eab963a5849039435427ea2880dba1c2 /gdb/testsuite
parent60d09f0a0d8000359b8f1dd14b51e7f013ea9e5c (diff)
downloadbinutils-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.c25
-rw-r--r--gdb/testsuite/gdb.base/dlmopen.c65
-rw-r--r--gdb/testsuite/gdb.base/dlmopen.exp150
-rw-r--r--gdb/testsuite/lib/gdb.exp96
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 {} {