diff options
Diffstat (limited to 'gdb/testsuite/gdb.base/dlmopen-ns-ids.exp')
-rw-r--r-- | gdb/testsuite/gdb.base/dlmopen-ns-ids.exp | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp new file mode 100644 index 0000000..bfce900 --- /dev/null +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp @@ -0,0 +1,316 @@ +# 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 <http://www.gnu.org/licenses/>. +# +# +# 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 +} + +# Return a list of shared libraries extract from the "info sharedlibrary" +# command. Each item in the list is itself a list with the following items: +# +# - "from" address +# - "to" address +# - namespace ID +# - name (file path) + +proc get_info_shared {} { + set from_re "($::hex)\\s+" + set to_re "($::hex)\\s+" + set ns_re "(?:($::decimal)\\s+)?" + set syms_read_re "(Yes( \\(\\*\\))?|No)\\s+" + set name_re "(\[^\r\n\]+)" + set libs {} + + gdb_test_multiple "info sharedlibrary" "" { + -re {From\s+To\s+(Linker NS\s+)?Syms Read\s+Shared Object Library\r\n} { + exp_continue + } + + -re "^${from_re}${to_re}${ns_re}${syms_read_re}${name_re}\r\n" { + set from $expect_out(1,string) + set to $expect_out(2,string) + set ns $expect_out(3,string) + set name $expect_out(4,string) + + lappend libs [list $from $to $ns $name] + exp_continue + } + + -re {^\(\*\): Shared library is missing debugging information\.\r\n} { + exp_continue + } + + -re "^$::gdb_prompt " { + pass $gdb_test_name + } + } + + return $libs +} + +# Verify that "info sharedlibrary" does not contain duplicate entries. + +proc check_no_duplicates {} { + with_test_prefix "check no duplicates" { + set libs [get_info_shared] + array set seen {} + set seen_duplicate 0 + + foreach lib $libs { + if {[info exists seen($lib)]} { + verbose -log "already seen: $lib" + set seen_duplicate 1 + } + + set seen($lib) 1 + } + + gdb_assert {!$seen_duplicate} "no duplicates" + } +} + +# 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+\(Linker 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+Linker NS\\s+Syms\\s+Read\\s+Shared Object Library.*" \ + "after loading everything" + + check_no_duplicates + + 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. +# Also tests that the namespace ID is only printed at the correct +# times. +proc_with_prefix test_conv_vars {} { + clean_restart $::binfile + + gdb_test "print \$_linker_namespace_count" "0" \ + "0 namespace before starting inferior" + gdb_test "print \$_linker_namespace" "No registers." \ + "No current namespace before starting inferior" + + if { ![runto_main] } { + return + } + + gdb_test "print \$_linker_namespace_count" "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 \$_linker_namespace_count" "4" "all SOs loaded" + + gdb_test "next" ".*second dlclose.*" "close one SO" + gdb_test "print \$_linker_namespace_count" "3" "one SOs unloaded" + gdb_test "next" ".*third dlclose.*" "close another SO" + gdb_test "print \$_linker_namespace_count" "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 n_libraries 999 + + gdb_test_multiple "info linker-namespaces \[\[0\]\]" "print namespace 0" { + -re -wrap "($::decimal) librar(?:y|ies) loaded in linker namespace 0:.*" { + set n_libraries $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, and up to 2 more libraries. + gdb_assert {$n_libraries <= 3} "the correct number of libraries was reported" + + set binfile_lib_re [string_to_regexp $::binfile_lib] + + foreach_with_prefix ns {1 2 3} { + set found_test_so false + set n_libraries 999 + + gdb_test_multiple "info linker-namespaces $ns" "print namespace $ns" { + -re ".*($::decimal) librar(?:y|ies) loaded in linker namespace $ns:\r\n" { + set n_libraries $expect_out(1,string) + exp_continue + } + + -re -wrap "${binfile_lib_re}.*" { + set found_test_so true + } + } + + # 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. + gdb_assert {$n_libraries <= 4} "the correct number of libraries was reported" + gdb_assert {$found_test_so} "this testfile's SO was reported" + } + + # 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\\." \ + "" \ + "$::decimal librar(y|ies) loaded in linker namespace 0:" \ + ".*" \ + "$::decimal librar(y|ies) loaded in linker namespace 1:" \ + ".*" \ + "$::decimal librar(y|ies) loaded in linker namespace 2:" \ + ".*" \ + "$::decimal librar(y|ies) loaded in linker namespace 3:" \ + ".*" ] "print namespaces with no argument" +} + +test_info_shared +test_conv_vars +test_info_linker_namespaces |