This is the mail archive of the archer@sourceware.org mailing list for the Archer project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Proof-of-concept on fd-connected linux-nat.c server


Hi Chris,

tried to implement the file-descriptor based client for GDB as a possible
client for the kernel module which would interface utrace.  Based on my mail:
  http://sourceware.org/ml/archer/2009-q1/msg00257.html

http://sourceware.org/gdb/wiki/ArcherBranchManagement
branch archer-jankratochvil-stork

Its current client is communicating over pipe with ./stork-server which is
ptrace based.  Expecting there would be no stork-server and this pipe would be
some socket communicating directly with kernel.

Currently waitpid()+ptrace() get implemented remotely over the pipe/fd/wire as
both need to be called from the same process to work properly with the goal to
exercise proper remote waitpid() events handling in GDB.  Sure the abstraction
layer should include also kill/tkill etc. but these are not interesting from
the events processing point of view.

Just while writing the code remembered Tom Tromey was discussing maybe GDB
should always run using gdbserver and this gdbserver could be whole in the
kernel.  While writing this code I found out I just duplicate the gdbserver
client/protocol/server functionality. While the gdbserver protocol is
ASCII=inefficient I do not think it is worth fixing on hardware nowadays
- expensive is inefficient/excessive symbols reading, not the ASCII hex
mangling/demangling.  What do you think about implementing gdbserver.ko?
(Unfortunately I have not much knowledge on gdbserver.)

It was only a proof-of-concept, the code below supports only attach
(+cont/step/detach), spawning new inferiors (=PTRACE_TRACEME) is not supported.

The change included some in fact unrelated parts:

* Removing local queue (waitpid_queue) would be IMHO good even for current FSF
  GDB HEAD - replaced here by new_stork_data_event_handler, discussed there:
+/* `new_stork_data_*' helpers are here to solve a different way the original
+   problem described at the top-level doc by:
+
+   # Before we return from linux_nat_wait, we transfer all unprocessed events
+   # from local queue back to the pipe, so that when we get back to event loop,
+   # event loop will notice there's something more to do.
+
+   Stork no longer needs any local pipe for its primary functionality as it
+   does not wait on any local SIGCHLDs.  It could use the similiar way
+   connection to the Stork server - to send SRT_PING which would generate
+   a response and GDB event loop would detect incoming data from the connection
+   file descriptor.  Other way would be to create a local pipe exclusively for
+   the notification of GDB event loop in the case all the events were processed
+   from the file descriptor and all are stored now locally in
+   response_waitpid_list;
+
+   Instead Stork uses new async_event_handler for this purpose - the original
+   code could also use it without Stork.
+
+   Still push_waitpid and linux_nat_event_pipe_push originally in use had to be
+   emulated somehow.  GDB could instead generate expected waitpid events for
+   real by kill_lwp (plus PTRACE_CONT as the inferior is usually stopped that
+   time which would generate no new waitpid event).  Other way would be to
+   request returning the expected event forth and back from the Stork server.
+   stork.c instead now generates an artificial internal-only event such as if
+   it came from the Stork server (by a stork.c function push_waitpid).  */

* Code for linux_supports_tracefork_flag and linux_supports_tracevforkdone_flag
  was removed as any utrace kernel will support it and more Stork code would be
  needed to make the tests working.

Whole SIGCHLD / signals / signals masking handling is gone by this patch.

Tested on Fedora 11 x86_64 today's snapshot for both sync and async mode:
killall -9 sleep gdb stork-server;sleep 1h&pid=$!;./gdb -nx -ex "attach $pid";kill -9 $pid
killall -9 sleep gdb stork-server;sleep 1h&pid=$!;./gdb -nx -ex "set non-stop yes" -ex "set target-async yes" -ex "attach $pid";kill -9 $pid
# ... and some bt, cont, ctrl-c, detach, not much functionality tried.


Regards,
Jan


commit 3be2de5be2d72369bf6b7f26f2171653539f96cf
---
 gdb/Makefile.in      |   11 +-
 gdb/linux-nat.c      |  811 ++++++--------------------------------------------
 gdb/stork-protocol.h |  202 +++++++++++++
 gdb/stork-server.c   |  332 +++++++++++++++++++++
 gdb/stork.c          |  608 +++++++++++++++++++++++++++++++++++++
 gdb/stork.h          |   50 +++
 6 files changed, 1297 insertions(+), 717 deletions(-)
 create mode 100644 gdb/stork-protocol.h
 create mode 100644 gdb/stork-server.c
 create mode 100644 gdb/stork.c
 create mode 100644 gdb/stork.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index bafd73d..803d7cc 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -853,7 +853,7 @@ generated_files = config.h observer.h observer.inc ada-lex.c \
 	$(COMPILE) $<
 	$(POSTCOMPILE)
 
-all: gdb$(EXEEXT) $(CONFIG_ALL)
+all: stork-server gdb$(EXEEXT) $(CONFIG_ALL)
 	@$(MAKE) $(FLAGS_TO_PASS) DO=all "DODIRS=`echo $(SUBDIRS) | sed 's/testsuite//'`" subdir_do
 .PHONY: all-tui
 all-tui: $(TUI)$(EXEEXT)
@@ -1071,6 +1071,13 @@ gdb$(EXEEXT): gdb.o libgdb.a $(ADD_DEPS) $(CDEPS) $(TDEPLIBS)
 		-o gdb$(EXEEXT) gdb.o libgdb.a \
 		$(TDEPLIBS) $(TUI_LIBRARY) $(CLIBS) $(LOADLIBES)
 
+stork-server: stork-server.o
+	$(CC_LD) $(INTERNAL_LDFLAGS) -o stork-server stork-server.o
+
+stork-server.o: $(srcdir)/stork-server.c
+	$(COMPILE) $(srcdir)/stork-server.c
+	$(POSTCOMPILE)
+
 $(TUI)$(EXEEXT): tui-main.o libgdb.a $(ADD_DEPS) $(CDEPS) $(TDEPLIBS)
 	rm -f $(TUI)$(EXEEXT)
 	$(CC_LD) $(INTERNAL_LDFLAGS) $(WIN32LDAPP) \
@@ -1895,7 +1902,7 @@ python-value.o: $(srcdir)/python/python-value.c
 # A list of all the objects we might care about in this build, for
 # dependency tracking.
 all_object_files = gdb.o tui-main.o $(LIBGDB_OBS) gdbtk-main.o \
-	test-cp-name-parser.o
+	test-cp-name-parser.o stork.o stork-server.o
 
 # Ensure that generated files are created early.  Use order-only
 # dependencies if available.  They require GNU make 3.80 or newer,
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 22d721a..4a2d926 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -53,6 +53,7 @@
 #include <sys/types.h>
 #include "gdb_dirent.h"
 #include "xml-support.h"
+#include "stork.h"
 
 #ifdef HAVE_PERSONALITY
 # include <sys/personality.h>
@@ -276,52 +277,12 @@ struct simple_pid_list
 };
 struct simple_pid_list *stopped_pids;
 
-/* This variable is a tri-state flag: -1 for unknown, 0 if PTRACE_O_TRACEFORK
-   can not be used, 1 if it can.  */
-
-static int linux_supports_tracefork_flag = -1;
-
-/* If we have PTRACE_O_TRACEFORK, this flag indicates whether we also have
-   PTRACE_O_TRACEVFORKDONE.  */
-
-static int linux_supports_tracevforkdone_flag = -1;
-
 /* Async mode support */
 
 /* Zero if the async mode, although enabled, is masked, which means
    linux_nat_wait should behave as if async mode was off.  */
 static int linux_nat_async_mask_value = 1;
 
-/* The read/write ends of the pipe registered as waitable file in the
-   event loop.  */
-static int linux_nat_event_pipe[2] = { -1, -1 };
-
-/* Number of queued events in the pipe.  */
-static volatile int linux_nat_num_queued_events;
-
-/* The possible SIGCHLD handling states.  */
-
-enum sigchld_state
-{
-  /* SIGCHLD disabled, with action set to sigchld_handler, for the
-     sigsuspend in linux_nat_wait.  */
-  sigchld_sync,
-  /* SIGCHLD enabled, with action set to async_sigchld_handler.  */
-  sigchld_async,
-  /* Set SIGCHLD to default action.  Used while creating an
-     inferior.  */
-  sigchld_default
-};
-
-/* The current SIGCHLD handling state.  */
-static enum sigchld_state linux_nat_async_events_state;
-
-static enum sigchld_state linux_nat_async_events (enum sigchld_state enable);
-static void pipe_to_local_event_queue (void);
-static void local_event_queue_to_pipe (void);
-static void linux_nat_event_pipe_push (int pid, int status, int options);
-static int linux_nat_event_pipe_pop (int* ptr_status, int* ptr_options);
-static void linux_nat_set_async_mode (int on);
 static void linux_nat_async (void (*callback)
 			     (enum inferior_event_type event_type, void *context),
 			     void *context);
@@ -330,20 +291,6 @@ static int kill_lwp (int lwpid, int signo);
 
 static int stop_callback (struct lwp_info *lp, void *data);
 
-/* Captures the result of a successful waitpid call, along with the
-   options used in that call.  */
-struct waitpid_result
-{
-  int pid;
-  int status;
-  int options;
-  struct waitpid_result *next;
-};
-
-/* A singly-linked list of the results of the waitpid calls performed
-   in the async SIGCHLD handler.  */
-static struct waitpid_result *waitpid_queue = NULL;
-
 /* Similarly to `waitpid', but check the local event queue instead of
    querying the kernel queue.  If PEEK, don't remove the event found
    from the queue.  */
@@ -351,68 +298,7 @@ static struct waitpid_result *waitpid_queue = NULL;
 static int
 queued_waitpid_1 (int pid, int *status, int flags, int peek)
 {
-  struct waitpid_result *msg = waitpid_queue, *prev = NULL;
-
-  if (debug_linux_nat_async)
-    fprintf_unfiltered (gdb_stdlog,
-			"\
-QWPID: linux_nat_async_events_state(%d), linux_nat_num_queued_events(%d)\n",
-			linux_nat_async_events_state,
-			linux_nat_num_queued_events);
-
-  if (flags & __WALL)
-    {
-      for (; msg; prev = msg, msg = msg->next)
-	if (pid == -1 || pid == msg->pid)
-	  break;
-    }
-  else if (flags & __WCLONE)
-    {
-      for (; msg; prev = msg, msg = msg->next)
-	if (msg->options & __WCLONE
-	    && (pid == -1 || pid == msg->pid))
-	  break;
-    }
-  else
-    {
-      for (; msg; prev = msg, msg = msg->next)
-	if ((msg->options & __WCLONE) == 0
-	    && (pid == -1 || pid == msg->pid))
-	  break;
-    }
-
-  if (msg)
-    {
-      int pid;
-
-      if (status)
-	*status = msg->status;
-      pid = msg->pid;
-
-      if (debug_linux_nat_async)
-	fprintf_unfiltered (gdb_stdlog, "QWPID: pid(%d), status(%x)\n",
-			    pid, msg->status);
-
-      if (!peek)
-	{
-	  if (prev)
-	    prev->next = msg->next;
-	  else
-	    waitpid_queue = msg->next;
-
-	  msg->next = NULL;
-	  xfree (msg);
-	}
-
-      return pid;
-    }
-
-  if (debug_linux_nat_async)
-    fprintf_unfiltered (gdb_stdlog, "QWPID: miss\n");
-
-  if (status)
-    *status = 0;
-  return -1;
+  return stork_waitpid_1 (pid, status, flags, peek);
 }
 
 /* Similarly to `waitpid', but check the local event queue.  */
@@ -423,37 +309,24 @@ queued_waitpid (int pid, int *status, int flags)
   return queued_waitpid_1 (pid, status, flags, 0);
 }
 
+/* Drain all queued events of PID.  If PID is -1, the effect is of
+   draining all events.  */
 static void
-push_waitpid (int pid, int status, int options)
+drain_queued_events (int pid)
 {
-  struct waitpid_result *event, *new_event;
+  /* This is not fully correct as stork_waitpid_data_available will not wait
+     for future events the server will receive.  The former code did wait for
+     any processes still running.
 
-  new_event = xmalloc (sizeof (*new_event));
-  new_event->pid = pid;
-  new_event->status = status;
-  new_event->options = options;
-  new_event->next = NULL;
+     But it is correct for the both current callers of drain_queued_events as
+     they only cleanup already killed/detached inferiors.  */
 
-  if (waitpid_queue)
+  while (stork_waitpid_data_available ())
     {
-      for (event = waitpid_queue;
-	   event && event->next;
-	   event = event->next)
-	;
+      int ret = queued_waitpid (pid, NULL, __WALL);
 
-      event->next = new_event;
+      gdb_assert (ret != -1);
     }
-  else
-    waitpid_queue = new_event;
-}
-
-/* Drain all queued events of PID.  If PID is -1, the effect is of
-   draining all events.  */
-static void
-drain_queued_events (int pid)
-{
-  while (queued_waitpid (pid, NULL, __WALL) != -1)
-    ;
 }
 
 
@@ -512,149 +385,8 @@ linux_tracefork_child (void)
 static int
 my_waitpid (int pid, int *status, int flags)
 {
-  int ret;
-
-  /* There should be no concurrent calls to waitpid.  */
-  gdb_assert (linux_nat_async_events_state == sigchld_sync);
-
-  ret = queued_waitpid (pid, status, flags);
-  if (ret != -1)
-    return ret;
-
-  do
-    {
-      ret = waitpid (pid, status, flags);
-    }
-  while (ret == -1 && errno == EINTR);
-
-  return ret;
-}
-
-/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events.
-
-   First, we try to enable fork tracing on ORIGINAL_PID.  If this fails,
-   we know that the feature is not available.  This may change the tracing
-   options for ORIGINAL_PID, but we'll be setting them shortly anyway.
-
-   However, if it succeeds, we don't know for sure that the feature is
-   available; old versions of PTRACE_SETOPTIONS ignored unknown options.  We
-   create a child process, attach to it, use PTRACE_SETOPTIONS to enable
-   fork tracing, and let it fork.  If the process exits, we assume that we
-   can't use TRACEFORK; if we get the fork notification, and we can extract
-   the new child's PID, then we assume that we can.  */
-
-static void
-linux_test_for_tracefork (int original_pid)
-{
-  int child_pid, ret, status;
-  long second_pid;
-  enum sigchld_state async_events_original_state;
-
-  async_events_original_state = linux_nat_async_events (sigchld_sync);
-
-  linux_supports_tracefork_flag = 0;
-  linux_supports_tracevforkdone_flag = 0;
-
-  ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACEFORK);
-  if (ret != 0)
-    return;
-
-  child_pid = fork ();
-  if (child_pid == -1)
-    perror_with_name (("fork"));
-
-  if (child_pid == 0)
-    linux_tracefork_child ();
-
-  ret = my_waitpid (child_pid, &status, 0);
-  if (ret == -1)
-    perror_with_name (("waitpid"));
-  else if (ret != child_pid)
-    error (_("linux_test_for_tracefork: waitpid: unexpected result %d."), ret);
-  if (! WIFSTOPPED (status))
-    error (_("linux_test_for_tracefork: waitpid: unexpected status %d."), status);
-
-  ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACEFORK);
-  if (ret != 0)
-    {
-      ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
-      if (ret != 0)
-	{
-	  warning (_("linux_test_for_tracefork: failed to kill child"));
-	  linux_nat_async_events (async_events_original_state);
-	  return;
-	}
-
-      ret = my_waitpid (child_pid, &status, 0);
-      if (ret != child_pid)
-	warning (_("linux_test_for_tracefork: failed to wait for killed child"));
-      else if (!WIFSIGNALED (status))
-	warning (_("linux_test_for_tracefork: unexpected wait status 0x%x from "
-		 "killed child"), status);
-
-      linux_nat_async_events (async_events_original_state);
-      return;
-    }
-
-  /* Check whether PTRACE_O_TRACEVFORKDONE is available.  */
-  ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0,
-		PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE);
-  linux_supports_tracevforkdone_flag = (ret == 0);
-
-  ret = ptrace (PTRACE_CONT, child_pid, 0, 0);
-  if (ret != 0)
-    warning (_("linux_test_for_tracefork: failed to resume child"));
-
-  ret = my_waitpid (child_pid, &status, 0);
-
-  if (ret == child_pid && WIFSTOPPED (status)
-      && status >> 16 == PTRACE_EVENT_FORK)
-    {
-      second_pid = 0;
-      ret = ptrace (PTRACE_GETEVENTMSG, child_pid, 0, &second_pid);
-      if (ret == 0 && second_pid != 0)
-	{
-	  int second_status;
-
-	  linux_supports_tracefork_flag = 1;
-	  my_waitpid (second_pid, &second_status, 0);
-	  ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
-	  if (ret != 0)
-	    warning (_("linux_test_for_tracefork: failed to kill second child"));
-	  my_waitpid (second_pid, &status, 0);
-	}
-    }
-  else
-    warning (_("linux_test_for_tracefork: unexpected result from waitpid "
-	     "(%d, status 0x%x)"), ret, status);
-
-  ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
-  if (ret != 0)
-    warning (_("linux_test_for_tracefork: failed to kill child"));
-  my_waitpid (child_pid, &status, 0);
-
-  linux_nat_async_events (async_events_original_state);
-}
-
-/* Return non-zero iff we have tracefork functionality available.
-   This function also sets linux_supports_tracefork_flag.  */
-
-static int
-linux_supports_tracefork (int pid)
-{
-  if (linux_supports_tracefork_flag == -1)
-    linux_test_for_tracefork (pid);
-  return linux_supports_tracefork_flag;
+  return queued_waitpid (pid, status, flags);
 }
-
-static int
-linux_supports_tracevforkdone (int pid)
-{
-  if (linux_supports_tracefork_flag == -1)
-    linux_test_for_tracefork (pid);
-  return linux_supports_tracevforkdone_flag;
-}
-
 
 void
 linux_enable_event_reporting (ptid_t ptid)
@@ -665,13 +397,8 @@ linux_enable_event_reporting (ptid_t ptid)
   if (pid == 0)
     pid = ptid_get_pid (ptid);
 
-  if (! linux_supports_tracefork (pid))
-    return;
-
   options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC
-    | PTRACE_O_TRACECLONE;
-  if (linux_supports_tracevforkdone (pid))
-    options |= PTRACE_O_TRACEVFORKDONE;
+    | PTRACE_O_TRACECLONE | PTRACE_O_TRACEVFORKDONE;
 
   /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support
      read-only process state.  */
@@ -755,52 +482,13 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
 
       if (has_vforked)
 	{
-	  gdb_assert (linux_supports_tracefork_flag >= 0);
-	  if (linux_supports_tracevforkdone (0))
-	    {
-	      int status;
+	  int status;
 
-	      ptrace (PTRACE_CONT, parent_pid, 0, 0);
-	      my_waitpid (parent_pid, &status, __WALL);
-	      if ((status >> 16) != PTRACE_EVENT_VFORK_DONE)
-		warning (_("Unexpected waitpid result %06x when waiting for "
-			 "vfork-done"), status);
-	    }
-	  else
-	    {
-	      /* We can't insert breakpoints until the child has
-		 finished with the shared memory region.  We need to
-		 wait until that happens.  Ideal would be to just
-		 call:
-		 - ptrace (PTRACE_SYSCALL, parent_pid, 0, 0);
-		 - waitpid (parent_pid, &status, __WALL);
-		 However, most architectures can't handle a syscall
-		 being traced on the way out if it wasn't traced on
-		 the way in.
-
-		 We might also think to loop, continuing the child
-		 until it exits or gets a SIGTRAP.  One problem is
-		 that the child might call ptrace with PTRACE_TRACEME.
-
-		 There's no simple and reliable way to figure out when
-		 the vforked child will be done with its copy of the
-		 shared memory.  We could step it out of the syscall,
-		 two instructions, let it go, and then single-step the
-		 parent once.  When we have hardware single-step, this
-		 would work; with software single-step it could still
-		 be made to work but we'd have to be able to insert
-		 single-step breakpoints in the child, and we'd have
-		 to insert -just- the single-step breakpoint in the
-		 parent.  Very awkward.
-
-		 In the end, 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.  */
-
-	      usleep (10000);
-	    }
+	  ptrace (PTRACE_CONT, parent_pid, 0, 0);
+	  my_waitpid (parent_pid, &status, __WALL);
+	  if ((status >> 16) != PTRACE_EVENT_VFORK_DONE)
+	    warning (_("Unexpected waitpid result %06x when waiting for "
+		     "vfork-done"), status);
 
 	  /* Since we vforked, breakpoints were removed in the parent
 	     too.  Put them back.  */
@@ -913,22 +601,16 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
 static void
 linux_child_insert_fork_catchpoint (int pid)
 {
-  if (! linux_supports_tracefork (pid))
-    error (_("Your system does not support fork catchpoints."));
 }
 
 static void
 linux_child_insert_vfork_catchpoint (int pid)
 {
-  if (!linux_supports_tracefork (pid))
-    error (_("Your system does not support vfork catchpoints."));
 }
 
 static void
 linux_child_insert_exec_catchpoint (int pid)
 {
-  if (!linux_supports_tracefork (pid))
-    error (_("Your system does not support exec catchpoints."));
 }
 
 /* On GNU/Linux there are no real LWP's.  The closest thing to LWP's
@@ -971,23 +653,6 @@ struct lwp_info *lwp_list;
 static int num_lwps;
 
 
-/* Original signal mask.  */
-static sigset_t normal_mask;
-
-/* Signal mask for use with sigsuspend in linux_nat_wait, initialized in
-   _initialize_linux_nat.  */
-static sigset_t suspend_mask;
-
-/* SIGCHLD action for synchronous mode.  */
-struct sigaction sync_sigchld_action;
-
-/* SIGCHLD action for asynchronous mode.  */
-static struct sigaction async_sigchld_action;
-
-/* SIGCHLD default action, to pass to new inferiors.  */
-static struct sigaction sigchld_default_action;
-
-
 /* Prototypes for local functions.  */
 static int stop_wait_callback (struct lwp_info *lp, void *data);
 static int linux_thread_alive (ptid_t ptid);
@@ -1269,12 +934,9 @@ int
 lin_lwp_attach_lwp (ptid_t ptid)
 {
   struct lwp_info *lp;
-  enum sigchld_state async_events_original_state;
 
   gdb_assert (is_lwp (ptid));
 
-  async_events_original_state = linux_nat_async_events (sigchld_sync);
-
   lp = find_lwp_pid (ptid);
 
   /* We assume that we're already attached to any LWP that has an id
@@ -1338,7 +1000,6 @@ lin_lwp_attach_lwp (ptid_t ptid)
       lp->stopped = 1;
     }
 
-  linux_nat_async_events (async_events_original_state);
   return 0;
 }
 
@@ -1347,7 +1008,6 @@ linux_nat_create_inferior (struct target_ops *ops,
 			   char *exec_file, char *allargs, char **env,
 			   int from_tty)
 {
-  int saved_async = 0;
 #ifdef HAVE_PERSONALITY
   int personality_orig = 0, personality_set = 0;
 #endif /* HAVE_PERSONALITY */
@@ -1355,25 +1015,6 @@ linux_nat_create_inferior (struct target_ops *ops,
   /* The fork_child mechanism is synchronous and calls target_wait, so
      we have to mask the async mode.  */
 
-  if (target_can_async_p ())
-    /* Mask async mode.  Creating a child requires a loop calling
-       wait_for_inferior currently.  */
-    saved_async = linux_nat_async_mask (0);
-  else
-    {
-      /* Restore the original signal mask.  */
-      sigprocmask (SIG_SETMASK, &normal_mask, NULL);
-      /* Make sure we don't block SIGCHLD during a sigsuspend.  */
-      suspend_mask = normal_mask;
-      sigdelset (&suspend_mask, SIGCHLD);
-    }
-
-  /* Set SIGCHLD to the default action, until after execing the child,
-     since the inferior inherits the superior's signal mask.  It will
-     be blocked again in linux_nat_wait, which is only reached after
-     the inferior execing.  */
-  linux_nat_async_events (sigchld_default);
-
 #ifdef HAVE_PERSONALITY
   if (disable_randomization)
     {
@@ -1403,9 +1044,6 @@ linux_nat_create_inferior (struct target_ops *ops,
 		 safe_strerror (errno));
     }
 #endif /* HAVE_PERSONALITY */
-
-  if (saved_async)
-    linux_nat_async_mask (saved_async);
 }
 
 static void
@@ -1419,15 +1057,6 @@ linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
      attach all of them.  */
   linux_ops->to_attach (ops, args, from_tty);
 
-  if (!target_can_async_p ())
-    {
-      /* Restore the original signal mask.  */
-      sigprocmask (SIG_SETMASK, &normal_mask, NULL);
-      /* Make sure we don't block SIGCHLD during a sigsuspend.  */
-      suspend_mask = normal_mask;
-      sigdelset (&suspend_mask, SIGCHLD);
-    }
-
   /* The ptrace base target adds the main thread with (pid,0,0)
      format.  Decorate it with lwp info.  */
   ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
@@ -1714,10 +1343,6 @@ linux_nat_resume (struct target_ops *ops,
 			signo ? strsignal (signo) : "0",
 			target_pid_to_str (inferior_ptid));
 
-  if (target_can_async_p ())
-    /* Block events while we're here.  */
-    linux_nat_async_events (sigchld_sync);
-
   /* A specific PTID means `step only this process id'.  */
   resume_all = (PIDGET (ptid) == -1);
 
@@ -2740,52 +2365,6 @@ linux_nat_filter_event (int lwpid, int status, int options)
   return lp;
 }
 
-/* Get the events stored in the pipe into the local queue, so they are
-   accessible to queued_waitpid.  We need to do this, since it is not
-   always the case that the event at the head of the pipe is the event
-   we want.  */
-
-static void
-pipe_to_local_event_queue (void)
-{
-  if (debug_linux_nat_async)
-    fprintf_unfiltered (gdb_stdlog,
-			"PTLEQ: linux_nat_num_queued_events(%d)\n",
-			linux_nat_num_queued_events);
-  while (linux_nat_num_queued_events)
-    {
-      int lwpid, status, options;
-      lwpid = linux_nat_event_pipe_pop (&status, &options);
-      gdb_assert (lwpid > 0);
-      push_waitpid (lwpid, status, options);
-    }
-}
-
-/* Get the unprocessed events stored in the local queue back into the
-   pipe, so the event loop realizes there's something else to
-   process.  */
-
-static void
-local_event_queue_to_pipe (void)
-{
-  struct waitpid_result *w = waitpid_queue;
-  while (w)
-    {
-      struct waitpid_result *next = w->next;
-      linux_nat_event_pipe_push (w->pid,
-				 w->status,
-				 w->options);
-      xfree (w);
-      w = next;
-    }
-  waitpid_queue = NULL;
-
-  if (debug_linux_nat_async)
-    fprintf_unfiltered (gdb_stdlog,
-			"LEQTP: linux_nat_num_queued_events(%d)\n",
-			linux_nat_num_queued_events);
-}
-
 static ptid_t
 linux_nat_wait (struct target_ops *ops,
 		ptid_t ptid, struct target_waitstatus *ourstatus)
@@ -2814,9 +2393,6 @@ linux_nat_wait (struct target_ops *ops,
       lp->resumed = 1;
     }
 
-  /* Block events while we're here.  */
-  linux_nat_async_events (sigchld_sync);
-
 retry:
 
   /* Make sure there is at least one LWP that has been resumed.  */
@@ -2965,7 +2541,7 @@ retry:
 		  return minus_one_ptid;
 		}
 
-	      sigsuspend (&suspend_mask);
+	      stork_waitpid_data_wait ();
 	    }
 	}
 
@@ -3380,21 +2956,6 @@ linux_nat_pid_to_str (struct target_ops *ops, ptid_t ptid)
   return normal_pid_to_str (ptid);
 }
 
-static void
-sigchld_handler (int signo)
-{
-  if (target_async_permitted
-      && linux_nat_async_events_state != sigchld_sync
-      && signo == SIGCHLD)
-    /* It is *always* a bug to hit this.  */
-    internal_error (__FILE__, __LINE__,
-		    "sigchld_handler called when async events are enabled");
-
-  /* Do nothing.  The only reason for this handler is that it allows
-     us to use sigsuspend in linux_nat_wait above to wait for the
-     arrival of a SIGCHLD.  */
-}
-
 /* Accepts an integer PID; Returns a string representing a file that
    can be opened to get the symbols for the child process.  */
 
@@ -4356,185 +3917,6 @@ linux_nat_async_mask (int mask)
   return current_state;
 }
 
-/* Pop an event from the event pipe.  */
-
-static int
-linux_nat_event_pipe_pop (int* ptr_status, int* ptr_options)
-{
-  struct waitpid_result event = {0};
-  int ret;
-
-  do
-    {
-      ret = read (linux_nat_event_pipe[0], &event, sizeof (event));
-    }
-  while (ret == -1 && errno == EINTR);
-
-  gdb_assert (ret == sizeof (event));
-
-  *ptr_status = event.status;
-  *ptr_options = event.options;
-
-  linux_nat_num_queued_events--;
-
-  return event.pid;
-}
-
-/* Push an event into the event pipe.  */
-
-static void
-linux_nat_event_pipe_push (int pid, int status, int options)
-{
-  int ret;
-  struct waitpid_result event = {0};
-  event.pid = pid;
-  event.status = status;
-  event.options = options;
-
-  do
-    {
-      ret = write (linux_nat_event_pipe[1], &event, sizeof (event));
-      gdb_assert ((ret == -1 && errno == EINTR) || ret == sizeof (event));
-    } while (ret == -1 && errno == EINTR);
-
-  linux_nat_num_queued_events++;
-}
-
-static void
-get_pending_events (void)
-{
-  int status, options, pid;
-
-  if (!target_async_permitted
-      || linux_nat_async_events_state != sigchld_async)
-    internal_error (__FILE__, __LINE__,
-		    "get_pending_events called with async masked");
-
-  while (1)
-    {
-      status = 0;
-      options = __WCLONE | WNOHANG;
-
-      do
-	{
-	  pid = waitpid (-1, &status, options);
-	}
-      while (pid == -1 && errno == EINTR);
-
-      if (pid <= 0)
-	{
-	  options = WNOHANG;
-	  do
-	    {
-	      pid = waitpid (-1, &status, options);
-	    }
-	  while (pid == -1 && errno == EINTR);
-	}
-
-      if (pid <= 0)
-	/* No more children reporting events.  */
-	break;
-
-      if (debug_linux_nat_async)
-	fprintf_unfiltered (gdb_stdlog, "\
-get_pending_events: pid(%d), status(%x), options (%x)\n",
-			    pid, status, options);
-
-      linux_nat_event_pipe_push (pid, status, options);
-    }
-
-  if (debug_linux_nat_async)
-    fprintf_unfiltered (gdb_stdlog, "\
-get_pending_events: linux_nat_num_queued_events(%d)\n",
-			linux_nat_num_queued_events);
-}
-
-/* SIGCHLD handler for async mode.  */
-
-static void
-async_sigchld_handler (int signo)
-{
-  if (debug_linux_nat_async)
-    fprintf_unfiltered (gdb_stdlog, "async_sigchld_handler\n");
-
-  get_pending_events ();
-}
-
-/* Set SIGCHLD handling state to STATE.  Returns previous state.  */
-
-static enum sigchld_state
-linux_nat_async_events (enum sigchld_state state)
-{
-  enum sigchld_state current_state = linux_nat_async_events_state;
-
-  if (debug_linux_nat_async)
-    fprintf_unfiltered (gdb_stdlog,
-			"LNAE: state(%d): linux_nat_async_events_state(%d), "
-			"linux_nat_num_queued_events(%d)\n",
-			state, linux_nat_async_events_state,
-			linux_nat_num_queued_events);
-
-  if (current_state != state)
-    {
-      sigset_t mask;
-      sigemptyset (&mask);
-      sigaddset (&mask, SIGCHLD);
-
-      /* Always block before changing state.  */
-      sigprocmask (SIG_BLOCK, &mask, NULL);
-
-      /* Set new state.  */
-      linux_nat_async_events_state = state;
-
-      switch (state)
-	{
-	case sigchld_sync:
-	  {
-	    /* Block target events.  */
-	    sigprocmask (SIG_BLOCK, &mask, NULL);
-	    sigaction (SIGCHLD, &sync_sigchld_action, NULL);
-	    /* Get events out of queue, and make them available to
-	       queued_waitpid / my_waitpid.  */
-	    pipe_to_local_event_queue ();
-	  }
-	  break;
-	case sigchld_async:
-	  {
-	    /* Unblock target events for async mode.  */
-
-	    sigprocmask (SIG_BLOCK, &mask, NULL);
-
-	    /* Put events we already waited on, in the pipe first, so
-	       events are FIFO.  */
-	    local_event_queue_to_pipe ();
-	    /* While in masked async, we may have not collected all
-	       the pending events.  Get them out now.  */
-	    get_pending_events ();
-
-	    /* Let'em come.   */
-	    sigaction (SIGCHLD, &async_sigchld_action, NULL);
-	    sigprocmask (SIG_UNBLOCK, &mask, NULL);
-	  }
-	  break;
-	case sigchld_default:
-	  {
-	    /* SIGCHLD default mode.  */
-	    sigaction (SIGCHLD, &sigchld_default_action, NULL);
-
-	    /* Get events out of queue, and make them available to
-	       queued_waitpid / my_waitpid.  */
-	    pipe_to_local_event_queue ();
-
-	    /* Unblock SIGCHLD.  */
-	    sigprocmask (SIG_UNBLOCK, &mask, NULL);
-	  }
-	  break;
-	}
-    }
-
-  return current_state;
-}
-
 static int async_terminal_is_ours = 1;
 
 /* target_terminal_inferior implementation.  */
@@ -4603,6 +3985,55 @@ linux_nat_async_file_handler (int error, gdb_client_data client_data)
   async_client_callback (INF_REG_EVENT, async_client_context);
 }
 
+/* `new_stork_data_*' helpers are here to solve a different way the original
+   problem described at the top-level doc by:
+
+   # Before we return from linux_nat_wait, we transfer all unprocessed events
+   # from local queue back to the pipe, so that when we get back to event loop,
+   # event loop will notice there's something more to do.
+
+   Stork no longer needs any local pipe for its primary functionality as it
+   does not wait on any local SIGCHLDs.  It could use the similiar way
+   connection to the Stork server - to send SRT_PING which would generate
+   a response and GDB event loop would detect incoming data from the connection
+   file descriptor.  Other way would be to create a local pipe exclusively for
+   the notification of GDB event loop in the case all the events were processed
+   from the file descriptor and all are stored now locally in
+   response_waitpid_list;
+
+   Instead Stork uses new async_event_handler for this purpose - the original
+   code could also use it without Stork.
+
+   Still push_waitpid and linux_nat_event_pipe_push originally in use had to be
+   emulated somehow.  GDB could instead generate expected waitpid events for
+   real by kill_lwp (plus PTRACE_CONT as the inferior is usually stopped that
+   time which would generate no new waitpid event).  Other way would be to
+   request returning the expected event forth and back from the Stork server.
+   stork.c instead now generates an artificial internal-only event such as if
+   it came from the Stork server (by a stork.c function push_waitpid).  */
+
+static struct async_event_handler *new_stork_data_event_handler;
+
+/* Function gets called from GDB event loop after this handler was marked to be
+   processed on the next GDB event loop processing.  */
+
+static void
+new_stork_data_event_handler_proc (void *unused)
+{
+  async_client_callback (INF_REG_EVENT, async_client_context);
+}
+
+/* Function gets called from stork.c when response_waitpid_list already
+   contains some waitpid events which should be processed but as they have been
+   already read from the connection the file descriptor they will no longer
+   generate linux_nat_async_file_handler event.  */
+
+static void
+new_stork_data_callback (void *unused)
+{
+  mark_async_event_handler (new_stork_data_event_handler);
+}
+
 /* target_async implementation.  */
 
 static void
@@ -4615,20 +4046,36 @@ linux_nat_async (void (*callback) (enum inferior_event_type event_type,
 
   if (callback != NULL)
     {
+      /* Original GDB code calls us multiple times.  */
+      gdb_assert (async_client_callback == NULL
+		  || async_client_callback == callback);
+
       async_client_callback = callback;
       async_client_context = context;
-      add_file_handler (linux_nat_event_pipe[0],
+      add_file_handler (stork_fd (),
 			linux_nat_async_file_handler, NULL);
 
-      linux_nat_async_events (sigchld_async);
+      if (!new_stork_data_event_handler)
+	{
+	  new_stork_data_event_handler = create_async_event_handler
+	    (new_stork_data_event_handler_proc, NULL);
+	  gdb_assert (new_stork_data_event_handler != NULL);
+	  stork_new_data_register (new_stork_data_callback, NULL);
+	}
     }
   else
     {
-      async_client_callback = callback;
-      async_client_context = context;
+      gdb_assert (context == NULL);
+      gdb_assert (async_client_callback != NULL);
 
-      linux_nat_async_events (sigchld_sync);
-      delete_file_handler (linux_nat_event_pipe[0]);
+      async_client_callback = NULL;
+      async_client_context = NULL;
+
+      delete_file_handler (stork_fd ());
+
+      gdb_assert (new_stork_data_event_handler != NULL);
+      delete_async_event_handler (&new_stork_data_event_handler);
+      gdb_assert (new_stork_data_event_handler == NULL);
     }
   return;
 }
@@ -4711,7 +4158,6 @@ linux_nat_stop (ptid_t ptid)
 {
   if (non_stop)
     {
-      linux_nat_async_events (sigchld_sync);
       iterate_over_lwps (linux_nat_stop_lwp, &ptid);
       target_async (inferior_event_handler, 0);
     }
@@ -4796,26 +4242,12 @@ linux_nat_get_siginfo (ptid_t ptid)
   return &lp->siginfo;
 }
 
-/* Enable/Disable async mode.  */
-
-static void
-linux_nat_setup_async (void)
-{
-  if (pipe (linux_nat_event_pipe) == -1)
-    internal_error (__FILE__, __LINE__,
-		    "creating event pipe failed.");
-  fcntl (linux_nat_event_pipe[0], F_SETFL, O_NONBLOCK);
-  fcntl (linux_nat_event_pipe[1], F_SETFL, O_NONBLOCK);
-}
-
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 extern initialize_file_ftype _initialize_linux_nat;
 
 void
 _initialize_linux_nat (void)
 {
-  sigset_t mask;
-
   add_info ("proc", linux_nat_info_proc_cmd, _("\
 Show /proc process information about any running process.\n\
 Specify any process id, or use the program being debugged by default.\n\
@@ -4843,39 +4275,6 @@ Enables printf debugging output."),
 			    show_debug_linux_nat_async,
 			    &setdebuglist, &showdebuglist);
 
-  /* Get the default SIGCHLD action.  Used while forking an inferior
-     (see linux_nat_create_inferior/linux_nat_async_events).  */
-  sigaction (SIGCHLD, NULL, &sigchld_default_action);
-
-  /* Block SIGCHLD by default.  Doing this early prevents it getting
-     unblocked if an exception is thrown due to an error while the
-     inferior is starting (sigsetjmp/siglongjmp).  */
-  sigemptyset (&mask);
-  sigaddset (&mask, SIGCHLD);
-  sigprocmask (SIG_BLOCK, &mask, NULL);
-
-  /* Save this mask as the default.  */
-  sigprocmask (SIG_SETMASK, NULL, &normal_mask);
-
-  /* The synchronous SIGCHLD handler.  */
-  sync_sigchld_action.sa_handler = sigchld_handler;
-  sigemptyset (&sync_sigchld_action.sa_mask);
-  sync_sigchld_action.sa_flags = SA_RESTART;
-
-  /* Make it the default.  */
-  sigaction (SIGCHLD, &sync_sigchld_action, NULL);
-
-  /* Make sure we don't block SIGCHLD during a sigsuspend.  */
-  sigprocmask (SIG_SETMASK, NULL, &suspend_mask);
-  sigdelset (&suspend_mask, SIGCHLD);
-
-  /* SIGCHLD handler for async mode.  */
-  async_sigchld_action.sa_handler = async_sigchld_handler;
-  sigemptyset (&async_sigchld_action.sa_mask);
-  async_sigchld_action.sa_flags = SA_RESTART;
-
-  linux_nat_setup_async ();
-
   add_setshow_boolean_cmd ("disable-randomization", class_support,
 			   &disable_randomization, _("\
 Set disabling of debuggee's virtual address space randomization."), _("\
@@ -4919,11 +4318,8 @@ get_signo (const char *name)
 void
 lin_thread_get_thread_signals (sigset_t *set)
 {
-  struct sigaction action;
   int restart, cancel;
-  sigset_t blocked_mask;
 
-  sigemptyset (&blocked_mask);
   sigemptyset (set);
 
   restart = get_signo ("__pthread_sig_restart");
@@ -4942,22 +4338,7 @@ lin_thread_get_thread_signals (sigset_t *set)
 
   sigaddset (set, restart);
   sigaddset (set, cancel);
-
-  /* The GNU/Linux Threads library makes terminating threads send a
-     special "cancel" signal instead of SIGCHLD.  Make sure we catch
-     those (to prevent them from terminating GDB itself, which is
-     likely to be their default action) and treat them the same way as
-     SIGCHLD.  */
-
-  action.sa_handler = sigchld_handler;
-  sigemptyset (&action.sa_mask);
-  action.sa_flags = SA_RESTART;
-  sigaction (cancel, &action, NULL);
-
-  /* We block the "cancel" signal throughout this code ...  */
-  sigaddset (&blocked_mask, cancel);
-  sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
-
-  /* ... except during a sigsuspend.  */
-  sigdelset (&suspend_mask, cancel);
 }
+
+/* Instead of config / * / *linux*.mh updates we link it in this way now.  */
+#include "stork.c"
diff --git a/gdb/stork-protocol.h b/gdb/stork-protocol.h
new file mode 100644
index 0000000..f03ff97
--- /dev/null
+++ b/gdb/stork-protocol.h
@@ -0,0 +1,202 @@
+/* Wire protocol private between stork.c and stork-server.c.
+
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <sys/procfs.h>
+#include <stdint.h>
+#include <signal.h>
+
+/* Requests sent to the server are numbered for request-response matching.  */
+
+typedef unsigned long stork_server_id;
+
+/* Asynchronous reply, there is no request for it like hypothetical
+   SRT_WAITPID.  No request will have SSI_WAITPID_RESPONSE as its ID.  */
+
+#define SSI_WAITPID_RESPONSE 0
+
+/* Used for stork_server_request.type.  */
+
+enum stork_request_type
+  {
+    /* Simulate ptrace(2).  */
+    SRT_PTRACE,
+
+    /* Return with no change, used to ensure any existing events get returned
+       from the Stork server before returning ECHILD for waitpid(WNOHANG).  */
+    SRT_PING
+  };
+
+/* Request sent from Stork client to Stork server over the wire.  */
+
+struct stork_server_request
+  {
+    stork_server_id id;
+    enum stork_request_type type;
+    union
+      {
+        struct
+	  {
+	    int request;
+	    pid_t pid;
+	    union stork_server_request_ptrace_u
+	      {
+		struct
+		  {
+		  }
+		attach;
+		struct
+		  {
+		  }
+		getregs;
+		struct
+		  {
+		  }
+		getfpregs;
+		struct
+		  {
+		    void *addr;
+		  }
+		peekuser;
+		struct
+		  {
+		    void *addr;
+		  }
+		peektext;
+		struct
+		  {
+		    void *addr;
+		    uintptr_t data;
+		  }
+		pokedata;
+		struct
+		  {
+		    int signal;
+		  }
+		cont;
+		struct
+		  {
+		    int signal;
+		  }
+		detach;
+		struct
+		  {
+		    int signal;
+		  }
+		singlestep;
+		struct
+		  {
+		    unsigned data;
+		  }
+		arch_prctl;
+		struct
+		  {
+		  }
+		getsiginfo;
+	      }
+	    u;
+	  }
+	ptrace;
+	struct
+	  {
+	  }
+	ping;
+      }
+    u;
+  };
+
+/* Response sent from Stork server to Stork client over the wire.  */
+
+struct stork_server_response
+  {
+    stork_server_id id;
+    int errno_val;
+    union
+      {
+        struct stork_server_response_waitpid
+	  {
+	    pid_t retval;	/* RETVAL is always > 0.  */
+	    int status;
+	    int options;	/* OPTIONS is WNOHANG or WNOHANG | __WCLONE.  */
+	  }
+	waitpid;
+        struct
+	  {
+	    union stork_server_response_ptrace_u
+	      {
+		struct
+		  {
+		  }
+		attach;
+		struct
+		  {
+		    elf_gregset_t regs;
+		  }
+		getregs;
+		struct
+		  {
+		    elf_fpregset_t fpregs;
+		  }
+		getfpregs;
+		struct
+		  {
+		    long retval;
+		  }
+		peekuser;
+		struct
+		  {
+		    long retval;
+		  }
+		peektext;
+		struct
+		  {
+		  }
+		pokedata;
+		struct
+		  {
+		  }
+		cont;
+		struct
+		  {
+		  }
+		detach;
+		struct
+		  {
+		  }
+		singlestep;
+		struct
+		  {
+		    void *base;
+		  }
+		arch_prctl;
+		struct
+		  {
+		    struct siginfo siginfo;
+		  }
+		getsiginfo;
+	      }
+	    u;
+	  }
+	ptrace;
+        struct
+	  {
+	  }
+	ping;
+      }
+    u;
+  };
diff --git a/gdb/stork-server.c b/gdb/stork-server.c
new file mode 100644
index 0000000..248fa25
--- /dev/null
+++ b/gdb/stork-server.c
@@ -0,0 +1,332 @@
+/* Stork server to communicate with GDB and its Stork client stork.c.
+
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#define _GNU_SOURCE	/* for ppoll(2) */
+
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <poll.h>
+#include <asm/ptrace-abi.h>
+
+#include "stork-protocol.h"
+#include "stork.h"
+
+/* Simulate GDB infrastructure without a need to use libgdb.a.  */
+
+#define _(x) x
+#define gdb_assert assert
+
+static void
+error (const char *string, ...)
+{
+  va_list ap;
+
+  va_start (ap, string);
+  vfprintf (stderr, string, ap);
+  va_end (ap);
+  fputc ('\n', stderr);
+  exit (1);
+}
+
+static void
+perror_with_name (const char *string)
+{
+  perror (string);
+  exit (1);
+}
+
+static void
+internal_error (const char *file, int line, const char *string, ...)
+{
+  va_list ap;
+
+  fprintf (stderr, "%s:%d: ", file, line);
+  va_start (ap, string);
+  vfprintf (stderr, string, ap);
+  va_end (ap);
+  exit (1);
+}
+
+/* Notify main loop SIGCHLD did happen.  */
+
+static volatile int handler_chld_hit;
+
+/* Catch SIGCHLD to notify main loop SIGCHLD did happen.  */
+
+static void
+handler_chld (int signo)
+{
+  gdb_assert (signo == SIGCHLD);
+
+  handler_chld_hit = 1;
+}
+
+/* Currently registered inferior for attach_hook.  */
+
+static pid_t attach_hook_pid;
+
+/* Delay PTRACE_SETOPTIONS until the inferior gets stopped first as otherwise
+   we could get ESRCH trying PTRACE_SETOPTIONS too early.  */
+
+static void
+attach_hook (pid_t pid)
+{
+  if (attach_hook_pid != pid)
+    return;
+
+  if (ptrace (PTRACE_SETOPTIONS, pid, NULL,
+              (void *) (PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK
+			| PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE
+			| PTRACE_O_TRACEVFORKDONE)) != 0)
+    perror_with_name ("stork-server PTRACE_SETOPTIONS");
+  attach_hook_pid = 0;
+}
+
+/* Send RESPONSE over the wire to Stork client.  */
+
+static void
+response_send (struct stork_server_response *response)
+{
+  if (write (STDOUT_FILENO, response, sizeof (*response)) != sizeof (*response))
+    perror_with_name ("stork-server response write");
+}
+
+/* Generate and send response to REQUEST.  */
+
+static void
+process_request (struct stork_server_request *request)
+{
+  struct stork_server_response response;
+
+  gdb_assert (request->id != SSI_WAITPID_RESPONSE);
+  response.id = request->id;
+
+  errno = 0;
+  response.errno_val = 0;
+  switch (request->type)
+    {
+    case SRT_PTRACE: {
+      union stork_server_request_ptrace_u *request_u = &request->u.ptrace.u;
+      union stork_server_response_ptrace_u *response_u = &response.u.ptrace.u;
+      pid_t pid = request->u.ptrace.pid;
+      int request_code = request->u.ptrace.request;
+
+      switch (request_code)
+	{
+	  case PTRACE_ATTACH:
+	    ptrace (request_code, pid, NULL, NULL);
+	    gdb_assert (attach_hook_pid == 0);
+	    attach_hook_pid = pid;
+	    break;
+	  case PTRACE_GETREGS:
+	    ptrace (request_code, pid, NULL, &response_u->getregs.regs);
+	    break;
+	  case PTRACE_GETFPREGS:
+	    ptrace (request_code, pid, NULL, &response_u->getfpregs.fpregs);
+	    break;
+	  case PTRACE_PEEKUSER:
+	    response_u->peekuser.retval = ptrace (request_code, pid,
+						  request_u->peekuser.addr,
+						  NULL);
+	    break;
+	  case PTRACE_PEEKTEXT:
+	    response_u->peektext.retval = ptrace (request_code, pid,
+						  request_u->peektext.addr,
+						  NULL);
+	    /* Workaround GDB bug, it accesses invalid location right after:
+	       { frame_pc_unwind (this_frame=-1) -> 0xa98a5120 }  */
+	    response.errno_val = errno;
+	    errno = 0;
+	    break;
+	  case PTRACE_POKEDATA:
+	    ptrace (request_code, pid, request_u->pokedata.addr,
+		    (void *) request_u->pokedata.data);
+	    break;
+	  case PTRACE_CONT:
+	    ptrace (request_code, pid, NULL,
+		    (void *) (intptr_t) request_u->cont.signal);
+	    break;
+	  case PTRACE_DETACH:
+	    ptrace (request_code, pid, NULL,
+		    (void *) (intptr_t) request_u->detach.signal);
+	    break;
+	  case PTRACE_SINGLESTEP:
+	    ptrace (request_code, pid, NULL,
+		    (void *) (intptr_t) request_u->singlestep.signal);
+	    break;
+	  case PTRACE_ARCH_PRCTL:
+	    ptrace (request_code, pid, &response_u->arch_prctl.base,
+		    (void *) (uintptr_t) request_u->arch_prctl.data);
+	    break;
+	  case PTRACE_GETSIGINFO:
+	    ptrace (request_code, pid, NULL, &response_u->getsiginfo.siginfo);
+	    break;
+	  default:
+	    internal_error (__FILE__, __LINE__,
+			    _("stork-server ptrace type %d"), request_code);
+	    break;
+	}
+      } break;
+    case SRT_PING:
+      break;
+    default:
+      internal_error (__FILE__, __LINE__, _("stork-server request type %d"),
+		      request->type);
+    }
+  if (errno)
+    perror_with_name ("stork-server ptrace unexpected error");
+
+  response_send (&response);
+}
+
+/* Check and send possibly already received notifications from inferiors of
+   type OPTIONS.  */
+
+static void
+process_chld_type (int options, const char *error_string)
+{
+  struct stork_server_response response;
+  struct stork_server_response_waitpid *response_s = &response.u.waitpid;
+
+  response.id = SSI_WAITPID_RESPONSE;
+  response_s->options = options;
+
+  for (;;)
+    {
+      errno = 0;
+      response_s->retval = waitpid (-1, &response_s->status,
+				    response_s->options);
+      response.errno_val = errno;
+      if (response_s->retval == 0
+	  || (response_s->retval == -1 && response.errno_val == ECHILD))
+	break;
+      if (response_s->retval == -1)
+	perror_with_name (error_string);
+      gdb_assert (response_s->retval > 0);
+
+      response_send (&response);
+
+      attach_hook (response_s->retval);
+    }
+}
+
+/* Check and send possibly already received notifications from inferiors.  */
+
+static void
+process_chld (void)
+{
+  process_chld_type (WNOHANG, "stork-server waitpid(WNOHANG)");
+  process_chld_type (WNOHANG | __WCLONE,
+		     "stork-server waitpid(WNOHANG | __WCLONE)");
+}
+
+/* Act as Stork server reading requests on STDIN_FILENO and sending
+   responses+events on STDOUT_FILENO.  */
+
+int
+main (void)
+{
+  union
+    {
+      struct stork_server_request request;
+      unsigned char buf[sizeof (struct stork_server_request)];
+    }
+  u;
+  size_t u_buf_count = 0;
+  struct sigaction act;
+  sigset_t mask_chld, mask_empty;
+
+  sigemptyset (&mask_chld);
+  sigaddset (&mask_chld, SIGCHLD);
+  if (sigprocmask (SIG_SETMASK, &mask_chld, NULL) != 0)
+    perror_with_name ("stork-server sigprocmask SIGCHLD");
+  sigemptyset (&mask_empty);
+
+  memset (&act, 0, sizeof (act));
+  act.sa_handler = handler_chld;
+  sigemptyset (&act.sa_mask);
+  if (sigaction (SIGCHLD, &act, NULL) != 0)
+    perror_with_name ("stork-server sigaction SIGCHLD");
+
+  for (;;)
+    {
+      ssize_t ssize;
+      struct pollfd pollfd;
+      int ppoll_err;
+
+      pollfd.fd = STDIN_FILENO;
+      pollfd.events = POLLIN | POLLPRI;
+#ifdef POLLRDHUP
+      pollfd.events |= POLLRDHUP;
+#endif
+      ppoll_err = ppoll (&pollfd, 1, NULL, &mask_empty);
+      if (ppoll_err == -1)
+	{
+	  if (errno != EINTR)
+	    perror_with_name ("stork-server ppoll");
+	}
+      else if (ppoll_err != 1)
+	error ("stork-server ppoll = %d\n", ppoll_err);
+
+      if (ppoll_err == 1)
+	{
+	  if (pollfd.revents == POLLHUP)
+	    error ("stork-server POLLHUP");
+	  if (pollfd.revents != POLLIN)
+	    error ("stork-server ppoll revents = 0x%x\n", pollfd.revents);
+	}
+
+      if (ppoll_err == 1)
+	{
+	  gdb_assert (u_buf_count < sizeof (struct stork_server_request));
+	  ssize = read (STDIN_FILENO, &u.buf[u_buf_count],
+			sizeof (u.buf) - u_buf_count);
+	  if (ssize == 0)
+	    error ("stork-server EOF");
+	  else if (ssize == -1)
+	    perror_with_name ("stork-server read = -1");
+	  else if (ssize > 0)
+	    {
+	      u_buf_count += ssize;
+	      gdb_assert (u_buf_count <= sizeof (struct stork_server_request));
+	    }
+	  else
+	    gdb_assert (0);
+
+	  if (u_buf_count == sizeof (struct stork_server_request));
+	    {
+	      u_buf_count = 0;
+	      process_request (&u.request);
+	    }
+	}
+
+      if (handler_chld_hit)
+	{
+	  handler_chld_hit = 0;
+
+	  process_chld ();
+	}
+    }
+}
diff --git a/gdb/stork.c b/gdb/stork.c
new file mode 100644
index 0000000..34b95ff
--- /dev/null
+++ b/gdb/stork.c
@@ -0,0 +1,608 @@
+/* Stork client to link with GDB and communicator with stork-server.c.
+
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#define STORK_SERVER_PATHNAME "./stork-server"
+/* #define STORK_SERVER_STRACE */
+
+#include <dlfcn.h>
+#include <asm/ptrace-abi.h>
+
+#include "stork.h"
+#include "stork-protocol.h"
+
+/* Pipe used for reading and writing in the GDB process.  */
+
+static int server_read, server_write;
+
+/* Non-zero if SERVER_READ and SERVER_WRITE are valid.  */
+
+static int server_started;
+
+/* Ensure valid SERVER_READ and SERVER_WRITE.  */
+
+static void
+stork_start (void)
+{
+  int fds_for_server_read[2], fds_for_server_write[2];
+
+  if (server_started)
+    return;
+
+  if (pipe2 (fds_for_server_read, O_CLOEXEC) != 0)
+    perror_with_name ("stork pipe fds_for_server_read");
+  server_read = fds_for_server_read[0];
+  if (pipe2 (fds_for_server_write, O_CLOEXEC) != 0)
+    perror_with_name ("stork pipe fds_for_server_write");
+  server_write = fds_for_server_write[1];
+
+  switch (fork ())
+    {
+    case -1:
+      perror_with_name ("stork fork");
+      break;
+    case 0:
+      if (dup2 (fds_for_server_read[1], STDOUT_FILENO) != STDOUT_FILENO)
+	perror_with_name ("stork dup2 STDOUT_FILENO");
+      if (dup2 (fds_for_server_write[0], STDIN_FILENO) != STDIN_FILENO)
+	perror_with_name ("stork dup2 STDIN_FILENO");
+#ifdef STORK_SERVER_STRACE
+      execlp ("strace", "strace", STORK_SERVER_PATHNAME, NULL);
+#else
+      execl (STORK_SERVER_PATHNAME, STORK_SERVER_PATHNAME, NULL);
+#endif
+      perror_with_name ("stork exec stork_server");
+      break;
+    default:
+      if (close (fds_for_server_read[1]) != 0)
+	perror_with_name ("stork close server_read other");
+      if (close (fds_for_server_write[0]) != 0)
+	perror_with_name ("stork close server_write other");
+      break;
+    }
+
+  server_started = 1;
+}
+
+/* File descriptor to wait on for reads from the server.  */
+
+int
+stork_fd (void)
+{
+  stork_start ();
+
+  return server_read;
+}
+
+/* State for stork_new_data_register.  */
+
+static stork_new_data_proc_t new_data_proc;
+static void *new_data_data;
+
+/* Register callback to notify when some data are available in internal
+   variables while STORK_FD may be empty now.  See the
+   new_stork_data_event_handler description for more.  */
+
+void
+stork_new_data_register (stork_new_data_proc_t proc, void *data)
+{
+  new_data_proc = proc;
+  new_data_data = data;
+}
+
+/* Send REQUEST data over the write to Stork server.  */
+
+static stork_server_id
+request_send (struct stork_server_request *request)
+{
+  static stork_server_id id;
+
+  stork_start ();
+
+  do
+    id++;
+  /* Skip reserved numbers.  */
+  while (id == SSI_WAITPID_RESPONSE);
+
+  request->id = id;
+  if (write (server_write, request, sizeof (*request)) != sizeof (*request))
+    perror_with_name ("stork ptrace write");
+
+  return request->id;
+}
+
+/* Linkage helper for Stork server responses.  */
+
+struct stork_list
+  {
+    struct stork_node *head, *tail;
+  };
+
+struct stork_node
+  {
+    struct stork_node *prev, *next;
+    void *data;
+  };
+
+/* Add DATA to a new node linked at the end of LIST.  */
+
+static void
+stork_list_add_tail (struct stork_list *list, void *data)
+{
+  struct stork_node *node;
+
+  node = xmalloc (sizeof (*node));
+  node->data = data;
+  node->prev = list->tail;
+  node->next = NULL;
+  if (list->tail)
+    list->tail->next = node;
+  else
+    list->head = node;
+  list->tail = node;
+}
+
+/* Remove and xfree NODE from LIST, no matter where it is linked there.  */
+
+static void
+stork_list_remove_node (struct stork_list *list, struct stork_node *node)
+{
+  if (node->prev == NULL)
+    list->head = node->next;
+  else
+    node->prev->next = node->next;
+  if (node->next == NULL)
+    list->tail = node->prev;
+  else
+    node->next->prev = node->prev;
+  xfree (node);
+}
+
+/* List for the SSI_WAITPID_RESPONSE events, events are stored in chronological
+   order - the oldest ones first.  */
+
+static struct stork_list response_waitpid_list;
+
+/* Helper to notify GDB if any data are available only internally and no longer
+   on STORK_FD.  See the new_stork_data_event_handler description for more.
+   This function must be called between any possible new reads from the Stork
+   server and before the control gets returned to GDB.
+
+   This function could be called already from each response_receive for
+   simplifity, it is just a performance optimization to caller it later.  */
+
+static void
+new_data_check (void)
+{
+  /* No data locally queued?  */
+  if (response_waitpid_list.head == NULL)
+    return;
+
+  /* No notification handler registered?  */
+  if (new_data_proc == NULL)
+    return;
+
+  /* Possibly check if STORK_FD does not have data available and avoid the
+     callback in such case.  */
+
+  (*new_data_proc) (new_data_data);
+}
+
+/* Register RESPONSE for later processing inside this file.  */
+
+static void
+response_enlist (struct stork_server_response *response)
+{
+  /* All other responses get immediately processed without being enlisted.  */
+  gdb_assert (response->id == SSI_WAITPID_RESPONSE);
+
+  stork_list_add_tail (&response_waitpid_list, response);
+}
+
+/* Return one allocated response from the server, block till one is available.
+   The response must get RESPONSE_ENLISTed if not processed otherwise.  */
+
+static struct stork_server_response *
+response_receive (void)
+{
+  union
+    {
+      struct stork_server_response response;
+      unsigned char buf[sizeof (struct stork_server_response)];
+    }
+  *u;
+  size_t u_buf_count = 0;
+  ssize_t ssize;
+
+  u = xmalloc (sizeof (*u));
+  while (u_buf_count < sizeof (struct stork_server_response))
+    {
+      ssize = read (server_read, &u->buf[u_buf_count],
+		    sizeof (u->buf) - u_buf_count);
+      if (ssize == 0)
+        error ("stork read EOF");
+      else if (ssize == -1)
+	perror_with_name ("stork read = -1");
+      else if (ssize > 0)
+	{
+	  u_buf_count += ssize;
+	  gdb_assert (u_buf_count <= sizeof (struct stork_server_response));
+	}
+      else
+	gdb_assert (0);
+    }
+  return &u->response;
+}
+
+/* Find response with ID, block if none is available so far.  */
+
+static struct stork_server_response *
+response_find_blocking (stork_server_id id)
+{
+  gdb_assert (id != SSI_WAITPID_RESPONSE);
+
+  stork_start ();
+
+  for (;;)
+    {
+      struct stork_server_response *response;
+
+      response = response_receive ();
+
+      if (response->id == id)
+	return response;
+
+      response_enlist (response);
+    }
+}
+
+/* Send REQUEST and return its matching response after it gets processed on the
+   server.  Asynchronous events may get RESPONSE_ENLISTed in the meantime.  */
+
+static struct stork_server_response *
+request_response_sync (struct stork_server_request *request)
+{
+  stork_server_id id;
+
+  id = request_send (request);
+  return response_find_blocking (id);
+}
+
+/* Return non-zero if some asynchronous waitpid notifications are received.
+   Never wait on any inferiors.  */
+
+int
+stork_waitpid_data_available (void)
+{
+  struct stork_server_request request;
+
+  if (response_waitpid_list.head != NULL)
+    return 1;
+
+  request.type = SRT_PING;
+  xfree (request_response_sync (&request));
+
+  return response_waitpid_list.head != NULL;
+}
+
+/* Wait till at least one waitpid notification event is available.  */
+
+void
+stork_waitpid_data_wait (void)
+{
+  while (response_waitpid_list.head == NULL)
+    response_enlist (response_receive ());
+}
+
+/* Return non-zero if RESPONSE_S satisfies the PID and FLAGS requirements.  */
+
+static int
+stork_waitpid_1_compare (struct stork_server_response_waitpid *response_s,
+			 int pid, int flags)
+{
+  if (pid == -1 || response_s->retval == pid)
+    {
+      if (flags & __WALL)
+	return 1;
+      if ((flags & __WCLONE) && (response_s->options & __WCLONE))
+	return 1;
+      if (!(flags & __WCLONE) && !(response_s->options & __WCLONE))
+	return 1;
+    }
+  return 0;
+}
+
+/* Simulate original queued_waitpid_1 from the Stork server.  */
+
+pid_t
+stork_waitpid_1 (int pid, int *status, int flags, int peek)
+{
+  pid_t retval;
+  struct stork_node *node;
+  struct stork_server_response *response;
+  struct stork_server_response_waitpid *response_s;
+
+  gdb_assert (server_started);
+  gdb_assert (pid == -1 || pid > 0);
+
+  for (node = response_waitpid_list.head; node; node = node->next)
+    {
+      response = node->data;
+      gdb_assert (response->id == SSI_WAITPID_RESPONSE);
+      response_s = &response->u.waitpid;
+      if (stork_waitpid_1_compare (response_s, pid, flags))
+	break;
+    }
+
+  if (!node)
+    {
+      struct stork_server_request request;
+
+      if (flags & WNOHANG)
+	{
+	  request.type = SRT_PING;
+	  request_send (&request);
+	}
+
+      /* This loop is incorrect for !WNOHANG when no children are left at all.
+	 We should return -1/ECHILD but we become hanging.  This is currently
+	 being handled by linux-nat.c drain_queued_events as it was the only
+	 GDB part exploiting it and I find such behavior too much waitpid
+	 syscall specific to taint the generic stork layer by it.  */
+
+      for (;;)
+	{
+	  response = response_receive ();
+	  response_s = &response->u.waitpid;
+	  if ((flags & WNOHANG) && response->id == request.id)
+	    {
+	      xfree (response);
+	      new_data_check ();
+
+	      errno = ECHILD;
+	      return -1;
+	    }
+	  response_enlist (response);
+	  if (response->id != SSI_WAITPID_RESPONSE)
+	    continue;
+	  if (stork_waitpid_1_compare (response_s, pid, flags))
+	    {
+	      if (flags & WNOHANG)
+		xfree (response_find_blocking (request.id));
+
+	      gdb_assert (response_waitpid_list.tail != NULL);
+	      gdb_assert (response_waitpid_list.tail->data == response);
+	      node = response_waitpid_list.tail;
+	      break;
+	    }
+	}
+    }
+
+  retval = response_s->retval;
+  if (status)
+    *status = response_s->status;
+
+  if (!peek)
+    {
+      xfree (response);
+
+      stork_list_remove_node (&response_waitpid_list, node);
+    }
+
+  new_data_check ();
+
+  return retval;
+}
+
+/* Artificially fake waitpid notification event with the passed parameters,
+   simulate the original linux-nat.c function.  */
+
+void
+push_waitpid (int pid, int status, int options)
+{
+  struct stork_server_response *response;
+  struct stork_server_response_waitpid *response_s;
+
+  gdb_assert (pid > 0);
+  gdb_assert ((options & ~(WNOHANG | __WCLONE)) == 0);
+
+  response = xmalloc (sizeof (*response));
+  response_s = &response->u.waitpid;
+  response->id = SSI_WAITPID_RESPONSE;
+  response->errno_val = 0;
+  response_s->retval = pid;
+  response_s->status = status;
+  response_s->options = options;
+
+  response_enlist (response);
+
+  new_data_check ();
+}
+
+/* Simulate the syscall by Stork server.  */
+
+/* long
+ptrace (int request, pid_t pid, void *addr, void *data); */
+long
+ptrace (enum __ptrace_request request, ...)
+{
+  struct stork_server_request server_request;
+  struct stork_server_response *server_response;
+  union stork_server_request_ptrace_u *request_u = &server_request.u.ptrace.u;
+  union stork_server_response_ptrace_u *response_u;
+  va_list ap;
+  pid_t pid;	/* from ap */
+  void *addr;	/* from ap */
+  void *data;	/* from ap */
+
+  /* A bit incorrect: These parameters may not be passed for some REQUESTs.  */
+  va_start (ap, request);
+  pid = va_arg (ap, pid_t);
+  addr = va_arg (ap, void *);
+  data = va_arg (ap, void *);
+  va_end (ap);
+
+  server_request.type = SRT_PTRACE;
+  server_request.u.ptrace.request = request;
+  server_request.u.ptrace.pid = pid;
+  switch (request)
+    {
+    case PTRACE_TRACEME:
+      internal_error (__FILE__, __LINE__,
+		     _("stork PTRACE_TRACEME is not supported - use `attach'"));
+      break;
+    case PTRACE_ATTACH:
+      break;
+    case PTRACE_SETOPTIONS:
+      /* This options set is the only one supported by the Stork interface.  */
+      gdb_assert ((uintptr_t) data == (PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK
+				       | PTRACE_O_TRACEEXEC
+				       | PTRACE_O_TRACECLONE
+				       | PTRACE_O_TRACEVFORKDONE));
+      return 0;
+      break;
+    case PTRACE_GETREGS:
+      break;
+    case PTRACE_GETFPREGS:
+      break;
+    case PTRACE_PEEKUSER:
+      request_u->peekuser.addr = addr;
+      break;
+    case PTRACE_PEEKTEXT:
+      request_u->peektext.addr = addr;
+      break;
+    case PTRACE_POKEDATA:
+      request_u->pokedata.addr = addr;
+      request_u->pokedata.data = (uintptr_t) data;
+      break;
+    case PTRACE_CONT:
+      request_u->cont.signal = (intptr_t) data;
+      break;
+    case PTRACE_DETACH:
+      request_u->detach.signal = (intptr_t) data;
+      break;
+    case PTRACE_SINGLESTEP:
+      request_u->singlestep.signal = (intptr_t) data;
+      break;
+    case PTRACE_ARCH_PRCTL:
+      request_u->arch_prctl.data = (uintptr_t) data;
+      break;
+    case PTRACE_GETSIGINFO:
+      break;
+    default:
+      internal_error (__FILE__, __LINE__, _("stork ptrace unsupported %d"),
+		      request);
+      break;
+    }
+  server_response = request_response_sync (&server_request);
+  new_data_check ();
+  errno = server_response->errno_val;
+  response_u = &server_response->u.ptrace.u;
+  switch (request)
+    {
+    case PTRACE_ATTACH:
+      return 0;
+      break;
+    case PTRACE_GETREGS:
+      memcpy (data, &response_u->getregs.regs,
+	      sizeof (response_u->getregs.regs));
+      return 0;
+      break;
+    case PTRACE_GETFPREGS:
+      memcpy (data, &response_u->getfpregs.fpregs,
+	      sizeof (response_u->getfpregs.fpregs));
+      return 0;
+      break;
+    case PTRACE_PEEKUSER:
+      return response_u->peekuser.retval;
+      break;
+    case PTRACE_PEEKTEXT:
+      return response_u->peektext.retval;
+      break;
+    case PTRACE_POKEDATA:
+      return 0;
+      break;
+    case PTRACE_CONT:
+      return 0;
+      break;
+    case PTRACE_DETACH:
+      return 0;
+      break;
+    case PTRACE_SINGLESTEP:
+      return 0;
+      break;
+    case PTRACE_ARCH_PRCTL:
+      *(void **) addr = response_u->arch_prctl.base;
+      return 0;
+      break;
+    case PTRACE_GETSIGINFO:
+      memcpy (data, &response_u->getsiginfo.siginfo,
+	      sizeof (response_u->getsiginfo.siginfo));
+      return 0;
+      break;
+    default:
+      break;
+    }
+  internal_error (__FILE__, __LINE__, _("stork legacy_ptrace response %d"),
+		  request);
+  errno = EINVAL;
+  return -1;
+}
+
+/* Stub for existing callers.  */
+
+void
+linux_nat_event_pipe_push (int pid, int status, int options)
+{
+  push_waitpid (pid, status, options);
+}
+
+/* Stub for existing callers.  */
+
+pid_t
+waitpid (int pid, int *status, int flags)
+{
+  /* pex-unix.c calls waitpid() during its startup.  */
+  if (!server_started)
+    {
+      pid_t (*waitpid_orig) (int pid, int *status, int flags);
+
+      waitpid_orig = dlsym (RTLD_NEXT, "waitpid");
+      gdb_assert (waitpid_orig != NULL);
+
+      return (*waitpid_orig) (pid, status, flags);
+    }
+
+  return stork_waitpid_1 (pid, status, flags, 0);
+}
+
+/* Stub for existing callers.  */
+
+pid_t
+wait (int *status)
+{
+  return waitpid (-1, status, 0);
+}
+
+/* Stub for existing callers.  */
+
+int
+waitid (idtype_t idtype, id_t id, siginfo_t *infop, int options)
+{
+  internal_error (__FILE__, __LINE__, _("stork waitid not implemented"));
+}
diff --git a/gdb/stork.h b/gdb/stork.h
new file mode 100644
index 0000000..fcb68d7
--- /dev/null
+++ b/gdb/stork.h
@@ -0,0 +1,50 @@
+/* Internal GDB interface for the callers of stork.c linked with GDB.
+
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef STORK_H
+#define STORK_H
+
+#include <sys/wait.h>
+
+/* Keeping GDB event loop happy.  */
+
+extern int stork_fd (void);
+typedef void (*stork_new_data_proc_t) (void *data);
+extern void stork_new_data_register (stork_new_data_proc_t proc, void *data);
+extern int stork_waitpid_data_available (void);
+extern void stork_waitpid_data_wait (void);
+
+/* Real waitpid events handling functions.  */
+
+extern pid_t stork_waitpid_1 (int pid, int *status, int flags, int peek);
+extern void push_waitpid (int pid, int status, int options);
+
+/* Simulate the syscall by Stork server.  */
+
+/*     long ptrace (int request, pid_t pid, void *addr, void *data); */
+extern long ptrace (enum __ptrace_request request, ...);
+
+/* Stubs for existing callers.  */
+
+extern void linux_nat_event_pipe_push (int pid, int status, int options);
+extern pid_t waitpid (int pid, int *status, int flags);
+extern pid_t wait (int *status);
+extern int waitid (idtype_t idtype, id_t id, siginfo_t *infop, int options);
+
+#endif /* STORK_H */
-- 
1.6.2.2


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]