diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c
index 7c1b78a..e3fc705 100644
--- a/gdb/common/linux-ptrace.c
+++ b/gdb/common/linux-ptrace.c
@@ -414,7 +414,7 @@ linux_test_for_tracefork (int child_pid)
   ret = ptrace (PTRACE_CONT, child_pid, (PTRACE_TYPE_ARG3) 0,
 		(PTRACE_TYPE_ARG4) 0);
   if (ret != 0)
-    warning (_("linux_test_for_tracefork: failed to resume child"));
+    warning (_("linux_test_for_tracefork: failed to resume child (a)"));
 
   ret = my_waitpid (child_pid, &status, 0);
 
@@ -455,10 +455,46 @@ linux_test_for_tracefork (int child_pid)
 		       "failed to kill second child"));
 	  my_waitpid (second_pid, &status, 0);
 	}
+      else
+	{
+	  /* Fork events are not supported.  */
+	  return;
+	}
     }
   else
-    warning (_("linux_test_for_tracefork: unexpected result from waitpid "
-	     "(%d, status 0x%x)"), ret, status);
+    {
+      warning (_("linux_test_for_tracefork: unexpected result from waitpid "
+	       "(%d, status 0x%x)"), ret, status);
+      return;
+    }
+
+#ifdef GDBSERVER
+  /* PTRACE_O_FORK is supported, so now test for PTRACE_O_TRACEEXIT.
+     First try to set the option.  If this fails, we know for sure that
+     it is not supported.  */
+  ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
+		(PTRACE_TYPE_ARG4) PTRACE_O_TRACEEXIT);
+  if (ret != 0)
+    return;
+
+  /* We don't know for sure that the feature is available; old
+     versions of PTRACE_SETOPTIONS ignored unknown options.  So
+     see if the process exit will generate a PTRACE_EVENT_EXIT.  */
+  ret = ptrace (PTRACE_CONT, child_pid, (PTRACE_TYPE_ARG3) 0,
+		(PTRACE_TYPE_ARG4) 0);
+  if (ret != 0)
+    warning (_("linux_test_for_tracefork: failed to resume child (b)"));
+
+  ret = my_waitpid (child_pid, &status, 0);
+
+  /* Check if we received an exit event notification.  */
+  if (ret == child_pid && WIFSTOPPED (status)
+      && status >> 16 == PTRACE_EVENT_EXIT)
+    {
+        /* PTRACE_O_TRACEEXIT is supported.  */
+        current_ptrace_options |= PTRACE_O_TRACEEXIT;
+    }
+#endif
 }
 
 /* Enable reporting of all currently supported ptrace events.  */
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 2d8d5f5..90e7b15 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -224,6 +224,7 @@ static void proceed_all_lwps (void);
 static int finish_step_over (struct lwp_info *lwp);
 static CORE_ADDR get_stop_pc (struct lwp_info *lwp);
 static int kill_lwp (unsigned long lwpid, int signo);
+static int num_lwps (int pid);
 
 /* True if the low target can hardware single-step.  Such targets
    don't need a BREAKPOINT_REINSERT_ADDR callback.  */
@@ -367,13 +368,32 @@ linux_add_process (int pid, int attached)
   return proc;
 }
 
+/* Check wait status for extended event */
+
+static int
+is_extended_waitstatus (int wstat)
+{
+    return wstat >> 16 != 0;
+}
+
+/* Check wait status for extended event */
+
+static int
+is_extended_exit (int wstat)
+{
+    return wstat >> 16 == PTRACE_EVENT_EXIT;
+}
+
 /* Handle a GNU/Linux extended wait response.  If we see a clone
    event, we need to add the new LWP to our list (and not report the
-   trap to higher layers).  */
+   trap to higher layers).  This function returns non-zero if the
+   event should be ignored and we should wait again.  The wait status
+   in WSTATP may be modified if an exit event occurred.  */
 
-static void
-handle_extended_wait (struct lwp_info *event_child, int wstat)
+static int
+handle_extended_wait (struct lwp_info *event_child, int *wstatp)
 {
+  int wstat = *wstatp;
   int event = wstat >> 16;
   struct thread_info *event_thr = get_lwp_thread (event_child);
   struct lwp_info *new_lwp;
@@ -448,11 +468,54 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	    linux_resume_one_lwp (new_lwp, 0, WSTOPSIG (status), NULL);
 	}
 
+      /* Enable extended events for the new thread.  */
+      linux_enable_event_reporting (new_pid);
+
       /* Always resume the current thread.  If we are stopping
 	 threads, it will have a pending SIGSTOP; we may as well
 	 collect it now.  */
       linux_resume_one_lwp (event_child, event_child->stepping, 0, NULL);
+
+      /* Don't report the event.  */
+      return 1;
+     }
+  else if (event == PTRACE_EVENT_EXIT)
+    {
+      unsigned long exit_status;
+      unsigned long lwpid = lwpid_of (event_thr);
+      int ret;
+
+      if (debug_threads)
+        debug_printf ("LHEW: Got exit event from LWP %ld\n",
+                      lwpid_of (event_thr));
+
+      ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0,
+	      &exit_status);
+
+      if (num_lwps (pid_of (event_thr)) > 1)
+        {
+	  /* If there is at least one more LWP, then the program still
+	     exists and the exit should not be reported to GDB.  */
+          delete_lwp (event_child);
+          ret = 1;
+        }
+      else
+        {
+          /* Set the exit status to the actual exit status, so normal
+             WIFEXITED/WIFSIGNALLED processing and reporting for the
+             last lwp in the process can proceed from here.  */
+          *wstatp = exit_status;
+          ret = 0;
+        }
+
+      /* Resume the thread so that it actually exits.  Subsequent exit
+         events for LWPs that were deleted above will be ignored.  */
+      ptrace (PTRACE_CONT, lwpid, (PTRACE_TYPE_ARG3) 0,
+              (PTRACE_TYPE_ARG4) 0);
+      return ret;
     }
+  internal_error (__FILE__, __LINE__,
+	          _("unknown ptrace event %d"), event);
 }
 
 /* Return the PC as read from the regcache of LWP, without any
@@ -516,7 +579,7 @@ get_stop_pc (struct lwp_info *lwp)
   if (WSTOPSIG (lwp->last_status) == SIGTRAP
       && !lwp->stepping
       && !lwp->stopped_by_watchpoint
-      && lwp->last_status >> 16 == 0)
+      && !is_extended_waitstatus (lwp->last_status))
     stop_pc -= the_low_target.decr_pc_after_break;
 
   if (debug_threads)
@@ -1031,7 +1094,7 @@ get_detach_signal (struct thread_info *thread)
     }
 
   /* Extended wait statuses aren't real SIGTRAPs.  */
-  if (WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+  if (WSTOPSIG (status) == SIGTRAP && is_extended_waitstatus (status))
     {
       if (debug_threads)
 	debug_printf ("GPS: lwp %s had stopped with extended "
@@ -1716,11 +1779,13 @@ cancel_breakpoint (struct lwp_info *lwp)
 
 /* Do low-level handling of the event, and check if we should go on
    and pass it to caller code.  Return the affected lwp if we are, or
-   NULL otherwise.  */
+   NULL otherwise.  The wait status in WSTATP may be modified if an
+   exit event occurred.  */
 
 static struct lwp_info *
-linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
+linux_low_filter_event (ptid_t filter_ptid, int lwpid, int *wstatp)
 {
+  int wstat = *wstatp;
   struct lwp_info *child;
   struct thread_info *thread;
 
@@ -1772,7 +1837,7 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
      architecture being defined already (so that CHILD has a valid
      regcache), and on LAST_STATUS being set (to check for SIGTRAP or
      not).  */
-  if (WIFSTOPPED (wstat))
+  if (WIFSTOPPED (wstat) && !is_extended_exit (wstat))
     {
       if (debug_threads
 	  && the_low_target.get_pc != NULL)
@@ -1808,7 +1873,8 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
      changes the debug registers meanwhile, we have the cached data we
      can rely on.  */
 
-  if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP)
+  if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
+      && !is_extended_waitstatus (wstat))
     {
       if (the_low_target.stopped_by_watchpoint == NULL)
 	{
@@ -1844,10 +1910,17 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
     }
 
   if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
-      && wstat >> 16 != 0)
+      && is_extended_waitstatus (wstat))
     {
-      handle_extended_wait (child, wstat);
-      return NULL;
+      if (handle_extended_wait (child, &wstat))
+        return NULL;
+      else
+        {
+          /* Update caller's wstat in case handle_extended_wait fixed
+             it up to reflect the actual status.  */
+          *wstatp = wstat;
+          return child;
+        }
     }
 
   if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP
@@ -2067,7 +2140,7 @@ linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
 	    }
 
 	  event_child = linux_low_filter_event (filter_ptid,
-						ret, *wstatp);
+						ret, wstatp);
 	  if (event_child != NULL)
 	    {
 	      /* We got an event to report to the core.  */
