diff options
Diffstat (limited to 'gdb/infrun.c')
-rw-r--r-- | gdb/infrun.c | 83 |
1 files changed, 72 insertions, 11 deletions
diff --git a/gdb/infrun.c b/gdb/infrun.c index c72b05d..9a2de5c 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1364,6 +1364,23 @@ write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr, do_cleanups (ptid_cleanup); } +/* Restore the contents of the copy area for thread PTID. */ + +static void +displaced_step_restore (struct displaced_step_inferior_state *displaced, + ptid_t ptid) +{ + ULONGEST len = gdbarch_max_insn_length (displaced->step_gdbarch); + + write_memory_ptid (ptid, displaced->step_copy, + displaced->step_saved_copy, len); + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: restored %s %s\n", + target_pid_to_str (ptid), + paddress (displaced->step_gdbarch, + displaced->step_copy)); +} + static void displaced_step_fixup (ptid_t event_ptid, enum target_signal signal) { @@ -1382,17 +1399,7 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal) old_cleanups = make_cleanup (displaced_step_clear_cleanup, displaced); - /* Restore the contents of the copy area. */ - { - ULONGEST len = gdbarch_max_insn_length (displaced->step_gdbarch); - - write_memory_ptid (displaced->step_ptid, displaced->step_copy, - displaced->step_saved_copy, len); - if (debug_displaced) - fprintf_unfiltered (gdb_stdlog, "displaced: restored %s\n", - paddress (displaced->step_gdbarch, - displaced->step_copy)); - } + displaced_step_restore (displaced, displaced->step_ptid); /* Did the instruction complete successfully? */ if (signal == TARGET_SIGNAL_TRAP) @@ -3373,6 +3380,60 @@ handle_inferior_event (struct execution_control_state *ecs) if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_FORKED\n"); + /* Check whether the inferior is displaced stepping. */ + { + struct regcache *regcache = get_thread_regcache (ecs->ptid); + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct displaced_step_inferior_state *displaced + = get_displaced_stepping_state (ptid_get_pid (ecs->ptid)); + + /* If checking displaced stepping is supported, and thread + ecs->ptid is displaced stepping. */ + if (displaced && ptid_equal (displaced->step_ptid, ecs->ptid)) + { + struct inferior *parent_inf + = find_inferior_pid (ptid_get_pid (ecs->ptid)); + struct regcache *child_regcache; + CORE_ADDR parent_pc; + + /* GDB has got TARGET_WAITKIND_FORKED or TARGET_WAITKIND_VFORKED, + indicating that the displaced stepping of syscall instruction + has been done. Perform cleanup for parent process here. Note + that this operation also cleans up the child process for vfork, + because their pages are shared. */ + displaced_step_fixup (ecs->ptid, TARGET_SIGNAL_TRAP); + + if (ecs->ws.kind == TARGET_WAITKIND_FORKED) + { + /* Restore scratch pad for child process. */ + displaced_step_restore (displaced, ecs->ws.value.related_pid); + } + + /* Since the vfork/fork syscall instruction was executed in the scratchpad, + the child's PC is also within the scratchpad. Set the child's PC + to the parent's PC value, which has already been fixed up. + FIXME: we use the parent's aspace here, although we're touching + the child, because the child hasn't been added to the inferior + list yet at this point. */ + + child_regcache + = get_thread_arch_aspace_regcache (ecs->ws.value.related_pid, + gdbarch, + parent_inf->aspace); + /* Read PC value of parent process. */ + parent_pc = regcache_read_pc (regcache); + + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, + "displaced: write child pc from %s to %s\n", + paddress (gdbarch, + regcache_read_pc (child_regcache)), + paddress (gdbarch, parent_pc)); + + regcache_write_pc (child_regcache, parent_pc); + } + } + if (!ptid_equal (ecs->ptid, inferior_ptid)) { context_switch (ecs->ptid); |