blob: 4d3e8eba2ab13bdd8fe74f5c1cdcff3ce72b191d [file] [log] [blame]
# 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
}
# 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