diff options
Diffstat (limited to 'gdbserver')
-rw-r--r-- | gdbserver/linux-low.cc | 11 | ||||
-rw-r--r-- | gdbserver/linux-low.h | 29 | ||||
-rw-r--r-- | gdbserver/server.cc | 6 | ||||
-rw-r--r-- | gdbserver/target.cc | 6 | ||||
-rw-r--r-- | gdbserver/target.h | 10 |
5 files changed, 62 insertions, 0 deletions
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc index d214aff..34991df 100644 --- a/gdbserver/linux-low.cc +++ b/gdbserver/linux-low.cc @@ -7119,6 +7119,17 @@ linux_process_target::thread_handle (ptid_t ptid, gdb_byte **handle, } #endif +thread_info * +linux_process_target::thread_pending_parent (thread_info *thread) +{ + lwp_info *parent = get_thread_lwp (thread)->pending_parent (); + + if (parent == nullptr) + return nullptr; + + return get_lwp_thread (parent); +} + /* 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 05067ff..819f915 100644 --- a/gdbserver/linux-low.h +++ b/gdbserver/linux-low.h @@ -311,6 +311,8 @@ public: int *handle_len) override; #endif + thread_info *thread_pending_parent (thread_info *thread) override; + bool supports_catch_syscall () override; /* Return the information to access registers. This has public @@ -721,6 +723,33 @@ struct pending_signal struct lwp_info { + /* If this LWP is a fork child that wasn't reported to GDB yet, return + its parent, else nullptr. */ + lwp_info *pending_parent () 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->fork_relative->status_pending_p) + return nullptr; + + const target_waitstatus &ws + = this->fork_relative->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 b1d4c92..8dde6fb 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -1656,6 +1656,12 @@ handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer) gdb_byte *handle; bool handle_status = target_thread_handle (ptid, &handle, &handle_len); + /* If this is a fork or vfork child (has a fork parent), GDB does not yet + know about this process, and must not know about it until it gets the + corresponding (v)fork event. Exclude this thread from the list. */ + if (target_thread_pending_parent (thread) != nullptr) + return; + write_ptid (ptid_s, ptid); buffer_xml_printf (buffer, "<thread id=\"%s\"", ptid_s); diff --git a/gdbserver/target.cc b/gdbserver/target.cc index bfa8605..136b510 100644 --- a/gdbserver/target.cc +++ b/gdbserver/target.cc @@ -835,6 +835,12 @@ process_stratum_target::thread_handle (ptid_t ptid, gdb_byte **handle, return false; } +thread_info * +process_stratum_target::thread_pending_parent (thread_info *thread) +{ + return nullptr; +} + bool process_stratum_target::supports_software_single_step () { diff --git a/gdbserver/target.h b/gdbserver/target.h index 6c863c8..1b0a120 100644 --- a/gdbserver/target.h +++ b/gdbserver/target.h @@ -488,6 +488,10 @@ public: virtual bool thread_handle (ptid_t ptid, gdb_byte **handle, int *handle_len); + /* If THREAD is a fork child that was not reported to GDB, return its parent + else nullptr. */ + virtual thread_info *thread_pending_parent (thread_info *thread); + /* Returns true if the target can software single step. */ virtual bool supports_software_single_step (); @@ -698,6 +702,12 @@ void done_accessing_memory (void); #define target_thread_handle(ptid, handle, handle_len) \ the_target->thread_handle (ptid, handle, handle_len) +static inline thread_info * +target_thread_pending_parent (thread_info *thread) +{ + return the_target->thread_pending_parent (thread); +} + int read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len); int set_desired_thread (); |