mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2026-08-27 00:26:02 -04:00
gdb: limit updating the address spaces to the current target
For the `update_address_spaces` function, there is this comment: It is assumed that there are no bound inferiors yet, otherwise, they'd be left with stale referenced to released aspaces. The function indeed iterates over all inferiors and resets their address spaces. The function has only one caller: remote_target::start_remote_1. This means, if we already have an inferior and then start a remote target in a second inferior, we may alter the address space of the first inferior. It seems the current code is a left-over from pre-multi-target days. To prevent bad things from happening, update the address spaces for a given target only. A scenario where unwanted behavior occurs is as follows: Start a multi-threaded program in non-stop mode. Hit a breakpoint with a thread. Create a second inferior on a remote target. Switch back to inferior 1. Continuing repeatedly hits the breakpoint. See below. $ gdb /tmp/multi-thread Reading symbols from /tmp/multi-thread... (gdb) set non-stop on (gdb) break 30 Breakpoint 1 at 0x151e: file multi-thread.cpp, line 30. (gdb) run Starting program: /tmp/multi-thread [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [New Thread 0x7ffff7a4f640 (LWP 3413070)] [New Thread 0x7ffff724e640 (LWP 3413071)] [New Thread 0x7ffff6a4d640 (LWP 3413072)] [New Thread 0x7ffff624c640 (LWP 3413073)] Thread 1 "multi-thread" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdec8) at multi-thread.cpp:30 30 int x = 42; (gdb) add-inferior -no-connection [New inferior 2] Added inferior 2 (gdb) inferior 2 [Switching to inferior 2 [<null>] (<noexec>)] (gdb) target extended-remote | gdbserver --multi - Remote debugging using | gdbserver --multi - Remote debugging using stdio (gdb) inferior 1 [Switching to inferior 1 [process 3413067] (/tmp/multi-thread)] [Switching to thread 1.1 (Thread 0x7ffff7a50740 (LWP 3413067))] #0 main (argc=1, argv=0x7fffffffdec8) at multi-thread.cpp:30 30 int x = 42; (gdb) continue Continuing. Thread 1.1 "multi-thread" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdec8) at multi-thread.cpp:30 30 int x = 42; (gdb) continue Continuing. Thread 1.1 "multi-thread" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdec8) at multi-thread.cpp:30 30 int x = 42; (gdb) continue Continuing. Thread 1.1 "multi-thread" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdec8) at multi-thread.cpp:30 30 int x = 42; (gdb) ... It was not possible to simplify the bug reproducer. For some reason, simplifications make observable problems go away. Adding the case above as a testcase. Regression-tested in Linux x86-64 with the default, native-gdbserver, and native-extended-gdbserver board files. Approved-By: Simon Marchi <simon.marchi@efficios.com>
This commit is contained in:
parent
36974d77e9
commit
adbc0c5542
5 changed files with 157 additions and 26 deletions
|
|
@ -445,36 +445,36 @@ maintenance_info_program_spaces_command (const char *args, int from_tty)
|
|||
print_program_space (current_uiout, requested);
|
||||
}
|
||||
|
||||
/* Update all program spaces matching to address spaces. The user may
|
||||
have created several program spaces, and loaded executables into
|
||||
them before connecting to the target interface that will create the
|
||||
inferiors. All that happens before GDB has a chance to know if the
|
||||
inferiors will share an address space or not. Call this after
|
||||
having connected to the target interface and having fetched the
|
||||
target description, to fixup the program/address spaces mappings.
|
||||
|
||||
It is assumed that there are no bound inferiors yet, otherwise,
|
||||
they'd be left with stale referenced to released aspaces. */
|
||||
/* See progspace.h. */
|
||||
|
||||
void
|
||||
update_address_spaces (void)
|
||||
update_address_spaces (process_stratum_target *target,
|
||||
gdbarch *gdbarch)
|
||||
{
|
||||
int shared_aspace
|
||||
= gdbarch_has_shared_address_space (current_inferior ()->arch ());
|
||||
gdb_assert (target != nullptr);
|
||||
gdb_assert (gdbarch != nullptr);
|
||||
|
||||
int shared_aspace = gdbarch_has_shared_address_space (gdbarch);
|
||||
|
||||
/* Find the program spaces that are being used by inferiors of the
|
||||
current target. We shouldn't alter inferiors of other targets. */
|
||||
gdb::unordered_set<program_space *> pspaces_to_update;
|
||||
for (inferior *inf : all_inferiors (target))
|
||||
pspaces_to_update.insert (inf->pspace);
|
||||
|
||||
if (shared_aspace)
|
||||
{
|
||||
address_space_ref_ptr aspace = new_address_space ();
|
||||
|
||||
for (struct program_space *pspace : program_spaces)
|
||||
for (program_space *pspace : pspaces_to_update)
|
||||
pspace->aspace = aspace;
|
||||
}
|
||||
else
|
||||
for (struct program_space *pspace : program_spaces)
|
||||
for (program_space *pspace : pspaces_to_update)
|
||||
pspace->aspace = new_address_space ();
|
||||
|
||||
for (inferior *inf : all_inferiors ())
|
||||
if (gdbarch_has_global_solist (current_inferior ()->arch ()))
|
||||
for (inferior *inf : all_inferiors (target))
|
||||
if (gdbarch_has_global_solist (gdbarch))
|
||||
inf->aspace = maybe_new_address_space ();
|
||||
else
|
||||
inf->aspace = inf->pspace->aspace;
|
||||
|
|
|
|||
|
|
@ -463,14 +463,19 @@ private:
|
|||
share an address space. */
|
||||
extern address_space_ref_ptr maybe_new_address_space ();
|
||||
|
||||
/* Update all program spaces matching to address spaces. The user may
|
||||
have created several program spaces, and loaded executables into
|
||||
them before connecting to the target interface that will create the
|
||||
inferiors. All that happens before GDB has a chance to know if the
|
||||
inferiors will share an address space or not. Call this after
|
||||
having connected to the target interface and having fetched the
|
||||
target description, to fixup the program/address spaces
|
||||
/* Update all program spaces matching to address spaces. This is done
|
||||
for the inferiors that have TARGET as their process stratum target
|
||||
and using GDBARCH to determine if there is a shared address space
|
||||
and if solibs are global.
|
||||
|
||||
The user may have created several program spaces, and loaded
|
||||
executables into them before connecting to the target interface
|
||||
that will create the inferiors. All that happens before GDB has a
|
||||
chance to know if the inferiors will share an address space or not.
|
||||
Call this after having connected to the target interface and having
|
||||
fetched the target description, to fixup the program/address spaces
|
||||
mappings. */
|
||||
extern void update_address_spaces (void);
|
||||
extern void update_address_spaces (process_stratum_target *target,
|
||||
gdbarch *gdbarch);
|
||||
|
||||
#endif /* GDB_PROGSPACE_H */
|
||||
|
|
|
|||
|
|
@ -5614,7 +5614,7 @@ remote_target::start_remote_1 (int from_tty, int extended_p)
|
|||
|
||||
/* Next, now that we know something about the target, update the
|
||||
address spaces in the program spaces. */
|
||||
update_address_spaces ();
|
||||
update_address_spaces (this, current_inferior ()->arch ());
|
||||
|
||||
/* On OSs where the list of libraries is global to all
|
||||
processes, we fetch them early. */
|
||||
|
|
|
|||
66
gdb/testsuite/gdb.multi/address-space-reset.c
Normal file
66
gdb/testsuite/gdb.multi/address-space-reset.c
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2026 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 <pthread.h>
|
||||
|
||||
static volatile int spin = 1;
|
||||
|
||||
static void *
|
||||
thread_func (void *arg)
|
||||
{
|
||||
/* Unleash the main thread. */
|
||||
spin = 0;
|
||||
|
||||
for (;;)
|
||||
sleep (1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
breakpoint_1 (void)
|
||||
{
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
static void
|
||||
breakpoint_2 (void)
|
||||
{
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
pthread_t thread;
|
||||
pthread_create (&thread, NULL, thread_func, NULL);
|
||||
|
||||
alarm (30);
|
||||
|
||||
/* Make sure the thread is up an running. */
|
||||
while (spin)
|
||||
sleep (1);
|
||||
|
||||
breakpoint_1 ();
|
||||
int a = 42;
|
||||
breakpoint_2 ();
|
||||
|
||||
pthread_join (thread, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
60
gdb/testsuite/gdb.multi/address-space-reset.exp
Normal file
60
gdb/testsuite/gdb.multi/address-space-reset.exp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Copyright 2026 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/>.
|
||||
|
||||
# When a remote target is set up in a new inferior, the address space
|
||||
# of the other inferiors would be reset. This is a regression test
|
||||
# for that scenario.
|
||||
|
||||
require allow_multi_inferior_tests
|
||||
|
||||
load_lib gdbserver-support.exp
|
||||
require allow_gdbserver_tests
|
||||
|
||||
standard_testfile
|
||||
|
||||
if {[build_executable "failed to prepare" $testfile $srcfile \
|
||||
{debug pthreads}]} {
|
||||
return -1
|
||||
}
|
||||
|
||||
# Problematic scenario is seen in non-stop mode.
|
||||
save_vars {GDBFLAGS} {
|
||||
append GDBFLAGS { -ex "set non-stop on"}
|
||||
clean_restart $testfile
|
||||
}
|
||||
|
||||
if {![runto "breakpoint_1"]} {
|
||||
return -1
|
||||
}
|
||||
|
||||
# Start an empty gdbserver in a new inferior.
|
||||
gdb_test "add-inferior -no-connection" "Added inferior 2"
|
||||
gdb_test "inferior 2" "Switching to inferior 2.*"
|
||||
|
||||
set res [gdbserver_start "--multi" ""]
|
||||
set gdbserver_gdbport [lindex $res 1]
|
||||
if {[gdb_target_cmd "extended-remote" $gdbserver_gdbport]} {
|
||||
return -1
|
||||
}
|
||||
|
||||
# Go back to inferior 1. Continuing should hit the next breakpoint.
|
||||
# Buggy GDB was hitting the first breakpoint.
|
||||
gdb_test "inferior 1" "Switching to inferior 1.*"
|
||||
gdb_breakpoint "breakpoint_2"
|
||||
gdb_test "continue" " breakpoint_2 \\(\\).*" "hit the next bp"
|
||||
|
||||
# Clean up gdbserver.
|
||||
gdb_test "inferior 2" "Switching to inferior 2.*" "back to inferior 2"
|
||||
gdbserver_exit 0
|
||||
Loading…
Add table
Add a link
Reference in a new issue