diff options
Diffstat (limited to 'gdb/testsuite/gdb.base/shlib-unload.exp')
-rw-r--r-- | gdb/testsuite/gdb.base/shlib-unload.exp | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.base/shlib-unload.exp b/gdb/testsuite/gdb.base/shlib-unload.exp new file mode 100644 index 0000000..9d47416 --- /dev/null +++ b/gdb/testsuite/gdb.base/shlib-unload.exp @@ -0,0 +1,299 @@ +# 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 $::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 $::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 $::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 $::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 |