aboutsummaryrefslogtreecommitdiff
path: root/gdbserver
diff options
context:
space:
mode:
Diffstat (limited to 'gdbserver')
-rw-r--r--gdbserver/linux-low.cc11
-rw-r--r--gdbserver/linux-low.h27
-rw-r--r--gdbserver/server.cc29
-rw-r--r--gdbserver/target.cc6
-rw-r--r--gdbserver/target.h10
5 files changed, 83 insertions, 0 deletions
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 34991df..8788804 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -7130,6 +7130,17 @@ linux_process_target::thread_pending_parent (thread_info *thread)
return get_lwp_thread (parent);
}
+thread_info *
+linux_process_target::thread_pending_child (thread_info *thread)
+{
+ lwp_info *child = get_thread_lwp (thread)->pending_child ();
+
+ if (child == nullptr)
+ return nullptr;
+
+ return get_lwp_thread (child);
+}
+
/* Default implementation of linux_target_ops method "set_pc" for
32-bit pc register which is literally named "pc". */
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index 819f915..b563537 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -312,6 +312,7 @@ public:
#endif
thread_info *thread_pending_parent (thread_info *thread) override;
+ thread_info *thread_pending_child (thread_info *thread) override;
bool supports_catch_syscall () override;
@@ -750,6 +751,32 @@ struct lwp_info
return this->fork_relative;
}
+ /* If this LWP is the parent of a fork child we haven't reported to GDB yet,
+ return that child, else nullptr. */
+ lwp_info *pending_child () const
+ {
+ if (this->fork_relative == nullptr)
+ return nullptr;
+
+ gdb_assert (this->fork_relative->fork_relative == this);
+
+ /* In a fork parent/child relationship, the parent has a status pending and
+ the child does not, and a thread can only be in one such relationship
+ at most. So we can recognize who is the parent based on which one has
+ a pending status. */
+ gdb_assert (!!this->status_pending_p
+ != !!this->fork_relative->status_pending_p);
+
+ if (!this->status_pending_p)
+ return nullptr;
+
+ const target_waitstatus &ws = this->waitstatus;
+ gdb_assert (ws.kind () == TARGET_WAITKIND_FORKED
+ || ws.kind () == TARGET_WAITKIND_VFORKED);
+
+ return this->fork_relative;
+ }
+
/* Backlink to the parent object. */
struct thread_info *thread = nullptr;
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 8dde6fb..27e2aba 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1250,6 +1250,35 @@ handle_detach (char *own_buf)
/* We'll need this after PROCESS has been destroyed. */
int pid = process->pid;
+ /* If this process has an unreported fork child, that child is not known to
+ GDB, so GDB won't take care of detaching it. We must do it here.
+
+ Here, we specifically don't want to use "safe iteration", as detaching
+ another process might delete the next thread in the iteration, which is
+ the one saved by the safe iterator. We will never delete the currently
+ iterated on thread, so standard iteration should be safe. */
+ for (thread_info *thread : all_threads)
+ {
+ /* Only threads that are of the process we are detaching. */
+ if (thread->id.pid () != pid)
+ continue;
+
+ /* Only threads that have a pending fork event. */
+ thread_info *child = target_thread_pending_child (thread);
+ if (child == nullptr)
+ continue;
+
+ process_info *fork_child_process = get_thread_process (child);
+ gdb_assert (fork_child_process != nullptr);
+
+ int fork_child_pid = fork_child_process->pid;
+
+ if (detach_inferior (fork_child_process) != 0)
+ warning (_("Failed to detach fork child %s, child of %s"),
+ target_pid_to_str (ptid_t (fork_child_pid)).c_str (),
+ target_pid_to_str (thread->id).c_str ());
+ }
+
if (detach_inferior (process) != 0)
write_enn (own_buf);
else
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index 136b510..aa3d424 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -841,6 +841,12 @@ process_stratum_target::thread_pending_parent (thread_info *thread)
return nullptr;
}
+thread_info *
+process_stratum_target::thread_pending_child (thread_info *thread)
+{
+ return nullptr;
+}
+
bool
process_stratum_target::supports_software_single_step ()
{
diff --git a/gdbserver/target.h b/gdbserver/target.h
index 1b0a120..331a21a 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -492,6 +492,10 @@ public:
else nullptr. */
virtual thread_info *thread_pending_parent (thread_info *thread);
+ /* If THREAD is the parent of a fork child that was not reported to GDB,
+ return this child, else nullptr. */
+ virtual thread_info *thread_pending_child (thread_info *thread);
+
/* Returns true if the target can software single step. */
virtual bool supports_software_single_step ();
@@ -708,6 +712,12 @@ target_thread_pending_parent (thread_info *thread)
return the_target->thread_pending_parent (thread);
}
+static inline thread_info *
+target_thread_pending_child (thread_info *thread)
+{
+ return the_target->thread_pending_child (thread);
+}
+
int read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len);
int set_desired_thread ();