diff --git a/gdb/progspace.c b/gdb/progspace.c index cf12939328a..484fd085b1f 100644 --- a/gdb/progspace.c +++ b/gdb/progspace.c @@ -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 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; diff --git a/gdb/progspace.h b/gdb/progspace.h index 36469eeebb4..e9261ff8590 100644 --- a/gdb/progspace.h +++ b/gdb/progspace.h @@ -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 */ diff --git a/gdb/remote.c b/gdb/remote.c index 4ad595bda68..22f584f0c57 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -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. */ diff --git a/gdb/testsuite/gdb.multi/address-space-reset.c b/gdb/testsuite/gdb.multi/address-space-reset.c new file mode 100644 index 00000000000..556aca0295e --- /dev/null +++ b/gdb/testsuite/gdb.multi/address-space-reset.c @@ -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 . */ + +#include +#include + +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; +} diff --git a/gdb/testsuite/gdb.multi/address-space-reset.exp b/gdb/testsuite/gdb.multi/address-space-reset.exp new file mode 100644 index 00000000000..659889719bb --- /dev/null +++ b/gdb/testsuite/gdb.multi/address-space-reset.exp @@ -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 . + +# 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