aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.base
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/testsuite/gdb.base')
-rw-r--r--gdb/testsuite/gdb.base/attach-deleted-exec.exp44
-rw-r--r--gdb/testsuite/gdb.base/bg-execution-repeat.c2
-rw-r--r--gdb/testsuite/gdb.base/bg-execution-repeat.exp11
-rw-r--r--gdb/testsuite/gdb.base/bp-cond-failure.exp4
-rw-r--r--gdb/testsuite/gdb.base/bp-permanent.c2
-rw-r--r--gdb/testsuite/gdb.base/bp-permanent.exp4
-rw-r--r--gdb/testsuite/gdb.base/break-dbg.cc31
-rw-r--r--gdb/testsuite/gdb.base/break-dbg.exp70
-rw-r--r--gdb/testsuite/gdb.base/break1.c8
-rw-r--r--gdb/testsuite/gdb.base/catch-fork-kill.exp2
-rw-r--r--gdb/testsuite/gdb.base/catch-fork-static.exp4
-rw-r--r--gdb/testsuite/gdb.base/catch-signal-fork.exp1
-rw-r--r--gdb/testsuite/gdb.base/coredump-filter-build-id.exp2
-rw-r--r--gdb/testsuite/gdb.base/corefile-shmem-zero-id-lib.c522
-rw-r--r--gdb/testsuite/gdb.base/corefile-shmem-zero-id.c63
-rw-r--r--gdb/testsuite/gdb.base/corefile-shmem-zero-id.exp228
-rw-r--r--gdb/testsuite/gdb.base/default.exp209
-rw-r--r--gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c6
-rw-r--r--gdb/testsuite/gdb.base/dlmopen-ns-ids.exp145
-rw-r--r--gdb/testsuite/gdb.base/exprs.exp18
-rw-r--r--gdb/testsuite/gdb.base/filename-completion.exp27
-rw-r--r--gdb/testsuite/gdb.base/foll-exec-c++.exp24
-rw-r--r--gdb/testsuite/gdb.base/foll-exec-c.exp23
-rw-r--r--gdb/testsuite/gdb.base/foll-exec.c33
-rw-r--r--gdb/testsuite/gdb.base/foll-exec.exp.tcl (renamed from gdb/testsuite/gdb.base/foll-exec.exp)90
-rw-r--r--gdb/testsuite/gdb.base/foll-fork-syscall.c35
-rw-r--r--gdb/testsuite/gdb.base/foll-fork-syscall.exp143
-rw-r--r--gdb/testsuite/gdb.base/foll-fork.exp2
-rw-r--r--gdb/testsuite/gdb.base/foll-vfork.exp7
-rw-r--r--gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp1
-rw-r--r--gdb/testsuite/gdb.base/fork-print-inferior-events.exp2
-rw-r--r--gdb/testsuite/gdb.base/fork-running-state.exp2
-rw-r--r--gdb/testsuite/gdb.base/gcore.exp1
-rw-r--r--gdb/testsuite/gdb.base/infcall-failure.exp16
-rw-r--r--gdb/testsuite/gdb.base/inferior-died.exp5
-rw-r--r--gdb/testsuite/gdb.base/info-shared.exp3
-rw-r--r--gdb/testsuite/gdb.base/interrupt-daemon.exp2
-rw-r--r--gdb/testsuite/gdb.base/jit-bfd-name.exp6
-rw-r--r--gdb/testsuite/gdb.base/jit-elf-fork.exp1
-rw-r--r--gdb/testsuite/gdb.base/jit-elf-solib.c2
-rw-r--r--gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp1
-rw-r--r--gdb/testsuite/gdb.base/maint.exp43
-rw-r--r--gdb/testsuite/gdb.base/multi-forks.exp6
-rw-r--r--gdb/testsuite/gdb.base/options.exp19
-rw-r--r--gdb/testsuite/gdb.base/pie-fork.exp2
-rw-r--r--gdb/testsuite/gdb.base/print-symbol-loading.exp2
-rw-r--r--gdb/testsuite/gdb.base/ptype.exp8
-rw-r--r--gdb/testsuite/gdb.base/run-control-while-bg-execution.exp5
-rw-r--r--gdb/testsuite/gdb.base/sigall.exp9
-rw-r--r--gdb/testsuite/gdb.base/solib-search.exp2
-rw-r--r--gdb/testsuite/gdb.base/step-over-exit.exp7
-rw-r--r--gdb/testsuite/gdb.base/style.exp289
-rw-r--r--gdb/testsuite/gdb.base/tls-common.exp.tcl50
-rw-r--r--gdb/testsuite/gdb.base/tls-dlobj-lib.c87
-rw-r--r--gdb/testsuite/gdb.base/tls-dlobj.c311
-rw-r--r--gdb/testsuite/gdb.base/tls-dlobj.exp378
-rw-r--r--gdb/testsuite/gdb.base/tls-multiobj.c89
-rw-r--r--gdb/testsuite/gdb.base/tls-multiobj.exp230
-rw-r--r--gdb/testsuite/gdb.base/tls-multiobj1.c26
-rw-r--r--gdb/testsuite/gdb.base/tls-multiobj2.c26
-rw-r--r--gdb/testsuite/gdb.base/tls-multiobj3.c26
-rw-r--r--gdb/testsuite/gdb.base/tls-nothreads.c57
-rw-r--r--gdb/testsuite/gdb.base/tls-nothreads.exp248
-rw-r--r--gdb/testsuite/gdb.base/user-namespace-attach.c35
-rw-r--r--gdb/testsuite/gdb.base/user-namespace-attach.exp148
-rw-r--r--gdb/testsuite/gdb.base/vfork-follow-parent.exp2
-rw-r--r--gdb/testsuite/gdb.base/watch-before-fork.exp2
-rw-r--r--gdb/testsuite/gdb.base/watch-vfork.exp2
-rw-r--r--gdb/testsuite/gdb.base/watchpoint-hw-attach.exp21
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