aboutsummaryrefslogtreecommitdiff
path: root/gdb/infrun.c
diff options
context:
space:
mode:
authorTom de Vries <tdevries@suse.de>2023-11-28 10:31:25 +0100
committerTom de Vries <tdevries@suse.de>2023-11-28 10:31:25 +0100
commitf9582a22dba747ff0905f4c1a80d84f677eeb928 (patch)
tree042f98562a1dc37eb3a6e964f65afb4e942fd138 /gdb/infrun.c
parent31477859c0c2a9b79a649be98830afebb9aa1d46 (diff)
downloadbinutils-f9582a22dba747ff0905f4c1a80d84f677eeb928.zip
binutils-f9582a22dba747ff0905f4c1a80d84f677eeb928.tar.gz
binutils-f9582a22dba747ff0905f4c1a80d84f677eeb928.tar.bz2
[gdb] Fix segfault in for_each_block, part 1
When running test-case gdb.base/vfork-follow-parent.exp on powerpc64 (likewise on s390x), I run into: ... (gdb) PASS: gdb.base/vfork-follow-parent.exp: \ exec_file=vfork-follow-parent-exit: target-non-stop=on: non-stop=off: \ resolution_method=schedule-multiple: print unblock_parent = 1 continue^M Continuing.^M Reading symbols from vfork-follow-parent-exit...^M ^M ^M Fatal signal: Segmentation fault^M ----- Backtrace -----^M 0x1027d3e7 gdb_internal_backtrace_1^M src/gdb/bt-utils.c:122^M 0x1027d54f _Z22gdb_internal_backtracev^M src/gdb/bt-utils.c:168^M 0x1057643f handle_fatal_signal^M src/gdb/event-top.c:889^M 0x10576677 handle_sigsegv^M src/gdb/event-top.c:962^M 0x3fffa7610477 ???^M 0x103f2144 for_each_block^M src/gdb/dcache.c:199^M 0x103f235b _Z17dcache_invalidateP13dcache_struct^M src/gdb/dcache.c:251^M 0x10bde8c7 _Z24target_dcache_invalidatev^M src/gdb/target-dcache.c:50^M ... or similar. The root cause for the segmentation fault is that linux_is_uclinux gives an incorrect result: it should always return false, given that we're running on a regular linux system, but instead it returns first true, then false. In more detail, the segmentation fault happens as follows: - a program space with an address space is created - a second program space is about to be created. maybe_new_address_space is called, and because linux_is_uclinux returns true, maybe_new_address_space returns false, and no new address space is created - a second program space with the same address space is created - a program space is deleted. Because linux_is_uclinux now returns false, gdbarch_has_shared_address_space (current_inferior ()->arch ()) returns false, and the address space is deleted - when gdb uses the address space of the remaining program space, we run into the segfault, because the address space is deleted. Hardcoding linux_is_uclinux to false makes the test-case pass. We leave addressing the root cause for the following commit in this series. For now, prevent the segmentation fault by making the address space a refcounted object. This was already suggested here [1]: ... A better solution might be to have the address spaces be reference counted ... Tested on top of trunk on x86_64-linux and ppc64le-linux. Tested on top of gdb-14-branch on ppc64-linux. Co-Authored-By: Simon Marchi <simon.marchi@polymtl.ca> PR gdb/30547 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30547 [1] https://sourceware.org/pipermail/gdb-patches/2023-October/202928.html
Diffstat (limited to 'gdb/infrun.c')
-rw-r--r--gdb/infrun.c47
1 files changed, 23 insertions, 24 deletions
diff --git a/gdb/infrun.c b/gdb/infrun.c
index cc6b203..cc2a4bc 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -531,8 +531,8 @@ holding the child stopped. Try \"set detach-on-fork\" or \
}
else
{
- child_inf->aspace = new address_space ();
- child_inf->pspace = new program_space (child_inf->aspace);
+ child_inf->pspace = new program_space (new_address_space ());
+ child_inf->aspace = child_inf->pspace->aspace;
child_inf->removable = true;
clone_program_space (child_inf->pspace, parent_inf->pspace);
}
@@ -610,8 +610,8 @@ holding the child stopped. Try \"set detach-on-fork\" or \
child_inf->aspace = parent_inf->aspace;
child_inf->pspace = parent_inf->pspace;
- parent_inf->aspace = new address_space ();
- parent_inf->pspace = new program_space (parent_inf->aspace);
+ parent_inf->pspace = new program_space (new_address_space ());
+ parent_inf->aspace = parent_inf->pspace->aspace;
clone_program_space (parent_inf->pspace, child_inf->pspace);
/* The parent inferior is still the current one, so keep things
@@ -620,8 +620,8 @@ holding the child stopped. Try \"set detach-on-fork\" or \
}
else
{
- child_inf->aspace = new address_space ();
- child_inf->pspace = new program_space (child_inf->aspace);
+ child_inf->pspace = new program_space (new_address_space ());
+ child_inf->aspace = child_inf->pspace->aspace;
child_inf->removable = true;
child_inf->symfile_flags = SYMFILE_NO_READ;
clone_program_space (child_inf->pspace, parent_inf->pspace);
@@ -1031,7 +1031,6 @@ handle_vfork_child_exec_or_exit (int exec)
if (vfork_parent->pending_detach)
{
struct program_space *pspace;
- struct address_space *aspace;
/* follow-fork child, detach-on-fork on. */
@@ -1056,9 +1055,8 @@ handle_vfork_child_exec_or_exit (int exec)
of" a hack. */
pspace = inf->pspace;
- aspace = inf->aspace;
- inf->aspace = nullptr;
inf->pspace = nullptr;
+ address_space_ref_ptr aspace = std::move (inf->aspace);
if (print_inferior_events)
{
@@ -2702,7 +2700,7 @@ resume_1 (enum gdb_signal sig)
inferior_ptid.to_string ().c_str (),
paddress (gdbarch, pc));
- const address_space *aspace = tp->inf->aspace;
+ const address_space *aspace = tp->inf->aspace.get ();
/* Normally, by the time we reach `resume', the breakpoints are either
removed or inserted, as appropriate. The exception is if we're sitting
@@ -3146,7 +3144,7 @@ thread_still_needs_step_over_bp (struct thread_info *tp)
{
struct regcache *regcache = get_thread_regcache (tp);
- if (breakpoint_here_p (tp->inf->aspace,
+ if (breakpoint_here_p (tp->inf->aspace.get (),
regcache_read_pc (regcache))
== ordinary_breakpoint_here)
return true;
@@ -3607,7 +3605,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
if (addr == (CORE_ADDR) -1)
{
- const address_space *aspace = cur_thr->inf->aspace;
+ const address_space *aspace = cur_thr->inf->aspace.get ();
if (cur_thr->stop_pc_p ()
&& pc == cur_thr->stop_pc ()
@@ -4037,7 +4035,7 @@ do_target_wait_1 (inferior *inf, ptid_t ptid,
paddress (gdbarch, pc));
discard = 1;
}
- else if (!breakpoint_inserted_here_p (tp->inf->aspace, pc))
+ else if (!breakpoint_inserted_here_p (tp->inf->aspace.get (), pc))
{
infrun_debug_printf ("previous breakpoint of %s, at %s gone",
tp->ptid.to_string ().c_str (),
@@ -4934,7 +4932,7 @@ adjust_pc_after_break (struct thread_info *thread,
if (decr_pc == 0)
return;
- const address_space *aspace = thread->inf->aspace;
+ const address_space *aspace = thread->inf->aspace.get ();
/* Find the location where (if we've hit a breakpoint) the
breakpoint would be. */
@@ -5086,7 +5084,7 @@ handle_syscall_event (struct execution_control_state *ecs)
infrun_debug_printf ("syscall number=%d", syscall_number);
ecs->event_thread->control.stop_bpstat
- = bpstat_stop_status_nowatch (ecs->event_thread->inf->aspace,
+ = bpstat_stop_status_nowatch (ecs->event_thread->inf->aspace.get (),
ecs->event_thread->stop_pc (),
ecs->event_thread, ecs->ws);
@@ -5285,7 +5283,7 @@ save_waitstatus (struct thread_info *tp, const target_waitstatus &ws)
&& ws.sig () == GDB_SIGNAL_TRAP)
{
struct regcache *regcache = get_thread_regcache (tp);
- const address_space *aspace = tp->inf->aspace;
+ const address_space *aspace = tp->inf->aspace.get ();
CORE_ADDR pc = regcache_read_pc (regcache);
adjust_pc_after_break (tp, tp->pending_waitstatus ());
@@ -6082,7 +6080,7 @@ handle_inferior_event (struct execution_control_state *ecs)
{
struct regcache *regcache = get_thread_regcache (ecs->event_thread);
- if (breakpoint_inserted_here_p (ecs->event_thread->inf->aspace,
+ if (breakpoint_inserted_here_p (ecs->event_thread->inf->aspace.get (),
regcache_read_pc (regcache)))
{
infrun_debug_printf ("Treating signal as SIGTRAP");
@@ -6114,8 +6112,9 @@ handle_inferior_event (struct execution_control_state *ecs)
handle_solib_event ();
ecs->event_thread->set_stop_pc (regcache_read_pc (regcache));
+ address_space *aspace = ecs->event_thread->inf->aspace.get ();
ecs->event_thread->control.stop_bpstat
- = bpstat_stop_status_nowatch (ecs->event_thread->inf->aspace,
+ = bpstat_stop_status_nowatch (aspace,
ecs->event_thread->stop_pc (),
ecs->event_thread, ecs->ws);
@@ -6309,7 +6308,7 @@ handle_inferior_event (struct execution_control_state *ecs)
(regcache_read_pc (get_thread_regcache (ecs->event_thread)));
ecs->event_thread->control.stop_bpstat
- = bpstat_stop_status_nowatch (ecs->event_thread->inf->aspace,
+ = bpstat_stop_status_nowatch (ecs->event_thread->inf->aspace.get (),
ecs->event_thread->stop_pc (),
ecs->event_thread, ecs->ws);
@@ -6455,7 +6454,7 @@ handle_inferior_event (struct execution_control_state *ecs)
(regcache_read_pc (get_thread_regcache (ecs->event_thread)));
ecs->event_thread->control.stop_bpstat
- = bpstat_stop_status_nowatch (ecs->event_thread->inf->aspace,
+ = bpstat_stop_status_nowatch (ecs->event_thread->inf->aspace.get (),
ecs->event_thread->stop_pc (),
ecs->event_thread, ecs->ws);
@@ -6881,7 +6880,7 @@ handle_signal_stop (struct execution_control_state *ecs)
CORE_ADDR pc;
regcache = get_thread_regcache (ecs->event_thread);
- const address_space *aspace = ecs->event_thread->inf->aspace;
+ const address_space *aspace = ecs->event_thread->inf->aspace.get ();
pc = regcache_read_pc (regcache);
@@ -6965,7 +6964,7 @@ handle_signal_stop (struct execution_control_state *ecs)
inline function call sites). */
if (ecs->event_thread->control.step_range_end != 1)
{
- const address_space *aspace = ecs->event_thread->inf->aspace;
+ const address_space *aspace = ecs->event_thread->inf->aspace.get ();
/* skip_inline_frames is expensive, so we avoid it if we can
determine that the address is one where functions cannot have
@@ -7043,7 +7042,7 @@ handle_signal_stop (struct execution_control_state *ecs)
/* See if there is a breakpoint/watchpoint/catchpoint/etc. that
handles this event. */
ecs->event_thread->control.stop_bpstat
- = bpstat_stop_status (ecs->event_thread->inf->aspace,
+ = bpstat_stop_status (ecs->event_thread->inf->aspace.get (),
ecs->event_thread->stop_pc (),
ecs->event_thread, ecs->ws, stop_chain);
@@ -8939,7 +8938,7 @@ keep_going_pass_signal (struct execution_control_state *ecs)
if (remove_bp
&& (remove_wps || !use_displaced_stepping (ecs->event_thread)))
{
- set_step_over_info (ecs->event_thread->inf->aspace,
+ set_step_over_info (ecs->event_thread->inf->aspace.get (),
regcache_read_pc (regcache), remove_wps,
ecs->event_thread->global_num);
}