blob: 0e1369eda4f4f78b232f64e0815359475ba3819d [file] [log] [blame]
# Copyright 2024-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/>.
# Tests for GDB's handling of a shared library being unloaded via a
# call to dlclose. See the individual test_* procs for a description
# of each test.
standard_testfile .c -lib.c
# One of the tests uses this Python file. The test_* proc checks that
# GDB supports Python tests. Some of the other procs don't use this
# Python file.
set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
# Build the library and copy it to the target.
set libname ${testfile}-lib
set libfile [standard_output_file $libname]
if { [build_executable "build shlib" $libfile $srcfile2 {debug shlib}] == -1} {
return
}
set libfile_on_target [gdb_download_shlib $libfile]
# Build the executable.
set opts [list debug shlib_load additional_flags=-DSHLIB_NAME=\"${libname}\"]
if { [build_executable "build exec" $binfile $srcfile $opts] == -1} {
return
}
# The line number of the dlclose call.
set bp_line [gdb_get_line_number "Break here" $srcfile]
# If the target is remote, then the library name in the bp_disabled_re
# below will have a 'target:' prefix.
if {[is_remote target]} {
set target_prefix_re "target:"
} else {
set target_prefix_re ""
}
# The line emitted when GDB disables breakpoints after unloading a
# shared library.
set bp_disabled_re "warning: Temporarily disabling breakpoints for unloaded shared library \"$target_prefix_re[string_to_regexp $::libfile_on_target]\""
# The complete regexp for when GDB stops on the line after BP_LINE,
# assuming that GDB has disabled some breakpoints.
set stop_after_bp_re [multi_line \
"^$::bp_disabled_re" \
"[expr $::bp_line + 1]\\s+assert \\(res == 0\\);"]
# Checking that a breakpoint with multiple locations in a shared
# library only triggers a single breakpoint modified event from
# disable_breakpoints_in_unloaded_shlib when the shared library is
# unloaded.
proc_with_prefix test_bp_modified_events {} {
if { ![allow_python_tests] } {
unsupported "python support needed"
return
}
clean_restart
gdb_load $::binfile
if {![runto_main]} {
return
}
# If the debug information doesn't allow GDB to identify inline
# functions then this test isn't going to work.
get_debug_format
if { [skip_inline_frame_tests] } {
unsupported "skipping inline frame tests"
return
}
gdb_breakpoint $::srcfile:$::bp_line
gdb_continue_to_breakpoint "stop before dlclose"
gdb_breakpoint inline_func
set bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
"get b/p number"]
gdb_test_no_output "source $::pyfile" "import python scripts"
gdb_test "next" $::stop_after_bp_re
# The breakpoint should have been modified once when some of its
# locations are made pending after the shared library is unloaded.
gdb_test_multiple "python print(bp_modified_counts\[$bp_num\])" "" {
-re -wrap "^1" {
pass $gdb_test_name
}
-re -wrap "^2" {
# A second event occurs when the pending breakpoint is
# incorrectly deleted.
kfail gdb/32404 $gdb_test_name
}
-re -wrap "^$::decimal" {
fail $gdb_test_name
}
}
}
# Check that GDB disables dprintf breakpoints within a shared library
# when the shared library is unloaded.
proc_with_prefix test_dprintf_after_unload {} {
clean_restart
gdb_load $::binfile
if {![runto_main]} {
return
}
gdb_breakpoint $::srcfile:$::bp_line
gdb_continue_to_breakpoint "stop before dlclose"
# Setup a dprintf within the shared library.
gdb_test "dprintf foo,\"In foo\""
set bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
"get b/p number"]
# Unload the shared library, GDB should disable our b/p.
gdb_test "next" $::stop_after_bp_re
# Check that our b/p is now showing as disabled.
gdb_test "info breakpoints $bp_num" \
[multi_line \
"^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \
"$bp_num\\s+dprintf\\s+keep\\s+y\\s+<PENDING>\\s+foo" \
"\\s+printf \"In foo\""]
}
# Create a dprintf breakpoint in a shared library. Restart the
# inferior. We should not get an error about re-setting the dprintf
# breakpoint.
proc_with_prefix test_dprintf_with_rerun {} {
clean_restart
gdb_load $::binfile
if {![runto_main]} {
return
}
gdb_breakpoint $::srcfile:$::bp_line
gdb_continue_to_breakpoint "stop before dlclose"
# Setup a dprintf within the shared library.
gdb_test "dprintf foo,\"In foo\""
set bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
"get b/p number"]
# Check that the dprintf b/p is initially not pending.
gdb_test "info breakpoints $bp_num" \
[multi_line \
"^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \
"$bp_num\\s+dprintf\\s+keep\\s+y\\s+$::hex\\s+in foo at \[^\r\n\]+" \
"\\s+printf \"In foo\""] \
"dprintf is non-pending before restart"
# Restart the inferior.
gdb_run_cmd
# The inferior will stop at the initial 'main' breakpoint. This
# is at a location before any of the shlibs have been loaded.
set saw_bp_reset_error false
set saw_bp_disable_warning false
gdb_test_multiple "" "stop before shlib are reloaded" {
-re "warning: Temporarily disabling breakpoints for unloaded shared library \"\[^\r\n\]+/$::libname\"\r\n" {
set saw_bp_disable_warning true
exp_continue
}
-re "Error in re-setting breakpoint $bp_num: \[^\r\n\]+" {
set saw_bp_reset_error true
exp_continue
}
-re "Breakpoint $::decimal, main \\(\\) at \[^\r\n\]+\r\n$::decimal\\s+\[^\r\n\]+\r\n$::gdb_prompt $" {
gdb_assert { !$saw_bp_reset_error && !$saw_bp_disable_warning } $gdb_test_name
}
}
# Check that the dprintf b/p is still enabled, but marked pending
# before the shlib are loaded.
gdb_test "info breakpoints $bp_num" \
[multi_line \
"^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \
"$bp_num\\s+dprintf\\s+keep\\s+y\\s+<PENDING>\\s+foo" \
"\\s+printf \"In foo\""] \
"dprintf is pending before shlib reload"
set saw_in_foo_output false
gdb_test_multiple "continue" "stop after libraries are reloaded" {
-re "^continue\r\n" {
exp_continue
}
-re "^Continuing\\.\r\n" {
exp_continue
}
-re "^In foo\r\n" {
set saw_in_foo_output true
exp_continue
}
-re "^Breakpoint $::decimal, main \\(\\) at .* Break here\\. \\*/\r\n$::gdb_prompt $" {
gdb_assert { $saw_in_foo_output } $gdb_test_name
}
}
# Check that the dprintf b/p is still enabled, but is now, no
# longer pending.
gdb_test "info breakpoints $bp_num" \
[multi_line \
"^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \
"$bp_num\\s+dprintf\\s+keep\\s+y\\s+$::hex\\s+in foo at \[^\r\n\]+" \
"\\s+breakpoint already hit 1 time" \
"\\s+printf \"In foo\""] \
"dprintf is non-pending after restart"
}
# Check that we see breakpoint modified events (where appropriate)
# when the 'nosharedlibrary' command is used to unload all shared
# libraries.
#
# Also check that the 'nosharedlibrary' doesn't trigger a warning
# about shared library breakpoints being disabled.
proc_with_prefix test_silent_nosharedlib {} {
if { ![allow_python_tests] } {
unsupported "python support needed"
return
}
foreach_with_prefix type { breakpoint dprintf } {
clean_restart
gdb_load $::binfile
if {![runto_main]} {
return
}
gdb_breakpoint $::srcfile:$::bp_line
gdb_continue_to_breakpoint "stop before dlclose"
# Setup a dprintf or breakpoint in the shared library.
if { $type eq "breakpoint" } {
gdb_test "break foo"
} else {
gdb_test "dprintf foo,\"In foo\""
}
# Record the number of the b/p (or dprintf) we just inserted.
set bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
"get b/p number"]
# Load Python library to track b/p modifications.
gdb_test_no_output "source $::pyfile" "import python scripts"
# Initialise the b/p modified hash. Currently dprintf style
# breakpoints are not visible from Python, so the modification
# count will remain unchanged in that case.
gdb_test_no_output "python bp_modified_counts\[$bp_num\] = 0"
# Discard symbols from all loaded shared libraries.
gdb_test_no_output "nosharedlibrary"
# Check that our b/p is now showing as disabled.
if { $type eq "breakpoint" } {
set re \
[list "$bp_num\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo"]
set count 1
} else {
set re \
[list \
"$bp_num\\s+dprintf\\s+keep\\s+y\\s+<PENDING>\\s+foo" \
"\\s+printf \"In foo\""]
set count 0
}
gdb_test "info breakpoints $bp_num" \
[multi_line "^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \
{*}$re]
# Check we've seen the expected number of breakpoint modified
# events. Currently dprintf breakpoints are not visible from
# Python, so we will not see an event in that case.
gdb_test "python print(bp_modified_counts\[$bp_num\])" "^$count"
}
}
test_bp_modified_events
test_dprintf_after_unload
test_dprintf_with_rerun
test_silent_nosharedlib