diff options
-rw-r--r-- | gdb/linux-nat.c | 61 |
1 files changed, 58 insertions, 3 deletions
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index c357ca3..48ecd36 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -296,6 +296,8 @@ static struct lwp_info *find_lwp_pid (ptid_t ptid); static int lwp_status_pending_p (struct lwp_info *lp); +static bool is_lwp_marked_dead (lwp_info *lp); + static void save_stop_reason (struct lwp_info *lp); static bool proc_mem_file_is_writable (); @@ -1467,9 +1469,7 @@ detach_one_lwp (struct lwp_info *lp, int *signo_p) /* If the lwp has exited or was terminated due to a signal, there's nothing left to do. */ - if (lp->waitstatus.kind () == TARGET_WAITKIND_EXITED - || lp->waitstatus.kind () == TARGET_WAITKIND_THREAD_EXITED - || lp->waitstatus.kind () == TARGET_WAITKIND_SIGNALLED) + if (is_lwp_marked_dead (lp)) { linux_nat_debug_printf ("Can't detach %s - it has exited or was terminated: %s.", @@ -2199,6 +2199,21 @@ mark_lwp_dead (lwp_info *lp, int status) lp->stopped = 1; } +/* Return true if LP is dead, with a pending exit/signalled event. */ + +static bool +is_lwp_marked_dead (lwp_info *lp) +{ + switch (lp->waitstatus.kind ()) + { + case TARGET_WAITKIND_EXITED: + case TARGET_WAITKIND_THREAD_EXITED: + case TARGET_WAITKIND_SIGNALLED: + return true; + } + return false; +} + /* Wait for LP to stop. Returns the wait status, or 0 if the LWP has exited. */ @@ -3830,6 +3845,20 @@ linux_proc_xfer_memory_partial (int pid, gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len, ULONGEST *xfered_len); +/* Look for an LWP of PID that we know is ptrace-stopped. Returns + NULL if none is found. */ + +static lwp_info * +find_stopped_lwp (int pid) +{ + for (lwp_info *lp : all_lwps ()) + if (lp->ptid.pid () == pid + && lp->stopped + && !is_lwp_marked_dead (lp)) + return lp; + return nullptr; +} + enum target_xfer_status linux_nat_target::xfer_partial (enum target_object object, const char *annex, gdb_byte *readbuf, @@ -3876,6 +3905,32 @@ linux_nat_target::xfer_partial (enum target_object object, return linux_proc_xfer_memory_partial (inferior_ptid.pid (), readbuf, writebuf, offset, len, xfered_len); + + /* Fallback to ptrace. This should only really trigger on old + systems. See "Accessing inferior memory" at the top. + + The target_xfer interface for memory access uses + inferior_ptid as sideband argument to indicate which process + to access. Memory access is process-wide, it is not + thread-specific, so inferior_ptid sometimes points at a + process ptid_t. If we fallback to inf_ptrace_target with + that inferior_ptid, then the ptrace code will do the ptrace + call targeting inferior_ptid.pid(), the leader LWP. That + may fail with ESRCH if the leader is currently running, or + zombie. So if we get a pid-ptid, we try to find a stopped + LWP to use with ptrace. + + Note that inferior_ptid may not exist in the lwp / thread / + inferior lists. This can happen when we're removing + breakpoints from a fork child that we're not going to stay + attached to. So if we don't find a stopped LWP, still do the + ptrace call, targeting the inferior_ptid we had on entry. */ + scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid); + lwp_info *stopped = find_stopped_lwp (inferior_ptid.pid ()); + if (stopped != nullptr) + inferior_ptid = stopped->ptid; + return inf_ptrace_target::xfer_partial (object, annex, readbuf, writebuf, + offset, len, xfered_len); } return inf_ptrace_target::xfer_partial (object, annex, readbuf, writebuf, |