diff mbox

[20/31] gdbserver/linux-low: turn process/thread addition/deletion ops into methods

Message ID 26d0ac036ac31925ab8d9d635b10141f3341ba09.1583529167.git.tankut.baris.aktemur@intel.com
State New
Headers show

Commit Message

Tankut Baris Aktemur March 6, 2020, 9:31 p.m. UTC
gdbserver/ChangeLog:
2020-03-06  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>

	Turn the 'new_process', 'delete_process', 'new_thread',
	'delete_thread', and 'new_fork' linux target ops into methods
	of linux_process_target.

	* linux-low.h (struct linux_target_ops): Remove the ops.
	(class linux_process_target) <add_linux_process>
	<add_lwp>
	<delete_lwp>
	<attach_lwp>
	<detach_one_lwp>
	<check_zombie_leaders>
	<filter_exit_event>
	<low_new_process>
	<low_delete_process>
	<low_new_thread>
	<low_delete_thread>
	<low_new_fork>: Declare.
	* linux-low.cc (delete_lwp): Turn into...
	(linux_process_target::delete_lwp): ...this.
	(linux_process_target::low_delete_thread): Define.
	(linux_add_process): Turn into...
	(linux_process_target::add_linux_process): ...this.
	(linux_process_target::low_new_process): Define.
	(linux_process_target::low_delete_process): Define.
	(linux_process_target::low_new_fork): Define.
	(add_lwp): Turn into...
	(linux_process_target::add_lwp): ...this.
	(linux_process_target::low_new_thread): Define.
	(linux_attach_lwp): Turn into...
	(linux_process_target::attach_lwp): ...this.
	(linux_detach_one_lwp): Turn into...
	(linux_process_target::detach_one_lwp): ...this.
	(linux_detach_lwp_callback): Remove and inline...
	(linux_process_target::detach): ...here.
	(check_zombie_leaders): Turn into...
	(linux_process_target::check_zombie_leaders): ...this.
	(filter_exit_event): Turn into...
	(linux_process_target::filter_exit_event): ...this.

	Update the callers below.

	(linux_process_target::handle_extended_wait)
	(linux_process_target::create_inferior)
	(attach_proc_task_lwp_callback)
	(linux_process_target::attach)
	(linux_process_target::detach)
	(linux_process_target::mourn)
	* thread-db.cc (attach_thread)

	* linux-x86-low.cc (class x86_target) <low_new_process>
	<low_delete_process>
	<low_new_thread>
	<low_delete_thread>
	<low_new_fork>: Declare.
	(x86_linux_new_process): Turn into...
	(x86_target::low_new_process): ...this.
	(x86_linux_delete_process): Turn into...
	(x86_target::low_delete_process): ...this.
	(x86_target::low_new_thread): Define.
	(x86_target::low_delete_thread): Define.
	(x86_linux_new_fork): Turn into...
	(x86_target::low_new_fork): ...this.
	(the_low_target): Remove the op fields.
	* linux-aarch64-low.cc (class aarch64_target) <low_new_process>
	<low_delete_process>
	<low_new_thread>
	<low_delete_thread>
	<low_new_fork>: Declare.
	(aarch64_linux_new_process): Turn into...
	(aarch64_target::low_new_process): ...this.
	(aarch64_linux_delete_process): Turn into...
	(aarch64_target::low_delete_process): ...this.
	(aarch64_target::low_new_thread): Define.
	(aarch64_target::low_delete_thread): Define.
	(aarch64_linux_new_fork): Turn into...
	(aarch64_target::low_new_fork): ...this.
	(the_low_target): Remove the op fields.
	* linux-arm-low.cc (class arm_target) <low_new_process>
	<low_delete_process>
	<low_new_thread>
	<low_delete_thread>
	<low_new_fork>: Declare.
	(arm_new_process): Turn into...
	(arm_target::low_new_process): ...this.
	(arm_delete_process): Turn into...
	(arm_target::low_delete_process): ...this.
	(arm_new_thread): Turn into...
	(arm_target::low_new_thread): ...this.
	(arm_delete_thread): Turn into...
	(arm_target::low_delete_thread): ...this.
	(arm_new_fork): Turn into...
	(arm_target::low_new_fork): ...this.
	(the_low_target): Remove the op fields.
	* linux-mips-low.cc (class mips_target) <low_new_process>
	<low_delete_process>
	<low_new_thread>
	<low_delete_thread>
	<low_new_fork>: Declare.
	(mips_linux_new_process): Turn into...
	(mips_target::low_new_process): ...this.
	(mips_linux_delete_process): Turn into...
	(mips_target::low_delete_process): ...this.
	(mips_linux_new_thread): Turn into...
	(mips_target::low_new_thread): ...this.
	(mips_linux_delete_thread): Turn into...
	(mips_target::low_delete_thread): ...this.
	(mips_linux_new_fork): Turn into...
	(mips_target::low_new_fork): ...this.
	(the_low_target): Remove the op fields.
	* linux-bfin-low.cc (the_low_target): Remove the op fields.
	* linux-crisv32-low.cc (the_low_target): Ditto.
	* linux-m32r-low.cc (the_low_target): Ditto.
	* linux-m68k-low.cc (the_low_target): Ditto.
	* linux-ppc-low.cc (the_low_target): Ditto.
	* linux-s390-low.cc (the_low_target): Ditto.
	* linux-sh-low.cc (the_low_target): Ditto.
	* linux-tic6x-low.cc (the_low_target): Ditto.
	* linux-tile-low.cc (the_low_target): Ditto.
	* linux-xtensa-low.cc (the_low_target): Ditto.
---
 gdbserver/linux-aarch64-low.cc |  47 +++++++----
 gdbserver/linux-arm-low.cc     |  35 ++++----
 gdbserver/linux-bfin-low.cc    |   5 --
 gdbserver/linux-crisv32-low.cc |   5 --
 gdbserver/linux-low.cc         | 142 +++++++++++++++++----------------
 gdbserver/linux-low.h          |  73 ++++++++++++-----
 gdbserver/linux-m32r-low.cc    |   5 --
 gdbserver/linux-m68k-low.cc    |   5 --
 gdbserver/linux-mips-low.cc    |  47 ++++++-----
 gdbserver/linux-ppc-low.cc     |   5 --
 gdbserver/linux-s390-low.cc    |   5 --
 gdbserver/linux-sh-low.cc      |   5 --
 gdbserver/linux-tic6x-low.cc   |   5 --
 gdbserver/linux-tile-low.cc    |   5 --
 gdbserver/linux-x86-low.cc     |  43 +++++++---
 gdbserver/linux-xtensa-low.cc  |   5 --
 gdbserver/thread-db.cc         |   2 +-
 17 files changed, 234 insertions(+), 205 deletions(-)
diff mbox

Patch

diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
index 458c5a77209..1b5efd56168 100644
--- a/gdbserver/linux-aarch64-low.cc
+++ b/gdbserver/linux-aarch64-low.cc
@@ -94,6 +94,16 @@  protected:
 
   bool low_siginfo_fixup (siginfo_t *native, gdb_byte *inf,
 			  int direction) override;
+
+  arch_process_info *low_new_process () override;
+
+  void low_delete_process (arch_process_info *info) override;
+
+  void low_new_thread (lwp_info *) override;
+
+  void low_delete_thread (arch_lwp_info *) override;
+
+  void low_new_fork (process_info *parent, process_info *child) override;
 };
 
 /* The singleton target ops object.  */
@@ -519,10 +529,10 @@  aarch64_target::low_siginfo_fixup (siginfo_t *native, gdb_byte *inf,
   return false;
 }
 
-/* Implementation of linux_target_ops method "new_process".  */
+/* Implementation of linux target ops method "low_new_process".  */
 
-static struct arch_process_info *
-aarch64_linux_new_process (void)
+arch_process_info *
+aarch64_target::low_new_process ()
 {
   struct arch_process_info *info = XCNEW (struct arch_process_info);
 
@@ -531,19 +541,31 @@  aarch64_linux_new_process (void)
   return info;
 }
 
-/* Implementation of linux_target_ops method "delete_process".  */
+/* Implementation of linux target ops method "low_delete_process".  */
 
-static void
-aarch64_linux_delete_process (struct arch_process_info *info)
+void
+aarch64_target::low_delete_process (arch_process_info *info)
 {
   xfree (info);
 }
 
-/* Implementation of linux_target_ops method "linux_new_fork".  */
+void
+aarch64_target::low_new_thread (lwp_info *lwp)
+{
+  aarch64_linux_new_thread (lwp);
+}
 
-static void
-aarch64_linux_new_fork (struct process_info *parent,
-			struct process_info *child)
+void
+aarch64_target::low_delete_thread (arch_lwp_info *arch_lwp)
+{
+  aarch64_linux_delete_thread (arch_lwp);
+}
+
+/* Implementation of linux target ops method "low_new_fork".  */
+
+void
+aarch64_target::low_new_fork (process_info *parent,
+			      process_info *child)
 {
   /* These are allocated by linux_add_process.  */
   gdb_assert (parent->priv != NULL
@@ -3118,11 +3140,6 @@  aarch64_supports_hardware_single_step (void)
 
 struct linux_target_ops the_low_target =
 {
-  aarch64_linux_new_process,
-  aarch64_linux_delete_process,
-  aarch64_linux_new_thread,
-  aarch64_linux_delete_thread,
-  aarch64_linux_new_fork,
   aarch64_linux_prepare_to_resume,
   NULL, /* process_qsupported */
   aarch64_supports_tracepoints,
diff --git a/gdbserver/linux-arm-low.cc b/gdbserver/linux-arm-low.cc
index 55859780ae7..cc2f3919e27 100644
--- a/gdbserver/linux-arm-low.cc
+++ b/gdbserver/linux-arm-low.cc
@@ -101,6 +101,16 @@  protected:
   bool low_stopped_by_watchpoint () override;
 
   CORE_ADDR low_stopped_data_address () override;
+
+  arch_process_info *low_new_process () override;
+
+  void low_delete_process (arch_process_info *info) override;
+
+  void low_new_thread (lwp_info *) override;
+
+  void low_delete_thread (arch_lwp_info *) override;
+
+  void low_new_fork (process_info *parent, process_info *child) override;
 };
 
 /* The singleton target ops object.  */
@@ -721,8 +731,8 @@  arm_target::low_stopped_data_address ()
 }
 
 /* Called when a new process is created.  */
-static struct arch_process_info *
-arm_new_process (void)
+arch_process_info *
+arm_target::low_new_process ()
 {
   struct arch_process_info *info = XCNEW (struct arch_process_info);
   return info;
@@ -730,15 +740,15 @@  arm_new_process (void)
 
 /* Called when a process is being deleted.  */
 
-static void
-arm_delete_process (struct arch_process_info *info)
+void
+arm_target::low_delete_process (arch_process_info *info)
 {
   xfree (info);
 }
 
 /* Called when a new thread is detected.  */
-static void
-arm_new_thread (struct lwp_info *lwp)
+void
+arm_target::low_new_thread (lwp_info *lwp)
 {
   struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
   int i;
@@ -753,14 +763,14 @@  arm_new_thread (struct lwp_info *lwp)
 
 /* Function to call when a thread is being deleted.  */
 
-static void
-arm_delete_thread (struct arch_lwp_info *arch_lwp)
+void
+arm_target::low_delete_thread (arch_lwp_info *arch_lwp)
 {
   xfree (arch_lwp);
 }
 
-static void
-arm_new_fork (struct process_info *parent, struct process_info *child)
+void
+arm_target::low_new_fork (process_info *parent, process_info *child)
 {
   struct arch_process_info *parent_proc_info;
   struct arch_process_info *child_proc_info;
@@ -1107,11 +1117,6 @@  arm_target::get_regs_info ()
 }
 
 struct linux_target_ops the_low_target = {
-  arm_new_process,
-  arm_delete_process,
-  arm_new_thread,
-  arm_delete_thread,
-  arm_new_fork,
   arm_prepare_to_resume,
   NULL, /* process_qsupported */
   NULL, /* supports_tracepoints */
diff --git a/gdbserver/linux-bfin-low.cc b/gdbserver/linux-bfin-low.cc
index 5843ac29f36..21facb24cd0 100644
--- a/gdbserver/linux-bfin-low.cc
+++ b/gdbserver/linux-bfin-low.cc
@@ -165,11 +165,6 @@  bfin_target::get_regs_info ()
 }
 
 struct linux_target_ops the_low_target = {
-  NULL, /* new_process */
-  NULL, /* delete_process */
-  NULL, /* new_thread */
-  NULL, /* delete_thread */
-  NULL, /* new_fork */
   NULL, /* prepare_to_resume */
   NULL, /* process_qsupported */
   NULL, /* supports_tracepoints */
diff --git a/gdbserver/linux-crisv32-low.cc b/gdbserver/linux-crisv32-low.cc
index 0e228a859e2..1397603ee36 100644
--- a/gdbserver/linux-crisv32-low.cc
+++ b/gdbserver/linux-crisv32-low.cc
@@ -470,11 +470,6 @@  crisv32_target::get_regs_info ()
 }
 
 struct linux_target_ops the_low_target = {
-  NULL, /* new_process */
-  NULL, /* delete_process */
-  NULL, /* new_thread */
-  NULL, /* delete_thread */
-  NULL, /* new_fork */
   NULL, /* prepare_to_resume */
   NULL, /* process_qsupported */
   NULL, /* supports_tracepoints */
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 47a36ee3052..50addb9d68e 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -268,7 +268,6 @@  int using_threads = 1;
 static int stabilizing_threads;
 
 static void unsuspend_all_lwps (struct lwp_info *except);
-static struct lwp_info *add_lwp (ptid_t ptid);
 static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
 static int lwp_is_marked_dead (struct lwp_info *lwp);
 static int finish_step_over (struct lwp_info *lwp);
@@ -413,8 +412,8 @@  linux_pid_exe_is_elf_64_file (int pid, unsigned int *machine)
   return elf_64_file_p (file, machine);
 }
 
-static void
-delete_lwp (struct lwp_info *lwp)
+void
+linux_process_target::delete_lwp (lwp_info *lwp)
 {
   struct thread_info *thr = get_lwp_thread (lwp);
 
@@ -423,31 +422,52 @@  delete_lwp (struct lwp_info *lwp)
 
   remove_thread (thr);
 
-  if (the_low_target.delete_thread != NULL)
-    the_low_target.delete_thread (lwp->arch_private);
-  else
-    gdb_assert (lwp->arch_private == NULL);
+  low_delete_thread (lwp->arch_private);
 
   free (lwp);
 }
 
-/* Add a process to the common process list, and set its private
-   data.  */
+void
+linux_process_target::low_delete_thread (arch_lwp_info *info)
+{
+  /* Default implementation should be overridden if architecture-specific
+     info is being used.  */
+  gdb_assert (info == nullptr);
+}
 
-static struct process_info *
-linux_add_process (int pid, int attached)
+process_info *
+linux_process_target::add_linux_process (int pid, int attached)
 {
   struct process_info *proc;
 
   proc = add_process (pid, attached);
   proc->priv = XCNEW (struct process_info_private);
 
-  if (the_low_target.new_process != NULL)
-    proc->priv->arch_private = the_low_target.new_process ();
+  proc->priv->arch_private = low_new_process ();
 
   return proc;
 }
 
+arch_process_info *
+linux_process_target::low_new_process ()
+{
+  return nullptr;
+}
+
+void
+linux_process_target::low_delete_process (arch_process_info *info)
+{
+  /* Default implementation must be overridden if architecture-specific
+     info exists.  */
+  gdb_assert (info == nullptr);
+}
+
+void
+linux_process_target::low_new_fork (process_info *parent, process_info *child)
+{
+  /* Nop.  */
+}
+
 void
 linux_process_target::arch_setup_thread (thread_info *thread)
 {
@@ -528,7 +548,7 @@  linux_process_target::handle_extended_wait (lwp_info **orig_event_lwp,
 	     will be detached, since we will need the process object and the
 	     breakpoints to remove any breakpoints from memory when we
 	     detach, and the client side will access registers.  */
-	  child_proc = linux_add_process (new_pid, 0);
+	  child_proc = add_linux_process (new_pid, 0);
 	  gdb_assert (child_proc != NULL);
 	  child_lwp = add_lwp (ptid);
 	  gdb_assert (child_lwp != NULL);
@@ -572,8 +592,7 @@  linux_process_target::handle_extended_wait (lwp_info **orig_event_lwp,
 	  child_proc->tdesc = tdesc;
 
 	  /* Clone arch-specific process data.  */
-	  if (the_low_target.new_fork != NULL)
-	    the_low_target.new_fork (parent_proc, child_proc);
+	  low_new_fork (parent_proc, child_proc);
 
 	  /* Save fork info in the parent thread.  */
 	  if (event == PTRACE_EVENT_FORK)
@@ -700,7 +719,7 @@  linux_process_target::handle_extended_wait (lwp_info **orig_event_lwp,
       current_thread = NULL;
 
       /* Create a new process/lwp/thread.  */
-      proc = linux_add_process (event_pid, 0);
+      proc = add_linux_process (event_pid, 0);
       event_lwp = add_lwp (event_ptid);
       event_thr = get_lwp_thread (event_lwp);
       gdb_assert (current_thread == event_thr);
@@ -921,8 +940,8 @@  linux_process_target::save_stop_reason (lwp_info *lwp)
   return true;
 }
 
-static struct lwp_info *
-add_lwp (ptid_t ptid)
+lwp_info *
+linux_process_target::add_lwp (ptid_t ptid)
 {
   struct lwp_info *lwp;
 
@@ -932,12 +951,17 @@  add_lwp (ptid_t ptid)
 
   lwp->thread = add_thread (ptid, lwp);
 
-  if (the_low_target.new_thread != NULL)
-    the_low_target.new_thread (lwp);
+  low_new_thread (lwp);
 
   return lwp;
 }
 
+void
+linux_process_target::low_new_thread (lwp_info *info)
+{
+  /* Nop.  */
+}
+
 /* Callback to be used when calling fork_inferior, responsible for
    actually initiating the tracing of the inferior.  */
 
@@ -994,7 +1018,7 @@  linux_process_target::create_inferior (const char *program,
 			 NULL, NULL, NULL, NULL);
   }
 
-  linux_add_process (pid, 0);
+  add_linux_process (pid, 0);
 
   ptid = ptid_t (pid, pid, 0);
   new_lwp = add_lwp (ptid);
@@ -1024,11 +1048,8 @@  linux_process_target::post_create_inferior ()
     }
 }
 
-/* Attach to an inferior process.  Returns 0 on success, ERRNO on
-   error.  */
-
 int
-linux_attach_lwp (ptid_t ptid)
+linux_process_target::attach_lwp (ptid_t ptid)
 {
   struct lwp_info *new_lwp;
   int lwpid = ptid.lwp ();
@@ -1125,7 +1146,7 @@  attach_proc_task_lwp_callback (ptid_t ptid)
       if (debug_threads)
 	debug_printf ("Found new lwp %d\n", lwpid);
 
-      err = linux_attach_lwp (ptid);
+      err = the_linux_target->attach_lwp (ptid);
 
       /* Be quiet if we simply raced with the thread exiting.  EPERM
 	 is returned if the thread's task still exists, and is marked
@@ -1167,11 +1188,11 @@  linux_process_target::attach (unsigned long pid)
   ptid_t ptid = ptid_t (pid, pid, 0);
   int err;
 
-  proc = linux_add_process (pid, 1);
+  proc = add_linux_process (pid, 1);
 
   /* Attach to PID.  We will check for other threads
      soon.  */
-  err = linux_attach_lwp (ptid);
+  err = attach_lwp (ptid);
   if (err != 0)
     {
       remove_process (proc);
@@ -1479,10 +1500,8 @@  get_detach_signal (struct thread_info *thread)
     }
 }
 
-/* Detach from LWP.  */
-
-static void
-linux_detach_one_lwp (struct lwp_info *lwp)
+void
+linux_process_target::detach_one_lwp (lwp_info *lwp)
 {
   struct thread_info *thread = get_lwp_thread (lwp);
   int sig;
@@ -1564,22 +1583,6 @@  linux_detach_one_lwp (struct lwp_info *lwp)
   delete_lwp (lwp);
 }
 
-/* Callback for for_each_thread.  Detaches from non-leader threads of a
-   given process.  */
-
-static void
-linux_detach_lwp_callback (thread_info *thread)
-{
-  /* We don't actually detach from the thread group leader just yet.
-     If the thread group exits, we must reap the zombie clone lwps
-     before we're able to reap the leader.  */
-  if (thread->id.pid () == thread->id.lwp ())
-    return;
-
-  lwp_info *lwp = get_thread_lwp (thread);
-  linux_detach_one_lwp (lwp);
-}
-
 int
 linux_process_target::detach (process_info *process)
 {
@@ -1606,10 +1609,20 @@  linux_process_target::detach (process_info *process)
   /* Detach from the clone lwps first.  If the thread group exits just
      while we're detaching, we must reap the clone lwps before we're
      able to reap the leader.  */
-  for_each_thread (process->pid, linux_detach_lwp_callback);
+  for_each_thread (process->pid, [this] (thread_info *thread)
+    {
+      /* We don't actually detach from the thread group leader just yet.
+	 If the thread group exits, we must reap the zombie clone lwps
+	 before we're able to reap the leader.  */
+      if (thread->id.pid () == thread->id.lwp ())
+	return;
+
+      lwp_info *lwp = get_thread_lwp (thread);
+      detach_one_lwp (lwp);
+    });
 
   main_lwp = find_lwp_pid (ptid_t (process->pid));
-  linux_detach_one_lwp (main_lwp);
+  detach_one_lwp (main_lwp);
 
   mourn (process);
 
@@ -1630,17 +1643,14 @@  linux_process_target::mourn (process_info *process)
   thread_db_mourn (process);
 #endif
 
-  for_each_thread (process->pid, [] (thread_info *thread)
+  for_each_thread (process->pid, [this] (thread_info *thread)
     {
       delete_lwp (get_thread_lwp (thread));
     });
 
   /* Freeing all private data.  */
   priv = process->priv;
-  if (the_low_target.delete_process != NULL)
-    the_low_target.delete_process (priv->arch_private);
-  else
-    gdb_assert (priv->arch_private == NULL);
+  low_delete_process (priv->arch_private);
   free (priv);
   process->priv = NULL;
 
@@ -1832,13 +1842,10 @@  iterate_over_lwps (ptid_t filter,
   return get_thread_lwp (thread);
 }
 
-/* Detect zombie thread group leaders, and "exit" them.  We can't reap
-   their exits until all other threads in the group have exited.  */
-
-static void
-check_zombie_leaders (void)
+void
+linux_process_target::check_zombie_leaders ()
 {
-  for_each_process ([] (process_info *proc) {
+  for_each_process ([this] (process_info *proc) {
     pid_t leader_pid = pid_of (proc);
     struct lwp_info *leader_lp;
 
@@ -2943,14 +2950,9 @@  ignore_event (struct target_waitstatus *ourstatus)
   return null_ptid;
 }
 
-/* Convenience function that is called when the kernel reports an exit
-   event.  This decides whether to report the event to GDB as a
-   process exit event, a thread exit event, or to suppress the
-   event.  */
-
-static ptid_t
-filter_exit_event (struct lwp_info *event_child,
-		   struct target_waitstatus *ourstatus)
+ptid_t
+linux_process_target::filter_exit_event (lwp_info *event_child,
+					 target_waitstatus *ourstatus)
 {
   client_state &cs = get_client_state ();
   struct thread_info *thread = get_lwp_thread (event_child);
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index ced71f34cf0..16d9b389275 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -131,27 +131,6 @@  struct lwp_info;
 
 struct linux_target_ops
 {
-  /* Hook to call when a new process is created or attached to.
-     If extra per-process architecture-specific data is needed,
-     allocate it here.  */
-  struct arch_process_info * (*new_process) (void);
-
-  /* Hook to call when a process is being deleted.  If extra per-process
-     architecture-specific data is needed, delete it here.  */
-  void (*delete_process) (struct arch_process_info *info);
-
-  /* Hook to call when a new thread is detected.
-     If extra per-thread architecture-specific data is needed,
-     allocate it here.  */
-  void (*new_thread) (struct lwp_info *);
-
-  /* Hook to call when a thread is being deleted.  If extra per-thread
-     architecture-specific data is needed, delete it here.  */
-  void (*delete_thread) (struct arch_lwp_info *);
-
-  /* Hook to call, if any, when a new fork is attached.  */
-  void (*new_fork) (struct process_info *parent, struct process_info *child);
-
   /* Hook to call prior to resuming a thread.  */
   void (*prepare_to_resume) (struct lwp_info *);
 
@@ -629,6 +608,37 @@  class linux_process_target : public process_stratum_target
   void siginfo_fixup (siginfo_t *siginfo, gdb_byte *inf_siginfo,
 		      int direction);
 
+  /* Add a process to the common process list, and set its private
+     data.  */
+  process_info *add_linux_process (int pid, int attached);
+
+  /* Add a new thread.  */
+  lwp_info *add_lwp (ptid_t ptid);
+
+  /* Delete a thread.  */
+  void delete_lwp (lwp_info *lwp);
+
+public: /* Make this public because it's used from outside.  */
+  /* Attach to an inferior process.  Returns 0 on success, ERRNO on
+     error.  */
+  int attach_lwp (ptid_t ptid);
+
+private: /* Back to private.  */
+  /* Detach from LWP.  */
+  void detach_one_lwp (lwp_info *lwp);
+
+  /* Detect zombie thread group leaders, and "exit" them.  We can't
+     reap their exits until all other threads in the group have
+     exited.  */
+  void check_zombie_leaders ();
+
+  /* Convenience function that is called when the kernel reports an exit
+     event.  This decides whether to report the event to GDB as a
+     process exit event, a thread exit event, or to suppress the
+     event.  */
+  ptid_t filter_exit_event (lwp_info *event_child,
+			    target_waitstatus *ourstatus);
+
 protected:
   /* The architecture-specific "low" methods are listed below.  */
 
@@ -691,6 +701,27 @@  class linux_process_target : public process_stratum_target
   virtual bool low_siginfo_fixup (siginfo_t *native, gdb_byte *inf,
 				  int direction);
 
+  /* Hook to call when a new process is created or attached to.
+     If extra per-process architecture-specific data is needed,
+     allocate it here.  */
+  virtual arch_process_info *low_new_process ();
+
+  /* Hook to call when a process is being deleted.  If extra per-process
+     architecture-specific data is needed, delete it here.  */
+  virtual void low_delete_process (arch_process_info *info);
+
+  /* Hook to call when a new thread is detected.
+     If extra per-thread architecture-specific data is needed,
+     allocate it here.  */
+  virtual void low_new_thread (lwp_info *);
+
+  /* Hook to call when a thread is being deleted.  If extra per-thread
+     architecture-specific data is needed, delete it here.  */
+  virtual void low_delete_thread (arch_lwp_info *);
+
+  /* Hook to call, if any, when a new fork is attached.  */
+  virtual void low_new_fork (process_info *parent, process_info *child);
+
   /* How many bytes the PC should be decremented after a break.  */
   int m_decr_pc_after_break;
 };
diff --git a/gdbserver/linux-m32r-low.cc b/gdbserver/linux-m32r-low.cc
index e664c6d2a82..bb1a2a0cd17 100644
--- a/gdbserver/linux-m32r-low.cc
+++ b/gdbserver/linux-m32r-low.cc
@@ -163,11 +163,6 @@  m32r_target::get_regs_info ()
 }
 
 struct linux_target_ops the_low_target = {
-  NULL, /* new_process */
-  NULL, /* delete_process */
-  NULL, /* new_thread */
-  NULL, /* delete_thread */
-  NULL, /* new_fork */
   NULL, /* prepare_to_resume */
   NULL, /* process_qsupported */
   NULL, /* supports_tracepoints */
diff --git a/gdbserver/linux-m68k-low.cc b/gdbserver/linux-m68k-low.cc
index 39e45171428..bfc682bacbe 100644
--- a/gdbserver/linux-m68k-low.cc
+++ b/gdbserver/linux-m68k-low.cc
@@ -259,11 +259,6 @@  m68k_supports_hardware_single_step (void)
 }
 
 struct linux_target_ops the_low_target = {
-  NULL, /* new_process */
-  NULL, /* delete_process */
-  NULL, /* new_thread */
-  NULL, /* delete_thread */
-  NULL, /* new_fork */
   NULL, /* prepare_to_resume */
   NULL, /* process_qsupported */
   NULL, /* supports_tracepoints */
diff --git a/gdbserver/linux-mips-low.cc b/gdbserver/linux-mips-low.cc
index 52be7f0f748..3ec56d404e5 100644
--- a/gdbserver/linux-mips-low.cc
+++ b/gdbserver/linux-mips-low.cc
@@ -72,6 +72,16 @@  protected:
 
   void low_supply_ptrace_register (regcache *regcache, int regno,
 				   const char *buf) override;
+
+  arch_process_info *low_new_process () override;
+
+  void low_delete_process (arch_process_info *info) override;
+
+  void low_new_thread (lwp_info *) override;
+
+  void low_delete_thread (arch_lwp_info *) override;
+
+  void low_new_fork (process_info *parent, process_info *child) override;
 };
 
 /* The singleton target ops object.  */
@@ -379,32 +389,32 @@  update_watch_registers_callback (thread_info *thread)
     linux_stop_lwp (lwp);
 }
 
-/* This is the implementation of linux_target_ops method
-   new_process.  */
+/* This is the implementation of linux target ops method
+   low_new_process.  */
 
-static struct arch_process_info *
-mips_linux_new_process (void)
+arch_process_info *
+mips_target::low_new_process ()
 {
   struct arch_process_info *info = XCNEW (struct arch_process_info);
 
   return info;
 }
 
-/* This is the implementation of linux_target_ops method
-   delete_process.  */
+/* This is the implementation of linux target ops method
+   low_delete_process.  */
 
-static void
-mips_linux_delete_process (struct arch_process_info *info)
+void
+mips_target::low_delete_process (arch_process_info *info)
 {
   xfree (info);
 }
 
-/* This is the implementation of linux_target_ops method new_thread.
+/* This is the implementation of linux target ops method low_new_thread.
    Mark the watch registers as changed, so the threads' copies will
    be updated.  */
 
-static void
-mips_linux_new_thread (struct lwp_info *lwp)
+void
+mips_target::low_new_thread (lwp_info *lwp)
 {
   struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
 
@@ -415,8 +425,8 @@  mips_linux_new_thread (struct lwp_info *lwp)
 
 /* Function to call when a thread is being deleted.  */
 
-static void
-mips_linux_delete_thread (struct arch_lwp_info *arch_lwp)
+void
+mips_target::low_delete_thread (arch_lwp_info *arch_lwp)
 {
   xfree (arch_lwp);
 }
@@ -444,9 +454,9 @@  mips_add_watchpoint (struct arch_process_info *priv, CORE_ADDR addr, int len,
 
 /* Hook to call when a new fork is attached.  */
 
-static void
-mips_linux_new_fork (struct process_info *parent,
-			struct process_info *child)
+void
+mips_target::low_new_fork (process_info *parent,
+			   process_info *child)
 {
   struct arch_process_info *parent_private;
   struct arch_process_info *child_private;
@@ -988,11 +998,6 @@  mips_target::get_regs_info ()
 }
 
 struct linux_target_ops the_low_target = {
-  mips_linux_new_process,
-  mips_linux_delete_process,
-  mips_linux_new_thread,
-  mips_linux_delete_thread,
-  mips_linux_new_fork,
   mips_linux_prepare_to_resume
 };
 
diff --git a/gdbserver/linux-ppc-low.cc b/gdbserver/linux-ppc-low.cc
index 625bc012623..9035b82be9d 100644
--- a/gdbserver/linux-ppc-low.cc
+++ b/gdbserver/linux-ppc-low.cc
@@ -3430,11 +3430,6 @@  ppc_get_ipa_tdesc_idx (void)
 }
 
 struct linux_target_ops the_low_target = {
-  NULL, /* new_process */
-  NULL, /* delete_process */
-  NULL, /* new_thread */
-  NULL, /* delete_thread */
-  NULL, /* new_fork */
   NULL, /* prepare_to_resume */
   NULL, /* process_qsupported */
   ppc_supports_tracepoints,
diff --git a/gdbserver/linux-s390-low.cc b/gdbserver/linux-s390-low.cc
index 937628fe25f..27bd292b20a 100644
--- a/gdbserver/linux-s390-low.cc
+++ b/gdbserver/linux-s390-low.cc
@@ -2839,11 +2839,6 @@  s390_emit_ops (void)
 }
 
 struct linux_target_ops the_low_target = {
-  NULL, /* new_process */
-  NULL, /* delete_process */
-  NULL, /* new_thread */
-  NULL, /* delete_thread */
-  NULL, /* new_fork */
   NULL, /* prepare_to_resume */
   NULL, /* process_qsupported */
   s390_supports_tracepoints,
diff --git a/gdbserver/linux-sh-low.cc b/gdbserver/linux-sh-low.cc
index 1bbfb472409..3a111658b81 100644
--- a/gdbserver/linux-sh-low.cc
+++ b/gdbserver/linux-sh-low.cc
@@ -194,11 +194,6 @@  sh_target::low_arch_setup ()
 }
 
 struct linux_target_ops the_low_target = {
-  NULL, /* new_process */
-  NULL, /* delete_process */
-  NULL, /* new_thread */
-  NULL, /* delete_thread */
-  NULL, /* new_fork */
   NULL, /* prepare_to_resume */
   NULL, /* process_qsupported */
   NULL, /* supports_tracepoints */
diff --git a/gdbserver/linux-tic6x-low.cc b/gdbserver/linux-tic6x-low.cc
index 77ffc28f186..00821e0d85f 100644
--- a/gdbserver/linux-tic6x-low.cc
+++ b/gdbserver/linux-tic6x-low.cc
@@ -425,11 +425,6 @@  tic6x_target::get_regs_info ()
 }
 
 struct linux_target_ops the_low_target = {
-  NULL, /* new_process */
-  NULL, /* delete_process */
-  NULL, /* new_thread */
-  NULL, /* delete_thread */
-  NULL, /* new_fork */
   NULL, /* prepare_to_resume */
   NULL, /* process_qsupported */
   NULL, /* supports_tracepoints */
diff --git a/gdbserver/linux-tile-low.cc b/gdbserver/linux-tile-low.cc
index 66302ef6fe5..0b2aafcf5c4 100644
--- a/gdbserver/linux-tile-low.cc
+++ b/gdbserver/linux-tile-low.cc
@@ -226,11 +226,6 @@  tile_supports_hardware_single_step (void)
 
 struct linux_target_ops the_low_target =
 {
-  NULL, /* new_process */
-  NULL, /* delete_process */
-  NULL, /* new_thread */
-  NULL, /* delete_thread */
-  NULL, /* new_fork */
   NULL, /* prepare_to_resume */
   NULL, /* process_qsupported */
   NULL, /* supports_tracepoints */
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 5d16e89aca4..b55fb44fb10 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -145,6 +145,16 @@  protected:
   /* Need to fix up i386 siginfo if host is amd64.  */
   bool low_siginfo_fixup (siginfo_t *native, gdb_byte *inf,
 			  int direction) override;
+
+  arch_process_info *low_new_process () override;
+
+  void low_delete_process (arch_process_info *info) override;
+
+  void low_new_thread (lwp_info *) override;
+
+  void low_delete_thread (arch_lwp_info *) override;
+
+  void low_new_fork (process_info *parent, process_info *child) override;
 };
 
 /* The singleton target ops object.  */
@@ -686,8 +696,8 @@  x86_target::low_stopped_data_address ()
 
 /* Called when a new process is created.  */
 
-static struct arch_process_info *
-x86_linux_new_process (void)
+arch_process_info *
+x86_target::low_new_process ()
 {
   struct arch_process_info *info = XCNEW (struct arch_process_info);
 
@@ -698,16 +708,30 @@  x86_linux_new_process (void)
 
 /* Called when a process is being deleted.  */
 
-static void
-x86_linux_delete_process (struct arch_process_info *info)
+void
+x86_target::low_delete_process (arch_process_info *info)
 {
   xfree (info);
 }
 
-/* Target routine for linux_new_fork.  */
+void
+x86_target::low_new_thread (lwp_info *lwp)
+{
+  /* This comes from nat/.  */
+  x86_linux_new_thread (lwp);
+}
 
-static void
-x86_linux_new_fork (struct process_info *parent, struct process_info *child)
+void
+x86_target::low_delete_thread (arch_lwp_info *alwp)
+{
+  /* This comes from nat/.  */
+  x86_linux_delete_thread (alwp);
+}
+
+/* Target routine for new_fork.  */
+
+void
+x86_target::low_new_fork (process_info *parent, process_info *child)
 {
   /* These are allocated by linux_add_process.  */
   gdb_assert (parent->priv != NULL
@@ -2923,11 +2947,6 @@  x86_get_ipa_tdesc_idx (void)
 
 struct linux_target_ops the_low_target =
 {
-  x86_linux_new_process,
-  x86_linux_delete_process,
-  x86_linux_new_thread,
-  x86_linux_delete_thread,
-  x86_linux_new_fork,
   x86_linux_prepare_to_resume,
   x86_linux_process_qsupported,
   x86_supports_tracepoints,
diff --git a/gdbserver/linux-xtensa-low.cc b/gdbserver/linux-xtensa-low.cc
index 957858980d6..c17c53c9bf9 100644
--- a/gdbserver/linux-xtensa-low.cc
+++ b/gdbserver/linux-xtensa-low.cc
@@ -331,11 +331,6 @@  xtensa_target::get_regs_info ()
 }
 
 struct linux_target_ops the_low_target = {
-  NULL, /* new_process */
-  NULL, /* delete_process */
-  NULL, /* new_thread */
-  NULL, /* delete_thread */
-  NULL, /* new_fork */
   NULL, /* prepare_to_resume */
   NULL, /* process_qsupported */
   NULL, /* supports_tracepoints */
diff --git a/gdbserver/thread-db.cc b/gdbserver/thread-db.cc
index 2bb6d28820e..89c45cd7b38 100644
--- a/gdbserver/thread-db.cc
+++ b/gdbserver/thread-db.cc
@@ -221,7 +221,7 @@  attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
   if (debug_threads)
     debug_printf ("Attaching to thread %ld (LWP %d)\n",
 		  (unsigned long) ti_p->ti_tid, ti_p->ti_lid);
-  err = linux_attach_lwp (ptid);
+  err = the_linux_target->attach_lwp (ptid);
   if (err != 0)
     {
       std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);