| # This testcase is part of GDB, the GNU debugger. | 
 | # | 
 | # Copyright 2021-2024 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. | 
 |  | 
 | require allow_dlmopen_tests | 
 |  | 
 | # Don't use 'dlmopen.c' as the source file name, glibc also has a file | 
 | # with that name.  Within our tests, we set the source directory search | 
 | # path order to: | 
 | # | 
 | #    (1) the test source directory, | 
 | #    (2) the compilation directory, and then | 
 | #    (3) the current working directory. | 
 | # | 
 | # Because (1) is first when we try to place a breakpoint on | 
 | # 'dlmopen.c', if the test source file has that name, then GDB will | 
 | # find both the test source file, and the source file from glibc. | 
 | # | 
 | # We could work around this by making (2) first in the source | 
 | # directory list, but that only works when the glibc source is | 
 | # installed.  If it isn't then GDB will try the compilation directory, | 
 | # fail to find the source, then try the test source directory, get a | 
 | # hit, and so still confuse the two files. | 
 | # | 
 | # You might think the problem can be solved by specifying the absolute | 
 | # path to the source file.  This doesn't work because the glibc file | 
 | # has its filename recorded as just "dlmopen.c", as such GDB has to | 
 | # figure out an absolute path to the file (if possible).  The absolute | 
 | # path is figured out based on where GDB can find a matching file in | 
 | # the source directory list, and because of the confusion above, GDB | 
 | # will usually think the test 'dlmopen.c' and the glibc 'dlmopen.c' | 
 | # are actually the same file. | 
 | # | 
 | # The conclusion is that it is just easier to rename the test source | 
 | # file to avoid conflicts with glibc. | 
 |  | 
 | standard_testfile -main.c -lib.c -lib-dep.c | 
 |  | 
 | set basename_lib dlmopen-lib | 
 | set srcfile_lib $srcfile2 | 
 | set binfile_lib1 [standard_output_file $basename_lib.1.so] | 
 | set binfile_lib2 [standard_output_file $basename_lib.2.so] | 
 | set srcfile_lib_dep $srcfile3 | 
 | set binfile_lib_dep [standard_output_file $basename_lib-dep.so] | 
 |  | 
 | if { [build_executable "build shlib dep" $binfile_lib_dep $srcfile_lib_dep \ | 
 | 	  {debug shlib}] == -1 } { | 
 |     return | 
 | } | 
 |  | 
 | if { [build_executable "build shlib" $binfile_lib1 $srcfile_lib \ | 
 | 	  [list debug shlib_load shlib libs=$binfile_lib_dep]] == -1 } { | 
 |     return | 
 | } | 
 |  | 
 | if { [build_executable "build shlib" $binfile_lib2 $srcfile_lib \ | 
 | 	  [list debug shlib_load shlib libs=$binfile_lib_dep]] == -1 } { | 
 |     return | 
 | } | 
 |  | 
 | if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ | 
 | 	  [list additional_flags=-DDSO1_NAME=\"$binfile_lib1\" \ | 
 | 	       additional_flags=-DDSO2_NAME=\"$binfile_lib2\" \ | 
 | 	       shlib_load 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 exp_glob } { | 
 |     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" ".*" | 
 |  | 
 |     # We expect different instances of GDB_DLMOPEN_GLOB per DSO. | 
 |     gdb_test "print amount" "= $exp_glob" | 
 |     gdb_test "print gdb_dlmopen_glob" "= $exp_glob" | 
 |  | 
 |     # Modify that DSO's instance, which should leave the others intact. | 
 |     gdb_test "print &gdb_dlmopen_glob" "= .*" | 
 |     gdb_test "print gdb_dlmopen_glob = -1" "= -1" | 
 | } | 
 |  | 
 | # The actual test.  We run it twice. | 
 | proc test_dlmopen {} { | 
 |     global srcfile basename_lib bp_main | 
 |  | 
 |     # Note that when loading dlmopen-lib.1.so and dlmopen-lib.2.so into | 
 |     # the same namespace, dlmopen-lib-dep.so is loaded only once, so in | 
 |     # this case, the changes to gdb_dlmopen_glob inside test_dlmopen_one | 
 |     # will actually be visible. | 
 |     # | 
 |     # Hence, we supply the expected value of this variable as argument to | 
 |     # test_dlmopen_one. | 
 |     with_test_prefix "dlmopen 1" { test_dlmopen_one 3 1 1 } | 
 |     with_test_prefix "dlmopen 2" { test_dlmopen_one 2 1 1 } | 
 |     with_test_prefix "dlmopen 3" { test_dlmopen_one 1 1 1 } | 
 |     with_test_prefix "dlmopen 4" { test_dlmopen_one 0 1 -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(). | 
 | require can_spawn_for_attach | 
 |  | 
 | 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. | 
 | if { ![gdb_attach $testpid] } { | 
 |     return | 
 | } | 
 |  | 
 | 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 | 
 | } |