diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 852d02e..5617f5e 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -379,7 +379,8 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
   struct thread_info *event_thr = get_lwp_thread (event_child);
   struct lwp_info *new_lwp;
 
-  if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_CLONE)
+  if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
+      || event == PTRACE_EVENT_CLONE)
     {
       ptid_t ptid;
       unsigned long new_pid;
@@ -404,7 +405,7 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	    warning ("wait returned unexpected status 0x%x", status);
 	}
 
-      if (event == PTRACE_EVENT_FORK)
+      if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
 	{
 	  struct process_info *parent_proc;
 	  struct process_info *child_proc;
@@ -414,12 +415,10 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	  ptid = ptid_build (new_pid, new_pid, 0);
 
 	  if (debug_threads)
-	    {
-	      debug_printf ("HEW: Got fork event "
-			    "from LWP %ld, new child is %d\n",
-			    ptid_get_lwp (ptid_of (event_thr)),
-			    ptid_get_pid (ptid));
-	    }
+	    debug_printf ("HEW: Got fork event "
+			  "from LWP %ld, new child is %d\n",
+			  ptid_get_lwp (ptid_of (event_thr)),
+			  ptid_get_pid (ptid));
 
 	  /* Add the new process to the tables and clone the breakpoint
 	     lists of the parent.  We need to do this even if the new process
@@ -446,12 +445,22 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	  if (the_low_target.new_fork != NULL)
 	    the_low_target.new_fork (parent_proc, child_proc);
 
-	  /* Save fork info for target processing.  */
-	  current_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
-	  current_inferior->pending_follow.value.related_pid = ptid;
+	  /* Clone arch-specific process data.  */
+	  if (the_low_target.new_fork != NULL)
+	    the_low_target.new_fork (parent_proc, child_proc);
 
-	  /* Save fork info for reporting to GDB.  */
-	  event_child->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	  /* Save fork info for target processing and reporting to GDB.  */
+	  if (event == PTRACE_EVENT_FORK)
+	    {
+	      current_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
+	      event_child->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	    }
+	  else if (event == PTRACE_EVENT_VFORK)
+	    {
+	      current_inferior->pending_follow.kind = TARGET_WAITKIND_VFORKED;
+	      event_child->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+	    }
+	  current_inferior->pending_follow.value.related_pid = ptid;
 	  event_child->waitstatus.value.related_pid = ptid;
 
 	  /* Report the event.  */
@@ -511,6 +520,11 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
       /* Don't report the event.  */
       return 1;
     }
+  else if (event == PTRACE_EVENT_VFORK_DONE)
+    {
+      event_child->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+      return 0;
+    }
 
   internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
 }
@@ -1262,6 +1276,7 @@ linux_follow_fork (int follow_child, int detach_fork)
 {
   struct inferior_list_entry *parent_inf;
   struct thread_info *parent_thread;
+  int has_vforked;
 
   parent_inf = find_inferior (&all_threads, is_parent_callback, NULL);
 
@@ -1271,7 +1286,8 @@ linux_follow_fork (int follow_child, int detach_fork)
     return 0;
 
   parent_thread = (struct thread_info *)parent_inf;
-  parent_thread->pending_follow.kind = TARGET_WAITKIND_IGNORE;
+  has_vforked = (parent_thread->pending_follow.kind
+		 == TARGET_WAITKIND_VFORKED);
 
   if (!follow_child)
     {
@@ -1323,8 +1339,63 @@ linux_follow_fork (int follow_child, int detach_fork)
 	      current_inferior = NULL;
 	    }
 	}
+
+      if (has_vforked)
+	{
+	  struct lwp_info *parent_lp;
+
+	  parent_lp = get_thread_lwp (parent_thread);
+	  gdb_assert (linux_supports_tracefork () >= 0);
+
+	  if (linux_supports_tracevforkdone ())
+	    {
+  	      if (debug_threads)
+		{
+		  debug_printf ("LFF: waiting for VFORK_DONE on %d\n",
+				ptid_get_pid (parent_thread->entry.id));
+		}
+	      parent_lp->stopped = 1;
+
+	      /* We'll handle the VFORK_DONE event like any other
+		 event, in target_wait.  */
+	    }
+	  else
+	    {
+	      /* We can't insert breakpoints until the child has
+		 finished with the shared memory region.  Without
+		 VFORK_DONE events, the best we can do is to make sure it
+		 runs for a little while.  Hopefully it will be out of
+		 range of any breakpoints we reinsert.  Usually this
+		 is only the single-step breakpoint at vfork's return
+		 point.
+
+		 See linux-nat.c:linux_child_follow_vfork for a more
+		 detailed discussion of this.  */
+
+  	      if (debug_threads)
+  		debug_printf ("LFF: no VFORK_DONE support, sleeping a bit\n");
+
+	      usleep (10000);
+
+	      /* Pretend we've seen a PTRACE_EVENT_VFORK_DONE event,
+		 and leave it pending.  The next linux_resume_one_lwp call
+		 will notice a pending event, and bypasses actually
+		 resuming the inferior.  */
+	      parent_lp->status_pending_p = 1;
+	      parent_lp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+	      parent_lp->stopped = 1;
+
+	      /* If we're in async mode, need to tell the event loop
+		 there's something here to process.  */
+	      if (target_is_async_p ())
+		async_file_mark ();
+	    }
+	}
     }
 
+  parent_thread->pending_follow.kind = TARGET_WAITKIND_IGNORE;
+  parent_thread->pending_follow.value.related_pid = null_ptid;
+
   return 0;
 }
 
@@ -2636,7 +2707,9 @@ extended_event_reported (const struct target_waitstatus *waitstatus)
   if (waitstatus == NULL)
     return 0;
 
-  return (waitstatus->kind == TARGET_WAITKIND_FORKED);
+  return (waitstatus->kind == TARGET_WAITKIND_FORKED
+	  || waitstatus->kind == TARGET_WAITKIND_VFORKED
+	  || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
 }
 
 /* Wait for process, returns status.  */
@@ -6348,6 +6421,8 @@ initialize_low (void)
   initialize_low_arch ();
 
   /* Enable extended events.  */
-  linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK);
+  linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK
+				      | PTRACE_O_TRACEVFORK
+				      | PTRACE_O_TRACEVFORKDONE);
   linux_ptrace_check_options ();
 }
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 5152a14..e816e5e 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1106,15 +1106,18 @@ prepare_resume_reply (char *buf, ptid_t ptid,
     {
     case TARGET_WAITKIND_STOPPED:
     case TARGET_WAITKIND_FORKED:
+    case TARGET_WAITKIND_VFORKED:
       {
 	struct thread_info *saved_inferior;
 	const char **regp;
 	struct regcache *regcache;
 
-	if (status->kind == TARGET_WAITKIND_FORKED && multi_process)
+	if ((status->kind == TARGET_WAITKIND_FORKED
+	     || status->kind == TARGET_WAITKIND_VFORKED) && multi_process)
 	  {
 	    enum gdb_signal signal = GDB_SIGNAL_TRAP;
-	    const char *event = "fork";
+	    const char *event = (status->kind == TARGET_WAITKIND_FORKED
+				 ?"fork":"vfork");
 
 	    sprintf (buf, "T%02x%s:p%x.%lx;", signal, event,
 		      ptid_get_pid (status->value.related_pid),
@@ -1225,6 +1228,15 @@ prepare_resume_reply (char *buf, ptid_t ptid,
       else
 	sprintf (buf, "X%02x", status->value.sig);
       break;
+    case TARGET_WAITKIND_VFORK_DONE:
+      if (multi_process)
+	{
+	  enum gdb_signal signal = GDB_SIGNAL_TRAP;
+	  const char *event = "vforkdone";
+
+	  sprintf (buf, "T%02x%s:;", signal, event);
+	}
+      break;
     default:
       error ("unhandled waitkind");
       break;
diff --git a/gdb/remote.c b/gdb/remote.c
index 19f3efb..6939eb5 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5553,6 +5553,21 @@ Packet: '%s'\n"),
 		  event->ws.value.related_pid = read_ptid (++p1, &p);
 		  event->ws.kind = TARGET_WAITKIND_FORKED;
 		}
+	      else if (strncmp (p, "vfork", p1 - p) == 0)
+		{
+		  event->ws.value.related_pid = read_ptid (++p1, &p);
+		  event->ws.kind = TARGET_WAITKIND_VFORKED;
+		}
+	      else if (strncmp (p, "vforkdone", p1 - p) == 0)
+		{
+		  p1++;
+		  p_temp = p1;
+		  while (*p_temp && *p_temp != ';')
+		    p_temp++;
+
+		  event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
+		  p = p_temp;
+		}
 	      else
 		{
 		  /* Silently skip unknown optional info.  */
