diff options
author | Tom de Vries <tdevries@suse.de> | 2023-11-28 10:31:25 +0100 |
---|---|---|
committer | Tom de Vries <tdevries@suse.de> | 2023-11-28 10:31:25 +0100 |
commit | f9582a22dba747ff0905f4c1a80d84f677eeb928 (patch) | |
tree | 042f98562a1dc37eb3a6e964f65afb4e942fd138 /gdb/infrun.c | |
parent | 31477859c0c2a9b79a649be98830afebb9aa1d46 (diff) | |
download | binutils-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.c | 47 |
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); } |