summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Olbrich <m.olbrich@pengutronix.de>2023-11-11 08:58:25 +0100
committerMichael Olbrich <m.olbrich@pengutronix.de>2023-11-18 09:24:01 +0100
commit5574a85f3308103bd4094cb023644d3c5e0e9d6b (patch)
treedd15eef2059af95cc99bb18ba09042922d9c74e5
parentcdec74aad340baa618137dc6f449ae21e7deeaf1 (diff)
downloadOSELAS.Toolchain-5574a85f3308103bd4094cb023644d3c5e0e9d6b.tar.gz
OSELAS.Toolchain-5574a85f3308103bd4094cb023644d3c5e0e9d6b.tar.xz
cross-gdb: add stable fixes
Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
-rw-r--r--patches/gdb-12.1/0001-Fix-core-file-detach-crash-corefiles-29275.patch162
-rw-r--r--patches/gdb-12.1/0002-gdb-fix-assert-when-quitting-GDB-while-a-thread-is-s.patch104
-rw-r--r--patches/gdb-12.1/0003-gdb-disable-commit-resumed-in-target_kill.patch275
-rw-r--r--patches/gdb-12.1/series7
4 files changed, 548 insertions, 0 deletions
diff --git a/patches/gdb-12.1/0001-Fix-core-file-detach-crash-corefiles-29275.patch b/patches/gdb-12.1/0001-Fix-core-file-detach-crash-corefiles-29275.patch
new file mode 100644
index 0000000..f3311b2
--- /dev/null
+++ b/patches/gdb-12.1/0001-Fix-core-file-detach-crash-corefiles-29275.patch
@@ -0,0 +1,162 @@
+From: Pedro Alves <pedro@palves.net>
+Date: Wed, 22 Jun 2022 18:44:37 +0100
+Subject: [PATCH] Fix core-file -> detach -> crash (corefiles/29275)
+
+After loading a core file, you're supposed to be able to use "detach"
+to unload the core file. That unfortunately regressed starting with
+GDB 11, with these commits:
+
+ 1192f124a308 - gdb: generalize commit_resume, avoid commit-resuming when threads have pending statuses
+ 408f66864a1a - detach in all-stop with threads running
+
+resulting in a GDB crash:
+
+ ...
+ Thread 1 "gdb" received signal SIGSEGV, Segmentation fault.
+ 0x0000555555e842bf in maybe_set_commit_resumed_all_targets () at ../../src/gdb/infrun.c:2899
+ 2899 if (proc_target->commit_resumed_state)
+ (top-gdb) bt
+ #0 0x0000555555e842bf in maybe_set_commit_resumed_all_targets () at ../../src/gdb/infrun.c:2899
+ #1 0x0000555555e848bf in scoped_disable_commit_resumed::reset (this=0x7fffffffd440) at ../../src/gdb/infrun.c:3023
+ #2 0x0000555555e84a0c in scoped_disable_commit_resumed::reset_and_commit (this=0x7fffffffd440) at ../../src/gdb/infrun.c:3049
+ #3 0x0000555555e739cd in detach_command (args=0x0, from_tty=1) at ../../src/gdb/infcmd.c:2791
+ #4 0x0000555555c0ba46 in do_simple_func (args=0x0, from_tty=1, c=0x55555662a600) at ../../src/gdb/cli/cli-decode.c:95
+ #5 0x0000555555c112b0 in cmd_func (cmd=0x55555662a600, args=0x0, from_tty=1) at ../../src/gdb/cli/cli-decode.c:2514
+ #6 0x0000555556173b1f in execute_command (p=0x5555565c5916 "", from_tty=1) at ../../src/gdb/top.c:699
+
+The code that crashes looks like:
+
+ static void
+ maybe_set_commit_resumed_all_targets ()
+ {
+ scoped_restore_current_thread restore_thread;
+
+ for (inferior *inf : all_non_exited_inferiors ())
+ {
+ process_stratum_target *proc_target = inf->process_target ();
+
+ if (proc_target->commit_resumed_state)
+ ^^^^^^^^^^^
+
+With 'proc_target' above being null. all_non_exited_inferiors filters
+out inferiors that have pid==0. We get here at the end of
+detach_command, after core_target::detach has already run, at which
+point the inferior _should_ have pid==0 and no process target. It is
+clear it no longer has a process target, but, it still has a pid!=0
+somehow.
+
+The reason the inferior still has pid!=0, is that core_target::detach
+just unpushes, and relies on core_target::close to actually do the
+getting rid of the core and exiting the inferior. The problem with
+that is that detach_command grabs an extra strong reference to the
+process stratum target, so the unpush_target inside
+core_target::detach doesn't actually result in a call to
+core_target::close.
+
+Fix this my moving the cleaning up the core inferior to a shared
+routine called by both core_target::close and core_target::detach. We
+still need to cleanup the inferior from within core_file::close
+because there are paths to it that want to get rid of the core without
+going through detach. E.g., "core-file" -> "run".
+
+This commit includes a new test added to gdb.base/corefile.exp to
+cover the "core-file core" -> "detach" scenario.
+
+Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29275
+
+Change-Id: Ic42bdd03182166b19f598428b0dbc2ce6f67c893
+---
+ gdb/corelow.c | 27 +++++++++++++++++++++------
+ gdb/testsuite/gdb.base/corefile.exp | 12 ++++++++++++
+ 2 files changed, 33 insertions(+), 6 deletions(-)
+
+diff --git a/gdb/corelow.c b/gdb/corelow.c
+index 001c4f147fc4..bdd7ddc59c28 100644
+--- a/gdb/corelow.c
++++ b/gdb/corelow.c
+@@ -120,6 +120,9 @@ class core_target final : public process_stratum_target
+
+ private: /* per-core data */
+
++ /* Get rid of the core inferior. */
++ void clear_core ();
++
+ /* The core's section table. Note that these target sections are
+ *not* mapped in the current address spaces' set of target
+ sections --- those should come only from pure executable or
+@@ -290,10 +293,8 @@ core_target::build_file_mappings ()
+ /* An arbitrary identifier for the core inferior. */
+ #define CORELOW_PID 1
+
+-/* Close the core target. */
+-
+ void
+-core_target::close ()
++core_target::clear_core ()
+ {
+ if (core_bfd)
+ {
+@@ -307,6 +308,14 @@ core_target::close ()
+
+ current_program_space->cbfd.reset (nullptr);
+ }
++}
++
++/* Close the core target. */
++
++void
++core_target::close ()
++{
++ clear_core ();
+
+ /* Core targets are heap-allocated (see core_target_open), so here
+ we delete ourselves. */
+@@ -592,9 +601,15 @@ core_target_open (const char *arg, int from_tty)
+ void
+ core_target::detach (inferior *inf, int from_tty)
+ {
+- /* Note that 'this' is dangling after this call. unpush_target
+- closes the target, and our close implementation deletes
+- 'this'. */
++ /* Get rid of the core. Don't rely on core_target::close doing it,
++ because target_detach may be called with core_target's refcount > 1,
++ meaning core_target::close may not be called yet by the
++ unpush_target call below. */
++ clear_core ();
++
++ /* Note that 'this' may be dangling after this call. unpush_target
++ closes the target if the refcount reaches 0, and our close
++ implementation deletes 'this'. */
+ inf->unpush_target (this);
+
+ /* Clear the register cache and the frame cache. */
+diff --git a/gdb/testsuite/gdb.base/corefile.exp b/gdb/testsuite/gdb.base/corefile.exp
+index 4ed92a029554..7f3d2efe3a2c 100644
+--- a/gdb/testsuite/gdb.base/corefile.exp
++++ b/gdb/testsuite/gdb.base/corefile.exp
+@@ -207,6 +207,16 @@ gdb_test "up" "#\[0-9\]* *\[0-9xa-fH'\]* in .* \\(.*\\).*" "up in corefile.exp (
+
+ gdb_test "core" "No core file now."
+
++# Test that we can unload the core with the "detach" command.
++
++proc_with_prefix corefile_detach {} {
++ clean_restart $::binfile
++
++ gdb_test "core-file $::corefile" "Core was generated by .*" "load core"
++ gdb_test "detach" "No core file now\\." "detach core"
++}
++
++corefile_detach
+
+ # Test a run (start) command will clear any loaded core file.
+
+@@ -222,6 +232,8 @@ proc corefile_test_run {} {
+ return
+ }
+
++ clean_restart $::binfile
++
+ gdb_test "core-file $corefile" "Core was generated by .*" "run: load core again"
+ gdb_test "info files" "\r\nLocal core dump file:\r\n.*" "run: sanity check we see the core file"
+
diff --git a/patches/gdb-12.1/0002-gdb-fix-assert-when-quitting-GDB-while-a-thread-is-s.patch b/patches/gdb-12.1/0002-gdb-fix-assert-when-quitting-GDB-while-a-thread-is-s.patch
new file mode 100644
index 0000000..333e0a8
--- /dev/null
+++ b/patches/gdb-12.1/0002-gdb-fix-assert-when-quitting-GDB-while-a-thread-is-s.patch
@@ -0,0 +1,104 @@
+From: Andrew Burgess <aburgess@redhat.com>
+Date: Mon, 21 Nov 2022 12:12:11 -0500
+Subject: [PATCH] gdb: fix assert when quitting GDB while a thread is stepping
+
+This commit addresses one of the issues identified in PR gdb/28275.
+
+Bug gdb/28275 identifies a number of situations in which this assert:
+
+ Assertion `!proc_target->commit_resumed_state' failed.
+
+could be triggered. There's actually a number of similar places where
+this assert is found in GDB, the two of interest in gdb/28275 are in
+target_wait and target_stop.
+
+In one of the comments:
+
+ https://sourceware.org/bugzilla/show_bug.cgi?id=28275#c1
+
+steps to trigger the assertion within target_stop were identified when
+using a modified version of the gdb.threads/detach-step-over.exp test
+script.
+
+In the gdb.threads/detach-step-over.exp test, we attach to a
+multi-threaded inferior, and continue the inferior in asynchronous
+(background) mode. Each thread is continuously hitting a conditional
+breakpoint where the condition is always false. While the inferior is
+running we detach. The goal is that we detach while GDB is performing a
+step-over for the conditional breakpoint in at least one thread.
+
+While detaching, if a step-over is in progress, then GDB has to
+complete the step over before we can detach. This involves calling
+target_stop and target_wait (see prepare_for_detach).
+
+As far as gdb/28275 is concerned, the interesting part here, is the
+the process_stratum_target::commit_resumed_state variable must be
+false when target_stop and target_wait are called.
+
+This is currently ensured because, in detach_command (infrun.c), we
+create an instance of scoped_disable_commit_resumed, this ensures that
+when target_detach is called, ::commit_resumed_state will be false.
+
+The modification to the test that I propose here, and which exposed
+the bug, is that, instead of using "detach" to detach from the
+inferior, we instead use "quit". Quitting GDB after attaching to an
+inferior will cause GDB to first detach, and then exit.
+
+When we quit GDB we end up calling target_detach via a different code
+path, the stack looks like:
+
+ #0 target_detach
+ #1 kill_or_detach
+ #2 quit_force
+ #3 quit_command
+
+Along this path there is no scoped_disable_commit_resumed created.
+::commit_resumed_state can be true when we reach prepare_for_detach,
+which calls target_wait and target_stop, so the assertion will trigger.
+
+In this commit, I propose fixing this by adding the creation of a
+scoped_disable_commit_resumed into target_detach. This will ensure
+that ::commit_resumed_state is false when calling prepare_for_detach
+from within target_detach.
+
+I did consider placing the scoped_disable_commit_resumed in
+prepare_for_detach, however, placing it in target_detach ensures that
+the target's commit_resumed_state flag is left to false if the detached
+inferior was the last one for that target. It's the same rationale as
+for patch "gdb: disable commit resumed in target_kill" that comes later
+in this series, but for detach instead of kill.
+
+detach_command still includes a scoped_disable_commit_resumed too, but I
+think it is still relevant to cover the resumption at the end of the
+function.
+
+Co-Authored-By: Simon Marchi <simon.marchi@efficios.com>
+Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28275
+Change-Id: Ie128f7aba6ef0e018859275eca372e6ea738e96f
+---
+ gdb/target.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/gdb/target.c b/gdb/target.c
+index 1ee051b520a5..0c86b571e1cb 100644
+--- a/gdb/target.c
++++ b/gdb/target.c
+@@ -2558,6 +2558,9 @@ target_preopen (int from_tty)
+ void
+ target_detach (inferior *inf, int from_tty)
+ {
++ /* Thread's don't need to be resumed until the end of this function. */
++ scoped_disable_commit_resumed disable_commit_resumed ("detaching");
++
+ /* After we have detached, we will clear the register cache for this inferior
+ by calling registers_changed_ptid. We must save the pid_ptid before
+ detaching, as the target detach method will clear inf->pid. */
+@@ -2588,6 +2591,8 @@ target_detach (inferior *inf, int from_tty)
+ inferior_ptid matches save_pid_ptid, but in our case, it does not
+ call it, as inferior_ptid has been reset. */
+ reinit_frame_cache ();
++
++ disable_commit_resumed.reset_and_commit ();
+ }
+
+ void
diff --git a/patches/gdb-12.1/0003-gdb-disable-commit-resumed-in-target_kill.patch b/patches/gdb-12.1/0003-gdb-disable-commit-resumed-in-target_kill.patch
new file mode 100644
index 0000000..7f22362
--- /dev/null
+++ b/patches/gdb-12.1/0003-gdb-disable-commit-resumed-in-target_kill.patch
@@ -0,0 +1,275 @@
+From: Simon Marchi <simon.marchi@efficios.com>
+Date: Mon, 21 Nov 2022 12:12:13 -0500
+Subject: [PATCH] gdb: disable commit resumed in target_kill
+
+New in this version:
+
+ - Better comment in target_kill
+ - Uncomment line in test to avoid hanging when exiting, when testing on
+ native-extended-gdbserver
+
+PR 28275 shows that doing a sequence of:
+
+ - Run inferior in background (run &)
+ - kill that inferior
+ - Run again
+
+We get into this assertion:
+
+ /home/smarchi/src/binutils-gdb/gdb/target.c:2590: internal-error: target_wait: Assertion `!proc_target->commit_resumed_state' failed.
+
+ #0 internal_error_loc (file=0x5606b344e740 "/home/smarchi/src/binutils-gdb/gdb/target.c", line=2590, fmt=0x5606b344d6a0 "%s: Assertion `%s' failed.") at /home/smarchi/src/binutils-gdb/gdbsupport/errors.cc:54
+ #1 0x00005606b6296475 in target_wait (ptid=..., status=0x7fffb9390630, options=...) at /home/smarchi/src/binutils-gdb/gdb/target.c:2590
+ #2 0x00005606b5767a98 in startup_inferior (proc_target=0x5606bfccb2a0 <the_amd64_linux_nat_target>, pid=3884857, ntraps=1, last_waitstatus=0x0, last_ptid=0x0) at /home/smarchi/src/binutils-gdb/gdb/nat/fork-inferior.c:482
+ #3 0x00005606b4e6c9c5 in gdb_startup_inferior (pid=3884857, num_traps=1) at /home/smarchi/src/binutils-gdb/gdb/fork-child.c:132
+ #4 0x00005606b50f14a5 in inf_ptrace_target::create_inferior (this=0x5606bfccb2a0 <the_amd64_linux_nat_target>, exec_file=0x604000039f50 "/home/smarchi/build/binutils-gdb/gdb/test", allargs="", env=0x61500000a580, from_tty=1)
+ at /home/smarchi/src/binutils-gdb/gdb/inf-ptrace.c:105
+ #5 0x00005606b53b6d23 in linux_nat_target::create_inferior (this=0x5606bfccb2a0 <the_amd64_linux_nat_target>, exec_file=0x604000039f50 "/home/smarchi/build/binutils-gdb/gdb/test", allargs="", env=0x61500000a580, from_tty=1)
+ at /home/smarchi/src/binutils-gdb/gdb/linux-nat.c:978
+ #6 0x00005606b512b79b in run_command_1 (args=0x0, from_tty=1, run_how=RUN_NORMAL) at /home/smarchi/src/binutils-gdb/gdb/infcmd.c:468
+ #7 0x00005606b512c236 in run_command (args=0x0, from_tty=1) at /home/smarchi/src/binutils-gdb/gdb/infcmd.c:526
+
+When running the kill command, commit_resumed_state for the
+process_stratum_target (linux-nat, here) is true. After the kill, when
+there are no more threads, commit_resumed_state is still true, as
+nothing touches this flag during the kill operation. During the
+subsequent run command, run_command_1 does:
+
+ scoped_disable_commit_resumed disable_commit_resumed ("running");
+
+We would think that this would clear the commit_resumed_state flag of
+our native target, but that's not the case, because
+scoped_disable_commit_resumed iterates on non-exited inferiors in order
+to find active process targets. And after the kill, the inferior is
+exited, and the native target was unpushed from it anyway. So
+scoped_disable_commit_resumed doesn't touch the commit_resumed_state
+flag of the native target, it stays true. When reaching target_wait, in
+startup_inferior (to consume the initial expect stop events while the
+inferior is starting up and working its way through the shell),
+commit_resumed_state is true, breaking the contract saying that
+commit_resumed_state is always false when calling the targets' wait
+method.
+
+(note: to be correct, I think that startup_inferior should toggle
+commit_resumed between the target_wait and target_resume calls, but I'll
+ignore that for now)
+
+I can see multiple ways to fix this. In the end, we need
+commit_resumed_state to be cleared by the time we get to that
+target_wait. It could be done at the end of the kill command, or at the
+beginning of the run command.
+
+To keep things in a coherent state, I'd like to make it so that after
+the kill command, when the target is left with no threads, its
+commit_resumed_state flag is left to false. This way, we can keep
+working with the assumption that a target with no threads (and therefore
+no running threads) has commit_resumed_state == false.
+
+Do this by adding a scoped_disable_commit_resumed in target_kill. It
+clears the target's commit_resumed_state on entry, and leaves it false
+if the target does not have any resumed thread on exit. That means,
+even if the target has another inferior with stopped threads,
+commit_resumed_state will be left to false, which makes sense.
+
+Add a test that tries to cover various combinations of actions done
+while an inferior is running (and therefore while commit_resumed_state
+is true on the process target).
+
+Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28275
+Change-Id: I8e6fe6dc1f475055921520e58cab68024039a1e9
+Approved-By: Andrew Burgess <aburgess@redhat.com>
+---
+ gdb/target.c | 9 ++
+ .../gdb.base/run-control-while-bg-execution.c | 33 ++++++
+ .../gdb.base/run-control-while-bg-execution.exp | 122 +++++++++++++++++++++
+ 3 files changed, 164 insertions(+)
+ create mode 100644 gdb/testsuite/gdb.base/run-control-while-bg-execution.c
+ create mode 100644 gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
+
+diff --git a/gdb/target.c b/gdb/target.c
+index 0c86b571e1cb..0eae5307785d 100644
+--- a/gdb/target.c
++++ b/gdb/target.c
+@@ -908,6 +908,15 @@ add_deprecated_target_alias (const target_info &tinfo, const char *alias)
+ void
+ target_kill (void)
+ {
++
++ /* If the commit_resume_state of the to-be-killed-inferior's process stratum
++ is true, and this inferior is the last live inferior with resumed threads
++ of that target, then we want to leave commit_resume_state to false, as the
++ target won't have any resumed threads anymore. We achieve this with
++ this scoped_disable_commit_resumed. On construction, it will set the flag
++ to false. On destruction, it will only set it to true if there are resumed
++ threads left. */
++ scoped_disable_commit_resumed disable ("killing");
+ current_inferior ()->top_target ()->kill ();
+ }
+
+diff --git a/gdb/testsuite/gdb.base/run-control-while-bg-execution.c b/gdb/testsuite/gdb.base/run-control-while-bg-execution.c
+new file mode 100644
+index 000000000000..8092fadc8b96
+--- /dev/null
++++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.c
+@@ -0,0 +1,33 @@
++/* This testcase is part of GDB, the GNU debugger.
++
++ Copyright 2020-2022 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>
++
++static pid_t mypid = -1;
++
++static void
++after_getpid (void)
++{
++}
++
++int
++main (void)
++{
++ mypid = getpid ();
++ after_getpid ();
++ sleep (30);
++}
+diff --git a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
+new file mode 100644
+index 000000000000..5b4834f0b324
+--- /dev/null
++++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
+@@ -0,0 +1,122 @@
++# Copyright 2022 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 aims at testing various operations after getting rid of an inferior
++# that was running in background, or while we have an inferior running in
++# background. The original intent was to expose cases where the commit-resumed
++# state of the process stratum target was not reset properly after killing an
++# inferior running in background, which would be a problem when trying to run
++# again. The test was expanded to test various combinations of
++# run-control-related actions done with an inferior running in background.
++
++if {[use_gdb_stub]} {
++ unsupported "test requires running"
++ return
++}
++
++standard_testfile
++
++if {[build_executable "failed to prepare" $testfile $srcfile]} {
++ return
++}
++
++# Run one variation of the test:
++#
++# 1. Start an inferior in the background with "run &"
++# 2. Do action 1
++# 3. Do action 2
++#
++# Action 1 indicates what to do with the inferior running in background:
++#
++# - kill: kill it
++# - detach: detach it
++# - add: add a new inferior and switch to it, leave the inferior running in
++# background alone
++# - none: do nothing, leave the inferior running in background alone
++#
++# Action 2 indicates what to do after that:
++#
++# - start: use the start command
++# - run: use the run command
++# - attach: start a process outside of GDB and attach it
++proc do_test { action1 action2 } {
++ save_vars { ::GDBFLAGS } {
++ append ::GDBFLAGS " -ex \"maintenance set target-non-stop on\""
++ clean_restart $::binfile
++ }
++
++ # Ensure we are at least after the getpid call, should we need it.
++ if { ![runto "after_getpid"] } {
++ return
++ }
++
++ # Some commands below ask for confirmation. Turn that off for simplicity.
++ gdb_test "set confirm off"
++ gdb_test_multiple "continue &" "" {
++ -re ".*\r\n$::gdb_prompt " {
++ pass $gdb_test_name
++ }
++ }
++
++ if { $action1 == "kill" } {
++ gdb_test "kill" "Inferior 1 .* killed.*"
++ } elseif { $action1 == "detach" } {
++ set child_pid [get_integer_valueof "mypid" -1]
++ if { $child_pid == -1 } {
++ fail "failed to extract child pid"
++ return
++ }
++
++ gdb_test "detach" "Inferior 1 .* detached.*" "detach from first instance"
++
++ # Kill the detached process, to avoid hanging when exiting GDBserver,
++ # when testing with the native-extended-gdbserver board.
++ remote_exec target "kill $child_pid"
++ } elseif { $action1 == "add" } {
++ gdb_test "add-inferior -exec $::binfile" \
++ "Added inferior 2 on connection 1.*" "add-inferior"
++ gdb_test "inferior 2" "Switching to inferior 2 .*"
++ } elseif { $action1 == "none" } {
++
++ } else {
++ error "invalid action 1"
++ }
++
++ if { $action2 == "start" } {
++ gdb_test "start" "Temporary breakpoint $::decimal\(?:\.$::decimal\)?, main .*"
++ } elseif { $action2 == "run" } {
++ gdb_test "break main" "Breakpoint $::decimal at $::hex.*"
++ gdb_test "run" "Breakpoint $::decimal\(?:\.$::decimal\)?, main .*"
++ } elseif { $action2 == "attach" } {
++ set test_spawn_id [spawn_wait_for_attach $::binfile]
++ set test_pid [spawn_id_get_pid $test_spawn_id]
++
++ if { [gdb_attach $test_pid] } {
++ gdb_test "detach" "Inferior $::decimal .* detached.*" \
++ "detach from second instance"
++ }
++
++ # Detach and kill this inferior so we don't leave it around.
++ kill_wait_spawned_process $test_spawn_id
++ } else {
++ error "invalid action 2"
++ }
++}
++
++foreach_with_prefix action1 { kill detach add none } {
++ foreach_with_prefix action2 { start run attach } {
++ do_test $action1 $action2
++ }
++}
diff --git a/patches/gdb-12.1/series b/patches/gdb-12.1/series
new file mode 100644
index 0000000..f2205c9
--- /dev/null
+++ b/patches/gdb-12.1/series
@@ -0,0 +1,7 @@
+# generated by git-ptx-patches
+#tag:base --start-number 1
+#tag:upstream --start-number 1
+0001-Fix-core-file-detach-crash-corefiles-29275.patch
+0002-gdb-fix-assert-when-quitting-GDB-while-a-thread-is-s.patch
+0003-gdb-disable-commit-resumed-in-target_kill.patch
+# e369ec6e6c6782c8d27cefebdbf58c7b - git-ptx-patches magic