aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/testsuite/gdb.base/dlmopen-ns-ids.exp')
-rw-r--r--gdb/testsuite/gdb.base/dlmopen-ns-ids.exp316
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