diff options
-rw-r--r-- | gdb/testsuite/gdb.base/errno.c | 37 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/errno.exp | 263 |
2 files changed, 300 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.base/errno.c b/gdb/testsuite/gdb.base/errno.c new file mode 100644 index 0000000..c6835a8 --- /dev/null +++ b/gdb/testsuite/gdb.base/errno.c @@ -0,0 +1,37 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 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/>. */ + +#include <errno.h> + +static void shadow_errno (); + +int main () +{ + errno = 42; + + shadow_errno (); /* main-breakpoint */ + return 0; +} + +#undef errno +static void +shadow_errno () +{ + int errno = 36; + + return; /* shadow_errno-breakpoint */ +} diff --git a/gdb/testsuite/gdb.base/errno.exp b/gdb/testsuite/gdb.base/errno.exp new file mode 100644 index 0000000..262176e --- /dev/null +++ b/gdb/testsuite/gdb.base/errno.exp @@ -0,0 +1,263 @@ +# Copyright 2024 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. + +# Check that errno can be accessed by GDB under a variety of +# circumstances. +# +# The challenge with GDB accessing errno is that, on modern systems, +# errno is a variable in thread-local storage. So, if GDB's access to +# thread local storage is broken or unavailable, some of these tests +# could fail. On Linux, this is/was known to happen on systems with +# older versions of glibc as well as when debugging statically linked +# binaries. +# +# Another possibility is that the environment lacks sufficient +# type information to print errno. This can happen for the errno +# variable itself or when the debuginfo contains a macro for errno +# which refers to a function lacking type information. +# +# When debugging core files, access to errno might not be possible +# both due to the situations described earlier along with the fact +# that inferior function calls are not possible (for the cases in +# which errno is a macro which calls a function returning errno's +# address). +# +# It's also possible for a program to declare errno in an inner scope +# causing the thread-local errno to be shadowed. GDB should still +# correctly print the masking errno for this case. +# +# At the time that this test was written, on GNU/Linux and on FreeBSD, +# there were always scenarios in which printing errno was problematic. +# This test attempts to identify the problem cases and set up xfails +# for them. So, hopefully, there should be no actual failures. But +# the "expected" failures encountered by running this test do +# genuinely illustrate problems that a user might encounter while +# attempting to print errno. + +standard_testfile + +proc do_tests {{do_xfail_cast 0} {do_xfail 0} {do_xfail_core_test 0}} { + clean_restart $::binfile + if ![runto_main] { + return + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint"] + gdb_continue_to_breakpoint "main-breakpoint" + + # Whether or not "print errno" will work often depends on the + # debuginfo available. We can make some inferences about whether + # some of the tests should have xfail set-up by looking at the + # output of "ptype errno". This test is set up to always pass + # even for less than ideal outputs, because the point is to set up + # the xfail(s). + gdb_test_multiple "ptype errno" "check errno type availability" { + -re -wrap "type = int" { + pass $gdb_test_name + } + -re -wrap "type = .*no debug info.*" { + pass $gdb_test_name + set do_xfail 1 + set do_xfail_core_test 1 + } + -re -wrap "Cannot find thread-local variables on this target.*" { + pass $gdb_test_name + set do_xfail 1 + set do_xfail_core_test 1 + set do_xfail_cast 1 + } + -re -wrap "Cannot find thread-local storage.*" { + pass $gdb_test_name + set do_xfail 1 + set do_xfail_core_test 1 + set do_xfail_cast 1 + } + -re -wrap "has unknown return type; cast the call to its declared return type.*" { + + # On systems which glibc as the C library, using -g3, + # which causes macro information to be included in the + # debuginfo, errno might be defined as follows: + # + # #define errno (*__errno_location ()) + # + # So, when we do "ptype errno", due to macro expansion, + # this ends up being "ptype (*__errno_location ())". So + # the call to __errno_location (or something similar on + # other OSes) is the call mentioned in the error message. + + pass $gdb_test_name + set do_xfail 1 + set do_xfail_core_test 1 + set do_xfail_cast 1 + } + } + + # If errno is defined as a macro that contains an obvious function + # call, it won't work when debugging a core file. + gdb_test_multiple "info macro errno" "check if errno is a macro" { + -re -wrap "Defined at.*\[\r\n\]#define.*\\\(\\\).*" { + set do_xfail_core_test 1 + pass $gdb_test_name + } + -re -wrap "Defined at.*\[\r\n\]#define.*" { + pass $gdb_test_name + } + -re -wrap "The symbol .errno. has no definition.*" { + pass $gdb_test_name + } + } + + # Sometimes, "ptype errno" will ferret out that thread local + # variables aren't accessible, but sometimes it won't. Dig deeper + # by trying to access memory using the "x/d" command. Again, the + # point here is to set up an xfail for the later tests, so we pass + # this test for other known outputs. + gdb_test_multiple "x/d &errno" "attempt to access errno memory" { + -re -wrap "Cannot find thread-local variables on this target.*" { + pass $gdb_test_name + set do_xfail 1 + set do_xfail_core_test 1 + set do_xfail_cast 1 + } + -re -wrap "Cannot find thread-local storage.*" { + pass $gdb_test_name + set do_xfail 1 + set do_xfail_core_test 1 + set do_xfail_cast 1 + } + -re -wrap "has unknown return type; cast the call to its declared return type.*" { + set do_xfail 1 + set do_xfail_core_test 1 + set do_xfail_cast 1 + pass $gdb_test_name + } + -re -wrap "$::hex.*?:\[\t \]$::decimal" { + pass $gdb_test_name + } + } + + if $do_xfail { + setup_xfail *-*-* + } + gdb_test "print errno" ".* = 42" + + if $do_xfail_cast { + setup_xfail *-*-* + } + gdb_test "print (int) errno" ".* = 42" + + set corefile ${::binfile}.core + set core_supported 0 + if { ![is_remote host] } { + set core_supported [gdb_gcore_cmd $corefile "save corefile"] + } + # Normally, we'd check core_supported here and return if it's + # not, but we'll defer that until after the shadow test. + + gdb_breakpoint [gdb_get_line_number "shadow_errno-breakpoint"] + gdb_continue_to_breakpoint "shadow_errno-breakpoint" + + # This test demonstrates why a simple hack to GDB for printing + # errno is a bad idea. (The hack was to intercept the string + # "errno" in process_print_command_args() and replace it with + # "*(*(int *(*)(void)) __errno_location) ()".) + gdb_test "print errno" ".* = 36" "print masking errno" + + # Finish test early if no core file was made. + if !$core_supported { + return + } + + clean_restart $::binfile + + set core_loaded [gdb_core_cmd $corefile "load corefile"] + if { $core_loaded == -1 } { + return + } + if $do_xfail_core_test { + setup_xfail *-*-* + } + gdb_test "print errno" ".* = 42" "check errno value from corefile" +} + +set binprefix $binfile + +with_test_prefix "default" { + set binfile $binprefix-default + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested "failed to compile" + } else { + do_tests + } +} + +with_test_prefix "macros" { + set binfile $binprefix-macros + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros}] != "" } { + untested "failed to compile" + } else { + do_tests + } +} + +with_test_prefix "static" { + set binfile $binprefix-static + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } { + untested "failed to compile" + } else { + do_tests + } +} + +with_test_prefix "static-macros" { + set binfile $binprefix-static-macros + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros "additional_flags=-static"}] != "" } { + untested "failed to compile" + } else { + do_tests + } +} + +with_test_prefix "pthreads" { + set binfile $binprefix-pthreads + if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested "failed to compile" + } else { + do_tests + } +} + +with_test_prefix "pthreads-macros" { + set binfile $binprefix-pthreads-macros + if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros}] != "" } { + untested "failed to compile" + } else { + do_tests + } +} + +with_test_prefix "pthreads-static" { + set binfile $binprefix-pthreads-static + if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } { + untested "failed to compile" + } else { + do_tests + } +} + +with_test_prefix "pthreads-static-macros" { + set binfile $binprefix-pthreads-static-macros + if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros "additional_flags=-static"}] != "" } { + untested "failed to compile" + } else { + do_tests + } +} |