# This testcase is part of GDB, the GNU debugger. # # Copyright 2025 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 . # # # Test several things related to handling linker namespaces: # * That the user-facing namespace ID is consistent; require allow_dlmopen_tests standard_testfile -main.c -lib.c set srcfile_lib $srcfile2 set so_name dlmopen-lib.so set binfile_lib [standard_output_file $so_name] if { [build_executable "build shlib" $binfile_lib $srcfile_lib \ [list debug shlib]] == -1 } { return } if { [build_executable "failed to build" $testfile $srcfile \ [list additional_flags=-DDSO_NAME=\"$binfile_lib\" \ shlib_load debug]] } { return } # Run the command "info sharedlibrary" and get the first namespace # for the so proc get_first_so_ns {} { set ns -1 set lib_regexp [string_to_regexp ${::binfile_lib}] gdb_test_multiple "info sharedlibrary $::so_name" "get SO namespace" -lbl { -re "\r\nFrom\\s+To\\s+\(NS\\s+\)?Syms\\s+Read\\s+Shared Object Library(?=\r\n)" { exp_continue } -re "\r\n$::hex\\s+$::hex\\s+\\\[\\\[($::decimal)\\\]\\\]\\s+\[^\r\n]+${lib_regexp}(?=\r\n)" { if {$ns == -1} { set ns $expect_out(1,string) } exp_continue } -re -wrap "" { } } return $ns } # Run the tests relating to the command "info sharedlibrary", to # verify that the namespace ID is consistent. proc test_info_shared {} { clean_restart $::binfile if { ![runto_main] } { return } # First test that we don't print a namespace column at the start. gdb_test "info sharedlibrary" \ "From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library.*" \ "before loading anything" gdb_breakpoint [gdb_get_line_number "TAG: first dlclose"] gdb_continue_to_breakpoint "TAG: first dlclose" # Next, test that we *do* print a namespace column after loading SOs. gdb_test "info sharedlibrary" \ "From\\s+To\\s+NS\\s+Syms\\s+Read\\s+Shared Object Library.*" \ "after loading everything" gdb_assert {[get_first_so_ns] == 1} "before closing any library" gdb_test "next" ".*second dlclose.*" "close first library" gdb_assert {[get_first_so_ns] == 2} "after closing one library" gdb_test "next" ".*third dlclose.*" "close second library" gdb_assert {[get_first_so_ns] == 3} "before closing two libraries" gdb_breakpoint [gdb_get_line_number "TAG: fourth dlclose"] gdb_continue_to_breakpoint "TAG: fourth dlclose" # As of writing this test, glibc's LMID is just an index on an array of # namespaces. After closing a namespace, requesting a new one will # return the index of the lowest-closed namespace, so this will likely # be namespace 1, and because of glibc's reuse of the r_debug object, # GDB should be able to assign the same number. gdb_assert {[get_first_so_ns] == [get_integer_valueof "lmid" "-1"]} \ "reopen a namespace" gdb_test "next" ".*return 0.*" "final namespace inactive" gdb_test "info sharedlibrary" \ "From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library.*" \ "after unloading everything" } # Run all tests related to the linkage namespaces convenience # variables, _active_namespaces and _current_namespaces. proc_with_prefix test_conv_vars {} { clean_restart $::binfile gdb_test "print \$_active_linker_namespaces" "1" \ "1 namespace before starting inferior" gdb_test "print \$_linker_namespace" "No registers." \ "No current namespace before starting inferior" if { ![runto_main] } { return } gdb_test "print \$_active_linker_namespaces" "1" \ "Before activating namespaces" gdb_test "print \$_linker_namespace" ".* = 0" \ "Still in the default namespace" gdb_breakpoint "inc" allow-pending gdb_breakpoint [gdb_get_line_number "TAG: first dlclose"] foreach_with_prefix dl {3 2 1} { gdb_continue_to_breakpoint "inc" gdb_test "print \$_linker_namespace" ".* = $dl" \ "Verify we're in namespace $dl" } # Check that we display the namespace of the selected # frame, not the lowermost one. gdb_test "up" "\#1.*in main.*" gdb_test "print \$_linker_namespace" ".* = 0" \ "print namespace of selected frame" gdb_continue_to_breakpoint "first dlclose" gdb_test "print \$_active_linker_namespaces" "4" "all SOs loaded" gdb_test "next" ".*second dlclose.*" "close one SO" gdb_test "print \$_active_linker_namespaces" "3" "one SOs unloaded" gdb_test "next" ".*third dlclose.*" "close another SO" gdb_test "print \$_active_linker_namespaces" "2" "two SOs unloaded" # Restarting GDB so that we can test setting a breakpoint # using the convenience variable, while a proper bp syntax # isn't implemented for namespaces clean_restart $::binfile if {![runto_main]} { return } # We need to load one SO because you can't have confitional # breakpoints and pending breakpoints at the same time with # gdb_breakpoint. gdb_test "next" ".*assert.*" "load the first SO" gdb_breakpoint "inc if \$_linker_namespace == 2" gdb_continue_to_breakpoint "inc" gdb_continue_to_end "" continue 1 } # Run several tests relating to the command "info namespaces". proc test_info_linker_namespaces {} { clean_restart $::binfile # Check that "info linker-namespaces" while the inferior is not running # doesn't crash. gdb_test "info linker-namespaces" \ "Current inferior does not support linker namespaces\\. Use \"info sharedlibrary\" instead\\." \ "info linker-namespaces before running" if { ![runto_main] } { return } with_test_prefix "info linker-namespaces" { gdb_breakpoint [gdb_get_line_number "TAG: first dlclose"] gdb_continue_to_breakpoint "TAG: first dlclose" } # First, test printing a single namespace, and ensure all of # them are correct, using both syntaxes. set found_all_libs false gdb_test_multiple "info linker-namespaces \[\[0\]\]" "print namespace 0" -lbl { -re "^\r\nThere are ($::decimal) libraries loaded in linker namespace \\\[\\\[0\\\]\\\]" { # Some systems may add libc and libm to every loaded namespace, # others may load only one or neither, because the SO doesn't # actually use either library. The best we can do is check if # we found the dynamic linker, and up to 2 more libraries. set libs $expect_out(1,string) set found_all_libs [expr $libs - 1 <= 2] exp_continue } -re "^\r\n$::gdb_prompt $" { gdb_assert $found_all_libs "the correct number of libraries was reported" } -re "(^\r\n)?\[^\r\n\]+(?=\r\n)" { exp_continue } } foreach_with_prefix ns {1 2 3} { set found_test_so false set found_all_libs false gdb_test_multiple "info linker-namespaces $ns" "print namespace $ns" -lbl { -re "^\r\nThere are ($::decimal) libraries loaded in linker namespace \\\[\\\[$ns\\\]\\\]" { set libs $expect_out(1,string) # Some systems may add libc and libm to every loaded namespace, # others may load only one or neither, because the SO doesn't # actually use either library. The best we can do is check if # we found the dynamic linker, the test SO, and maybe up to 2 # more libraries. set found_all_libs [expr $libs - 2 <= 2] exp_continue } -re "^\r\n\[^\r\n\]+${::binfile_lib}\[^\r\n\]*(?=\r\n)" { set found_test_so true exp_continue } -re "^\r\n$::gdb_prompt $" { gdb_assert $found_test_so "this testfle's SO was reported" gdb_assert $found_all_libs "the correct number of libraries was reported" } -re "(^\r\n)?\[^\r\n\]+(?=\r\n)" { exp_continue } } } # These patterns are simpler, and purposefully glob multiple lines. # The point is to ensure that we find and display all the namespaces, # without worrying about the libraries printed, since that was tested # above. gdb_test "info linker-namespaces" \ [multi_line "There are 4 linker namespaces loaded" \ "There are $::decimal libraries loaded in linker namespace ..0.." \ ".*" \ "There are $::decimal libraries loaded in linker namespace ..1.." \ ".*" \ "There are $::decimal libraries loaded in linker namespace ..2.." \ ".*" \ "There are $::decimal libraries loaded in linker namespace ..3.." \ ".*" ] "print namespaces with no argument" } test_info_shared test_conv_vars test_info_linker_namespaces