diff options
Diffstat (limited to 'gdb/testsuite/gdb.base')
69 files changed, 3764 insertions, 168 deletions
diff --git a/gdb/testsuite/gdb.base/attach-deleted-exec.exp b/gdb/testsuite/gdb.base/attach-deleted-exec.exp index 82a7bb4..45fac0d 100644 --- a/gdb/testsuite/gdb.base/attach-deleted-exec.exp +++ b/gdb/testsuite/gdb.base/attach-deleted-exec.exp @@ -67,5 +67,49 @@ if { [regexp $re_nfs $filename] } { gdb_assert { [string equal $filename /proc/${testpid}/exe] } $test } +# Restart GDB. +clean_restart + +# Setup an empty sysroot. GDB will fail to find the executable within +# the sysroot. Additionally, the presence of a sysroot should prevent +# GDB from trying to load the executable from /proc/PID/exe. +set sysroot [standard_output_file "sysroot"] +gdb_test_no_output "set sysroot $sysroot" \ + "setup sysroot" + +# Attach to the inferior. GDB should complain about failing to find +# the executable. It is the name of the executable that GDB doesn't +# find that we're interesting in here. For native targets GDB should +# be looking for BINFILE, not /proc/PID/exe. +# +# For extended-remote targets things are unfortunately harder. Native +# GDB looks for BINFILE because it understands that GDB will be +# looking in the sysroot. But remote GDB doesn't know if GDB is using +# a sysroot or not. As such, gdbserver will return /proc/PID/exe if +# it knows that the file has been deleted locally. This isn't great +# if GDB then plans to look in a sysroot, but equally, if the remote +# file has been deleted, then the name GDB will return, will have had +# " (deleted" appended, so we're unlikely to get a hit in the sysroot +# either way. +if { [target_info gdb_protocol] == "extended-remote" } { + set filename_re "/proc/$testpid/exe" +} else { + set filename_re "\[^\r\n\]+/${testfile} \\(deleted\\)" +} + +verbose -log "APB: warning: No executable has been specified, and target executable $filename_re could not be found\\. Try using the \"file\" command\\." + +gdb_test "attach $testpid" \ + [multi_line \ + "Attaching to process $decimal" \ + "warning: No executable has been specified, and target executable $filename_re could not be found\\. Try using the \"file\" command\\." \ + ".*"] \ + "attach to inferior" + +# Check GDB hasn't managed to load an executable. +gdb_test "info inferior" \ + "\\*\[^)\]+\\)\\s*" \ + "confirm no executable is loaded." + # Cleanup. kill_wait_spawned_process $test_spawn_id diff --git a/gdb/testsuite/gdb.base/bg-execution-repeat.c b/gdb/testsuite/gdb.base/bg-execution-repeat.c index 8e9bae4..3c0cc76 100644 --- a/gdb/testsuite/gdb.base/bg-execution-repeat.c +++ b/gdb/testsuite/gdb.base/bg-execution-repeat.c @@ -37,9 +37,9 @@ main (void) { alarm (60); + do_wait = 1; foo (); - do_wait = 1; wait (); /* do_wait set to 0 externally. */ diff --git a/gdb/testsuite/gdb.base/bg-execution-repeat.exp b/gdb/testsuite/gdb.base/bg-execution-repeat.exp index b1496ee..d5580fb 100644 --- a/gdb/testsuite/gdb.base/bg-execution-repeat.exp +++ b/gdb/testsuite/gdb.base/bg-execution-repeat.exp @@ -67,6 +67,17 @@ proc test {continue_cmd} { # enable the "set var" command with an interrupt / continue& pair. gdb_test -no-prompt-anchor "interrupt" + set test "interrupt received" + set re [string_to_regexp "Program received signal SIGINT, Interrupt."] + gdb_expect { + -re $re { + pass $test + } + timeout { + fail "$test (timeout)" + } + } + # Allow the breakpoint to trigger. gdb_test -no-prompt-anchor "set var do_wait=0" diff --git a/gdb/testsuite/gdb.base/bp-cond-failure.exp b/gdb/testsuite/gdb.base/bp-cond-failure.exp index d645454..4d03e7b 100644 --- a/gdb/testsuite/gdb.base/bp-cond-failure.exp +++ b/gdb/testsuite/gdb.base/bp-cond-failure.exp @@ -75,7 +75,7 @@ proc run_test { cond_eval access_type bpexpr nloc } { "Error in testing condition for breakpoint ${bp_num}.2:" \ "Cannot access memory at address 0x0" \ "" \ - "Breakpoint ${bp_num}.2, foo \\(c=49 ...\\) at \[^\r\n\]+:\[0-9\]+" \ + "(Thread \[^\r\n\]+ hit )?Breakpoint ${bp_num}.2, foo \\(c=49 ...\\) at \[^\r\n\]+:\[0-9\]+" \ "${::decimal}\\s+\[^\r\n\]+ breakpoint here\\. \[^\r\n\]+"] } else { gdb_test "continue" \ @@ -84,7 +84,7 @@ proc run_test { cond_eval access_type bpexpr nloc } { "Error in testing condition for breakpoint ${bp_num}:" \ "Cannot access memory at address 0x0" \ "" \ - "Breakpoint ${bp_num}, bar \\(\\) at \[^\r\n\]+:\[0-9\]+" \ + "(Thread \[^\r\n\]+ hit )?Breakpoint ${bp_num}, bar \\(\\) at \[^\r\n\]+:\[0-9\]+" \ "${::decimal}\\s+\[^\r\n\]+ breakpoint here\\. \[^\r\n\]+"] } } diff --git a/gdb/testsuite/gdb.base/bp-permanent.c b/gdb/testsuite/gdb.base/bp-permanent.c index d586acc..72e5e8a 100644 --- a/gdb/testsuite/gdb.base/bp-permanent.c +++ b/gdb/testsuite/gdb.base/bp-permanent.c @@ -101,7 +101,7 @@ test_signal_no_handler (void) } static void -test_signal_nested_handler () +test_signal_nested_handler (int sig) { test (); } diff --git a/gdb/testsuite/gdb.base/bp-permanent.exp b/gdb/testsuite/gdb.base/bp-permanent.exp index 62ce3f6..c6c6269 100644 --- a/gdb/testsuite/gdb.base/bp-permanent.exp +++ b/gdb/testsuite/gdb.base/bp-permanent.exp @@ -134,7 +134,7 @@ proc test {always_inserted sw_watchpoint} { unsupported "failed to stop at permanent breakpoint" return } - -re "Program received signal SIGTRAP.*$gdb_prompt $" { + -re "received signal SIGTRAP.*$gdb_prompt $" { pass $test } } @@ -174,7 +174,7 @@ proc test {always_inserted sw_watchpoint} { # disabled, it should act as if we hadn't created it in the first # place. IOW, we should get a random signal, and, the breakpoint's # command should not run. - gdb_test "continue" "Program received signal SIGTRAP.*" \ + gdb_test "continue" "received signal SIGTRAP.*" \ "disabled permanent breakpoint doesn't explain stop" gdb_test "info breakpoints" \ diff --git a/gdb/testsuite/gdb.base/break-dbg.cc b/gdb/testsuite/gdb.base/break-dbg.cc new file mode 100644 index 0000000..642ded0 --- /dev/null +++ b/gdb/testsuite/gdb.base/break-dbg.cc @@ -0,0 +1,31 @@ +/* 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/>. */ + +volatile int global_var = 0; + +int +foo () +{ + return global_var; +} + +int +main () +{ + int res = foo (); + return res; +} diff --git a/gdb/testsuite/gdb.base/break-dbg.exp b/gdb/testsuite/gdb.base/break-dbg.exp new file mode 100644 index 0000000..3652b8e --- /dev/null +++ b/gdb/testsuite/gdb.base/break-dbg.exp @@ -0,0 +1,70 @@ +# 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/>. */ + +# Some basic testing of 'set debug breakpoint on'. At one point a bug +# meant that some breakpoints would immediately trigger a segfault if +# GDB tried to run with breakpoint debugging turned on. +# +# Test is compiled as C++ only so 'catch catch/throw/rethrow' have a +# something to do. The original bug would trigger for any 'catch' +# style breakpoint, so C++ isn't really a hard requirement. + +standard_testfile .cc + +require allow_cplus_tests + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + {debug c++}] } { + return +} + +if {![runto_main]} { + return +} + +gdb_test "catch catch" "^Catchpoint $decimal \\(catch\\)" +gdb_test "catch throw" "^Catchpoint $decimal \\(throw\\)" +gdb_test "catch rethrow" "^Catchpoint $decimal \\(rethrow\\)" + +gdb_test "catch exec" "^Catchpoint $decimal \\(exec\\)" +gdb_test "catch fork" "^Catchpoint $decimal \\(fork\\)" +gdb_test "catch vfork" "^Catchpoint $decimal \\(vfork\\)" + +gdb_test "catch load" "^Catchpoint $decimal \\(load\\)" +gdb_test "catch unload" "^Catchpoint $decimal \\(unload\\)" + +gdb_test "catch signal" "^Catchpoint $decimal \\(standard signals\\)" +gdb_test "catch syscall" "^Catchpoint $decimal \\(any syscall\\)" + +gdb_test "watch -l global_var" "\[Ww]atchpoint $decimal: -location global_var" + +gdb_test_no_output "set debug breakpoint on" + +set saw_bp_debug_line false +gdb_test_multiple "step" "" { + -re "^step\r\n" { + exp_continue + } + -re "^\\\[breakpoint\\\] \[^\r\n\]+\r\n" { + set saw_bp_debug_line true + exp_continue + } + -re "^$gdb_prompt $" { + gdb_assert { $saw_bp_debug_line } $gdb_test_name + } + -re "^\[^\r\n\]*\r\n" { + exp_continue + } +} diff --git a/gdb/testsuite/gdb.base/break1.c b/gdb/testsuite/gdb.base/break1.c index 110341c..26c4663 100644 --- a/gdb/testsuite/gdb.base/break1.c +++ b/gdb/testsuite/gdb.base/break1.c @@ -23,7 +23,13 @@ struct some_struct { int a_field; int b_field; - union { int z_field; }; + union + { + struct + { + int z_field; + }; + }; }; struct some_struct values[50]; diff --git a/gdb/testsuite/gdb.base/catch-fork-kill.exp b/gdb/testsuite/gdb.base/catch-fork-kill.exp index 0fd853b..224a8df 100644 --- a/gdb/testsuite/gdb.base/catch-fork-kill.exp +++ b/gdb/testsuite/gdb.base/catch-fork-kill.exp @@ -32,6 +32,8 @@ standard_testfile +require allow_fork_tests + # Build two programs -- one for fork, and another for vfork. set testfile_fork "${testfile}-fork" set testfile_vfork "${testfile}-vfork" diff --git a/gdb/testsuite/gdb.base/catch-fork-static.exp b/gdb/testsuite/gdb.base/catch-fork-static.exp index b171a6d..9d50d5d 100644 --- a/gdb/testsuite/gdb.base/catch-fork-static.exp +++ b/gdb/testsuite/gdb.base/catch-fork-static.exp @@ -21,9 +21,7 @@ # ld.so probes before reaching main, and ptrace flags were set then. But a # static executable would just keep running and never catch the fork. -# Until "catch fork" is implemented on other targets... -# -require {is_any_target "*-*-linux*" "*-*-openbsd*"} +require allow_fork_tests # Reusing foll-fork.c since it's a simple forking program. standard_testfile foll-fork.c diff --git a/gdb/testsuite/gdb.base/catch-signal-fork.exp b/gdb/testsuite/gdb.base/catch-signal-fork.exp index dea0cc4..2a33ee1 100644 --- a/gdb/testsuite/gdb.base/catch-signal-fork.exp +++ b/gdb/testsuite/gdb.base/catch-signal-fork.exp @@ -14,6 +14,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. require {!target_info exists gdb,nosignals} +require allow_fork_tests standard_testfile diff --git a/gdb/testsuite/gdb.base/coredump-filter-build-id.exp b/gdb/testsuite/gdb.base/coredump-filter-build-id.exp index 7594cc2..eb5b489 100644 --- a/gdb/testsuite/gdb.base/coredump-filter-build-id.exp +++ b/gdb/testsuite/gdb.base/coredump-filter-build-id.exp @@ -28,7 +28,7 @@ if { ![istarget *-*-linux*] } { untested "$testfile.exp" return -1 } -require is_x86_64_m64_target +require is_x86_64_m64_target gcore_cmd_available if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug build-id}] } { return -1 diff --git a/gdb/testsuite/gdb.base/corefile-shmem-zero-id-lib.c b/gdb/testsuite/gdb.base/corefile-shmem-zero-id-lib.c new file mode 100644 index 0000000..58fdec6 --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-shmem-zero-id-lib.c @@ -0,0 +1,522 @@ +/* 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/>. */ + +/* This file contains a library that can be preloaded into GDB on Linux + using the LD_PRELOAD technique. + + The library intercepts calls to OPEN, CLOSE, READ, and PREAD in order to + fake the inode number of a shared memory mapping. + + When GDB creates a core file (e.g. with the 'gcore' command), then + shared memory mappings should be included in the generated core file. + + The 'id' for the shared memory mapping shares the inode slot in the + /proc/PID/smaps file, which is what GDB consults to decide which + mappings should be included in the core file. + + It is possible for a shared memory mapping to have an 'id' of zero. + + At one point there was a bug in GDB where mappings with an inode of zero + would not be included in the generated core file. This meant that most + shared memory mappings would be included in the generated core file, + but, if a shared memory mapping happened to get an 'id' of zero, then, + because this would appear as a zero inode in the smaps file, this shared + memory mapping would be excluded from the generated core file. + + This preload library spots when GDB opens a /proc/PID/smaps file and + immediately copies the contents of this file into an internal buffer. + The buffer is then scanned looking for a shared memory mapping, and, if + a shared memory mapping is found, its 'id' (in the inode position) is + changed to zero. + + Calls to read/pread are intercepted, and attempts to read from the smaps + file are then served from the modified buffer contents. + + The close calls are monitored and, when the smaps file is closed, the + internal buffer is released. + + This works with GDB (currently) because the requirements for access to + the smaps file are pretty simple. GDB opens the file and grabs the + entire contents with a single pread call and a large buffer. There's no + seeking within the file or anything like that. + + The intention is that this library is preloaded into a GDB session which + is then used to start an inferior and generate a core file. GDB will + then see the zero inode for the shared memory mapping and should, if the + bug is correctly fixed, still add the shared memory mapping to the + generated core file. */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdarg.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <stdbool.h> +#include <assert.h> + +/* Logging. */ + +static void +log_msg (const char *fmt, ...) +{ +#ifdef LOGGING + va_list ap; + + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); +#endif /* LOGGING */ +} + +/* Error handling, message and exit. */ + +static void +error (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + + exit (EXIT_FAILURE); +} + +/* The type of the open() function. */ +typedef int (*open_func_type)(const char *pathname, int flags, ...); + +/* The type of the close() function. */ +typedef int (*close_func_type)(int fd); + +/* The type of the read() function. */ +typedef ssize_t (*read_func_type)(int fd, void *buf, size_t count); + +/* The type of the pread() function. */ +typedef ssize_t (*pread_func_type) (int fd, void *buf, size_t count, off_t offset); + +/* Structure that holds information about a /proc/PID/smaps file that has + been opened. */ +struct interesting_file +{ + /* The file descriptor for the opened file. */ + int fd; + + /* The read offset within the file. Set to zero when the file is + opened. Any 'read' calls will update this offset. */ + size_t offset; + + /* The size of the contents within the buffer. This is not the total + buffer size (which might be larger). Attempts to read beyond SIZE + indicate an attempt to read beyond the end of the file. */ + size_t size; + + /* The (possibly modified) contents of the file. */ + char *content; +}; + +/* We only track a single interesting file. Currently, for the use case + we imagine, GDB will only ever open one /proc/PID/smaps file at once. */ +struct interesting_file the_file = { -1, 0, 0, NULL }; + +/* Update the contents of the global THE_FILE buffer. It is assumed that + the file contents have already been loaded into THE_FILE's content + buffer. + + Look for any lines that represent a shared memory mapping and modify + the inode field (which holds the shared memory id) to be zero. */ +static void +update_file_content_buffer (void) +{ + assert (the_file.content != NULL); + + char *start = the_file.content; + do + { + /* Every line, even the last one, ends with a newline. */ + char *end = strchrnul (start, '\n'); + assert (end != NULL); + assert (*end != '\0'); + + /* Attribute lines start with an uppercase letter. The lines we want + to modify should start with a lower case hex character, + i.e. [0-9a-f]. Also, every line that we want to consider should + be long enough, but just in case, check the longest possible + filename that we care about. */ + if (isxdigit (*start) && (isdigit (*start) || islower (*start)) + && (end - start) > 23) + { + /* There are two possible filenames that we look for: + /SYSV%08x + /SYSV%08x (deleted) + The END pointer is pointing to the first character after the + filename. + + Setup OFFSET to be the offset from END to the start of the + filename. As we check the filename we set OFFSET to 0 if the + filename doesn't match one of the expected patterns. */ + size_t offset; + if (strncmp ((end - 13), "/SYSV", 5) == 0) + offset = 13; + else if (strncmp ((end - 23), "/SYSV", 5) == 0) + { + if (strncmp ((end - 10), " (deleted)", 10) == 0) + offset = 23; + else + offset = 0; + } + else + offset = 0; + + for (int i = 0; i < 8 && offset != 0; ++i) + { + if (!isdigit (*(end - offset + 5 + i))) + offset = 0; + } + + /* If OFFSET is non-zero then the filename on this line looks + like a shared memory mapping, and OFFSET is the offset from + END to the first character of the filename. */ + if (offset != 0) + { + log_msg ("[LD_PRELOAD] shared memory entry: %.*s\n", + offset, (end - offset)); + + /* Set PTR to the first character before the filename. This + should be a white space character. */ + char *ptr = end - offset - 1; + assert (isspace (*ptr)); + + /* Walk backwards until we find the inode field. */ + while (isspace (*ptr)) + --ptr; + + /* Now replace every character in the inode field, except the + first one, with a space character. */ + while (!isspace (*(ptr - 1))) + { + assert (isdigit (*ptr)); + *ptr = ' '; + --ptr; + } + + /* Replace the first character with '0'. */ + assert (isdigit (*ptr)); + *ptr = '0'; + + /* This print is checked for from GDB. */ + printf ("[LD_PRELOAD] updated a shared memory mapping\n"); + } + } + + /* Update START to point to the next line. The last line of the + file will be empty. */ + assert (*end == '\n'); + start = end; + while (*start == '\n') + ++start; + } + while (*start != '\0'); +} + +/* Return true if PATHNAME has for form "/proc/PID/smaps" (without the + quotes). Otherwise, return false. */ + +static bool +is_smaps_file (const char *pathname) +{ + if (strncmp (pathname, "/proc/", 6) == 0) + { + int idx = 6; + while (isdigit (pathname[idx])) + idx++; + if (idx > 6 && strcmp (&pathname[idx], "/smaps") == 0) + return true; + } + + return false; +} + +/* Return true if PATHNAME should be considered interesting. PATHNAME is + interesting if it has the form /proc/PID/smaps, and there is no + interesting file already opened. */ + +static bool +is_interesting_pathname (const char *pathname) +{ + return the_file.fd == -1 && is_smaps_file (pathname); +} + +/* Read the contents of an interesting file from FD (and open file + descriptor) into the global THE_FILE variable, making the file FD the + current interesting file. There should be no already open interesting + file when this function is called. + + The contents of the file FD are read into a memory buffer and updated so + that any shared memory mappings listed within FD (which will be an smaps + file) will have the id zero. */ + +static void +read_interesting_file_contents (int fd) +{ +#define BLOCK_SIZE 1024 + /* Slurp contents into a local buffer. */ + size_t buffer_size = 1024; + size_t offset = 0; + + assert (the_file.size == 0); + assert (the_file.content == NULL); + assert (the_file.fd == -1); + assert (the_file.offset == 0); + + do + { + the_file.content = (char *) realloc (the_file.content, buffer_size); + if (the_file.content == NULL) + error ("[LD_PRELOAD] Failed allocating memory: %s\n", strerror (errno)); + + ssize_t bytes_read = read (fd, the_file.content + offset, BLOCK_SIZE); + if (bytes_read == -1) + error ("[LD_PRELOAD] Failed reading file: %s\n", strerror (errno)); + + the_file.size += bytes_read; + + if (bytes_read < BLOCK_SIZE) + break; + + offset += BLOCK_SIZE; + buffer_size += BLOCK_SIZE; + } + while (true); + + /* Add a null terminator. This makes the update easier. We know + there will be space because we only break out of the loop above + when the last read returns less than BLOCK_SIZE bytes. This means + we allocated an extra BLOCK_SIZE bytes, but didn't fill them all. + This means there must be at least 1 byte available for the null. */ + the_file.content[the_file.size] = '\0'; + + /* Reset the seek pointer. */ + if (lseek (fd, 0, SEEK_SET) == (off_t) -1) + error ("[LD_PRELOAD] Failed to lseek in file: %s\n", strerror (errno)); + + /* Record the file descriptor, this is used in read, pread, and close + in order to spot when we need to intercept the call. */ + the_file.fd = fd; + + update_file_content_buffer (); +#undef BLOCK_SIZE +} + +/* Intercept calls to 'open'. If this is an attempt to open a + /proc/PID/smaps file then intercept it, load the file contents into a + buffer and update the file contents. For all other open requests, just + forward to the real open function. */ +int +open (const char *pathname, int flags, ...) +{ + /* Pointer to the real open function. */ + static open_func_type real_open = NULL; + + /* Mode is only used if the O_CREAT flag is set in FLAGS. */ + mode_t mode = 0; + + /* Set true if this is a /proc/PID/smaps file. */ + bool is_interesting = is_interesting_pathname (pathname); + + /* Check if O_CREAT is in flags. If it is, get the mode. */ + if (flags & O_CREAT) + { + va_list args; + va_start (args, flags); + mode = va_arg (args, mode_t); + va_end (args); + } + + /* Debug. */ + if (is_interesting) + log_msg ("[LD_PRELOAD] Opening file: %s\n", pathname); + + /* Make sure we have a pointer to the real open() function. */ + if (real_open == NULL) + { + /* Get the address of the real open() function. */ + real_open = (open_func_type) dlsym (RTLD_NEXT, "open"); + if (real_open == NULL) + error ("[LD_PRELOAD] dlsym() error for 'open': %s\n", dlerror ()); + } + + /* Call the original open() function with the provided arguments. */ + int res = -1; + if (flags & O_CREAT) + res = real_open (pathname, flags, mode); + else + res = real_open (pathname, flags); + + if (res != -1 && is_interesting) + read_interesting_file_contents (res); + + return res; +} + +/* Like above, but for open64. */ + +int +open64 (const char *pathname, int flags, ...) +{ + /* Pointer to the real open64 function. */ + static open_func_type real_open64 = NULL; + + /* Mode is only used if the O_CREAT flag is set in FLAGS. */ + mode_t mode = 0; + + /* Set true if this is a /proc/PID/smaps file. */ + bool is_interesting = is_interesting_pathname (pathname); + + /* Check if O_CREAT is in flags. If it is, get the mode. */ + if (flags & O_CREAT) + { + va_list args; + va_start (args, flags); + mode = va_arg (args, mode_t); + va_end (args); + } + + /* Debug. */ + if (is_interesting) + log_msg ("[LD_PRELOAD] Opening file: %s\n", pathname); + + /* Make sure we have a pointer to the real open64() function. */ + if (real_open64 == NULL) + { + /* Get the address of the real open64() function. */ + real_open64 = (open_func_type) dlsym (RTLD_NEXT, "open64"); + if (real_open64 == NULL) + error ("[LD_PRELOAD] dlsym() error for 'open64': %s\n", dlerror ()); + } + + /* Call the original open64() function with the provided arguments. */ + int res = -1; + if (flags & O_CREAT) + res = real_open64 (pathname, flags, mode); + else + res = real_open64 (pathname, flags); + + if (res != -1 && is_interesting) + read_interesting_file_contents (res); + + return res; +} + +/* Intercept the 'close' function. If this is a previously opened + interesting file then clean up. Otherwise, forward to the normal close + function. */ +int +close (int fd) +{ + static close_func_type real_close = NULL; + + if (fd == the_file.fd) + { + the_file.fd = -1; + free (the_file.content); + the_file.content = NULL; + the_file.offset = 0; + the_file.size = 0; + log_msg ("[LD_PRELOAD] Closing file.\n"); + } + + /* Make sure we have a pointer to the real open() function. */ + if (real_close == NULL) + { + /* Get the address of the real open() function. */ + real_close = (close_func_type) dlsym (RTLD_NEXT, "close"); + if (real_close == NULL) + error ("[LD_PRELOAD] dlsym() error for 'close': %s\n", dlerror ()); + } + + return real_close (fd); +} + +/* Intercept 'pread' calls. If this is a pread from a previously opened + interesting file, then read from the in memory buffer. Otherwise, + forward to the real pread function. */ +ssize_t +pread (int fd, void *buf, size_t count, off_t offset) +{ + static pread_func_type real_pread = NULL; + + if (fd == the_file.fd) + { + size_t max; + + if (offset > the_file.size) + max = 0; + else + max = the_file.size - offset; + if (count > max) + count = max; + + memcpy (buf, the_file.content + offset, count); + log_msg ("[LD_PRELOAD] Read from file.\n"); + return count; + } + + if (real_pread == NULL) + { + /* Get the address of the real read() function. */ + real_pread = (pread_func_type) dlsym (RTLD_NEXT, "pread"); + if (real_pread == NULL) + error ("[LD_PRELOAD] dlsym() error for 'pread': %s\n", dlerror ()); + } + + return real_pread (fd, buf, count, offset); +} + +/* Intercept 'read' calls. If this is a read from a previously opened + interesting file, then read from the in memory buffer. Otherwise, + forward to the real read function. */ +ssize_t +read (int fd, void *buf, size_t count) +{ + static read_func_type real_read = NULL; + + if (fd == the_file.fd) + { + ssize_t bytes_read = pread (fd, buf, count, the_file.offset); + if (bytes_read > 0) + the_file.offset += bytes_read; + return bytes_read; + } + + if (real_read == NULL) + { + /* Get the address of the real read() function. */ + real_read = (read_func_type) dlsym (RTLD_NEXT, "read"); + if (real_read == NULL) + error ("[LD_PRELOAD] dlsym() error for 'read': %s\n", dlerror ()); + } + + return real_read (fd, buf, count); +} diff --git a/gdb/testsuite/gdb.base/corefile-shmem-zero-id.c b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.c new file mode 100644 index 0000000..92d2edf --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.c @@ -0,0 +1,63 @@ +/* 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/>. */ + +#include <sys/ipc.h> +#include <sys/shm.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <time.h> + +void +breakpt (void) +{ + /* Nothing. */ +} + +int +main (void) +{ + /* Create a shared memory mapping. */ + int sid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | IPC_EXCL | 0777); + if (sid == -1) + { + perror ("shmget"); + exit (1); + } + + /* Attach the shared memory mapping. */ + void *addr = shmat (sid, NULL, SHM_RND); + if (addr == (void *) -1L) + { + perror ("shmat"); + exit (1); + } + + breakpt (); + + /* Mark the shared memory mapping as deleted -- once the last user + has finished with it. */ + if (shmctl (sid, IPC_RMID, NULL) != 0) + { + perror ("shmctl"); + exit (1); + } + + return 0; +} diff --git a/gdb/testsuite/gdb.base/corefile-shmem-zero-id.exp b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.exp new file mode 100644 index 0000000..57c665e --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.exp @@ -0,0 +1,228 @@ +# 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/>. + +# This test script tries to check GDB's ability to create a core file +# (e.g. with 'gcore' command) when there's a shared memory mapping +# with the id zero. +# +# Testing this case is hard. Older kernels don't even seem to give +# out the shared memory id zero. And on new kernels you still cannot +# guarantee to grab the zero id for testing; the id might be in use by +# some other process, or the kernel might just not give out that id +# for some other reason. +# +# To figure out which mappings to include in the core file, GDB reads +# the /proc/PID/smaps file. There is a field in this file which for +# file backed mappings, holds the inode of the file. But for shared +# memory mappings this field holds the shared memory id. The problem +# was that GDB would ignore any entry in /proc/PID/smaps with an inode +# entry of zero, which would catch the shared memory mapping with a +# zero id. +# +# There was an attempt to write a test which spammed out requests for +# shared memory mappings and tried to find the one with id zero, but +# this was still really unreliable. +# +# This test takes a different approach. We compile a library which we +# preload into the GDB process. This library intercepts calls to +# open, close, read, and pread, and watches for an attempt to open the +# /proc/PID/smaps file. +# +# When we see that file being opened, we copy the file contents into a +# memory buffer and modify the buffer so that the inode field for any +# shared memory mappings is set to zero. We then intercept calls to +# read and pread and return results from that in memory buffer. +# +# The test executable itself create a shared memory mapping (which +# might have any id). +# +# GDB, with the pre-load library in place, start the inferior and then +# uses the 'gcore' command to dump a core file. When GDB opens the +# smaps file and reads from it, the preload library ensures that GDB +# sees an inode of zero. +# + +# This test only works on Linux +require isnative +require {!is_remote host} +require {!is_remote target} +require {istarget *-linux*} +require gcore_cmd_available + +standard_testfile .c -lib.c + +set libfile ${testfile}-lib +set libobj [standard_output_file ${libfile}.so] + +# Compile the preload library. We only get away with this as we +# limit this test to running when ISNATIVE is true. +if { [build_executable "build preload lib" $libobj $srcfile2 \ + {debug shlib libs=-ldl}] == -1 } { + return +} + +# Now compile the inferior executable. +if {[build_executable "build executable" $testfile $srcfile] == -1} { + return +} + +# Spawn GDB with LIBOBJ preloaded using LD_PRELOAD. +save_vars { env(LD_PRELOAD) env(ASAN_OPTIONS) } { + if { ![info exists env(LD_PRELOAD) ] + || $env(LD_PRELOAD) == "" } { + set env(LD_PRELOAD) "$libobj" + } else { + append env(LD_PRELOAD) ":$libobj" + } + + # Prevent address sanitizer error: + # ASan runtime does not come first in initial library list; you should + # either link runtime to your application or manually preload it with + # LD_PRELOAD. + append_environment_default ASAN_OPTIONS verify_asan_link_order 0 + + clean_restart $binfile + + # Start GDB with the modified environment, this means that, when + # using remote targets, gdbserver will also use the preload + # library. + if {![runto_main]} { + return + } +} + +gdb_breakpoint breakpt +gdb_continue_to_breakpoint "run to breakpt" + +# Check the /proc/PID/smaps file itself. The call to 'cat' should +# inherit the preload library, so should see the modified file +# contents. Check that the shared memory mapping line has an id of +# zero. This confirms that the preload library is working. If the +# preload library breaks then we'll start seeing non-zero shared +# memory ids, which always worked, so we'd never know that this test +# is broken! +# +# This check ensures the test is working as expected. +set shmem_line_count 0 +set fixup_line_count 0 +set inf_pid [get_inferior_pid] +gdb_test_multiple "shell cat /proc/${inf_pid}/smaps" "check smaps" { + -re "^\\\[LD_PRELOAD\\\] updated a shared memory mapping\r\n" { + incr fixup_line_count + exp_continue + } + -re "^\[^\r\n\]+($decimal)\\s+/SYSV\[0-9\]{8}(?: \\(deleted\\))?\r\n" { + set id $expect_out(1,string) + if { $id == 0 } { + incr shmem_line_count + } + exp_continue + } + -re "^$gdb_prompt $" { + with_test_prefix $gdb_test_name { + gdb_assert { $shmem_line_count == 1 } \ + "single shared memory mapping found" + gdb_assert { $fixup_line_count == 1 } \ + "single fixup line found" + } + } + -re "^\[^\r\n\]+\r\n" { + exp_continue + } +} + +# Now generate a core file. This will use the preload library to read +# the smaps file. The code below is copied from 'proc gdb_gcore_cmd', +# but we don't use that as we also look for a message that is printed +# by the LD_PRELOAD library. This is an extra level of check that the +# preload library is triggering when needed. +set corefile [standard_output_file ${testfile}.core] +set saw_ld_preload_msg false +set saw_saved_msg false +with_timeout_factor 3 { + gdb_test_multiple "gcore $corefile" "save core file" { + -re "^\\\[LD_PRELOAD\\\] updated a shared memory mapping\r\n" { + # GDB actually reads the smaps file multiple times when + # creating a core file, so we'll see multiple of these + # fixup lines. + set saw_ld_preload_msg true + exp_continue + } + -re "^Saved corefile \[^\r\n\]+\r\n" { + set saw_saved_msg true + exp_continue + } + -re "^$gdb_prompt $" { + with_test_prefix $gdb_test_name { + gdb_assert { $saw_saved_msg } \ + "saw 'Saved corefile' message" + + # If we're using a remote target then the message from + # the preload library will go to gdbservers stdout, + # not GDB's, so don't check for it. + if { [gdb_protocol_is_native] } { + gdb_assert { $saw_ld_preload_msg } \ + "saw LD_PRELOAD message from library" + } + } + } + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } +} + +# Restart GDB. This time we are _not_ using the preload library. We +# no longer need it as we are only analysing the core file now. +clean_restart $binfile + +# Load the core file. +gdb_test "core-file $corefile" \ + "Program terminated with signal SIGTRAP, Trace/breakpoint trap\\..*" \ + "load core file" + +# Look through the mappings. We _should_ see the shared memory +# mapping. We _should_not_ see any of the special '[blah]' style +# mappings, e.g. [vdso], [vstack], [vsyscalls], etc. +set saw_special_mapping false +set saw_shmem_mapping false +gdb_test_multiple "info proc mappings" "" { + -re "\r\nStart Addr\[^\r\n\]+File\\s*\r\n" { + exp_continue + } + + -re "^$hex\\s+$hex\\s+$hex\\s+$hex\\s+\\\[\\S+\\\]\\s*\r\n" { + set saw_special_mapping true + exp_continue + } + + -re "^$hex\\s+$hex\\s+$hex\\s+$hex\\s+/SYSV\[0-9\]+ \\(deleted\\)\\s*\r\n" { + set saw_shmem_mapping true + exp_continue + } + + -re "^$hex\\s+$hex\\s+$hex\\s+$hex\[^\r\n\]*\r\n" { + exp_continue + } + + -re "^$gdb_prompt $" { + with_test_prefix $gdb_test_name { + gdb_assert { $saw_shmem_mapping } \ + "check shared memory mapping exists" + gdb_assert { !$saw_special_mapping } \ + "check no special mappings added" + } + } +} diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index d4d6b20..01e3cc1 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -253,10 +253,39 @@ gdb_test "h" "List of classes of commands:(\[^\r\n\]*\[\r\n\])+aliases -- User-d gdb_test "help" "List of classes of commands:(\[^\r\n\]*\[\r\n\])+aliases -- User-defined aliases of other commands(\[^\r\n\]*\[\r\n\])+breakpoints -- Making program stop at certain points(\[^\r\n\]*\[\r\n\])+data -- Examining data(\[^\r\n\]*\[\r\n\])+files -- Specifying and examining files(\[^\r\n\]*\[\r\n\])+obscure -- Obscure features(\[^\r\n\]*\[\r\n\])+running -- Running the program(\[^\r\n\]*\[\r\n\])+stack -- Examining the stack(\[^\r\n\]*\[\r\n\])+status -- Status inquiries(\[^\r\n\]*\[\r\n\])+support -- Support facilities(\[^\r\n\]*\[\r\n\])+user-defined -- User-defined commands(\[^\r\n\]*\[\r\n\])+Type \"help\" followed by a class name for a list of commands in that class.(\[^\r\n\]*\[\r\n\])+Type \"help\" followed by command name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." #test handle gdb_test "handle" "Argument required .signal to handle.*" -#test info "i" abbreviation -gdb_test "i" "List of \"info\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help info\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "info \"i\" abbreviation" + +proc test_info_command { command message } { + set saw_info_header 0 + set saw_help_info 0 + set saw_command_abbrev 0 + gdb_test_multiple $command $message -lbl { + -re "\r\nList of \"info\" subcommands:" { + verbose "Info header displayed" + set saw_info_header 1 + exp_continue + } + -re "Type \"help info\" followed by subcommand name for full documentation\\." { + verbose "Help info displayed" + set saw_help_info 1 + exp_continue + } + -re "\r\nCommand name abbreviations are allowed if unambiguous\\." { + verbose "Command name abbreviations displayed" + set saw_command_abbrev 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $saw_info_header && $saw_help_info + && $saw_command_abbrev } $gdb_test_name + } + } +} + +#test info "i" abbreviation +test_info_command "i" "info \"i\" abbreviation" #test info -gdb_test "info" "List of \"info\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help info\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." +test_info_command "info" "info" + #test ignore gdb_test "ignore" "Argument required .a breakpoint number.*" #test info address @@ -379,38 +408,52 @@ gdb_test "info registers" "The program has no registers now." gdb_test "info s" "No stack." "info stack \"s\" abbreviation" #test info stack gdb_test "info stack" "No stack." -#test info set -# Test improved to check three parts: -# 1) confirm -# 2) prompt -# 3) write -# And only succeed if all three are matched. -# This should fix an old problem on native solaris 2.8, -# where this test fails due to this line: + +#test "info set" and "show" commands +# The test needs to match the "prompt: ..." part to fix an old problem on native +# Solaris 2.8, where this test fails due to this line: # prompt: Gdb's prompt is "(gdb) ".^M -set set_confirm_seen 0 -set set_prompt_seen 0 -gdb_test_multiple "info set" "info set" { - -re "confirm: Whether to confirm potentially dangerous operations is o\[a-z\]*.(\[^\r\n\]*\[\r\n\])+history filename: The filename in which to record the command history is (\[^\r\n\]*\[\r\n\])+listsize: Number of source lines gdb will list by default is 10" { - verbose "Confirm dislayed" - set set_confirm_seen 1 - exp_continue - } - -re "Gdb's prompt is \"$gdb_prompt \"" { - verbose "GDB prompt displayed" - set set_prompt_seen 1 - exp_continue - } - -re "Writing into executable.*$gdb_prompt $" { - verbose "write displayed" - if { $set_prompt_seen && $set_confirm_seen } { - pass "info set" - } else { - verbose "prompt $set_prompt_seen confirm $set_confirm_seen" - fail "info set (incomplete output)" +proc test_info_set_show { command } { + set set_confirm_seen 0 + set set_history_filename_seen 0 + set set_listsize_seen 0 + set set_prompt_seen 0 + set set_write_seen 0 + gdb_test_multiple $command $command -lbl { + -re "\r\nconfirm: Whether to confirm potentially dangerous operations is o\[a-z\]+\\." { + verbose "Confirm displayed" + set set_confirm_seen 1 + exp_continue + } + -re "\r\nhistory filename: The filename in which to record the command history is \[^\r\n\]+\\." { + verbose "History filename displayed" + set set_history_filename_seen 1 + exp_continue + } + -re "\r\nlistsize: Number of source lines gdb will list by default is 10\\." { + verbose "Listsize displayed" + set set_listsize_seen 1 + exp_continue + } + -re "\r\nprompt: Gdb's prompt is \"$::gdb_prompt \"" { + verbose "GDB prompt displayed" + set set_prompt_seen 1 + exp_continue + } + -re "write: Writing into executable and core files is o\[a-z\]+\\." { + verbose "Write displayed" + set set_write_seen 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $set_confirm_seen && $set_history_filename_seen + && $set_listsize_seen && $set_prompt_seen + && $set_write_seen } $gdb_test_name } } } +test_info_set_show "info set" + gdb_test "info symbol" "Argument required .address.." #test info source gdb_test "info source" "No current source file..*" @@ -591,12 +634,41 @@ gdb_test "set history" "List of \"set history\" subcommands:(\[^\r\n\]*\[\r\n\]) gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, asm, c, c.., d, fortran, go, minimal, modula-2, objective-c, opencl, pascal, rust." #test set listsize gdb_test "set listsize" "Argument required .integer to set it to.*" + +proc test_set_print { command message } { + set saw_info_header 0 + set saw_help_info 0 + set saw_command_abbrev 0 + gdb_test_multiple $command $message -lbl { + -re "\r\nList of \"set print\" subcommands:" { + verbose "Info header displayed" + set saw_info_header 1 + exp_continue + } + -re "Type \"help set print\" followed by subcommand name for full documentation\\." { + verbose "Help info displayed" + set saw_help_info 1 + exp_continue + } + -re "\r\nCommand name abbreviations are allowed if unambiguous\\." { + verbose "Command name abbreviations displayed" + set saw_command_abbrev 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $saw_info_header && $saw_help_info + && $saw_command_abbrev } $gdb_test_name + } + } +} + #test set print "p" abbreviation -gdb_test "set p" "List of \"set print\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help set print\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set print \"p\" abbreviation" +test_set_print "set p" "set print \"p\" abbreviation" #test set print "pr" abbreviation -gdb_test "set pr" "List of \"set print\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help set print\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set print \"pr\" abbreviation" +test_set_print "set pr" "set print \"pr\" abbreviation" #test set print -gdb_test "set print" "List of \"set print\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help set print\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." +test_set_print "set print" "set print" + #test set print address gdb_test_no_output "set print address" "set print address" #test set print array @@ -699,6 +771,8 @@ set show_conv_list \ {$_gdb_minor = 1} \ {$_shell_exitsignal = void} \ {$_shell_exitcode = 0} \ + {$_active_linker_namespaces = 1} \ + {$_linker_namespace = <error: No registers.>}\ } if [allow_python_tests] { append show_conv_list \ @@ -739,12 +813,47 @@ gdb_test "show history" "history expansion: *History expansion on command input gdb_test "show language" "The current source language is \"auto; currently c\"." #test show listsize gdb_test "show listsize" "Number of source lines gdb will list by default is 10." + +proc test_show_print { command } { + set saw_print_address 0 + set saw_print_frame_args 0 + set saw_print_symbol 0 + set saw_print_vtbl 0 + gdb_test_multiple $command $command -lbl { + -re "\r\nprint address: Printing of addresses is o\[a-z\]+\\." { + verbose "Print address displayed" + set saw_print_address 1 + exp_continue + } + -re "\r\nprint frame-arguments: Printing of non-scalar frame arguments is \[^\r\n\]+\\." { + verbose "Print frame-arguments displayed" + set saw_print_frame_args 1 + exp_continue + } + -re "\r\nprint symbol: Printing of symbols when printing pointers is o\[a-z\]+\\." { + verbose "Print symbol displayed" + set saw_print_symbol 1 + exp_continue + } + -re "\r\nprint vtbl: Printing of C\\+\\+ virtual function tables is o\[a-z\]+\\." { + verbose "Print vtbl displayed" + set saw_print_vtbl 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $saw_print_address && $saw_print_frame_args + && $saw_print_symbol && $saw_print_vtbl } $gdb_test_name + } + } +} + #test show print "p" abbreviation -gdb_test "show p" ".*" +test_show_print "show p" #test show print "pr" abbreviation -gdb_test "show pr" ".*" +test_show_print "show pr" #test show print -gdb_test "show print" ".*" +test_show_print "show print" + #test show paths gdb_test "show paths" "Executable and object file path:.*" #test show print address @@ -790,30 +899,10 @@ gdb_test "show width" "Number of characters gdb thinks are in a line is.*" #test show write # This is only supported on targets which use exec.o. gdb_test "show write" "Writing into executable and core files is o.*" + #test show -set show_confirm_seen 0 -set show_prompt_seen 0 -gdb_test_multiple "show" "show" { - -re "confirm: *Whether to confirm potentially dangerous operations is on.(\[^\r\n\]*\[\r\n\])+history filename: *The filename in which to record the command history is (\[^\r\n\]*\[\r\n\])+history save: *Saving of the history record on exit is on.(\[^\r\n\]*\[\r\n\])+history size: *The size of the command history is(\[^\r\n\]*\[\r\n\])+listsize: *Number of source lines gdb will list by default is 10(\[^\r\n]*\[\r\n\])+print elements: *Limit on string chars or array elements to print is 200." { - verbose "Confirm displayed" - set show_confirm_seen 1 - exp_continue - } - -re "Gdb's prompt is \"$gdb_prompt \"" { - verbose "GDB prompt displayed" - set show_prompt_seen 1 - exp_continue - } - -re "Writing into executable.*$gdb_prompt $" { - verbose "write displayed" - if { $show_prompt_seen && $show_confirm_seen } { - pass "show" - } else { - verbose "prompt $show_prompt_seen confirm $show_confirm_seen" - fail "show (incomplete output)" - } - } -} +test_info_set_show "show" + #history saving should stay disabled gdb_test_no_output "set history save off" "set history save off" #test stepi "si" abbreviation diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c b/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c index 3bcd819..c7c038a 100644 --- a/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c @@ -41,6 +41,12 @@ main (void) handle[2] = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL); assert (handle[2] != NULL); + for (dl = 2; dl >= 0; dl--) + { + fun = dlsym (handle[dl], "inc"); + fun (dl); + } + dlclose (handle[0]); /* TAG: first dlclose */ dlclose (handle[1]); /* TAG: second dlclose */ dlclose (handle[2]); /* TAG: third dlclose */ diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp index 3ddc07e..4d3e8eb 100644 --- a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp @@ -105,4 +105,149 @@ proc test_info_shared {} { "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 diff --git a/gdb/testsuite/gdb.base/exprs.exp b/gdb/testsuite/gdb.base/exprs.exp index eb2d0e4..f703c18 100644 --- a/gdb/testsuite/gdb.base/exprs.exp +++ b/gdb/testsuite/gdb.base/exprs.exp @@ -284,3 +284,21 @@ gdb_test "print v_short + " \ # Test for a syntax error in the middle of an expression. gdb_test "print v_short =}{= 3" \ "A syntax error in expression, near `\\}\\{= 3'\\." + +gdb_test_no_output "set debug parse 1" +set saw_start 0 +set saw_val 0 +gdb_test_multiple "print 23" "print with debugging" -lbl { + -re "\r\nStarting parse(?=\r\n)" { + set saw_start 1 + exp_continue + } + -re "\r\n.$decimal = 23(?=\r\n)" { + set saw_val 1 + exp_continue + } + + -re -wrap "" { + gdb_assert {$saw_start && $saw_val} $gdb_test_name + } +} diff --git a/gdb/testsuite/gdb.base/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp index cb3fc90..c10941c 100644 --- a/gdb/testsuite/gdb.base/filename-completion.exp +++ b/gdb/testsuite/gdb.base/filename-completion.exp @@ -385,7 +385,7 @@ proc run_quoting_and_escaping_tests { root } { remove-symbol-file \ "target core" "target exec" "target tfile" \ "maint print c-tdesc" "save gdb-index" - "save gdb-index -dwarf-5" } + "save gdb-index -dwarf-5" "shell ls"} if { [allow_compile_tests] } { lappend all_cmds "compile file" } @@ -408,6 +408,31 @@ proc run_quoting_and_escaping_tests { root } { run_quoting_and_escaping_tests_1 $root $cmd } } + + # Some additional testing of shell command. Test 'shell' and '!' + # when there are multiple filenames on the command line. This + # focuses on completion of the final filename. There is also some + # testing of the shell command above, this tests completion within + # the line. + foreach_with_prefix shell_cmd { "shell " "!" "pipe print 1 | " } { + foreach suffix { "aaa/aa bb" "bb2/dir 1/unique file" } { + set dir $root/$suffix + + regsub -all " " "$dir" "\\ " dir_with_backslash + + with_test_prefix "suffix='$suffix'" { + with_test_prefix "with_backslash" { + run_quoting_and_escaping_tests_1 $root "${shell_cmd}ls $dir_with_backslash" + } + with_test_prefix "with double quotes" { + run_quoting_and_escaping_tests_1 $root "${shell_cmd}ls \"$dir\"" + } + with_test_prefix "with single quotes" { + run_quoting_and_escaping_tests_1 $root "${shell_cmd}ls '$dir'" + } + } + } + } } # Helper for run_unquoted_tests. ROOT is the root directory as setup diff --git a/gdb/testsuite/gdb.base/foll-exec-c++.exp b/gdb/testsuite/gdb.base/foll-exec-c++.exp new file mode 100644 index 0000000..d96310b --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-exec-c++.exp @@ -0,0 +1,24 @@ +# 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/>. + +# This file is part of the gdb testsuite + +# See foll-exec.exp.tcl for test details. This file runs the test +# using the C++ compiler. + +require allow_cplus_tests +set lang c++ + +source $srcdir/$subdir/foll-exec.exp.tcl diff --git a/gdb/testsuite/gdb.base/foll-exec-c.exp b/gdb/testsuite/gdb.base/foll-exec-c.exp new file mode 100644 index 0000000..67f62cc --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-exec-c.exp @@ -0,0 +1,23 @@ +# 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/>. + +# This file is part of the gdb testsuite + +# See foll-exec.exp.tcl for test details. This file runs the test +# using the C compiler. + +set lang c + +source $srcdir/$subdir/foll-exec.exp.tcl diff --git a/gdb/testsuite/gdb.base/foll-exec.c b/gdb/testsuite/gdb.base/foll-exec.c index a1c9b70..291f803 100644 --- a/gdb/testsuite/gdb.base/foll-exec.c +++ b/gdb/testsuite/gdb.base/foll-exec.c @@ -19,25 +19,38 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> - +#include <libgen.h> +#include <assert.h> #include <limits.h> int global_i = 100; +#ifndef EXECD_PROG +#define EXECD_PROG "execd-prog" +#endif + int main (int argc, char ** argv) { int local_j = global_i + 1; int local_k = local_j + 1; char prog[PATH_MAX]; - int len; + size_t len = PATH_MAX - 1; + + printf ("foll-exec is about to execlp(%s)...\n", EXECD_PROG); + + prog [len] = '\0'; + + strncpy (prog, dirname (argv[0]), len); + len -= strlen (prog); + assert (len > 0); - printf ("foll-exec is about to execlp(execd-prog)...\n"); + strncat (prog, "/", len); + len -= 1; + assert (len > 0); - strcpy (prog, argv[0]); - len = strlen (prog); - /* Replace "foll-exec" with "execd-prog". */ - memcpy (prog + len - 9, "execd-prog", 10); - prog[len + 1] = 0; + strncat (prog, EXECD_PROG, len); + len -= strlen (EXECD_PROG); + assert (len > 0); /* In the following function call, maximum line length exceed the limit 80. This is intentional and required for clang compiler such that complete @@ -45,7 +58,7 @@ int main (int argc, char ** argv) multi-line. */ execlp (prog, /* tbreak-execlp */ prog, "execlp arg1 from foll-exec", (char *) 0); - printf ("foll-exec is about to execl(execd-prog)...\n"); + printf ("foll-exec is about to execl(%s)...\n", EXECD_PROG); /* In the following function call, maximum line length exceed the limit 80. This is intentional and required for clang compiler such that complete @@ -61,7 +74,7 @@ int main (int argc, char ** argv) argv[0] = prog; - printf ("foll-exec is about to execv(execd-prog)...\n"); + printf ("foll-exec is about to execv(%s)...\n", EXECD_PROG); execv (prog, argv); /* tbreak-execv */ } diff --git a/gdb/testsuite/gdb.base/foll-exec.exp b/gdb/testsuite/gdb.base/foll-exec.exp.tcl index ad4c3516..8f96a55 100644 --- a/gdb/testsuite/gdb.base/foll-exec.exp +++ b/gdb/testsuite/gdb.base/foll-exec.exp.tcl @@ -22,33 +22,55 @@ require {istarget "*-linux*"} standard_testfile foll-exec.c -set testfile2 "execd-prog" -set srcfile2 ${testfile2}.c -set binfile2 [standard_output_file ${testfile2}] +# Compile a program that performs an exec as EXECER_LANG, and a +# program that will be exec'd as EXECEE_LANG. Either language can be +# 'c' or 'c++'. Then run various test associated with 'catch exec' +# using the compiled programs. +proc do_exec_tests { execer_lang execee_lang } { + global srcfile testfile + global gdb_prompt -set compile_options debug + # First compile the program to be exec'd, the execee. + set execee_base_filename "execd-prog" + set srcfile2 ${execee_base_filename}.c + set execee_testfile "execd-prog-${execee_lang}" + set execee_testfile_re [string_to_regexp $execee_testfile] + set execee_binfile [standard_output_file $execee_testfile] -# build the first test case -if { [gdb_compile "${srcdir}/${subdir}/${srcfile2}" "${binfile2}" executable $compile_options] != "" } { - untested "failed to compile" - return -1 -} + set execee_flags debug + if { $execee_lang == "c++" } { + lappend execee_flags "c++" + } -if { [is_remote target] } { - gdb_remote_download target $binfile2 -} + if { [build_executable "failed to build $execee_testfile" $execee_testfile \ + $srcfile2 $execee_flags] == -1 } { + return + } -if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $compile_options] != "" } { - untested "failed to compile" - return -1 -} + if { [is_remote target] } { + gdb_remote_download target $execee_binfile + } -proc do_exec_tests {} { - global binfile srcfile srcfile2 testfile testfile2 - global gdb_prompt + + # Now compile the program to do the exec, the execer. + set execer_testfile "$testfile-${execee_lang}" + set execer_binfile [standard_output_file $execer_testfile] + + set execer_flags debug + if { $execer_lang == "c++" } { + lappend execer_flags "c++" + } + lappend execer_flags "additional_flags=-DEXECD_PROG=\"${execee_testfile}\"" + + if { [build_executable "failed to build $execer_testfile" $execer_testfile \ + $srcfile $execer_flags] == -1 } { + return + } + + # Now we can start running the tests. + clean_restart $execer_binfile # Start the program running, and stop at main. - # if {![runto_main]} { return } @@ -71,7 +93,7 @@ proc do_exec_tests {} { return } - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -120,7 +142,7 @@ proc do_exec_tests {} { set execd_line [gdb_get_line_number "after-exec" $srcfile2] send_gdb "next\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "step through execlp call"} -re "$gdb_prompt $" {fail "step through execlp call"} timeout {fail "(timeout) step through execlp call"} @@ -160,7 +182,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -193,7 +215,7 @@ proc do_exec_tests {} { send_gdb "continue\n" gdb_expect { - -re ".*xecuting new program:.*${testfile2}.*Catchpoint .*(exec\'d .*${testfile2}).*$gdb_prompt $"\ + -re ".*xecuting new program:.*${execee_testfile_re}.*Catchpoint .*(exec\'d .*${execee_testfile_re}).*$gdb_prompt $"\ {pass "hit catch exec"} -re "$gdb_prompt $" {fail "hit catch exec"} timeout {fail "(timeout) hit catch exec"} @@ -210,7 +232,7 @@ proc do_exec_tests {} { # set msg "info shows catchpoint exec pathname" gdb_test_multiple "info breakpoints" $msg { - -re ".*catchpoint.*keep y.*exec, program \".*${testfile2}\".*$gdb_prompt $" { + -re ".*catchpoint.*keep y.*exec, program \".*${execee_testfile_re}\".*$gdb_prompt $" { pass $msg } } @@ -228,7 +250,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -269,7 +291,7 @@ proc do_exec_tests {} { # send_gdb "next 2\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "step through execl call"} -re "$gdb_prompt $" {fail "step through execl call"} timeout {fail "(timeout) step through execl call"} @@ -295,7 +317,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -330,7 +352,7 @@ proc do_exec_tests {} { } send_gdb "next\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "step through execv call"} -re "$gdb_prompt $" {fail "step through execv call"} timeout {fail "(timeout) step through execv call"} @@ -356,7 +378,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -370,13 +392,13 @@ proc do_exec_tests {} { # send_gdb "continue\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "continue through exec"} -re "$gdb_prompt $" {fail "continue through exec"} timeout {fail "(timeout) continue through exec"} } } -clean_restart $binfile - -do_exec_tests +foreach_with_prefix execee_lang { c c++ } { + do_exec_tests $lang $execee_lang +} diff --git a/gdb/testsuite/gdb.base/foll-fork-syscall.c b/gdb/testsuite/gdb.base/foll-fork-syscall.c new file mode 100644 index 0000000..ef695f5 --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-fork-syscall.c @@ -0,0 +1,35 @@ +/* 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/>. */ + +#include <unistd.h> +#include <stdio.h> + +int +main (int argc, char **argv) +{ + int pid, x = 0; + + pid = fork (); + if (pid == 0) /* set breakpoint here */ + printf ("I am the child\n"); + else + printf ("I am the parent\n"); + + chdir ("."); + ++x; /* set exit breakpoint here */ + return 0; +} diff --git a/gdb/testsuite/gdb.base/foll-fork-syscall.exp b/gdb/testsuite/gdb.base/foll-fork-syscall.exp new file mode 100644 index 0000000..21ef334 --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-fork-syscall.exp @@ -0,0 +1,143 @@ +# 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 catching syscalls with all permutations of follow-fork parent/child +# and detach-on-fork on/off. + +# Test relies on checking follow-fork output. Do not run if gdb debug is +# enabled because it will be redirected to the log. +require !gdb_debug_enabled +require {is_any_target "i?86-*-*" "x86_64-*-*"} +require allow_fork_tests + +standard_testfile + +if {[build_executable "failed to prepare" $testfile $srcfile debug]} { + return -1 +} + +proc setup_gdb {} { + global testfile + + clean_restart $testfile + + if {![runto_main]} { + return false + } + + # Set a breakpoint after the fork is "complete." + if {![gdb_breakpoint [gdb_get_line_number "set breakpoint here"]]} { + return false + } + + # Set exit breakpoint (to prevent inferior from exiting). + if {![gdb_breakpoint [gdb_get_line_number "set exit breakpoint here"]]} { + return false + } + return true +} + +# Check that fork catchpoints are supported, as an indicator for whether +# fork-following is supported. Return 1 if they are, else 0. + +proc_with_prefix check_fork_catchpoints {} { + global gdb_prompt + + if { ![setup_gdb] } { + return false + } + + # Verify that the system supports "catch fork". + gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" "insert first fork catchpoint" + set has_fork_catchpoints false + gdb_test_multiple "continue" "continue to first fork catchpoint" { + -re ".*Your system does not support this type\r\nof catchpoint.*$gdb_prompt $" { + unsupported "continue to first fork catchpoint" + } + -re ".*Catchpoint.*$gdb_prompt $" { + set has_fork_catchpoints true + pass "continue to first fork catchpoint" + } + } + + return $has_fork_catchpoints +} + +proc_with_prefix test_catch_syscall {follow-fork-mode detach-on-fork} { + # Start with shiny new gdb instance. + if {![setup_gdb]} { + return + } + + # The "Detaching..." and "Attaching..." messages may be hidden by + # default. + gdb_test_no_output "set verbose" + + # Setup modes to test. + gdb_test_no_output "set follow-fork-mode ${follow-fork-mode}" + gdb_test_no_output "set detach-on-fork ${detach-on-fork}" + + gdb_test "catch fork" "Catchpoint . \\(fork\\)" + gdb_test "catch syscall chdir" "Catchpoint . \\(syscall 'chdir'.*\\)" + + # Which inferior we're expecting to follow. Assuming the parent + # will be inferior #1, and the child will be inferior #2. + if {${follow-fork-mode} == "parent"} { + set following_inf 1 + } else { + set followin_inf 2 + } + # Next stop should be the fork catchpoint. + set expected_re "" + append expected_re "Catchpoint . \\(forked process.*" + gdb_test "continue" $expected_re "continue to fork catchpoint" + + # Next stop should be the breakpoint after the fork. + set expected_re ".*" + if {${follow-fork-mode} == "child" || ${detach-on-fork} == "off"} { + append expected_re "\\\[New inferior.*" + } + if {${detach-on-fork} == "on"} { + append expected_re "\\\[Detaching after fork from " + if {${follow-fork-mode} == "parent"} { + append expected_re "child" + } else { + append expected_re "parent" + } + append expected_re " process.*" + } + append expected_re "Breakpoint .*set breakpoint here.*" + gdb_test "continue" $expected_re "continue to breakpoint after fork" + + # Next stop should be the syscall catchpoint. + set expected_re ".*Catchpoint . \\(call to syscall chdir\\).*" + gdb_test continue $expected_re "continue to chdir syscall" +} + +# Check for follow-fork support. +if {![check_fork_catchpoints]} { + untested "follow-fork not supported" + return +} + +# Test all permutations. +foreach_with_prefix follow-fork-mode {"parent" "child"} { + + # Do not run tests when not detaching from the parent. + # See breakpoints/13457 for discussion. + foreach_with_prefix detach-on-fork {"on"} { + test_catch_syscall ${follow-fork-mode} ${detach-on-fork} + } +} diff --git a/gdb/testsuite/gdb.base/foll-fork.exp b/gdb/testsuite/gdb.base/foll-fork.exp index 94755c6..12db516 100644 --- a/gdb/testsuite/gdb.base/foll-fork.exp +++ b/gdb/testsuite/gdb.base/foll-fork.exp @@ -17,6 +17,8 @@ # enabled as it will be redirected to the log. require !gdb_debug_enabled +require allow_fork_tests + standard_testfile if {[build_executable "failed to prepare" $testfile $srcfile debug]} { diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp b/gdb/testsuite/gdb.base/foll-vfork.exp index 266df46..6ca7711 100644 --- a/gdb/testsuite/gdb.base/foll-vfork.exp +++ b/gdb/testsuite/gdb.base/foll-vfork.exp @@ -18,12 +18,7 @@ # either execs or exits --- since those events take somewhat different # code paths in GDB, both variants are exercised. -# Until "set follow-fork-mode" and "catch vfork" are implemented on -# other targets... -# -if {![istarget "*-linux*"]} { - continue -} +require allow_fork_tests standard_testfile .c -exit.c vforked-prog.c diff --git a/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp b/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp index 311d7ba..2d47d5d 100644 --- a/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp +++ b/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp @@ -23,6 +23,7 @@ # in the source of the shlib, and "list" should display the source where # the program stopped. +require allow_fork_tests require allow_shlib_tests standard_testfile .c -shlib.c diff --git a/gdb/testsuite/gdb.base/fork-print-inferior-events.exp b/gdb/testsuite/gdb.base/fork-print-inferior-events.exp index 26ed2f9..19ace00 100644 --- a/gdb/testsuite/gdb.base/fork-print-inferior-events.exp +++ b/gdb/testsuite/gdb.base/fork-print-inferior-events.exp @@ -19,6 +19,8 @@ # inferior-events [on,off]', 'set follow-fork-mode [child,parent]' and # 'set detach-on-fork [on,off]' are the correct ones. +require allow_fork_tests + # This test relies on "run", so it cannot run on target remote stubs. require !use_gdb_stub diff --git a/gdb/testsuite/gdb.base/fork-running-state.exp b/gdb/testsuite/gdb.base/fork-running-state.exp index 4b810a6..c446800 100644 --- a/gdb/testsuite/gdb.base/fork-running-state.exp +++ b/gdb/testsuite/gdb.base/fork-running-state.exp @@ -17,6 +17,8 @@ # in non-stop). GDB used to miss updating the parent/child running # states after a fork. +require allow_fork_tests + standard_testfile # The test proper. diff --git a/gdb/testsuite/gdb.base/gcore.exp b/gdb/testsuite/gdb.base/gcore.exp index 5251e3f..0a9f099 100644 --- a/gdb/testsuite/gdb.base/gcore.exp +++ b/gdb/testsuite/gdb.base/gcore.exp @@ -16,6 +16,7 @@ # This file was written by Michael Snyder (msnyder@redhat.com) # This is a test for the gdb command "generate-core-file". +require gcore_cmd_available standard_testfile diff --git a/gdb/testsuite/gdb.base/infcall-failure.exp b/gdb/testsuite/gdb.base/infcall-failure.exp index 66bccd1..e7aeac1 100644 --- a/gdb/testsuite/gdb.base/infcall-failure.exp +++ b/gdb/testsuite/gdb.base/infcall-failure.exp @@ -131,7 +131,13 @@ proc_with_prefix run_cond_hits_segfault_test { async_p non_stop_p } { [multi_line \ "Continuing\\." \ "" \ - "Program received signal SIGSEGV, Segmentation fault\\." \ + [string cat \ + [string_to_regexp \ + "Program received signal SIGSEGV, Segmentation fault."] \ + "("] \ + [string cat \ + [string_to_regexp "Address not mapped to object."] \ + ")?"] \ "${::hex} in func_segfault \\(\\) at \[^\r\n\]+:${::segv_line}" \ "${::decimal}\\s+\[^\r\n\]+Segfault here\[^\r\n\]+" \ "Error in testing condition for breakpoint ${bp_1_num}:" \ @@ -161,7 +167,13 @@ proc_with_prefix run_call_hits_segfault_test { async_p non_stop_p } { gdb_test "call func_segfault ()" \ [multi_line \ "" \ - "Program received signal SIGSEGV, Segmentation fault\\." \ + [string cat \ + [string_to_regexp \ + "Program received signal SIGSEGV, Segmentation fault."] \ + "("] \ + [string cat \ + [string_to_regexp "Address not mapped to object."] \ + ")?"] \ "${::hex} in func_segfault \\(\\) at \[^\r\n\]+:${::segv_line}" \ "${::decimal}\\s+\[^\r\n\]+Segfault here\[^\r\n\]+" \ "The program being debugged was signaled while in a function called from GDB\\." \ diff --git a/gdb/testsuite/gdb.base/inferior-died.exp b/gdb/testsuite/gdb.base/inferior-died.exp index 3992561..764a88d 100644 --- a/gdb/testsuite/gdb.base/inferior-died.exp +++ b/gdb/testsuite/gdb.base/inferior-died.exp @@ -13,10 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# Until "set follow-fork-mode" and "catch fork" are implemented on -# other targets... -# -require {istarget "*-*-linux*"} +require allow_fork_tests require support_displaced_stepping diff --git a/gdb/testsuite/gdb.base/info-shared.exp b/gdb/testsuite/gdb.base/info-shared.exp index 6f1b2d6..e81b28e 100644 --- a/gdb/testsuite/gdb.base/info-shared.exp +++ b/gdb/testsuite/gdb.base/info-shared.exp @@ -79,6 +79,9 @@ proc check_info_shared { test expect1 expect2 } { } } +# Check that "info shared" before running doesn't crash. +check_info_shared "info sharedlibrary before running" 0 0 + # Start the inferior, and check neither of the libraries are loaded at # the start. if ![runto_main] { diff --git a/gdb/testsuite/gdb.base/interrupt-daemon.exp b/gdb/testsuite/gdb.base/interrupt-daemon.exp index 161f854..8b8c61d 100644 --- a/gdb/testsuite/gdb.base/interrupt-daemon.exp +++ b/gdb/testsuite/gdb.base/interrupt-daemon.exp @@ -16,6 +16,8 @@ # Make sure that we can interrupt an inferior that forks and moves to # its own session. +require allow_fork_tests + standard_testfile if {[build_executable "failed to build" $testfile $srcfile {debug}]} { diff --git a/gdb/testsuite/gdb.base/jit-bfd-name.exp b/gdb/testsuite/gdb.base/jit-bfd-name.exp index 9e4daa1..219929b 100644 --- a/gdb/testsuite/gdb.base/jit-bfd-name.exp +++ b/gdb/testsuite/gdb.base/jit-bfd-name.exp @@ -67,11 +67,13 @@ gdb_breakpoint [gdb_get_line_number "break here 1" $::main_srcfile] gdb_continue_to_breakpoint "break here 1" # Confirm that the two expected functions are available. +set re_f1 [string_to_regexp "int jit_function_0001(void)"] +set re_f2 [string_to_regexp "int jit_function_0002(void)"] gdb_test "info function ^jit_function" \ [multi_line \ "File \[^\r\n\]+jit-elf-solib.c:" \ - "${decimal}:\\s+int jit_function_0001\\(\\);" \ - "${decimal}:\\s+int jit_function_0002\\(\\);"] + "${decimal}:\\s+$re_f1;" \ + "${decimal}:\\s+$re_f2;"] # Capture the addresses of each JIT symfile. set symfile_addrs {} diff --git a/gdb/testsuite/gdb.base/jit-elf-fork.exp b/gdb/testsuite/gdb.base/jit-elf-fork.exp index 81d3350..c1fa428 100644 --- a/gdb/testsuite/gdb.base/jit-elf-fork.exp +++ b/gdb/testsuite/gdb.base/jit-elf-fork.exp @@ -15,6 +15,7 @@ # Test fork handling of an inferior that has JIT-ed objfiles. +require allow_fork_tests require allow_shlib_tests load_lib jit-elf-helpers.exp diff --git a/gdb/testsuite/gdb.base/jit-elf-solib.c b/gdb/testsuite/gdb.base/jit-elf-solib.c index 690d7a0..c6fcb89 100644 --- a/gdb/testsuite/gdb.base/jit-elf-solib.c +++ b/gdb/testsuite/gdb.base/jit-elf-solib.c @@ -22,4 +22,4 @@ #error "Must define the FUNCTION_NAME macro to set a jited function name" #endif -int FUNCTION_NAME() { return 42; } +int FUNCTION_NAME(void) { return 42; } diff --git a/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp b/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp index ef4bb88..57ec330 100644 --- a/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp +++ b/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp @@ -19,6 +19,7 @@ # commands. require can_spawn_for_attach +require allow_multi_inferior_tests standard_testfile set executable $testfile diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp index e006d55..7936e53 100644 --- a/gdb/testsuite/gdb.base/maint.exp +++ b/gdb/testsuite/gdb.base/maint.exp @@ -52,6 +52,40 @@ if {[prepare_for_testing "failed to prepare" $testfile \ return -1 } +# Check "maint set per-command" warnings. We do this early so that +# the following tests don't need to expect them, as GDB only warns +# once. + +with_test_prefix "warnings" { + # Potential warning given by "maint set per-command time". + set maybe_per_command_warning \ + "(?:warning: per-thread run time information not available on this platform)?" + + # This one should not issue the "per-command time" warning. + with_test_prefix "per-command space" { + gdb_test_no_output "mt set per-command space on" + gdb_test_no_output "mt set per-command space off" + } + + # These might warn. "per-command on" enables all sub commands, so + # might trigger the "per-command time" warning. + foreach cmd {"per-command" "per-command time"} { + with_test_prefix $cmd { + # GDB only warns once, so restart between commands. + clean_restart $binfile + gdb_test "mt set $cmd on" "$maybe_per_command_warning" + gdb_test "mt set $cmd off" "command started" + gdb_test_no_output "mt set $cmd on" \ + "mt set $cmd on, again" + gdb_test "mt set $cmd off" "command started" \ + "mt set $cmd off, again" + } + } + + # We've already warned once above, so the following tests don't + # need to expect the warning. +} + set readnow_p [readnow] # The commands we test here produce many lines of output; disable "press @@ -205,8 +239,8 @@ set re \ "( Number of \"partial\" symbols read: $decimal" \ ")?( Number of psym tables \\(not yet expanded\\): $decimal" \ ")?( Total memory used for psymbol cache: $decimal" \ - ")?( Number of read CUs: $decimal" \ - " Number of unread CUs: $decimal" \ + ")?( Number of read units: $decimal" \ + " Number of unread units: $decimal" \ ")? Total memory used for objfile obstack: $decimal" \ " Total memory used for BFD obstack: $decimal" \ " Total memory used for string cache: $decimal" \ @@ -513,4 +547,9 @@ gdb_test_no_output "maint print symbols" gdb_test_no_output "maint print msymbols" gdb_test_no_output "maint print psymbols" +gdb_test "maint canonicalize int short" "canonical = short" +gdb_test "maint canonicalize fn<ty<int>>" \ + "canonical = fn<ty<int> >" +gdb_test "maint canonical unsigned int" "No change\\." + gdb_exit diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp index 61a240f..3facccb 100644 --- a/gdb/testsuite/gdb.base/multi-forks.exp +++ b/gdb/testsuite/gdb.base/multi-forks.exp @@ -13,11 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# Until "set follow-fork-mode" and "catch fork" are implemented on -# other targets... -# -require {istarget "*-*-linux*"} - +require allow_fork_tests standard_testfile .c diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp index f32d258..a0947e2 100644 --- a/gdb/testsuite/gdb.base/options.exp +++ b/gdb/testsuite/gdb.base/options.exp @@ -62,8 +62,7 @@ proc check_completion_result {expected test} { # just checking whether GDB recognizes the option and auto-appends a # space. proc test_completer_recognizes {res input_line} { - set expected_re [string_to_regexp $input_line] - test_gdb_complete_unique $input_line $expected_re + test_gdb_complete_unique $input_line $input_line check_completion_result $res $input_line } @@ -509,12 +508,26 @@ proc_with_prefix test-thread-apply {} { proc_with_prefix test-info-threads {} { test_gdb_complete_multiple "info threads " "" "" { "-gid" + "-running" + "-stopped" "ID" } + test_gdb_complete_multiple "info threads " "-" "" { + "-gid" + "-running" + "-stopped" + } + test_gdb_complete_unique \ - "info threads -" \ + "info threads -g" \ "info threads -gid" + test_gdb_complete_unique \ + "info threads -r" \ + "info threads -running" + test_gdb_complete_unique \ + "info threads -s" \ + "info threads -stopped" # "ID" isn't really something the user can type. test_gdb_complete_none "info threads I" diff --git a/gdb/testsuite/gdb.base/pie-fork.exp b/gdb/testsuite/gdb.base/pie-fork.exp index 48c01d9..86407b4 100644 --- a/gdb/testsuite/gdb.base/pie-fork.exp +++ b/gdb/testsuite/gdb.base/pie-fork.exp @@ -16,6 +16,8 @@ # Test that we can follow forks properly when the executable is # position-independent. +require allow_fork_tests + standard_testfile set opts [list debug pie] diff --git a/gdb/testsuite/gdb.base/print-symbol-loading.exp b/gdb/testsuite/gdb.base/print-symbol-loading.exp index 15f2c19..c9e2480 100644 --- a/gdb/testsuite/gdb.base/print-symbol-loading.exp +++ b/gdb/testsuite/gdb.base/print-symbol-loading.exp @@ -15,7 +15,7 @@ # Test the "print symbol-loading" option. -require allow_shlib_tests +require allow_shlib_tests gcore_cmd_available standard_testfile print-symbol-loading-main.c set libfile print-symbol-loading-lib diff --git a/gdb/testsuite/gdb.base/ptype.exp b/gdb/testsuite/gdb.base/ptype.exp index 788cdfc..6971f4c 100644 --- a/gdb/testsuite/gdb.base/ptype.exp +++ b/gdb/testsuite/gdb.base/ptype.exp @@ -544,10 +544,10 @@ proc ptype_maybe_prototyped { id prototyped plain { overprototyped "NO-MATCH" } fail "ptype $id (compiler doesn't emit prototyped types)" } -re "type = $overprototyped\[\r\n\]+$gdb_prompt $" { - if { [test_compiler_info "armcc-*"] } { - setup_xfail "*-*-*" - } - fail "ptype $id (compiler doesn't emit unprototyped types)" + # C23 no longer supports non-prototype function declaration, in which + # case the overprototyped regexp is the expected one. Simply pass + # in all cases. + pass "ptype $id (overprototyped)" } } } diff --git a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp index 22913ca..eaee010 100644 --- a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp +++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp @@ -49,6 +49,11 @@ if {[build_executable "failed to prepare" $testfile $srcfile]} { # - run: use the run command # - attach: start a process outside of GDB and attach it proc do_test { action1 action2 } { + + if {$action1 == "add" && ![allow_multi_inferior_tests]} { + return + } + save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"maintenance set target-non-stop on\"" clean_restart $::binfile diff --git a/gdb/testsuite/gdb.base/sigall.exp b/gdb/testsuite/gdb.base/sigall.exp index b23e3c5..461a92b 100644 --- a/gdb/testsuite/gdb.base/sigall.exp +++ b/gdb/testsuite/gdb.base/sigall.exp @@ -41,13 +41,14 @@ proc test_one_sig {nextsig} { setup_xfail "i*86-pc-linuxoldld-gnu" "i*86-pc-linuxaout-gnu" } # On Linux SPARC64 systems SIGLOST==SIGPWR and gdb identifies - # the raised signal as PWR. - if {$thissig == "LOST" && [istarget "sparc64-*-linux*"]} { + # the raised signal as PWR. Same for Cygwin. + if {$thissig == "LOST" + && ([istarget "sparc64-*-linux*"] || [istarget "*-*-cygwin*"])} { set esig "PWR" } gdb_test "continue" \ - "Continuing.*Program received signal SIG$esig.*" \ + "Continuing.* received signal SIG$esig.*" \ "get signal $esig" } @@ -177,7 +178,7 @@ gdb_test "handle SIGTERM stop print" \ "SIGTERM\[ \t\]*Yes\[ \t\]*Yes\[ \t\]*Yes.*" gdb_test "b handle_TERM" "Breakpoint \[0-9\]+ .*" gdb_test "continue" \ - "Continuing.*Program received signal SIGTERM.*" \ + "Continuing.* received signal SIGTERM.*" \ "get signal TERM" gdb_test "continue" "Breakpoint.*handle_TERM.*" "send signal TERM" gdb_continue_to_end "continue to sigall exit" diff --git a/gdb/testsuite/gdb.base/solib-search.exp b/gdb/testsuite/gdb.base/solib-search.exp index 2efad18..35b0314 100644 --- a/gdb/testsuite/gdb.base/solib-search.exp +++ b/gdb/testsuite/gdb.base/solib-search.exp @@ -16,7 +16,7 @@ # Test solib-search-path, and in the case of solib-svr4.c whether l_addr_p # is properly reset when the path is changed. -require allow_shlib_tests +require allow_shlib_tests gcore_cmd_available require {!is_remote target} # Build "wrong" and "right" versions of the libraries in separate directories. diff --git a/gdb/testsuite/gdb.base/step-over-exit.exp b/gdb/testsuite/gdb.base/step-over-exit.exp index 2370f97..6dfa7bb 100644 --- a/gdb/testsuite/gdb.base/step-over-exit.exp +++ b/gdb/testsuite/gdb.base/step-over-exit.exp @@ -13,11 +13,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -standard_testfile - # Test a thread is doing step-over a syscall instruction which is exit, # and GDBserver should cleanup its state of step-over properly. +# The testcase relies on follow-fork-mode child. +require allow_fork_tests + +standard_testfile + set syscall_insn "" # Define the syscall instruction for each target. diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp index 59c93ee..503671b 100644 --- a/gdb/testsuite/gdb.base/style.exp +++ b/gdb/testsuite/gdb.base/style.exp @@ -329,6 +329,18 @@ proc run_style_tests { } { "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \ "Version's TrueColor foreground style" } + + gdb_test_no_output "set host-charset UTF-8" + # Chosen since it will print an error. + gdb_test "maint translate-address" \ + "❌️ requires argument.*" \ + "emoji output" + + gdb_test_no_output "set style error-prefix abcd:" \ + "set the error prefix" + gdb_test "maint translate-address" \ + "abcd:requires argument.*" \ + "error prefix" } } @@ -739,6 +751,280 @@ proc test_enable_styling_warning { } { } } +# Run an 'apropos' command. Each line of output starts with a +# non-default style (command style). Ensure that pagination triggers +# during the 'apropos' output such that, at the point pagination kicks +# in, a non-default style is in effect. +# +# Then, at the pagination prompt, quit the command. +# +# Next, run a command which switches to a different style, and then +# back to the current style. +# +# At one point, a bug in the pagination code would leave the +# non-default style from the 'apropos' command recorded as the current +# style, such that the second command would switch back to the earlier +# style. +proc test_pagination_cmd_after_quit_styling {} { + with_ansi_styling_terminal { + clean_restart + } + + # We're going to use 'apropos time'. Check that with a height of + # 12 lines, each line starts with a non-default style, and that we + # do see the pagination prompt. This means that there are more + # than 12 lines for this command. + with_test_prefix "validate apropos output" { + gdb_test_no_output "set height 12" + + set saw_pagination_prompt false + gdb_test_multiple "apropos time" "" { + -re "^apropos time\r\n" { + exp_continue + } + -re "^\033\\\[39;49;1;27m\[^\r\n\]+\r\n" { + exp_continue + } + -re "^$::pagination_prompt$" { + set saw_pagination_prompt true + send_gdb "q\n" + exp_continue + } + -re "^q\r\n" { + exp_continue + } + -re "^Quit\r\n" { + exp_continue + } + -re "^$::gdb_prompt $" { + gdb_assert { $saw_pagination_prompt } $gdb_test_name + } + -re "^\[^\r\n\]+\r\n" { + exp_continue + } + } + } + + # Now reduce the height to 10 and re-run 'apropos time'. Based on + # the previous check, we know that this is going to present the + # pagination prompt when a non-default style is in use. + gdb_test_no_output "set height 10" + + set saw_pagination_prompt false + gdb_test_multiple "apropos time" "" { + -re "$::pagination_prompt" { + set saw_pagination_prompt true + send_gdb "q\n" + exp_continue + } + -re "\r\n$::gdb_prompt $" { + gdb_assert { $saw_pagination_prompt } $gdb_test_name + } + } + + # The help output for this maintenance command switches to a + # different style, and then back to the default. If the + # pagination bug still exists, then this would switch back to the + # non-default style that was in use when pagination kicked in + # above. + gdb_test "maintenance time" \ + "^\"\033\\\[39;49;1;27mmaintenance time\033\\\[m\" takes a numeric argument\\." +} + +# Helper for test_pagination_prompt_styling. Return false if STR, a +# line that appears immediately before a pagination prompt, matches +# the pattern for needing a style reset at the end, but does not have +# the style reset. +# +# In all other cases, return true. So lines that don't match the +# known pattern for neededing a style reset will always return true, +# as will lines that match the pattern, and do have the style reset. +proc previous_line_is_ok { str } { + + # Create a copy of STR with all the '\033' characters removed. + # Then compare string lengths to get a count of the '\033' + # charactes present in STR. + regsub -all "\033" $str {} stripped + set count [expr [string length $str] - [string length $stripped]] + + # If STR switched styles, then it _must_ switch back again, + # otherwise the pagination prompt will be in the wrong style. + # This means that there _must_ be an even number of '\033' + # characters in STR. If there is not then we switched style, but + # failed to switch back. + if { [expr $count % 2] != 0 } { + return false + } + + # For lines that don't match this pattern, we cannot comment on + # where the style reset should occur, so lets just claim the line + # is fine. + if { ![regexp "\\s+$::hex - $::hex is \[^\r\n\]+ in " $str] } { + return true + } + + # This line did match the above pattern, so we know that a style + # reset _must_ occur at the end of the line. If it doesn't then + # this line is not OK. + if { ![regexp "\033\\\[m$" $str] } { + return false + } + + # All tests passed, this line looks OK. + return true +} + +# Test that the pagination prompt is displayed unstyled. This is done +# by looking at the 'info files' output and selecting a width that +# will mean we should get a pagination prompt part way through a +# styled filename. +# +# Then, re-run 'info files' and check that for every pagination +# prompt, the previous line disables styling as expected. +proc test_pagination_prompt_styling {} { + with_ansi_styling_terminal { + clean_restart $::binfile + } + + if {![runto_main]} { + return + } + + # Set height so we actually get a pagination prompt. + gdb_test_no_output "set height 3" + + # Scan the 'info files' output and set DESIRED_WIDTH such that it + # will trigger pagination part-way through a styled filename. + set desired_width 0 + gdb_test_multiple "info files" "find good test width" { + -re "^info files\r\n" { + exp_continue + } + + -re "^$::pagination_prompt$" { + send_gdb "\n" + exp_continue + } + + -re "^$::gdb_prompt $" { + } + + -re "^((\\s+$::hex - $::hex is \[^\r\n\]+ in )\[^\r\n\]+)\r\n" { + if { $desired_width == 0 } { + set full_line $expect_out(1,string) + set inner_line $expect_out(2,string) + set desired_width [expr [string length $inner_line] + ([string length $full_line] - [string length $inner_line]) / 2] + } + exp_continue + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } + + # Now setup the screen width. + gdb_test_no_output "set width $desired_width" + + # Re-run 'info files'. Check that the content before any + # pagination prompt correctly disables styling. + set saw_bad_line false + set prev_line "" + gdb_test_multiple "info files" "check pagination prompt styling" { + -re "^info files\r\n" { + exp_continue + } + + -re "^$::pagination_prompt$" { + if { ![previous_line_is_ok $prev_line] } { + set saw_bad_line true + } + send_gdb "\n" + exp_continue + } + + -re "^(\[^\r\n\]+)$::pagination_prompt$" { + set prev_line $expect_out(1,string) + if { ![previous_line_is_ok $prev_line] } { + set saw_bad_line true + } + send_gdb "\n" + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { !$saw_bad_line } $gdb_test_name + } + + -re "^(\[^\r\n\]*)\r\n" { + set prev_line $expect_out(1,string) + exp_continue + } + } +} + +# Test that GDB can correctly restore the current style after a +# pagination prompt. +# +# Set the logging file to a garbage string based on LENGTH (is +# actually 2x LENGTH), then 'show logging file'. Press return at the +# pagination prompt, and check that the reset of the filename is +# styled correctly, and that GDB correctly switches back to the +# default style once the logging file has finished. +proc test_pagination_continue_styling_1 { length } { + with_ansi_styling_terminal { + clean_restart $::binfile + } + + set filename [string repeat "ax" $length] + + gdb_test_no_output "set logging file $filename" + + gdb_test_no_output "set height 3" + gdb_test_no_output "set width 80" + + set saw_bad_styling false + gdb_test_multiple "show logging file" "" { + -re "^show logging file\r\n" { + exp_continue + } + + -re "^The current logfile is \"\033\\\[32;49;22;27m(?:ax)+\033\\\[m" { + exp_continue + } + + -re "^\r\n\033\\\[32;49;22;27m(?:ax)+\033\\\[m(?=--)" { + exp_continue + } + + -re "^\r\n\033\\\[32;49;22;27m(?:ax)+(?=--)" { + set saw_bad_styling true + exp_continue + } + + -re "^\r\n\033\\\[32;49;22;27m(?:ax)+\033\\\[m\"\\.\r\n" { + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { !$saw_bad_styling } $gdb_test_name + } + + -re "^$::pagination_prompt$$" { + send_gdb "\n" + exp_continue + } + } +} + +# Wrapper around test_pagination_continue_styling_1, calls that +# function with different lengths. +proc test_pagination_continue_styling { } { + foreach_with_prefix length { 80 160 } { + test_pagination_continue_styling_1 $length + } +} + # Check to see if the Python styling of disassembler output is # expected or not, this styling requires Python support in GDB, and # the Python pygments module to be available. @@ -781,3 +1067,6 @@ test_colorsupport_truecolor test_colorsupport_truecolor_only test_enable_styling_warning +test_pagination_cmd_after_quit_styling +test_pagination_prompt_styling +test_pagination_continue_styling diff --git a/gdb/testsuite/gdb.base/tls-common.exp.tcl b/gdb/testsuite/gdb.base/tls-common.exp.tcl new file mode 100644 index 0000000..7aa7f46 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-common.exp.tcl @@ -0,0 +1,50 @@ +# 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. + +# Require statement, variables and procs used by tls-nothreads.exp, +# tls-multiobj.exp, and tls-dlobj.exp. + +# The tests listed above are known to work for the targets listed on +# the 'require' line, below. +# +# At the moment, only the Linux target is listed, but, ideally, these +# tests should be run on other targets too. E.g, testing on FreeBSD +# shows many failures which should be addressed in some fashion before +# enabling it for that target. + +require {is_any_target "*-*-linux*"} + +# These are the targets which have support for internal TLS lookup: + +set internal_tls_linux_targets {"x86_64-*-linux*" "aarch64-*-linux*" + "riscv*-*-linux*" "powerpc64*-*-linux*" + "s390x*-*-linux*"} + +# The "maint set force-internal-tls-address-lookup" command is only +# available for certain Linux architectures. Don't attempt to force +# use of internal TLS support for architectures which don't support +# it. + +if [is_any_target {*}$internal_tls_linux_targets] { + set internal_tls_iters { false true } +} else { + set internal_tls_iters { false } +} + +# Set up a kfail with message KFAIL_MSG when KFAIL_COND holds, then +# issue gdb_test with command CMD and regular expression RE. + +proc gdb_test_with_kfail {cmd re kfail_cond kfail_msg} { + if [uplevel 1 [list expr $kfail_cond]] { + setup_kfail $kfail_msg *-*-* + } + gdb_test $cmd $re +} diff --git a/gdb/testsuite/gdb.base/tls-dlobj-lib.c b/gdb/testsuite/gdb.base/tls-dlobj-lib.c new file mode 100644 index 0000000..c69bab7 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-dlobj-lib.c @@ -0,0 +1,87 @@ +/* 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/>. */ + +/* This program needs to be compiled with preprocessor symbol set to + a small integer, e.g. "gcc -DN=1 ..." With N defined, the CONCAT2 + and CONCAT3 macros will construct suitable names for the global + variables and functions. */ + +#define CONCAT2(a,b) CONCAT2_(a,b) +#define CONCAT2_(a,b) a ## b + +#define CONCAT3(a,b,c) CONCAT3_(a,b,c) +#define CONCAT3_(a,b,c) a ## b ## c + +/* For N=1, this ends up being... + __thread int tls_lib1_tbss_1; + __thread int tls_lib1_tbss_2; + __thread int tls_lib1_tdata_1 = 196; + __thread int tls_lib1_tdata_2 = 197; */ + +__thread int CONCAT3(tls_lib, N, _tbss_1); +__thread int CONCAT3(tls_lib, N, _tbss_2); +__thread int CONCAT3(tls_lib, N, _tdata_1) = CONCAT2(N, 96); +__thread int CONCAT3(tls_lib, N, _tdata_2) = CONCAT2(N, 97); + +/* Substituting for N, define function: + + int get_tls_libN_var (int which) . */ + +int +CONCAT3(get_tls_lib, N, _var) (int which) +{ + switch (which) + { + case 0: + return -1; + case 1: + return CONCAT3(tls_lib, N, _tbss_1); + case 2: + return CONCAT3(tls_lib, N, _tbss_2); + case 3: + return CONCAT3(tls_lib, N, _tdata_1); + case 4: + return CONCAT3(tls_lib, N, _tdata_2); + } + return -1; +} + +/* Substituting for N, define function: + + void set_tls_libN_var (int which, int val) . */ + +void +CONCAT3(set_tls_lib, N, _var) (int which, int val) +{ + switch (which) + { + case 0: + break; + case 1: + CONCAT3(tls_lib, N, _tbss_1) = val; + break; + case 2: + CONCAT3(tls_lib, N, _tbss_2) = val; + break; + case 3: + CONCAT3(tls_lib, N, _tdata_1) = val; + break; + case 4: + CONCAT3(tls_lib, N, _tdata_2) = val; + break; + } +} diff --git a/gdb/testsuite/gdb.base/tls-dlobj.c b/gdb/testsuite/gdb.base/tls-dlobj.c new file mode 100644 index 0000000..322bdda --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-dlobj.c @@ -0,0 +1,311 @@ +/* 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 <dlfcn.h> +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +typedef void (*setter_ftype) (int which, int val); + +__thread int tls_main_tbss_1; +__thread int tls_main_tbss_2; +__thread int tls_main_tdata_1 = 96; +__thread int tls_main_tdata_2 = 97; + +extern void set_tls_lib10_var (int which, int val); +extern void set_tls_lib11_var (int which, int val); + +volatile int data; + +static void +set_tls_main_var (int which, int val) +{ + switch (which) + { + case 1: + tls_main_tbss_1 = val; + break; + case 2: + tls_main_tbss_2 = val; + break; + case 3: + tls_main_tdata_1 = val; + break; + case 4: + tls_main_tdata_2 = val; + break; + } +} + +void +use_it (int a) +{ + data = a; +} + +static void * +load_dso (char *dso_name, int n, setter_ftype *setterp) +{ + char buf[80]; + void *sym; + void *handle = dlopen (dso_name, RTLD_NOW | RTLD_GLOBAL); + if (handle == NULL) + { + fprintf (stderr, "dlopen of DSO '%s' failed: %s\n", dso_name, dlerror ()); + exit (1); + } + sprintf (buf, "set_tls_lib%d_var", n); + sym = dlsym (handle, buf); + assert (sym != NULL); + *setterp = sym; + + /* Some libc implementations (for some architectures) refuse to + initialize TLS data structures (specifically, the DTV) without + first calling dlsym on one of the TLS symbols. */ + sprintf (buf, "tls_lib%d_tdata_1", n); + assert (dlsym (handle, buf) != NULL); + + return handle; +} + +int +main (int argc, char **argv) +{ + int i, status; + setter_ftype s0, s1, s2, s3, s4, s10, s11; + void *h1 = load_dso (OBJ1, 1, &s1); + void *h2 = load_dso (OBJ2, 2, &s2); + void *h3 = load_dso (OBJ3, 3, &s3); + void *h4 = load_dso (OBJ4, 4, &s4); + s0 = set_tls_main_var; + s10 = set_tls_lib10_var; + s11 = set_tls_lib11_var; + + use_it (0); /* main-breakpoint-1 */ + + /* Set TLS variables in main program and all libraries. */ + for (i = 1; i <= 4; i++) + s0 (i, 10 + i); + for (i = 1; i <= 4; i++) + s1 (i, 110 + i); + for (i = 1; i <= 4; i++) + s2 (i, 210 + i); + for (i = 1; i <= 4; i++) + s3 (i, 310 + i); + for (i = 1; i <= 4; i++) + s4 (i, 410 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1010 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1110 + i); + + use_it (0); /* main-breakpoint-2 */ + + /* Unload lib2 and lib3. */ + status = dlclose (h2); + assert (status == 0); + status = dlclose (h3); + assert (status == 0); + + /* Set TLS variables in main program and in libraries which are still + loaded. */ + for (i = 1; i <= 4; i++) + s0 (i, 20 + i); + for (i = 1; i <= 4; i++) + s1 (i, 120 + i); + for (i = 1; i <= 4; i++) + s4 (i, 420 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1020 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1120 + i); + + use_it (0); /* main-breakpoint-3 */ + + /* Load lib3. */ + h3 = load_dso (OBJ3, 3, &s3); + + /* Set TLS vars again; currently, only lib2 is not loaded. */ + for (i = 1; i <= 4; i++) + s0 (i, 30 + i); + for (i = 1; i <= 4; i++) + s1 (i, 130 + i); + for (i = 1; i <= 4; i++) + s3 (i, 330 + i); + for (i = 1; i <= 4; i++) + s4 (i, 430 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1030 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1130 + i); + + use_it (0); /* main-breakpoint-4 */ + + /* Unload lib1 and lib4; load lib2. */ + status = dlclose (h1); + assert (status == 0); + status = dlclose (h4); + assert (status == 0); + h2 = load_dso (OBJ2, 2, &s2); + + /* Set TLS vars; currently, lib2 and lib3 are loaded, + lib1 and lib4 are not. */ + for (i = 1; i <= 4; i++) + s0 (i, 40 + i); + for (i = 1; i <= 4; i++) + s2 (i, 240 + i); + for (i = 1; i <= 4; i++) + s3 (i, 340 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1040 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1140 + i); + + use_it (0); /* main-breakpoint-5 */ + + /* Load lib4 and lib1. Unload lib2. */ + h4 = load_dso (OBJ4, 4, &s4); + h1 = load_dso (OBJ1, 1, &s1); + status = dlclose (h2); + assert (status == 0); + + /* Set TLS vars; currently, lib1, lib3, and lib4 are loaded; + lib2 is not loaded. */ + for (i = 1; i <= 4; i++) + s0 (i, 50 + i); + for (i = 1; i <= 4; i++) + s1 (i, 150 + i); + for (i = 1; i <= 4; i++) + s3 (i, 350 + i); + for (i = 1; i <= 4; i++) + s4 (i, 450 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1050 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1150 + i); + + use_it (0); /* main-breakpoint-6 */ + + /* Load lib2, unload lib1, lib3, and lib4; then load lib3 again. */ + h2 = load_dso (OBJ2, 2, &s2); + status = dlclose (h1); + assert (status == 0); + status = dlclose (h3); + assert (status == 0); + status = dlclose (h4); + assert (status == 0); + h3 = load_dso (OBJ3, 3, &s3); + + /* Set TLS vars; currently, lib2 and lib3 are loaded; + lib1 and lib4 are not loaded. */ + for (i = 1; i <= 4; i++) + s0 (i, 60 + i); + for (i = 1; i <= 4; i++) + s2 (i, 260 + i); + for (i = 1; i <= 4; i++) + s3 (i, 360 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1060 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1160 + i); + + use_it (0); /* main-breakpoint-7 */ + + /* Unload lib3 and lib2, then (re)load lib4, lib3, lib2, and lib1, + in that order. */ + status = dlclose (h3); + assert (status == 0); + status = dlclose (h2); + assert (status == 0); + h4 = load_dso (OBJ4, 4, &s4); + h3 = load_dso (OBJ3, 3, &s3); + h2 = load_dso (OBJ2, 2, &s2); + h1 = load_dso (OBJ1, 1, &s1); + + /* Set TLS vars; currently, lib1, lib2, lib3, and lib4 are all + loaded. */ + for (i = 1; i <= 4; i++) + s0 (i, 70 + i); + for (i = 1; i <= 4; i++) + s1 (i, 170 + i); + for (i = 1; i <= 4; i++) + s2 (i, 270 + i); + for (i = 1; i <= 4; i++) + s3 (i, 370 + i); + for (i = 1; i <= 4; i++) + s4 (i, 470 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1070 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1170 + i); + + use_it (0); /* main-breakpoint-8 */ + + /* Unload lib3, lib1, and lib4. */ + status = dlclose (h3); + assert (status == 0); + status = dlclose (h1); + assert (status == 0); + status = dlclose (h4); + assert (status == 0); + + /* Set TLS vars; currently, lib2 is loaded; lib1, lib3, and lib4 are + not. */ + for (i = 1; i <= 4; i++) + s0 (i, 80 + i); + for (i = 1; i <= 4; i++) + s2 (i, 280 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1080 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1180 + i); + + use_it (0); /* main-breakpoint-9 */ + + /* Load lib3, unload lib2, load lib4. */ + h3 = load_dso (OBJ3, 3, &s3); + status = dlclose (h2); + assert (status == 0); + h4 = load_dso (OBJ4, 4, &s4); + + /* Set TLS vars; currently, lib3 and lib4 are loaded; lib1 and lib2 + are not. */ + for (i = 1; i <= 4; i++) + s0 (i, 90 + i); + for (i = 1; i <= 4; i++) + s3 (i, 390 + i); + for (i = 1; i <= 4; i++) + s4 (i, 490 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1090 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1190 + i); + + use_it (0); /* main-breakpoint-10 */ + + /* Attempt to keep variables in the main program from being optimized + away. */ + use_it (tls_main_tbss_1); + use_it (tls_main_tbss_2); + use_it (tls_main_tdata_1); + use_it (tls_main_tdata_2); + + use_it (100); /* main-breakpoint-last */ + + return 0; +} diff --git a/gdb/testsuite/gdb.base/tls-dlobj.exp b/gdb/testsuite/gdb.base/tls-dlobj.exp new file mode 100644 index 0000000..02f2ff8 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-dlobj.exp @@ -0,0 +1,378 @@ +# 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. + +# Test that the GDB-internal TLS link map to module id mapping code +# works correctly when debugging a program which is linked against +# shared objects and which also loads and unloads other shared objects +# in different orders. For targets which have GDB-internal TLS +# support, it'll check both GDB-internal TLS support as well as that +# provided by a helper library such as libthread_db. + +source $srcdir/$subdir/tls-common.exp.tcl + +require allow_shlib_tests + +standard_testfile + +set libsrc "${srcdir}/${subdir}/${testfile}-lib.c" + +# These will be dlopen'd: +set lib1obj [standard_output_file "${testfile}1-lib.so"] +set lib2obj [standard_output_file "${testfile}2-lib.so"] +set lib3obj [standard_output_file "${testfile}3-lib.so"] +set lib4obj [standard_output_file "${testfile}4-lib.so"] + +# These will be dynamically linked with the main program: +set lib10obj [standard_output_file "${testfile}10-lib.so"] +set lib11obj [standard_output_file "${testfile}11-lib.so"] + +# Due to problems with some versions of glibc, we expect some tests to +# fail due to TLS storage not being allocated/initialized. Test +# command CMD using regular expression RE, and use XFAIL instead of +# FAIL when the relevant RE is matched and COND is true when evaluated +# in the upper level. + +proc gdb_test_with_xfail { cmd re cond} { + gdb_test_multiple $cmd $cmd { + -re -wrap $re { + pass $gdb_test_name + } + -re -wrap "The inferior has not yet allocated storage for thread-local variables.*" { + if [ uplevel 1 [list expr $cond]] { + xfail $gdb_test_name + } else { + fail $gdb_test_name + } + } + } +} + +proc do_tests {force_internal_tls} { + clean_restart $::binfile + if ![runto_main] { + return + } + + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"] + gdb_continue_to_breakpoint "main-breakpoint-1" + + with_test_prefix "before assignments" { + gdb_test "print tls_main_tbss_1" ".* = 0" + gdb_test "print tls_main_tbss_2" ".* = 0" + gdb_test "print tls_main_tdata_1" ".* = 96" + gdb_test "print tls_main_tdata_2" ".* = 97" + + # For these tests, where we're attempting to access TLS vars + # in a dlopen'd library, but before assignment to any of the + # vars, so it could happen that storage hasn't been allocated + # yet. But it might also work. (When testing against MUSL, + # things just work; GLIBC ends to produce the TLS error.) So + # accept either the right answer or a TLS error message. + + set tlserr "The inferior has not yet allocated storage for thread-local variables.*" + foreach n {1 2 3 4} { + gdb_test "print tls_lib${n}_tbss_1" \ + "0|${tlserr}" + gdb_test "print tls_lib${n}_tbss_2" \ + "0|${tlserr}" + gdb_test "print tls_lib${n}_tdata_1" \ + "96|${tlserr}" + gdb_test "print tls_lib${n}_tdata_2" \ + "97|${tlserr}" + } + foreach n {10 11} { + gdb_test "print tls_lib${n}_tbss_1" ".* = 0" + gdb_test "print tls_lib${n}_tbss_2" ".* = 0" + gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}96" + gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}97" + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"] + gdb_continue_to_breakpoint "main-breakpoint-2" + + with_test_prefix "at main-breakpoint-2" { + gdb_test "print tls_main_tbss_1" ".* = 11" + gdb_test "print tls_main_tbss_2" ".* = 12" + gdb_test "print tls_main_tdata_1" ".* = 13" + gdb_test "print tls_main_tdata_2" ".* = 14" + + foreach n {1 2 3 4 10 11} { + gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}11" + gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}12" + gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}13" + gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}14" + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-3"] + gdb_continue_to_breakpoint "main-breakpoint-3" + + # At this point lib2 and lib3 have been unloaded. Also, TLS vars + # in remaining libraries have been changed. + + with_test_prefix "at main-breakpoint-3" { + gdb_test "print tls_main_tbss_1" ".* = 21" + gdb_test "print tls_main_tbss_2" ".* = 22" + gdb_test "print tls_main_tdata_1" ".* = 23" + gdb_test "print tls_main_tdata_2" ".* = 24" + + foreach n {1 4 10 11} { + gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}21" + gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}22" + gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}23" + gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}24" + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-4"] + gdb_continue_to_breakpoint "main-breakpoint-4" + + # lib3 has been loaded again; lib2 is the only one not loaded. + + with_test_prefix "at main-breakpoint-4" { + gdb_test "print tls_main_tbss_1" ".* = 31" + gdb_test "print tls_main_tbss_2" ".* = 32" + gdb_test "print tls_main_tdata_1" ".* = 33" + gdb_test "print tls_main_tdata_2" ".* = 34" + + set cond { $n == 3 } + foreach n {1 3 4 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}31" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}32" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}33" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}34" $cond + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-5"] + gdb_continue_to_breakpoint "main-breakpoint-5" + + # lib2 and lib3 are loaded; lib1 and lib4 are not. + + with_test_prefix "at main-breakpoint-5" { + gdb_test "print tls_main_tbss_1" ".* = 41" + gdb_test "print tls_main_tbss_2" ".* = 42" + gdb_test "print tls_main_tdata_1" ".* = 43" + gdb_test "print tls_main_tdata_2" ".* = 44" + + set cond { $n == 2 || $n == 3 } + foreach n {2 3 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}41" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}42" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}43" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}44" $cond + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-6"] + gdb_continue_to_breakpoint "main-breakpoint-6" + + # lib1, lib3 and lib4 are loaded; lib2 is not loaded. + + with_test_prefix "at main-breakpoint-6" { + gdb_test "print tls_main_tbss_1" ".* = 51" + gdb_test "print tls_main_tbss_2" ".* = 52" + gdb_test "print tls_main_tdata_1" ".* = 53" + gdb_test "print tls_main_tdata_2" ".* = 54" + + set cond { $n == 1 || $n == 3 || $n == 4} + foreach n {1 3 4 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}51" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}52" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}53" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}54" $cond + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-7"] + gdb_continue_to_breakpoint "main-breakpoint-7" + + # lib2 and lib3 are loaded; lib1 and lib4 are not. + + with_test_prefix "at main-breakpoint-7" { + gdb_test "print tls_main_tbss_1" ".* = 61" + gdb_test "print tls_main_tbss_2" ".* = 62" + gdb_test "print tls_main_tdata_1" ".* = 63" + gdb_test "print tls_main_tdata_2" ".* = 64" + + set cond { $n == 2 || $n == 3 } + foreach n {2 3 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}61" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}62" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}63" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}64" $cond + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-8"] + gdb_continue_to_breakpoint "main-breakpoint-8" + + # lib1, lib2, lib3, and lib4 are all loaded. + + with_test_prefix "at main-breakpoint-8" { + gdb_test "print tls_main_tbss_1" ".* = 71" + gdb_test "print tls_main_tbss_2" ".* = 72" + gdb_test "print tls_main_tdata_1" ".* = 73" + gdb_test "print tls_main_tdata_2" ".* = 74" + + foreach n {1 2 3 4 10 11} { + gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}71" + gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}72" + gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}73" + gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}74" + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-9"] + gdb_continue_to_breakpoint "main-breakpoint-9" + + # lib2 is loaded; lib1, lib3, and lib4 are not. + + with_test_prefix "at main-breakpoint-9" { + gdb_test "print tls_main_tbss_1" ".* = 81" + gdb_test "print tls_main_tbss_2" ".* = 82" + gdb_test "print tls_main_tdata_1" ".* = 83" + gdb_test "print tls_main_tdata_2" ".* = 84" + + foreach n {2 10 11} { + gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}81" + gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}82" + gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}83" + gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}84" + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-10"] + gdb_continue_to_breakpoint "main-breakpoint-10" + + # lib3 and lib4 are loaded; lib1 and lib2 are not. + + with_test_prefix "at main-breakpoint-10" { + gdb_test "print tls_main_tbss_1" ".* = 91" + gdb_test "print tls_main_tbss_2" ".* = 92" + gdb_test "print tls_main_tdata_1" ".* = 93" + gdb_test "print tls_main_tdata_2" ".* = 94" + + set cond { $n == 3 || $n == 4 } + foreach n {3 4 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}91" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}92" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}93" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}94" $cond + } + } + + # gdb_interact + + set corefile ${::binfile}.core + set core_supported 0 + if { ![is_remote host] } { + set core_supported [gdb_gcore_cmd $corefile "save corefile"] + } + + # 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 + } + + with_test_prefix "core file" { + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + gdb_test "print tls_main_tbss_1" ".* = 91" + gdb_test "print tls_main_tbss_2" ".* = 92" + gdb_test "print tls_main_tdata_1" ".* = 93" + gdb_test "print tls_main_tdata_2" ".* = 94" + + set cond { $n == 3 || $n == 4 } + foreach n {3 4 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}91" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}92" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}93" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}94" $cond + } + } +} + +# Build shared objects for dlopen: +if { [gdb_compile_shlib $libsrc $lib1obj [list debug additional_flags=-DN=1]] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $libsrc $lib2obj [list debug additional_flags=-DN=2]] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $libsrc $lib3obj [list debug additional_flags=-DN=3]] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $libsrc $lib4obj [list debug additional_flags=-DN=4]] != "" } { + untested "failed to compile shared object" + return -1 +} + +# Build shared objects to link against main program: +if { [gdb_compile_shlib $libsrc $lib10obj [list debug additional_flags=-DN=10]] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $libsrc $lib11obj [list debug additional_flags=-DN=11]] != "" } { + untested "failed to compile shared object" + return -1 +} + +# Use gdb_compile_pthreads to build and link the main program for +# testing. It's also possible to run the tests using plain old +# gdb_compile, but this adds complexity with setting up additional +# KFAILs. (When run using GLIBC versions earlier than 2.34, a program +# that's not dynamically linked against libpthread will lack a working +# libthread_db, and, therefore, won't be able to access thread local +# storage without GDB-internal TLS support. Additional complications +# arise from when testing on x86_64 with -m32, which tends to work +# okay on GLIBC 2.34 and newer, but not older versions. It gets messy +# to properly sort out all of these cases.) +# +# This test was originally written to do it both ways, i.e. with both +# both gdb_compile and gdb_compile_pthreads, but the point of this +# test is to check that the link map address to TLS module id mapping +# code works correctly in programs which use lots of dlopen and +# dlclose calls in various orders - and that can be done using just +# gdb_compile_pthreads. + +if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \ + [list debug shlib_load \ + shlib=${lib10obj} \ + shlib=${lib11obj} \ + additional_flags=-DOBJ1=\"${lib1obj}\" \ + additional_flags=-DOBJ2=\"${lib2obj}\" \ + additional_flags=-DOBJ3=\"${lib3obj}\" \ + additional_flags=-DOBJ4=\"${lib4obj}\" \ + ]] != "" } { + untested "failed to compile" +} else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + do_tests $force_internal_tls + } +} diff --git a/gdb/testsuite/gdb.base/tls-multiobj.c b/gdb/testsuite/gdb.base/tls-multiobj.c new file mode 100644 index 0000000..10e67da --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-multiobj.c @@ -0,0 +1,89 @@ +/* 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/>. */ + +__thread int tls_main_tbss_1; +__thread int tls_main_tbss_2; +__thread int tls_main_tdata_1 = 96; +__thread int tls_main_tdata_2 = 97; + +extern __thread int tls_lib1_tbss_1; +extern __thread int tls_lib1_tbss_2; +extern __thread int tls_lib1_tdata_1; +extern __thread int tls_lib1_tdata_2; + +extern __thread int tls_lib2_tbss_1; +extern __thread int tls_lib2_tbss_2; +extern __thread int tls_lib2_tdata_1; +extern __thread int tls_lib2_tdata_2; + +extern __thread int tls_lib3_tbss_1; +extern __thread int tls_lib3_tbss_2; +extern __thread int tls_lib3_tdata_1; +extern __thread int tls_lib3_tdata_2; + +extern void lib1_func (); +extern void lib2_func (); +extern void lib3_func (); + +volatile int data; + +void +use_it (int a) +{ + data = a; +} + +int +main (int argc, char **argv) +{ + use_it (-1); + + tls_main_tbss_1 = 51; /* main-breakpoint-1 */ + tls_main_tbss_2 = 52; + tls_main_tdata_1 = 53; + tls_main_tdata_2 = 54; + + tls_lib1_tbss_1 = 151; + tls_lib1_tbss_2 = 152; + tls_lib1_tdata_1 = 153; + tls_lib1_tdata_2 = 154; + + tls_lib2_tbss_1 = 251; + tls_lib2_tbss_2 = 252; + tls_lib2_tdata_1 = 253; + tls_lib2_tdata_2 = 254; + + tls_lib3_tbss_1 = 351; + tls_lib3_tbss_2 = 352; + tls_lib3_tdata_1 = 353; + tls_lib3_tdata_2 = 354; + + lib1_func (); + lib2_func (); + lib3_func (); + + /* Attempt to keep variables in the main program from being optimized + away. */ + use_it (tls_main_tbss_1); + use_it (tls_main_tbss_2); + use_it (tls_main_tdata_1); + use_it (tls_main_tdata_2); + + use_it (100); /* main-breakpoint-2 */ + + return 0; +} diff --git a/gdb/testsuite/gdb.base/tls-multiobj.exp b/gdb/testsuite/gdb.base/tls-multiobj.exp new file mode 100644 index 0000000..97acb33 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-multiobj.exp @@ -0,0 +1,230 @@ +# 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. + +# Using different compilation/linking scenarios, attempt to access +# thread-local variables in a non-threaded program using multiple +# shared objects. + +source $srcdir/$subdir/tls-common.exp.tcl + +standard_testfile + +set lib1src "${srcdir}/${subdir}/${testfile}1.c" +set lib2src "${srcdir}/${subdir}/${testfile}2.c" +set lib3src "${srcdir}/${subdir}/${testfile}3.c" + +set lib1obj [standard_output_file "${testfile}1-lib.so"] +set lib2obj [standard_output_file "${testfile}2-lib.so"] +set lib3obj [standard_output_file "${testfile}3-lib.so"] + +proc do_tests {force_internal_tls {do_kfail_tls_access 0}} { + clean_restart $::binfile + if ![runto_main] { + return + } + + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + if { $do_kfail_tls_access && [istarget "*-*-linux*"] } { + # Turn off do_kfail_tls_access when libthread_db is loaded. + # This can happen for the default case when testing x86_64 + # w/ -m32 using glibc versions 2.34 or newer. + gdb_test_multiple "maint check libthread-db" "Check for loaded libthread_db" { + -re -wrap "libthread_db integrity checks passed." { + set do_kfail_tls_access 0 + pass $gdb_test_name + } + -re -wrap "No libthread_db loaded" { + pass $gdb_test_name + } + } + # Also turn off do_kfail_tls_access when connected to a + # gdbserver and we observe that accessing a TLS variable + # works. + if [target_is_gdbserver] { + gdb_test_multiple "print tls_main_tbss_1" \ + "Check TLS accessibility when connected to a gdbserver" { + -re -wrap "= 0" { + set do_kfail_tls_access 0 + pass $gdb_test_name + } + -re -wrap "Remote target failed to process qGetTLSAddr request" { + pass $gdb_test_name + } + } + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"] + gdb_continue_to_breakpoint "main-breakpoint-1" + + set t $do_kfail_tls_access + set m "tls not available" + with_test_prefix "before assignments" { + gdb_test_with_kfail "print tls_main_tbss_1" ".* = 0" $t $m + gdb_test_with_kfail "print tls_main_tbss_2" ".* = 0" $t $m + gdb_test_with_kfail "print tls_main_tdata_1" ".* = 96" $t $m + gdb_test_with_kfail "print tls_main_tdata_2" ".* = 97" $t $m + + gdb_test_with_kfail "print tls_lib1_tbss_1" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib1_tbss_2" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_1" ".* = 196" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_2" ".* = 197" $t $m + + gdb_test_with_kfail "print tls_lib2_tbss_1" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib2_tbss_2" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_1" ".* = 296" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_2" ".* = 297" $t $m + + gdb_test_with_kfail "print tls_lib3_tbss_1" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib3_tbss_2" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_1" ".* = 396" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_2" ".* = 397" $t $m + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"] + gdb_continue_to_breakpoint "main-breakpoint-2" + + with_test_prefix "after assignments" { + gdb_test_with_kfail "print tls_main_tbss_1" ".* = 51" $t $m + gdb_test_with_kfail "print tls_main_tbss_2" ".* = 52" $t $m + gdb_test_with_kfail "print tls_main_tdata_1" ".* = 53" $t $m + gdb_test_with_kfail "print tls_main_tdata_2" ".* = 54" $t $m + + gdb_test_with_kfail "print tls_lib1_tbss_1" ".* = 151" $t $m + gdb_test_with_kfail "print tls_lib1_tbss_2" ".* = 152" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_1" ".* = 153" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_2" ".* = 154" $t $m + + gdb_test_with_kfail "print tls_lib2_tbss_1" ".* = 251" $t $m + gdb_test_with_kfail "print tls_lib2_tbss_2" ".* = 252" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_1" ".* = 253" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_2" ".* = 254" $t $m + + gdb_test_with_kfail "print tls_lib3_tbss_1" ".* = 351" $t $m + gdb_test_with_kfail "print tls_lib3_tbss_2" ".* = 352" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_1" ".* = 353" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_2" ".* = 354" $t $m + } + + set corefile ${::binfile}.core + set core_supported 0 + if { ![is_remote host] } { + set core_supported [gdb_gcore_cmd $corefile "save corefile"] + } + + # 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 + } + + with_test_prefix "core file" { + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + gdb_test_with_kfail "print tls_main_tbss_1" ".* = 51" $t $m + gdb_test_with_kfail "print tls_main_tbss_2" ".* = 52" $t $m + gdb_test_with_kfail "print tls_main_tdata_1" ".* = 53" $t $m + gdb_test_with_kfail "print tls_main_tdata_2" ".* = 54" $t $m + + gdb_test_with_kfail "print tls_lib1_tbss_1" ".* = 151" $t $m + gdb_test_with_kfail "print tls_lib1_tbss_2" ".* = 152" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_1" ".* = 153" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_2" ".* = 154" $t $m + + gdb_test_with_kfail "print tls_lib2_tbss_1" ".* = 251" $t $m + gdb_test_with_kfail "print tls_lib2_tbss_2" ".* = 252" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_1" ".* = 253" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_2" ".* = 254" $t $m + + gdb_test_with_kfail "print tls_lib3_tbss_1" ".* = 351" $t $m + gdb_test_with_kfail "print tls_lib3_tbss_2" ".* = 352" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_1" ".* = 353" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_2" ".* = 354" $t $m + } +} + +if { [gdb_compile_shlib $lib1src $lib1obj {debug}] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $lib2src $lib2obj {debug}] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $lib3src $lib3obj {debug}] != "" } { + untested "failed to compile shared object" + return -1 +} + +# Certain linux target architectures implement support for internal +# TLS lookup which is used when thread stratum support (via +# libthread_db) is missing or when the linux-only GDB maintenance +# setting 'force-internal-tls-address-lookup' is 'on'. Thus for some +# of the testing scenarios, such as statically linked executables, +# this internal support will be used. Set 'do_kfail_tls_access' to 1 +# for those architectures which don't implement internal tls support. +if {[istarget *-*-linux*] + && ![is_any_target {*}$internal_tls_linux_targets]} { + set do_kfail_tls_access 1 +} elseif {[istarget *-*-linux*] && [is_x86_like_target]} { + # This covers the case of x86_64 with -m32: + set do_kfail_tls_access 1 +} else { + set do_kfail_tls_access 0 +} + +set binprefix $binfile + +with_test_prefix "default" { + set binfile $binprefix-default + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \ + [list debug shlib=${lib1obj} \ + shlib=${lib2obj} \ + shlib=${lib3obj}]] != "" } { + untested "failed to compile" + } else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + # Depending on glibc version, it might not be appropriate + # for do_kfail_tls_access to be set here. That will be + # handled in 'do_tests', disabling it if necessary. + # + # Specifically, glibc versions 2.34 and later have the + # thread library (and libthread_db availability) in + # programs not linked against libpthread.so + do_tests $force_internal_tls $do_kfail_tls_access + } + } +} + +with_test_prefix "pthreads" { + set binfile $binprefix-pthreads + if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \ + [list debug shlib=${lib1obj} \ + shlib=${lib2obj} \ + shlib=${lib3obj}]] != "" } { + untested "failed to compile" + } else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + do_tests $force_internal_tls + } + } +} diff --git a/gdb/testsuite/gdb.base/tls-multiobj1.c b/gdb/testsuite/gdb.base/tls-multiobj1.c new file mode 100644 index 0000000..86e7222 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-multiobj1.c @@ -0,0 +1,26 @@ +/* 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/>. */ + +__thread int tls_lib1_tbss_1; +__thread int tls_lib1_tbss_2; +__thread int tls_lib1_tdata_1 = 196; +__thread int tls_lib1_tdata_2 = 197; + +void +lib1_func () +{ +} diff --git a/gdb/testsuite/gdb.base/tls-multiobj2.c b/gdb/testsuite/gdb.base/tls-multiobj2.c new file mode 100644 index 0000000..cea0709 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-multiobj2.c @@ -0,0 +1,26 @@ +/* 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/>. */ + +__thread int tls_lib2_tbss_1; +__thread int tls_lib2_tbss_2; +__thread int tls_lib2_tdata_1 = 296; +__thread int tls_lib2_tdata_2 = 297; + +void +lib2_func () +{ +} diff --git a/gdb/testsuite/gdb.base/tls-multiobj3.c b/gdb/testsuite/gdb.base/tls-multiobj3.c new file mode 100644 index 0000000..bb0f239 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-multiobj3.c @@ -0,0 +1,26 @@ +/* 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/>. */ + +__thread int tls_lib3_tbss_1; +__thread int tls_lib3_tbss_2; +__thread int tls_lib3_tdata_1 = 396; +__thread int tls_lib3_tdata_2 = 397; + +void +lib3_func () +{ +} diff --git a/gdb/testsuite/gdb.base/tls-nothreads.c b/gdb/testsuite/gdb.base/tls-nothreads.c new file mode 100644 index 0000000..b3aaa33 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-nothreads.c @@ -0,0 +1,57 @@ +/* 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/>. */ + +__thread int tls_tbss_1; +__thread int tls_tbss_2; +__thread int tls_tbss_3; + +__thread int tls_tdata_1 = 21; +__thread int tls_tdata_2 = 22; +__thread int tls_tdata_3 = 23; + +volatile int data; + +void +use_it (int a) +{ + data = a; +} + +int +main (int argc, char **argv) +{ + use_it (-1); + + tls_tbss_1 = 24; /* main-breakpoint-1 */ + tls_tbss_2 = 25; + tls_tbss_3 = 26; + + tls_tdata_1 = 42; + tls_tdata_2 = 43; + tls_tdata_3 = 44; + + use_it (tls_tbss_1); + use_it (tls_tbss_2); + use_it (tls_tbss_3); + use_it (tls_tdata_1); + use_it (tls_tdata_2); + use_it (tls_tdata_3); + + use_it (100); /* main-breakpoint-2 */ + + return 0; +} diff --git a/gdb/testsuite/gdb.base/tls-nothreads.exp b/gdb/testsuite/gdb.base/tls-nothreads.exp new file mode 100644 index 0000000..92a5cd9 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-nothreads.exp @@ -0,0 +1,248 @@ +# 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. + +# Using different compilation/linking scenarios, attempt to access +# thread-local variables in a non-threaded program. Also test that +# GDB internal TLS lookup works correctly. + +source $srcdir/$subdir/tls-common.exp.tcl + +standard_testfile + +proc do_tests {force_internal_tls {do_kfail_tls_access 0}} { + clean_restart $::binfile + if ![runto_main] { + return + } + + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + if { $do_kfail_tls_access && [istarget "*-*-linux*"] } { + # Turn off do_kfail_tls_access when libthread_db is loaded. + # This can happen for the default case when testing x86_64 + # w/ -m32 using glibc versions 2.34 or newer. + gdb_test_multiple "maint check libthread-db" "Check for loaded libthread_db" { + -re -wrap "libthread_db integrity checks passed." { + set do_kfail_tls_access 0 + pass $gdb_test_name + } + -re -wrap "No libthread_db loaded" { + pass $gdb_test_name + } + } + # Also turn off do_kfail_tls_access when connected to a + # gdbserver and we observe that accessing a TLS variable + # works. + if [target_is_gdbserver] { + gdb_test_multiple "print tls_tbss_1" "Check TLS accessibility when connected to a gdbserver" { + -re -wrap "= 0" { + set do_kfail_tls_access 0 + pass $gdb_test_name + } + -re -wrap "Remote target failed to process qGetTLSAddr request" { + pass $gdb_test_name + } + } + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"] + gdb_continue_to_breakpoint "main-breakpoint-1" + + set t $do_kfail_tls_access + set m "tls not available" + with_test_prefix "before assignments" { + gdb_test_with_kfail "print tls_tbss_1" ".* = 0" $t $m + gdb_test_with_kfail "print tls_tbss_2" ".* = 0" $t $m + gdb_test_with_kfail "print tls_tbss_3" ".* = 0" $t $m + + gdb_test_with_kfail "print tls_tdata_1" ".* = 21" $t $m + gdb_test_with_kfail "print tls_tdata_2" ".* = 22" $t $m + gdb_test_with_kfail "print tls_tdata_3" ".* = 23" $t $m + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"] + gdb_continue_to_breakpoint "main-breakpoint-2" + + with_test_prefix "after assignments" { + gdb_test_with_kfail "print tls_tbss_1" ".* = 24" $t $m + gdb_test_with_kfail "print tls_tbss_2" ".* = 25" $t $m + gdb_test_with_kfail "print tls_tbss_3" ".* = 26" $t $m + + gdb_test_with_kfail "print tls_tdata_1" ".* = 42" $t $m + gdb_test_with_kfail "print tls_tdata_2" ".* = 43" $t $m + gdb_test_with_kfail "print tls_tdata_3" ".* = 44" $t $m + } + + # Make a core file now, but save testing using it until the end + # in case core files are not supported. + set corefile ${::binfile}.core + set core_supported 0 + if { ![is_remote host] } { + set core_supported [gdb_gcore_cmd $corefile "save corefile"] + } + + # Now continue to end and see what happens when attempting to + # access a TLS variable when the program is no longer running. + gdb_continue_to_end + with_test_prefix "after exit" { + gdb_test "print tls_tbss_1" \ + "Cannot (?:read|find address of TLS symbol) `tls_tbss_1' without registers" + } + + with_test_prefix "stripped" { + set binfile_stripped "${::binfile}.stripped" + set objcopy [gdb_find_objcopy] + set cmd "$objcopy --strip-debug ${::binfile} $binfile_stripped" + if ![catch "exec $cmd" cmd_output] { + clean_restart $binfile_stripped + if ![runto_main] { + return + } + + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + # While there are no debug (e.g. DWARF) symbols, there + # are minimal symbols, so we should be able to place a + # breakpoint in use_it and continue to it. Continuing + # twice should put us past the assignments, at which point + # we can see if the TLS variables are still accessible. + gdb_test "break use_it" "Breakpoint 2 at $::hex" + gdb_test "continue" "Breakpoint 2, $::hex in use_it.*" + gdb_test "continue" "Breakpoint 2, $::hex in use_it.*" "continue 2" + + # Note that a cast has been added in order to avoid the + # "...has unknown type; cast it to its declared type" + # problem. + gdb_test_with_kfail "print (int) tls_tbss_1" ".* = 24" $t $m + gdb_test_with_kfail "print (int) tls_tbss_2" ".* = 25" $t $m + gdb_test_with_kfail "print (int) tls_tbss_3" ".* = 26" $t $m + + gdb_test_with_kfail "print (int) tls_tdata_1" ".* = 42" $t $m + gdb_test_with_kfail "print (int) tls_tdata_2" ".* = 43" $t $m + gdb_test_with_kfail "print (int) tls_tdata_3" ".* = 44" $t $m + + # Get rid of the "use_it" breakpoint + gdb_test_no_output "del 2" + + # Continue to program exit + gdb_continue_to_end + + # TLS variables should not be accessible after program exit + # (This case initially caused GDB to crash during development + # of GDB-internal TLS lookup support.) + with_test_prefix "after exit" { + gdb_test "print (int) tls_tbss_1" \ + "Cannot find address of TLS symbol `tls_tbss_1' without registers" + } + } + } + + # 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 + } + + with_test_prefix "core file" { + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + gdb_test_with_kfail "print tls_tbss_1" ".* = 24" $t $m + gdb_test_with_kfail "print tls_tbss_2" ".* = 25" $t $m + gdb_test_with_kfail "print tls_tbss_3" ".* = 26" $t $m + + gdb_test_with_kfail "print tls_tdata_1" ".* = 42" $t $m + gdb_test_with_kfail "print tls_tdata_2" ".* = 43" $t $m + gdb_test_with_kfail "print tls_tdata_3" ".* = 44" $t $m + } +} + +# Certain linux target architectures implement support for internal +# TLS lookup which is used when thread stratum support (via +# libthread_db) is missing or when the linux-only GDB maintenance +# setting 'force-internal-tls-address-lookup' is 'on'. Thus for some +# of the testing scenarios, such as statically linked executables, +# this internal support will be used. Set 'do_kfail_tls_access' to 1 +# for those architectures which don't implement internal TLS support. +if {[istarget *-*-linux*] + && ![is_any_target {*}$internal_tls_linux_targets]} { + set do_kfail_tls_access 1 +} elseif {[istarget *-*-linux*] && [is_x86_like_target]} { + # This covers the case of x86_64 with -m32: + set do_kfail_tls_access 1 +} else { + set do_kfail_tls_access 0 +} + +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 { + foreach_with_prefix force_internal_tls $internal_tls_iters { + # Depending on glibc version, it might not be appropriate + # for do_kfail_tls_access to be set here. That will be + # handled in 'do_tests', disabling it if necessary. + # + # Specifically, glibc versions 2.34 and later have the + # thread library (and libthread_db availability) in + # programs not linked against libpthread.so + do_tests $force_internal_tls $do_kfail_tls_access + } + } +} + +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 { + foreach_with_prefix force_internal_tls $internal_tls_iters { + do_tests $force_internal_tls $do_kfail_tls_access + } + } +} + +with_test_prefix "pthreads" { + set binfile $binprefix-pthreads + if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested "failed to compile" + } else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + do_tests $force_internal_tls + } + } +} + +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 { + foreach_with_prefix force_internal_tls $internal_tls_iters { + do_tests $force_internal_tls $do_kfail_tls_access + } + } +} diff --git a/gdb/testsuite/gdb.base/user-namespace-attach.c b/gdb/testsuite/gdb.base/user-namespace-attach.c new file mode 100644 index 0000000..684ce1c --- /dev/null +++ b/gdb/testsuite/gdb.base/user-namespace-attach.c @@ -0,0 +1,35 @@ +/* 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/>. */ + +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> + +volatile int spin_p = 1; + +int +main () +{ + alarm (60); + + printf ("pid = %lld\n", ((long long) getpid ())); + + while (spin_p) + sleep (1); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/user-namespace-attach.exp b/gdb/testsuite/gdb.base/user-namespace-attach.exp new file mode 100644 index 0000000..741093c --- /dev/null +++ b/gdb/testsuite/gdb.base/user-namespace-attach.exp @@ -0,0 +1,148 @@ +# 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/>. + +# Check that GDB can attach to a process started using 'unshare'. The +# inferior is started in a separate mnt namespace. + +require can_spawn_for_attach + +standard_testfile + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile] == -1} { + return +} + +# This test relies (at least in some parts) on the sysroot being +# 'target:'. Grab the current sysroot now so we can skip those tests +# if the board file has changed the sysroot. +set sysroot "" +set test "show sysroot" +gdb_test_multiple $test $test { + -re -wrap "The current system root is \"(.*)\"\\." { + set sysroot $expect_out(1,string) + } +} + +# Start a process using 'unshare FLAGS', then attach to the process +# from GDB. Check that the attach worked as expected. +proc run_test { flags } { + + # If FLAGS contains '--mount' then a separate mnt namespace will + # be created, in which case the executable will have been read + # from the 'target:'. Otherwise, the executable will have been + # read from the local filesystem, and there will be no prefix. + # + # Of course, this only applies if the sysroot is 'target:', some + # boards change this, so skip these tests on those boards. + if { [lsearch -exact [split $flags " "] "--mount"] != -1 } { + if { $::sysroot ne "target:" } { + return + } + + set prefix "target:" + } else { + set prefix "" + } + + set unshare_cmd "unshare $flags" + + # Run '/bin/true' using UNSHARE_CMD. If the flags in UNSHARE_CMD + # aren't supported then this will fail, this means we shouldn't + # spawn the command with our test executable and try attaching. + # + # This will also fail if /bin/true isn't present, or doesn't work + # as we expect. But this should be fine for many targets. + set res [remote_exec target "$unshare_cmd /bin/true"] + if { [lindex $res 0] != 0 } { + unsupported "unshare flags not supported" + return + } + + set inferior_spawn_id \ + [spawn_wait_for_attach [list "$unshare_cmd $::binfile"]] + if { $inferior_spawn_id == -1 } { + unsupported "failed to spawn for attach" + return + } + + set inferior_pid [spawn_id_get_pid $inferior_spawn_id] + + clean_restart + + set saw_bad_warning false + gdb_test_multiple "attach $inferior_pid" "attach to inferior" { + -re "^attach $::decimal\r\n" { + exp_continue + } + + -re "^warning: \[^\r\n\]+: could not open as an executable file: \[^\r\n\]+\r\n" { + set saw_bad_warning true + exp_continue + } + + -re "^warning: \[^\r\n\]+: can't open to read symbols: \[^\r\n\]+\r\n" { + set saw_bad_warning true + exp_continue + } + + -re "^warning: Could not load vsyscall page because no executable was specified\r\n" { + # This warning is a secondary consequence of the above bad + # warnings, so don't count this as a bad warnings, ignore + # it instead. + exp_continue + } + + -re "^warning:\\s+$::decimal\\s*\[^\r\n\]+: No such file or directory\r\n" { + # This unrelated warning is seen when GDB stops in libc, + # and the source code for libc is not available. + exp_continue + } + + -re "^warning: \[^\r\n\]+\r\n" { + # If we ignore "other" warnings then, should the above + # warnings strings change we'll start ignoring the bad + # warnings, and the test will appear to pass. + # + # If you are seeing a warning here that really has nothing + # to do with the test failing, then the correct solution + # is to add a new regexp to specifically match _that_ + # warning, and ignore it. + set saw_bad_warning true + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { !$saw_bad_warning } $gdb_test_name + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } + + # Ensure GDB could access the executable. + set binfile_re [string_to_regexp $::binfile] + gdb_test "info inferiors" \ + "\r\n\\*\\s+$::decimal\\s+\[^\r\n\]+\\s+${prefix}${binfile_re}\\s*" +} + +set test_flags [list \ + "--mount --map-root-user" \ + "--user" \ + "--user --map-root-user"] + +foreach_with_prefix flags $test_flags { + run_test $flags +} diff --git a/gdb/testsuite/gdb.base/vfork-follow-parent.exp b/gdb/testsuite/gdb.base/vfork-follow-parent.exp index fca2993..8cb785d 100644 --- a/gdb/testsuite/gdb.base/vfork-follow-parent.exp +++ b/gdb/testsuite/gdb.base/vfork-follow-parent.exp @@ -19,6 +19,8 @@ # schedule-multiple on" or "set detach-on-fork on". Test these two resolution # methods. +require allow_fork_tests + standard_testfile .c vforked-prog.c set binfile ${testfile}-exit diff --git a/gdb/testsuite/gdb.base/watch-before-fork.exp b/gdb/testsuite/gdb.base/watch-before-fork.exp index 074cfbd..509561e 100644 --- a/gdb/testsuite/gdb.base/watch-before-fork.exp +++ b/gdb/testsuite/gdb.base/watch-before-fork.exp @@ -20,6 +20,8 @@ # This test uses "awatch". require allow_hw_watchpoint_access_tests +require allow_fork_tests + standard_testfile if {[build_executable "failed to prepare" $testfile $srcfile debug]} { diff --git a/gdb/testsuite/gdb.base/watch-vfork.exp b/gdb/testsuite/gdb.base/watch-vfork.exp index 1bc61bc..503727d 100644 --- a/gdb/testsuite/gdb.base/watch-vfork.exp +++ b/gdb/testsuite/gdb.base/watch-vfork.exp @@ -17,6 +17,8 @@ standard_testfile .c +require allow_fork_tests + if { [build_executable ${testfile}.exp ${testfile} $srcfile {debug}] } { untested "failed to compile" return -1 diff --git a/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp b/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp index b3892f3..fa63edb 100644 --- a/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp +++ b/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp @@ -26,29 +26,18 @@ if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} { return -1 } -if ![runto_main] { - return -1 -} +set test_spawn_id [spawn_wait_for_attach $binfile] +set testpid [spawn_id_get_pid $test_spawn_id] -# Run to the point where mypid in the test program has been -# populated. -gdb_breakpoint [gdb_get_line_number "pidacquired"] -gdb_continue_to_breakpoint "pidacquired" - -# Get the PID of the test process. -set testpid [get_integer_valueof "mypid" 0] +gdb_test "attach $testpid" "Attaching to program: .*, process $testpid.*" "attach once" gdb_test "detach" "Detaching from program: .*, process $testpid\r\n\\\[Inferior $decimal \\(process $testpid\\) detached\\\]" -if {$testpid == ""} { - return -1 -} - # A clean restart is needed to force the hardware watchpoint setup # logic to run post attach rather than post inferior launch. clean_restart $binfile -gdb_test "attach $testpid" "Attaching to program: .*, process $testpid.*" "attach" +gdb_test "attach $testpid" "Attaching to program: .*, process $testpid.*" "attach twice" # Ensure the test program is in the top frame so the required # variables are in scope. @@ -62,3 +51,5 @@ gdb_test "watch watched_variable" \ gdb_test "continue" \ "continue.*Continuing.*\.Hardware watchpoint $decimal: watched_variable.*Old value = 0.*New value = 4.*watched_variable\\);" + +kill_wait_spawned_process $test_spawn_id |