This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB 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]

Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver


On 08/21/2013 06:29 PM, Pedro Alves wrote:
On 08/21/2013 04:08 AM, Luis Machado wrote:
Hi,

On 08/20/2013 01:39 PM, Pedro Alves wrote:
On 08/20/2013 12:27 AM, Luis Machado wrote:

diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c
index d5ac061..4198d1d 100644
--- a/gdb/common/linux-ptrace.c
+++ b/gdb/common/linux-ptrace.c
@@ -25,10 +25,16 @@

   #include "linux-ptrace.h"
   #include "linux-procfs.h"
+#include "nat/linux-waitpid.h"
   #include "buffer.h"
   #include "gdb_assert.h"
   #include "gdb_wait.h"

+/* Stores the currently supported ptrace options.  A value of
+   -1 means we did not check for features yet.  A value of 0 means
+   there are no supported features.  */
+static int current_ptrace_options = -1;
+
   /* Find all possible reasons we could fail to attach PID and append these
      newline terminated reason strings to initialized BUFFER.  '\0' termination
      of BUFFER must be done by the caller.  */
@@ -222,6 +228,284 @@ linux_ptrace_test_ret_to_nx (void)
   #endif /* defined __i386__ || defined __x86_64__ */
   }

+/* Helper function to fork a process and make the child process call
+   the function FUNCTION passing ARG as parameter.  */
+
+static int
+linux_fork_to_function (void *arg, void (*function) (void *))

The describing comment and the function prototype imply some
sort of generity.  But ...

+{
+  int child_pid;
+
+  gdb_byte *stack = (gdb_byte *) arg;

The arg has a definite particular use.

+
+  /* Sanity check the function pointer.  */
+  gdb_assert (function != NULL);
+
+#if defined(__UCLIBC__) && defined(HAS_NOMMU)
+#define STACK_SIZE 4096
+
+    if (arg == NULL)
+      stack = xmalloc (STACK_SIZE * 4);

Etc.  Something's odd with the abstration.



I've improved this now. We have a special case for MMU-less targets
(gdbserver's side), though it is not quite clear due to the lack of
comments in that code.

IMO, we're still not there.

/* Helper function to fork a process and make the child process call
    the function FUNCTION passing ARG as parameter.

    For MMU-less targets, if ARG is NULL, stack space is allocated
    via malloc and passed to FUNCTION.  */

static int
linux_fork_to_function (void *arg, void (*function) (void *))

This still suggests that ARG is something generic, while it is
not.  I'm thinking this would make things clearer:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---
  gdb/common/linux-ptrace.c | 37 +++++++++++++++++--------------------
  1 file changed, 17 insertions(+), 20 deletions(-)

diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c
index bcd3c94..2fb6dd8 100644
--- a/gdb/common/linux-ptrace.c
+++ b/gdb/common/linux-ptrace.c
@@ -229,18 +229,17 @@ linux_ptrace_test_ret_to_nx (void)
  }

  /* Helper function to fork a process and make the child process call
-   the function FUNCTION passing ARG as parameter.
+   the function FUNCTION, passing CHILD_STACK as parameter.

-   For MMU-less targets, if ARG is NULL, stack space is allocated
-   via malloc and passed to FUNCTION.  */
+   For MMU-less targets, clone is used instead of fork, and
+   CHILD_STACK is used as stack space for the cloned child.  If NULL,
+   stack space is allocated via malloc (and subsequently passed to
+   FUNCTION).  For MMU targets, CHILD_STACK is ignored.  */

  static int
-linux_fork_to_function (void *arg, void (*function) (void *))
+linux_fork_to_function (gdb_byte *child_stack, void (*function) (gdb_byte *))
  {
    int child_pid;
-#if defined(__UCLIBC__) && defined(HAS_NOMMU)
-  gdb_byte *stack;
-#endif

    /* Sanity check the function pointer.  */
    gdb_assert (function != NULL);
@@ -248,24 +247,22 @@ linux_fork_to_function (void *arg, void (*function) (void *))
  #if defined(__UCLIBC__) && defined(HAS_NOMMU)
  #define STACK_SIZE 4096

-    if (arg == NULL)
-      stack = xmalloc (STACK_SIZE * 4);
-    else
-      stack = (gdb_byte *) arg;
+    if (child_stack == NULL)
+      child_stack = xmalloc (STACK_SIZE * 4);

      /* Use CLONE_VM instead of fork, to support uClinux (no MMU).  */
      #ifdef __ia64__
-      child_pid = __clone2 (function, stack, STACK_SIZE,
-			    CLONE_VM | SIGCHLD, stack + STACK_SIZE * 2);
+      child_pid = __clone2 (function, child_stack, STACK_SIZE,
+			    CLONE_VM | SIGCHLD, child_stack + STACK_SIZE * 2);
      #else /* !__ia64__ */
-      child_pid = clone (function, stack + STACK_SIZE,
-			 CLONE_VM | SIGCHLD, stack + STACK_SIZE * 2);
+      child_pid = clone (function, child_stack + STACK_SIZE,
+			 CLONE_VM | SIGCHLD, child_stack + STACK_SIZE * 2);
    #endif /* !__ia64__ */
  #else /* !defined(__UCLIBC) && defined(HAS_NOMMU) */
    child_pid = fork ();

    if (child_pid == 0)
-    function (arg);
+    function (NULL);
  #endif /* defined(__UCLIBC) && defined(HAS_NOMMU) */

    if (child_pid == -1)
@@ -278,10 +275,10 @@ linux_fork_to_function (void *arg, void (*function) (void *))
     the child forks a grandchild.  */

  static void
-linux_grandchild_function (void *arg)
+linux_grandchild_function (gdb_byte *child_stack)
  {
    /* Free any allocated stack.  */
-  xfree (arg);
+  xfree (child_stack);

    /* This code is only reacheable by the grandchild (child's child)
       process.  */
@@ -293,13 +290,13 @@ linux_grandchild_function (void *arg)
     be traced by its parent.  */

  static void
-linux_child_function (void *arg)
+linux_child_function (gdb_byte *child_stack)
  {
    ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
    kill (getpid (), SIGSTOP);

    /* Fork a grandchild.  */
-  linux_fork_to_function (arg, linux_grandchild_function);
+  linux_fork_to_function (child_stack, linux_grandchild_function);

    /* This code is only reacheable by the child (grandchild's parent)
       process.  */
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(BTW, this is a case of mixing code motion with code changes.  It'd
have been easier if this refactoring on the gdbserver side was made before
sharing code with GDB.  As is, it's hard to compare the new version with
either current GDB's or GDBserver's.)


Yeah. I see what you mean. It seemed like doing the gdbserver fix prior to merging things would be a "wasted" change, since we would've needed to change gdbserver's code again to make it work for GDB's case.

Anyway, i went with your patch here. Thanks.

+
+int
+linux_supports_tracefork (void)
+{
+  return ptrace_supports_feature (PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK
+				  | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC);

Well, this is now wrong given gdbserver will only set PTRACE_O_TRACECLONE
in the current_ptrace_options.  Given the revised comment above,
this can all be replaced with:

    return ptrace_supports_feature (PTRACE_O_TRACECLONE);

and so it'll work for both gdb and gdbserver.  When gdbserver supports
tracing forks, we can go back here and check PTRACE_O_TRACEFORK instead,
just for clarity of the code.  WDYT?

I don't think it looks great, but it should do the job for the moment
while we teach gdbserver to support the other features. Good enough maybe?

Well we could do better.  If you look at gdbserver's usage of
linux_supports_tracefork:

   /* If the kernel supports tracing forks then it also supports tracing
      clones, and then we don't need to use the magic thread event breakpoint
      to learn about threads.  */
   thread_db_init (!linux_supports_tracefork ());

... that suggests PTRACE_O_TRACECLONE is really the right
flag to check, and actually we should rename the function:

int
linux_supports_traceclone (void)
{
   return ptrace_supports_feature (PTRACE_O_TRACECLONE);
}

... but then, if we look at GDB's use of linux_supports_tracefork,
then we see it's really used to check for fork support.  So,
what we really need is both:

int
linux_supports_traceclone (void)
{
   return ptrace_supports_feature (PTRACE_O_TRACECLONE);
}

int
linux_supports_tracefork (void)
{
   return ptrace_supports_feature (PTRACE_O_TRACEFORK);
}

gdbserver's linux_look_up_symbols would then be made to use
the former.

(I don't think there's any real need to check more
than PTRACE_O_TRACEFORK in the latter function, and honestly,
checking for PTRACE_O_TRACEEXEC looked odd, given the function
is called ..._tracefork.  I think just the comment mentioning
these flags were added at the same time it sufficient).


I'm using your suggestion now. We have a linux_supports_traceclone function now.



BTW, these function are extern, and the same describing comment is
duplicated in the declarations in the header file.  Best leave only one
copy, near the declarations.


Done.

diff --git a/gdb/common/linux-ptrace.h b/gdb/common/linux-ptrace.h
index 8f02c82..005da3d 100644
--- a/gdb/common/linux-ptrace.h
+++ b/gdb/common/linux-ptrace.h
@@ -22,6 +22,24 @@ struct buffer;


+#ifdef GDBSERVER
+#if !defined (PTRACE_TYPE_ARG3)

Why the #if !defines if the PTRACE_TYPE_... definitions
have been removed from linux-low.h ?


I've replaced these with a configure-time check in configure.ac as
Tromey hinted at, similar to GDB's. Looks cleaner this way.

GDB does this at configure time because of shared modules like
inf-ptrace.c.  gdbserver doesn't have this, so hardcoding the
ptrace types is sufficient.  I have no issue with going the
configure.ac route, but if going that direction, then please
don't duplicate a bunch of code like that.  Especially not
in a patch that whose main purpose is to reduce duplication (!).
We should instead move all that ptrace autoconf code to say, a
new gdb/nat/ptrace.m4 file, included by both gdb's and gdbserver's
acinclude.m4.  I think this actually changes types of the 3rd
and 4th arguments to long, from the current void*.  Shouldn't
affect anything in practice (as long as the PTRACE_TYPE_FOO casts are
in place), but it's yet another change that could be done
separately.


I think this change will need to be included later then. I've reverted the configure bits and added back the hardcoded type definitions and casted the appropriate parameters from all the ptrace calls in common/linux-ptrace.c.

My intention is to pursue a ptrace wrapper at a later stage, so i can probably address this by then.

I've also included linux-ptrace.o by default in the linux object files
list (in configure.srv).

All the other small nits are hopefully fixed now as well.

Regression-tested again. Results look good.


+/* Determine ptrace features available on this target.  */
+
+static void
+linux_check_ptrace_features (void)
+{
+  int child_pid, ret, status;
+  long second_pid;
+
+  /* Initialize the options.  */
+  current_ptrace_options = 0;
+
+  /* Fork a child so we can do some testing.  The child will call
+     linux_child_function and will get traced.  The child will
+     eventually fork a grandchild so we can test fork event
+     reporting.  */
+  child_pid = linux_fork_to_function (NULL, linux_child_function);
+
+  ret = my_waitpid (child_pid, &status, 0);
+  if (ret == -1)
+    perror_with_name (("waitpid"));
+  else if (ret != child_pid)
+    error (_("linux_check_ptrace_features: waitpid: unexpected result %d."),
+	   ret);
+  if (! WIFSTOPPED (status))
+    error (_("linux_check_ptrace_features: waitpid: unexpected status %d."),
+	   status);
+
+  /* First, set the PTRACE_O_TRACEFORK option.  If this fails, we
+     know for sure that it is not supported.  */
+  ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
+		PTRACE_O_TRACEFORK);

BTW, I've now noticed several places miss the PTRACE_TYPE_ARG4 cast.


Fixed now. I had to use an intermediate uintptr_t cast to avoid int-to-pointer warnings due to differences in size.

+
+/* Enable reporting of all currently supported ptrace events.  */
+
+void
+linux_enable_event_reporting (pid_t pid)
+{
+  /* Check if we have initialized the ptrace features for this
+     target.  If not, do it now.  */
+  if (current_ptrace_options == -1)
+    linux_check_ptrace_features ();
+
+  /* Set the options.  */
+  ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
+	  current_ptrace_options);
+}
+
+/* Returns non-zero if PTRACE_OPTIONS is contained within
+   CURRENT_PTRACE_OPTIONS, therefore supported.  Returns 0
+   otherwise.  */
+
+static int
+ptrace_supports_feature (int ptrace_options)
+{
+  gdb_assert (current_ptrace_options >= 0);
+
+  return ((current_ptrace_options & ptrace_options) == ptrace_options);
+}
+
+/* Returns non-zero if PTRACE_EVENT_FORK is supported by ptrace,
+   0 otherwise.  Note that if PTRACE_EVENT_FORK is supported so is
+   PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK,
+   since they were all added to the kernel at the same time.  */
+
+int
+linux_supports_tracefork (void)
+{
+  return ptrace_supports_feature (PTRACE_O_TRACECLONE);
+}
+
+/* Returns non-zero if PTRACE_O_TRACEVFORKDONE is supported by
+   ptrace, 0 otherwise  */

Missing period.


Fixed.

+
+int
+linux_supports_tracevforkdone (void)
+{
+  return ptrace_supports_feature (PTRACE_O_TRACEVFORKDONE);
+}
+
+/* Returns non-zero if PTRACE_O_TRACESYSGOOD is supported by ptrace,
+   0 otherwise  */

Ditto.


Fixed.


diff --git a/gdb/config/powerpc/spu-linux.mh b/gdb/config/powerpc/spu-linux.mh
index 1bc279a..200fe6b 100644
--- a/gdb/config/powerpc/spu-linux.mh
+++ b/gdb/config/powerpc/spu-linux.mh
@@ -3,6 +3,6 @@
  # This implements a 'pseudo-native' GDB running on the
  # PPU side of the Cell BE and debugging the SPU side.

-NATDEPFILES = spu-linux-nat.o fork-child.o inf-ptrace.o \
+NATDEPFILES = spu-linux-nat.o fork-child.o linux-waitpid.o linux-waitpid.o inf-ptrace.o \
  	      linux-procfs.o linux-ptrace.o

"linux-waitpid.o linux-waitpid.o" added twice.  (It's a pity
linux-waitpid.o right next to the other linux-foo.o objects, here
and elsewhere, maintaining the somewhat existing order...)  I wouldn't
say anything if it weren't for this duplication here.  ;-)


Fair enough. I've fixed the order in which linux-waitpid.o appears in the .mh files and in the gdb/gdbserver/configure.srv file.

diff --git a/gdb/gdbserver/config.in b/gdb/gdbserver/config.in
index dada2fb..c00d0e4 100644
--- a/gdb/gdbserver/config.in
+++ b/gdb/gdbserver/config.in
@@ -26,6 +26,10 @@
     */
  #undef HAVE_DECL_PERROR

+/* Define to 1 if you have the declaration of `ptrace', and to 0 if you don't.
+   */
+#undef HAVE_DECL_PTRACE
+
  /* Define to 1 if you have the declaration of `strerror', and to 0 if you
     don't. */
  #undef HAVE_DECL_STRERROR
@@ -76,6 +80,9 @@
  /* Define to 1 if you have the `dl' library (-ldl). */
  #undef HAVE_LIBDL

+/* Define to 1 if you have the `mcheck' library (-lmcheck). */
+#undef HAVE_LIBMCHECK

Hmm.  Guess I forgot to update in config.in when adding
libmcheck support to gdbserver...  Should be done separately,
in case it causes trouble.  I'll fix that.


I dropped the configure change, so you should be free to check that change in.

diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index b9dfd6c..29f89c4 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -39,16 +39,18 @@ srv_amd64_xmlfiles="i386/amd64.xml i386/amd64-avx.xml i386/x32.xml i386/x32-avx.
  srv_i386_linux_xmlfiles="i386/i386-linux.xml i386/i386-avx-linux.xml i386/i386-mmx-linux.xml i386/32bit-linux.xml $srv_i386_32bit_xmlfiles"
  srv_amd64_linux_xmlfiles="i386/amd64-linux.xml i386/amd64-avx-linux.xml i386/64bit-linux.xml i386/x32-linux.xml i386/x32-avx-linux.xml $srv_i386_64bit_xmlfiles"

+
+# Linux object files.  This is so we don't have to repeat
+# these files over and over again.
+srv_native_linux_obj="linux-waitpid.o linux-low.o linux-osdata.o linux-procfs.o linux-ptrace.o"

"native" here stuck behind.  Please, s/srv_native_linux_obj/srv_linux_obj/.


Dropped the native bit throughout the file.

@@ -316,15 +304,14 @@ case "${target}" in
  			srv_xmlfiles="${srv_xmlfiles} tic6x-core.xml"
  			srv_xmlfiles="${srv_xmlfiles} tic6x-gp.xml"
  			srv_xmlfiles="${srv_xmlfiles} tic6x-c6xp.xml"
-  			srv_tgtobj="linux-low.o linux-osdata.o linux-tic6x-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+  			srv_tgtobj="$srv_native_linux_obj linux-tic6x-low.o"

Spaces vs tabs mixup here.

  			srv_linux_regsets=yes
  			srv_linux_usrregs=yes
  			srv_linux_thread_db=yes
  			;;


@@ -1998,7 +1916,7 @@ linux_wait_for_event (ptid_t ptid, int *wstat, int options)

        if (event_child->must_set_ptrace_flags)
  	{
-	  linux_enable_event_reporting (lwpid_of (event_child));
+	  linux_enable_event_reporting (pid_of (event_child));

This change is wrong.  We really want the lwpid of event_child,
not the overall process id.



Fixed as well.

diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index db23433..0aac73a 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c

...

-/* 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.  */
+/* Initialize ptrace warnings and check for supported ptrace
+   features given PTID.  */

given PID.


Fixed.

@@ -1429,7 +1190,7 @@ lin_lwp_attach_lwp (ptid_t ptid)

        if (ptrace (PTRACE_ATTACH, lwpid, 0, 0) < 0)
  	{
-	  if (linux_supports_tracefork_flag)
+	  if (linux_supports_tracefork ())
  	    {
  	      /* If we haven't stopped all threads when we get here,
  		 we may have seen a thread listed in thread_db's list,
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index cb8f1da..855c802 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -128,8 +128,6 @@ void linux_proc_pending_signals (int pid, sigset_t *pending,
  				 sigset_t *blocked, sigset_t *ignored);

  /* linux-nat functions for handling fork events.  */
-extern void linux_enable_event_reporting (ptid_t ptid);
-

Looks like that comment ends up stale.


Removed.

How does this third iteration look?

Thanks,
Luis
2013-08-21  Luis Machado  <lgustavo@codesourcery.com>

	gdb/
	* Makefile.in (HFILES_NO_SRCDIR): Add nat/linux-nat.h and
	nat/linux-waitpid.h.
	(linux-waitpid.o): New object file rule.
	* common/linux-ptrace.c: Include nat/linux-waitpid.h.
	(current_ptrace_options): Moved from linux-nat.c.
	(linux_ptrace_test_ret_to_nx): Use type casts for ptrace
	parameters.
	(linux_fork_to_function): New function.
	(linux_grandchild_function): Likewise.
	(linux_child_function): Likewise.
	(linux_check_ptrace_features): New function, heavily
	based on linux-nat.c:linux_test_for_tracefork.
	(linux_enable_event_reporting): New function.
	(ptrace_supports_feature): Likewise.
	(linux_supports_tracefork): Likewise.
	(linux_supports_traceclone): Likewise.
	(linux_supports_tracevforkdone): Likewise.
	(linux_supports_tracesysgood): Likewise.
	* common/linux-ptrace.h (HAS_NOMMU): Moved from
	gdbserver/linux-low.c.
	(linux_enable_event_reporting): New declaration.
	(linux_supports_tracefork): Likewise.
	(linux_supports_traceclone): Likewise.
	(linux_supports_tracevforkdone): Likewise.
	(linux_supports_tracesysgood): Likewise.
	* config.in (PTRACE_TYPE_ARG4): Regenerate.
	* config/aarch64/linux.mh (NATDEPFILES): Add linux-waitpid.o.
	* config/alpha/alpha-linux.mh (NATDEPFILES): Likewise.
	* config/arm/linux.mh (NATDEPFILES): Likewise.
	* config/i386/linux.mh (NATDEPFILES): Likewise.
	* config/i386/linux64.mh (NATDEPFILES): Likewise.
	* config/ia64/linux.mh (NATDEPFILES): Likewise.
	* config/m32r/linux.mh (NATDEPFILES): Likewise.
	* config/m68k/linux.mh (NATDEPFILES): Likewise.
	* config/mips/linux.mh (NATDEPFILES): Likewise.
	* config/pa/linux.mh (NATDEPFILES): Likewise..
	* config/powerpc/linux.mh (NATDEPFILES): Likewise..
	* config/powerpc/ppc64-linux.mh (NATDEPFILES): Likewise.
	* config/powerpc/spu-linux.mh (NATDEPFILES): Likewise.
	* config/sparc/linux.mh (NATDEPFILES): Likewise.
	* config/sparc/linux64.mh (NATDEPFILES): Likewise.
	* config/tilegx/linux.mh (NATDEPFILES): Likewise.
	* config/xtensa/linux.mh (NATDEPFILES): Likewise.
	* configure.ac (AC_CACHE_CHECK): Add void * to the list of
	ptrace's 4th argument's types.
	Check the type of PTRACE_TYPE_ARG4.
	* configure: Regenerate.
	* linux-nat.c: Include nat/linux-nat.h and nat/linux-waitpid.h.
	(SYSCALL_SIGTRAP): Moved to nat/linux-nat.h.
	(linux_supports_tracefork_flag): Remove.
	(linux_supports_tracesysgood_flag): Likewise.
	(linux_supports_tracevforkdone_flag): Likewise.
	(current_ptrace_options): Moved to
	common/linux-ptrace.c.
	(linux_tracefork_child): Remove.
	(my_waitpid): Remove.
	(linux_test_for_tracefork): Renamed to
	linux_check_ptrace_features and moved to common/linux-ptrace.c.
	(linux_test_for_tracesysgood): Remove.
	(linux_supports_tracesysgood): Remove.
	(linux_supports_tracefork): Remove.
	(linux_supports_tracevforkdone): Remove.
	(linux_enable_tracesysgood): Remove.
	(linux_enable_event_reporting): Remove.
	(linux_init_ptrace): New function.
	(linux_child_post_attach): Call linux_init_ptrace.
	(linux_child_post_startup_inferior): Call linux_init_ptrace.
	(linux_child_follow_fork): Call linux_supports_tracefork
	and linux_supports_tracevforkdone.
	(linux_child_insert_fork_catchpoint): Call
	linux_supports_tracefork.
	(linux_child_insert_vfork_catchpoint): Likewise.
	(linux_child_set_syscall_catchpoint): Call
	linux_supports_tracesysgood.
	(lin_lwp_attach_lwp): Call linux_supports_tracefork.
	* nat/linux-nat.h: New file.
	* nat/linux-waitpid.c: New file.
	* nat/linux-waitpid.h: New file.

	gdb/gdbserver/
	* Makefile.in: Explain why ../target and ../nat are not
	listed as include file search paths.
	(linux-waitpid.o): New object file rule.
	* configure.srv (srv_native_linux_obj): New variable.
	Replace all occurrences of linux native object files with
	$srv_native_linux_obj.
	* linux-low.c: Include nat/linux-nat.h and nat/linux-waitpid.h.
	(HAS_NOMMU): Move defining logic to common/linux-ptrace.c.
	(linux_enable_event_reporting): Remove declaration.
	(my_waitpid): Moved to common/linux-waitpid.c.
	(linux_wait_for_event): Pass ptid when calling
	linux_enable_event_reporting.
	(linux_supports_tracefork_flag): Remove.
	(linux_enable_event_reporting): Likewise.
	(linux_tracefork_grandchild): Remove.
	(STACK_SIZE): Moved to common/linux-ptrace.c.
	(linux_tracefork_child): Remove.
	(linux_test_for_tracefork): Remove.
	(linux_look_up_symbols): Call linux_supports_traceclone.
	(initialize_low): Remove call to linux_test_for_tracefork.
	* linux-low.h (PTRACE_TYPE_ARG3): Move to
	common/linux-ptrace.h.
	(PTRACE_TYPE_ARG4): Likewise.
	Include linux-ptrace.h.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 45cddaf..c75ec38 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -855,7 +855,7 @@ common/format.h common/host-defs.h utils.h common/queue.h common/gdb_string.h \
 common/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h \
 gdb_bfd.h sparc-ravenscar-thread.h ppc-ravenscar-thread.h common/linux-btrace.h \
 ctf.h common/i386-cpuid.h common/i386-gcc-cpuid.h target/resume.h \
-target/wait.h target/waitstatus.h
+target/wait.h target/waitstatus.h nat/linux-nat.h nat/linux-waitpid.h
 
 # Header files that already have srcdir in them, or which are in objdir.
 
@@ -2037,6 +2037,15 @@ waitstatus.o: ${srcdir}/target/waitstatus.c
 	$(COMPILE) $(srcdir)/target/waitstatus.c
 	$(POSTCOMPILE)
 
+# gdb/nat/ dependencies
+#
+# Need to explicitly specify the compile rule as make will do nothing
+# or try to compile the object file into the sub-directory.
+
+linux-waitpid.o: ${srcdir}/nat/linux-waitpid.c
+	$(COMPILE) $(srcdir)/nat/linux-waitpid.c
+	$(POSTCOMPILE)
+
 #
 # gdb/tui/ dependencies
 #
diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c
index d5ac061..c4808ab 100644
--- a/gdb/common/linux-ptrace.c
+++ b/gdb/common/linux-ptrace.c
@@ -25,10 +25,16 @@
 
 #include "linux-ptrace.h"
 #include "linux-procfs.h"
+#include "nat/linux-waitpid.h"
 #include "buffer.h"
 #include "gdb_assert.h"
 #include "gdb_wait.h"
 
+/* Stores the currently supported ptrace options.  A value of
+   -1 means we did not check for features yet.  A value of 0 means
+   there are no supported features.  */
+static int current_ptrace_options = -1;
+
 /* Find all possible reasons we could fail to attach PID and append these
    newline terminated reason strings to initialized BUFFER.  '\0' termination
    of BUFFER must be done by the caller.  */
@@ -97,7 +103,8 @@ linux_ptrace_test_ret_to_nx (void)
       return;
 
     case 0:
-      l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+      l = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) NULL,
+		  (PTRACE_TYPE_ARG4) NULL);
       if (l != 0)
 	warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: %s"),
 		 strerror (errno));
@@ -163,9 +170,11 @@ linux_ptrace_test_ret_to_nx (void)
 
   errno = 0;
 #if defined __i386__
-  l = ptrace (PTRACE_PEEKUSER, child, (void *) (uintptr_t) (EIP * 4), NULL);
+  l = ptrace (PTRACE_PEEKUSER, child, (PTRACE_TYPE_ARG3) (uintptr_t) (EIP * 4),
+	      (PTRACE_TYPE_ARG4) NULL);
 #elif defined __x86_64__
-  l = ptrace (PTRACE_PEEKUSER, child, (void *) (uintptr_t) (RIP * 8), NULL);
+  l = ptrace (PTRACE_PEEKUSER, child, (PTRACE_TYPE_ARG3) (uintptr_t) (RIP * 8),
+	      (PTRACE_TYPE_ARG4) NULL);
 #else
 # error "!__i386__ && !__x86_64__"
 #endif
@@ -178,7 +187,8 @@ linux_ptrace_test_ret_to_nx (void)
   pc = (void *) (uintptr_t) l;
 
   kill (child, SIGKILL);
-  ptrace (PTRACE_KILL, child, NULL, NULL);
+  ptrace (PTRACE_KILL, child, (PTRACE_TYPE_ARG3) NULL,
+	  (PTRACE_TYPE_ARG4) NULL);
 
   errno = 0;
   got_pid = waitpid (child, &kill_status, 0);
@@ -222,6 +232,292 @@ linux_ptrace_test_ret_to_nx (void)
 #endif /* defined __i386__ || defined __x86_64__ */
 }
 
+/* Helper function to fork a process and make the child process call
+   the function FUNCTION, passing CHILD_STACK as parameter.
+
+   For MMU-less targets, clone is used instead of fork, and
+   CHILD_STACK is used as stack space for the cloned child.  If NULL,
+   stack space is allocated via malloc (and subsequently passed to
+   FUNCTION).  For MMU targets, CHILD_STACK is ignored.  */
+
+static int
+linux_fork_to_function (gdb_byte *child_stack, void (*function) (gdb_byte *))
+{
+  int child_pid;
+
+  /* Sanity check the function pointer.  */
+  gdb_assert (function != NULL);
+
+#if defined(__UCLIBC__) && defined(HAS_NOMMU)
+#define STACK_SIZE 4096
+
+    if (child_stack == NULL)
+      child_stack = xmalloc (STACK_SIZE * 4);
+
+    /* Use CLONE_VM instead of fork, to support uClinux (no MMU).  */
+    #ifdef __ia64__
+      child_pid = __clone2 (function, child_stack, STACK_SIZE,
+			    CLONE_VM | SIGCHLD, child_stack + STACK_SIZE * 2);
+    #else /* !__ia64__ */
+      child_pid = clone (function, child_stack + STACK_SIZE,
+			 CLONE_VM | SIGCHLD, child_stack + STACK_SIZE * 2);
+  #endif /* !__ia64__ */
+#else /* !defined(__UCLIBC) && defined(HAS_NOMMU) */
+  child_pid = fork ();
+
+  if (child_pid == 0)
+    function (NULL);
+#endif /* defined(__UCLIBC) && defined(HAS_NOMMU) */
+
+  if (child_pid == -1)
+    perror_with_name (("fork"));
+
+  return child_pid;
+}
+
+/* A helper function for linux_check_ptrace_features, called after
+   the child forks a grandchild.  */
+
+static void
+linux_grandchild_function (gdb_byte *child_stack)
+{
+  /* Free any allocated stack.  */
+  xfree (child_stack);
+
+  /* This code is only reacheable by the grandchild (child's child)
+     process.  */
+  _exit (0);
+}
+
+/* A helper function for linux_check_ptrace_features, called after
+   the parent process forks a child.  The child allows itself to
+   be traced by its parent.  */
+
+static void
+linux_child_function (gdb_byte *child_stack)
+{
+  ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
+  kill (getpid (), SIGSTOP);
+
+  /* Fork a grandchild.  */
+  linux_fork_to_function (child_stack, linux_grandchild_function);
+
+  /* This code is only reacheable by the child (grandchild's parent)
+     process.  */
+  _exit (0);
+}
+
+/* Determine ptrace features available on this target.  */
+
+static void
+linux_check_ptrace_features (void)
+{
+  int child_pid, ret, status;
+  long second_pid;
+
+  /* Initialize the options.  */
+  current_ptrace_options = 0;
+
+  /* Fork a child so we can do some testing.  The child will call
+     linux_child_function and will get traced.  The child will
+     eventually fork a grandchild so we can test fork event
+     reporting.  */
+  child_pid = linux_fork_to_function (NULL, linux_child_function);
+
+  ret = my_waitpid (child_pid, &status, 0);
+  if (ret == -1)
+    perror_with_name (("waitpid"));
+  else if (ret != child_pid)
+    error (_("linux_check_ptrace_features: waitpid: unexpected result %d."),
+	   ret);
+  if (! WIFSTOPPED (status))
+    error (_("linux_check_ptrace_features: waitpid: unexpected status %d."),
+	   status);
+
+  /* First, set the PTRACE_O_TRACEFORK 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_TRACEFORK);
+
+  if (ret != 0)
+    {
+      ret = ptrace (PTRACE_KILL, child_pid, (PTRACE_TYPE_ARG3) 0,
+		    (PTRACE_TYPE_ARG4) 0);
+      if (ret != 0)
+	{
+	  warning (_("linux_check_ptrace_features: failed to kill child"));
+	  return;
+	}
+
+      ret = my_waitpid (child_pid, &status, 0);
+      if (ret != child_pid)
+	warning (_("linux_check_ptrace_features: failed "
+		   "to wait for killed child"));
+      else if (!WIFSIGNALED (status))
+	warning (_("linux_check_ptrace_features: unexpected "
+		   "wait status 0x%x from killed child"), status);
+
+      return;
+    }
+
+#ifdef GDBSERVER
+  /* gdbserver does not support PTRACE_O_TRACESYSGOOD or
+     PTRACE_O_TRACEVFORKDONE yet.  */
+#else
+  /* Check if the target supports PTRACE_O_TRACESYSGOOD.  */
+  ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
+		(PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
+  if (ret == 0)
+    current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+
+  /* Check if the target supports PTRACE_O_TRACEVFORKDONE.  */
+  ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
+		(PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
+				    | PTRACE_O_TRACEVFORKDONE));
+  if (ret == 0)
+    current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
+#endif
+
+  /* Setting PTRACE_O_TRACEFORK did not cause an error, however we
+     don't know for sure that the feature is available; old
+     versions of PTRACE_SETOPTIONS ignored unknown options.
+     Therefore, we attach to the child process, use PTRACE_SETOPTIONS
+     to enable fork tracing, and let it fork.  If the process exits,
+     we assume that we can't use PTRACE_O_TRACEFORK; if we get the
+     fork notification, and we can extract the new child's PID, then
+     we assume that we can.
+
+     We do not explicitly check for vfork tracing here.  It is
+     assumed that vfork tracing is available whenever fork tracing
+     is available.  */
+  ret = ptrace (PTRACE_CONT, child_pid, (PTRACE_TYPE_ARG3) 0,
+		(PTRACE_TYPE_ARG4) 0);
+  if (ret != 0)
+    warning (_("linux_check_ptrace_features: failed to resume child"));
+
+  ret = my_waitpid (child_pid, &status, 0);
+
+  /* Check if we received a fork event notification.  */
+  if (ret == child_pid && WIFSTOPPED (status)
+      && status >> 16 == PTRACE_EVENT_FORK)
+    {
+      /* We did receive a fork event notification.  Make sure its PID
+	 is reported.  */
+      second_pid = 0;
+      ret = ptrace (PTRACE_GETEVENTMSG, child_pid, (PTRACE_TYPE_ARG3) 0,
+		    (PTRACE_TYPE_ARG4) &second_pid);
+      if (ret == 0 && second_pid != 0)
+	{
+	  int second_status;
+
+	  /* We got the PID from the grandchild, which means fork
+	     tracing is supported.  */
+#ifdef GDBSERVER
+	  /* Do not enable all the options for now since gdbserver does not
+	     properly support them.  This restriction will be lifted when
+	     gdbserver is augmented to support them.  */
+	  current_ptrace_options |= PTRACE_O_TRACECLONE;
+#else
+	  current_ptrace_options |= PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK
+	    | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC;
+
+	  /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
+	     support read-only process state.  */
+#endif
+
+	  /* Do some cleanup and kill the grandchild.  */
+	  my_waitpid (second_pid, &second_status, 0);
+	  ret = ptrace (PTRACE_KILL, second_pid, (PTRACE_TYPE_ARG3) 0,
+			(PTRACE_TYPE_ARG4) 0);
+	  if (ret != 0)
+	    warning (_("linux_check_ptrace_features: "
+		       "failed to kill second child"));
+	  my_waitpid (second_pid, &status, 0);
+	}
+    }
+  else
+    warning (_("linux_check_ptrace_features: unexpected result from waitpid "
+	     "(%d, status 0x%x)"), ret, status);
+
+  /* Clean things up and kill any pending children.  */
+  do
+    {
+      ret = ptrace (PTRACE_KILL, child_pid, (PTRACE_TYPE_ARG3) 0,
+		    (PTRACE_TYPE_ARG4) 0);
+      if (ret != 0)
+	warning ("linux_check_ptrace_features: failed to kill child");
+      my_waitpid (child_pid, &status, 0);
+    }
+  while (WIFSTOPPED (status));
+}
+
+/* Enable reporting of all currently supported ptrace events.  */
+
+void
+linux_enable_event_reporting (pid_t pid)
+{
+  /* Check if we have initialized the ptrace features for this
+     target.  If not, do it now.  */
+  if (current_ptrace_options == -1)
+    linux_check_ptrace_features ();
+
+  /* Set the options.  */
+  ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
+	  (PTRACE_TYPE_ARG4) (uintptr_t) current_ptrace_options);
+}
+
+/* Returns non-zero if PTRACE_OPTIONS is contained within
+   CURRENT_PTRACE_OPTIONS, therefore supported.  Returns 0
+   otherwise.  */
+
+static int
+ptrace_supports_feature (int ptrace_options)
+{
+  gdb_assert (current_ptrace_options >= 0);
+
+  return ((current_ptrace_options & ptrace_options) == ptrace_options);
+}
+
+/* Returns non-zero if PTRACE_EVENT_FORK is supported by ptrace,
+   0 otherwise.  Note that if PTRACE_EVENT_FORK is supported so is
+   PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK,
+   since they were all added to the kernel at the same time.  */
+
+int
+linux_supports_tracefork (void)
+{
+  return ptrace_supports_feature (PTRACE_O_TRACEFORK);
+}
+
+/* Returns non-zero if PTRACE_EVENT_CLONE is supported by ptrace,
+   0 otherwise.  Note that if PTRACE_EVENT_CLONE is supported so is
+   PTRACE_EVENT_FORK, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK,
+   since they were all added to the kernel at the same time.  */
+
+int
+linux_supports_traceclone (void)
+{
+  return ptrace_supports_feature (PTRACE_O_TRACECLONE);
+}
+
+/* Returns non-zero if PTRACE_O_TRACEVFORKDONE is supported by
+   ptrace, 0 otherwise.  */
+
+int
+linux_supports_tracevforkdone (void)
+{
+  return ptrace_supports_feature (PTRACE_O_TRACEVFORKDONE);
+}
+
+/* Returns non-zero if PTRACE_O_TRACESYSGOOD is supported by ptrace,
+   0 otherwise.  */
+
+int
+linux_supports_tracesysgood (void)
+{
+  return ptrace_supports_feature (PTRACE_O_TRACESYSGOOD);
+}
+
 /* Display possible problems on this system.  Display them only once per GDB
    execution.  */
 
diff --git a/gdb/common/linux-ptrace.h b/gdb/common/linux-ptrace.h
index 8f02c82..39c943e 100644
--- a/gdb/common/linux-ptrace.h
+++ b/gdb/common/linux-ptrace.h
@@ -22,6 +22,22 @@ struct buffer;
 
 #include <sys/ptrace.h>
 
+#ifdef __UCLIBC__
+#if !(defined(__UCLIBC_HAS_MMU__) || defined(__ARCH_HAS_MMU__))
+/* PTRACE_TEXT_ADDR and friends.  */
+#include <asm/ptrace.h>
+#define HAS_NOMMU
+#endif
+#endif
+
+#if !defined(PTRACE_TYPE_ARG3)
+#define PTRACE_TYPE_ARG3 void *
+#endif
+
+#if !defined(PTRACE_TYPE_ARG4)
+#define PTRACE_TYPE_ARG4 void *
+#endif
+
 #ifndef PTRACE_GETSIGINFO
 # define PTRACE_GETSIGINFO 0x4202
 # define PTRACE_SETSIGINFO 0x4203
@@ -69,5 +85,10 @@ struct buffer;
 
 extern void linux_ptrace_attach_warnings (pid_t pid, struct buffer *buffer);
 extern void linux_ptrace_init_warnings (void);
+extern void linux_enable_event_reporting (pid_t pid);
+extern int linux_supports_tracefork (void);
+extern int linux_supports_traceclone (void);
+extern int linux_supports_tracevforkdone (void);
+extern int linux_supports_tracesysgood (void);
 
 #endif /* COMMON_LINUX_PTRACE_H */
diff --git a/gdb/config.in b/gdb/config.in
index 76abd04..03b0972 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -659,6 +659,9 @@
 /* Define to the type of arg 3 for ptrace. */
 #undef PTRACE_TYPE_ARG3
 
+/* Define to the type of arg 4 for ptrace. */
+#undef PTRACE_TYPE_ARG4
+
 /* Define to the type of arg 5 for ptrace. */
 #undef PTRACE_TYPE_ARG5
 
diff --git a/gdb/config/aarch64/linux.mh b/gdb/config/aarch64/linux.mh
index 2b2202e..18a12ab 100644
--- a/gdb/config/aarch64/linux.mh
+++ b/gdb/config/aarch64/linux.mh
@@ -21,7 +21,7 @@
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o aarch64-linux-nat.o \
 	proc-service.o linux-thread-db.o linux-nat.o linux-fork.o \
-	linux-procfs.o linux-ptrace.o linux-osdata.o
+	linux-procfs.o linux-ptrace.o linux-osdata.o linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES= -ldl $(RDYNAMIC)
diff --git a/gdb/config/alpha/alpha-linux.mh b/gdb/config/alpha/alpha-linux.mh
index 9eb9e4b..81819a1 100644
--- a/gdb/config/alpha/alpha-linux.mh
+++ b/gdb/config/alpha/alpha-linux.mh
@@ -2,7 +2,8 @@
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o alpha-linux-nat.o \
 	fork-child.o proc-service.o linux-thread-db.o \
-	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o
+	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
+	linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The dynamically loaded libthread_db needs access to symbols in the
diff --git a/gdb/config/arm/linux.mh b/gdb/config/arm/linux.mh
index c0a1c66..63c3eae 100644
--- a/gdb/config/arm/linux.mh
+++ b/gdb/config/arm/linux.mh
@@ -3,7 +3,8 @@
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o arm-linux-nat.o \
 	proc-service.o linux-thread-db.o \
-	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o
+	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
+	linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES= -ldl $(RDYNAMIC)
diff --git a/gdb/config/i386/linux.mh b/gdb/config/i386/linux.mh
index 7c64e83..10a2584 100644
--- a/gdb/config/i386/linux.mh
+++ b/gdb/config/i386/linux.mh
@@ -5,7 +5,7 @@ NATDEPFILES= inf-ptrace.o fork-child.o \
 	i386-nat.o i386-linux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
-	linux-btrace.o
+	linux-btrace.o linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The dynamically loaded libthread_db needs access to symbols in the
diff --git a/gdb/config/i386/linux64.mh b/gdb/config/i386/linux64.mh
index 8d782c1..686c363 100644
--- a/gdb/config/i386/linux64.mh
+++ b/gdb/config/i386/linux64.mh
@@ -3,7 +3,8 @@ NATDEPFILES= inf-ptrace.o fork-child.o \
 	i386-nat.o amd64-nat.o amd64-linux-nat.o \
 	linux-nat.o linux-osdata.o \
 	proc-service.o linux-thread-db.o linux-fork.o \
-	linux-procfs.o linux-ptrace.o linux-btrace.o
+	linux-procfs.o linux-ptrace.o linux-btrace.o \
+	linux-waitpid.o
 NAT_FILE= config/nm-linux.h
 NAT_CDEPS = $(srcdir)/proc-service.list
 
diff --git a/gdb/config/ia64/linux.mh b/gdb/config/ia64/linux.mh
index 1a4c68e..a2aff44 100644
--- a/gdb/config/ia64/linux.mh
+++ b/gdb/config/ia64/linux.mh
@@ -5,7 +5,7 @@ NATDEPFILES= inf-ptrace.o fork-child.o \
 	core-regset.o ia64-linux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
-	linux-procfs.o linux-ptrace.o
+	linux-procfs.o linux-ptrace.o linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/config/m32r/linux.mh b/gdb/config/m32r/linux.mh
index b461806..e9bb82c 100644
--- a/gdb/config/m32r/linux.mh
+++ b/gdb/config/m32r/linux.mh
@@ -3,7 +3,8 @@
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o				\
 	m32r-linux-nat.o proc-service.o linux-thread-db.o	\
-	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o
+	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
+	linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES= -ldl $(RDYNAMIC)
diff --git a/gdb/config/m68k/linux.mh b/gdb/config/m68k/linux.mh
index e3aaf38..bcec295 100644
--- a/gdb/config/m68k/linux.mh
+++ b/gdb/config/m68k/linux.mh
@@ -4,7 +4,8 @@ NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o \
 	m68klinux-nat.o \
 	proc-service.o linux-thread-db.o \
-	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o
+	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
+	linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The dynamically loaded libthread_db needs access to symbols in the
diff --git a/gdb/config/mips/linux.mh b/gdb/config/mips/linux.mh
index a4f23e3..021838e 100644
--- a/gdb/config/mips/linux.mh
+++ b/gdb/config/mips/linux.mh
@@ -3,7 +3,8 @@ NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o mips-linux-nat.o \
 	linux-thread-db.o proc-service.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
-	linux-procfs.o linux-ptrace.o mips-linux-watch.o
+	linux-procfs.o linux-ptrace.o linux-waitpid.o \
+	mips-linux-watch.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/config/pa/linux.mh b/gdb/config/pa/linux.mh
index fa46db6..f1c0c32 100644
--- a/gdb/config/pa/linux.mh
+++ b/gdb/config/pa/linux.mh
@@ -3,7 +3,7 @@ NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o \
 	hppa-linux-nat.o proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
-	linux-procfs.o linux-ptrace.o
+	linux-procfs.o linux-ptrace.o linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/config/powerpc/linux.mh b/gdb/config/powerpc/linux.mh
index b0d4ce7..a807d3f 100644
--- a/gdb/config/powerpc/linux.mh
+++ b/gdb/config/powerpc/linux.mh
@@ -5,7 +5,8 @@ XM_CLIBS=
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o \
 	ppc-linux-nat.o proc-service.o linux-thread-db.o \
-	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o
+	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
+	linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/config/powerpc/ppc64-linux.mh b/gdb/config/powerpc/ppc64-linux.mh
index 367a818..4b91408 100644
--- a/gdb/config/powerpc/ppc64-linux.mh
+++ b/gdb/config/powerpc/ppc64-linux.mh
@@ -5,7 +5,8 @@ XM_CLIBS=
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o \
 	ppc-linux-nat.o proc-service.o linux-thread-db.o \
-	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o
+	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
+	linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The PowerPC has severe limitations on TOC size, and uses them even
diff --git a/gdb/config/powerpc/spu-linux.mh b/gdb/config/powerpc/spu-linux.mh
index 1bc279a..7ce7a5f 100644
--- a/gdb/config/powerpc/spu-linux.mh
+++ b/gdb/config/powerpc/spu-linux.mh
@@ -4,5 +4,5 @@
 # PPU side of the Cell BE and debugging the SPU side.
 
 NATDEPFILES = spu-linux-nat.o fork-child.o inf-ptrace.o \
-	      linux-procfs.o linux-ptrace.o
+	      linux-procfs.o linux-ptrace.o linux-waitpid.o
 
diff --git a/gdb/config/sparc/linux.mh b/gdb/config/sparc/linux.mh
index 6a2cefd..d802591 100644
--- a/gdb/config/sparc/linux.mh
+++ b/gdb/config/sparc/linux.mh
@@ -4,7 +4,7 @@ NATDEPFILES= sparc-nat.o sparc-linux-nat.o \
 	core-regset.o fork-child.o inf-ptrace.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
-	linux-procfs.o linux-ptrace.o
+	linux-procfs.o linux-ptrace.o linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The dynamically loaded libthread_db needs access to symbols in the
diff --git a/gdb/config/sparc/linux64.mh b/gdb/config/sparc/linux64.mh
index d1e1a97..c83097e 100644
--- a/gdb/config/sparc/linux64.mh
+++ b/gdb/config/sparc/linux64.mh
@@ -5,7 +5,7 @@ NATDEPFILES= sparc-nat.o sparc64-nat.o sparc64-linux-nat.o \
 	fork-child.o inf-ptrace.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
-	linux-procfs.o linux-ptrace.o
+	linux-procfs.o linux-ptrace.o linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 # The dynamically loaded libthread_db needs access to symbols in the
diff --git a/gdb/config/tilegx/linux.mh b/gdb/config/tilegx/linux.mh
index 56ef694..1ed9c33 100644
--- a/gdb/config/tilegx/linux.mh
+++ b/gdb/config/tilegx/linux.mh
@@ -5,7 +5,7 @@ NATDEPFILES= inf-ptrace.o fork-child.o \
 	tilegx-linux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
-	linux-procfs.o linux-ptrace.o
+	linux-procfs.o linux-ptrace.o linux-waitpid.o
 
 # The dynamically loaded libthread_db needs access to symbols in the
 # gdb executable.
diff --git a/gdb/config/xtensa/linux.mh b/gdb/config/xtensa/linux.mh
index deffe25..8ef84b4 100644
--- a/gdb/config/xtensa/linux.mh
+++ b/gdb/config/xtensa/linux.mh
@@ -4,7 +4,8 @@ NAT_FILE= config/nm-linux.h
 
 NATDEPFILES= inf-ptrace.o fork-child.o xtensa-linux-nat.o \
 	linux-thread-db.o proc-service.o \
-	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o
+	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
+	linux-waitpid.o
 NAT_CDEPS = $(srcdir)/proc-service.list
 
 LOADLIBES = -ldl $(RDYNAMIC)
diff --git a/gdb/configure b/gdb/configure
index 8067825..a722b93 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -10336,7 +10336,7 @@ else
 for gdb_arg1 in 'int' 'long'; do
  for gdb_arg2 in 'pid_t' 'int' 'long'; do
   for gdb_arg3 in 'int *' 'caddr_t' 'int' 'long' 'void *'; do
-   for gdb_arg4 in 'int' 'long'; do
+   for gdb_arg4 in 'int' 'long' 'void *'; do
      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 $gdb_ptrace_headers
@@ -10399,6 +10399,11 @@ cat >>confdefs.h <<_ACEOF
 #define PTRACE_TYPE_ARG3 $3
 _ACEOF
 
+
+cat >>confdefs.h <<_ACEOF
+#define PTRACE_TYPE_ARG4 $4
+_ACEOF
+
 if test -n "$5"; then
 
 cat >>confdefs.h <<_ACEOF
diff --git a/gdb/configure.ac b/gdb/configure.ac
index 667821f..0982cac 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1207,7 +1207,7 @@ AC_CACHE_CHECK([types of arguments for ptrace], gdb_cv_func_ptrace_args, [
 for gdb_arg1 in 'int' 'long'; do
  for gdb_arg2 in 'pid_t' 'int' 'long'; do
   for gdb_arg3 in 'int *' 'caddr_t' 'int' 'long' 'void *'; do
-   for gdb_arg4 in 'int' 'long'; do
+   for gdb_arg4 in 'int' 'long' 'void *'; do
      AC_TRY_COMPILE($gdb_ptrace_headers, [
 extern $gdb_cv_func_ptrace_ret
   ptrace ($gdb_arg1, $gdb_arg2, $gdb_arg3, $gdb_arg4);
@@ -1234,6 +1234,8 @@ IFS=$ac_save_IFS
 shift
 AC_DEFINE_UNQUOTED(PTRACE_TYPE_ARG3, $[3],
   [Define to the type of arg 3 for ptrace.])
+AC_DEFINE_UNQUOTED(PTRACE_TYPE_ARG4, $[4],
+  [Define to the type of arg 4 for ptrace.])
 if test -n "$[5]"; then
   AC_DEFINE_UNQUOTED(PTRACE_TYPE_ARG5, $[5],
     [Define to the type of arg 5 for ptrace.])
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 2cdbf47..45e03a2 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -100,6 +100,11 @@ GNULIB_H = $(GNULIB_BUILDDIR)/import/string.h @GNULIB_STDINT_H@
 # -I. for config files.
 # -I${srcdir} for our headers.
 # -I$(srcdir)/../regformats for regdef.h.
+#
+# We do not include ../target or ../nat in here because headers
+# in those directories should be included with the subdirectory.
+# e.g.: "target/wait.h".
+#
 INCLUDE_CFLAGS = -I. -I${srcdir} -I$(srcdir)/../common \
 	-I$(srcdir)/../regformats -I$(srcdir)/../ -I$(INCLUDE_DIR) \
 	$(INCGNU)
@@ -562,6 +567,12 @@ linux-btrace.o: ../common/linux-btrace.c $(linux_btrace_h) $(server_h)
 mips-linux-watch.o: ../common/mips-linux-watch.c $(mips_linux_watch_h) $(server_h)
 	$(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $<
 
+# Native object files rules from ../nat
+
+linux-waitpid.o: ../nat/linux-waitpid.c
+	$(COMPILE) $<
+	$(POSTCOMPILE)
+
 # We build vasprintf with -DHAVE_CONFIG_H because we want that unit to
 # include our config.h file.  Otherwise, some system headers do not get
 # included, and the compiler emits a warning about implicitly defined
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index b9dfd6c..b3c0421 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -39,16 +39,18 @@ srv_amd64_xmlfiles="i386/amd64.xml i386/amd64-avx.xml i386/x32.xml i386/x32-avx.
 srv_i386_linux_xmlfiles="i386/i386-linux.xml i386/i386-avx-linux.xml i386/i386-mmx-linux.xml i386/32bit-linux.xml $srv_i386_32bit_xmlfiles"
 srv_amd64_linux_xmlfiles="i386/amd64-linux.xml i386/amd64-avx-linux.xml i386/64bit-linux.xml i386/x32-linux.xml i386/x32-avx-linux.xml $srv_i386_64bit_xmlfiles"
 
+
+# Linux object files.  This is so we don't have to repeat
+# these files over and over again.
+srv_linux_obj="linux-low.o linux-osdata.o linux-procfs.o linux-ptrace.o linux-waitpid.o"
+
 # Input is taken from the "${target}" variable.
 
 case "${target}" in
   aarch64*-*-linux*)
 			srv_regobj="aarch64.o"
 			srv_tgtobj="linux-aarch64-low.o"
-			srv_tgtobj="${srv_tgtobj} linux-low.o"
-			srv_tgtobj="${srv_tgtobj} linux-osdata.o"
-			srv_tgtobj="${srv_tgtobj} linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="${srv_tgtobj} $srv_linux_obj"
 			srv_xmlfiles="aarch64.xml"
 			srv_xmlfiles="${srv_xmlfiles} aarch64-core.xml"
 			srv_xmlfiles="${srv_xmlfiles} aarch64-fpu.xml"
@@ -60,8 +62,7 @@ case "${target}" in
 			srv_regobj="${srv_regobj} arm-with-vfpv2.o"
 			srv_regobj="${srv_regobj} arm-with-vfpv3.o"
 			srv_regobj="${srv_regobj} arm-with-neon.o"
-			srv_tgtobj="linux-low.o linux-osdata.o linux-arm-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-arm-low.o"
 			srv_xmlfiles="arm-with-iwmmxt.xml"
 			srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv2.xml"
 			srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv3.xml"
@@ -83,20 +84,17 @@ case "${target}" in
 			srv_mingwce=yes
 			;;
   bfin-*-*linux*)	srv_regobj=reg-bfin.o
-			srv_tgtobj="linux-low.o linux-osdata.o linux-bfin-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-bfin-low.o"
 			srv_linux_usrregs=yes
 			srv_linux_thread_db=yes
 			;;
   crisv32-*-linux*)	srv_regobj=reg-crisv32.o
-			srv_tgtobj="linux-low.o linux-osdata.o linux-crisv32-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-crisv32-low.o"
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			;;
   cris-*-linux*)	srv_regobj=reg-cris.o
-			srv_tgtobj="linux-low.o linux-osdata.o linux-cris-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-cris-low.o"
 			srv_linux_usrregs=yes
 			srv_linux_thread_db=yes
 			;;
@@ -110,8 +108,8 @@ case "${target}" in
 			    srv_regobj="$srv_regobj $srv_amd64_linux_regobj"
 			    srv_xmlfiles="${srv_xmlfiles} $srv_amd64_linux_xmlfiles"
 			fi
-			srv_tgtobj="linux-low.o linux-osdata.o linux-x86-low.o i386-low.o i387-fp.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o linux-btrace.o"
+			srv_tgtobj="$srv_linux_obj linux-x86-low.o i386-low.o i387-fp.o"
+			srv_tgtobj="${srv_tgtobj} linux-btrace.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
@@ -146,13 +144,11 @@ case "${target}" in
 			srv_qnx="yes"
 			;;
   ia64-*-linux*)	srv_regobj=reg-ia64.o
-			srv_tgtobj="linux-low.o linux-osdata.o linux-ia64-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-ia64-low.o"
 			srv_linux_usrregs=yes
 			;;
   m32r*-*-linux*)	srv_regobj=reg-m32r.o
-			srv_tgtobj="linux-low.o linux-osdata.o linux-m32r-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-m32r-low.o"
 			srv_linux_usrregs=yes
  			srv_linux_thread_db=yes
 			;;
@@ -161,8 +157,7 @@ case "${target}" in
                         else
                           srv_regobj=reg-m68k.o
                         fi
-			srv_tgtobj="linux-low.o linux-osdata.o linux-m68k-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-m68k-low.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
@@ -172,8 +167,7 @@ case "${target}" in
                         else
                           srv_regobj=reg-m68k.o
                         fi
-			srv_tgtobj="linux-low.o linux-osdata.o linux-m68k-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-m68k-low.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
@@ -182,8 +176,7 @@ case "${target}" in
 			srv_regobj="${srv_regobj} mips-dsp-linux.o"
 			srv_regobj="${srv_regobj} mips64-linux.o"
 			srv_regobj="${srv_regobj} mips64-dsp-linux.o"
-			srv_tgtobj="linux-low.o linux-osdata.o linux-mips-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-mips-low.o"
 			srv_tgtobj="${srv_tgtobj} mips-linux-watch.o"
 			srv_xmlfiles="mips-linux.xml"
 			srv_xmlfiles="${srv_xmlfiles} mips-dsp-linux.xml"
@@ -202,8 +195,7 @@ case "${target}" in
 			srv_linux_thread_db=yes
 			;;
   nios2*-*-linux*)	srv_regobj="nios2-linux.o"
-			srv_tgtobj="linux-low.o linux-osdata.o linux-nios2-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-nios2-low.o"
 			srv_xmlfiles="nios2-linux.xml"
 			srv_xmlfiles="${srv_xmlfiles} nios2-cpu.xml"
 			srv_linux_regsets=yes
@@ -225,8 +217,7 @@ case "${target}" in
 			srv_regobj="${srv_regobj} powerpc-isa205-64l.o"
 			srv_regobj="${srv_regobj} powerpc-isa205-altivec64l.o"
 			srv_regobj="${srv_regobj} powerpc-isa205-vsx64l.o"
-			srv_tgtobj="linux-low.o linux-osdata.o linux-ppc-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-ppc-low.o"
 			srv_xmlfiles="rs6000/powerpc-32l.xml"
 			srv_xmlfiles="${srv_xmlfiles} rs6000/powerpc-altivec32l.xml"
 			srv_xmlfiles="${srv_xmlfiles} rs6000/powerpc-cell32l.xml"
@@ -271,8 +262,7 @@ case "${target}" in
 			srv_regobj="${srv_regobj} s390x-linux64.o"
 			srv_regobj="${srv_regobj} s390x-linux64v1.o"
 			srv_regobj="${srv_regobj} s390x-linux64v2.o"
-			srv_tgtobj="linux-low.o linux-osdata.o linux-s390-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-s390-low.o"
 			srv_xmlfiles="s390-linux32.xml"
 			srv_xmlfiles="${srv_xmlfiles} s390-linux32v1.xml"
 			srv_xmlfiles="${srv_xmlfiles} s390-linux32v2.xml"
@@ -292,15 +282,13 @@ case "${target}" in
 			srv_linux_thread_db=yes
 			;;
   sh*-*-linux*)		srv_regobj=reg-sh.o
-			srv_tgtobj="linux-low.o linux-osdata.o linux-sh-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-sh-low.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			;;
   sparc*-*-linux*)	srv_regobj=reg-sparc64.o
-			srv_tgtobj="linux-low.o linux-osdata.o linux-sparc-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-sparc-low.o"
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			;;
@@ -316,15 +304,14 @@ case "${target}" in
 			srv_xmlfiles="${srv_xmlfiles} tic6x-core.xml"
 			srv_xmlfiles="${srv_xmlfiles} tic6x-gp.xml"
 			srv_xmlfiles="${srv_xmlfiles} tic6x-c6xp.xml"
-  			srv_tgtobj="linux-low.o linux-osdata.o linux-tic6x-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-tic6x-low.o"
 			srv_linux_regsets=yes
 			srv_linux_usrregs=yes
 			srv_linux_thread_db=yes
 			;;
   x86_64-*-linux*)	srv_regobj="$srv_amd64_linux_regobj $srv_i386_linux_regobj"
-			srv_tgtobj="linux-low.o linux-osdata.o linux-x86-low.o i386-low.o i387-fp.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o linux-btrace.o"
+			srv_tgtobj="$srv_linux_obj linux-x86-low.o i386-low.o i387-fp.o"
+			srv_tgtobj="${srv_tgtobj} linux-btrace.o"
 			srv_xmlfiles="$srv_i386_linux_xmlfiles $srv_amd64_linux_xmlfiles"
 			srv_linux_usrregs=yes # This is for i386 progs.
 			srv_linux_regsets=yes
@@ -343,14 +330,12 @@ case "${target}" in
 			;;
 
   xtensa*-*-linux*)	srv_regobj=reg-xtensa.o
-			srv_tgtobj="linux-low.o linux-osdata.o linux-xtensa-low.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-xtensa-low.o"
 			srv_linux_regsets=yes
 			;;
   tilegx-*-linux*)	srv_regobj=reg-tilegx.o
 			srv_regobj="${srv_regobj} reg-tilegx32.o"
-			srv_tgtobj="linux-low.o linux-tile-low.o linux-osdata.o linux-procfs.o"
-			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
+			srv_tgtobj="$srv_linux_obj linux-osdata.o"
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			;;
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 217cd2e..12208dc 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -21,6 +21,8 @@
 #include "linux-osdata.h"
 #include "agent.h"
 
+#include "nat/linux-nat.h"
+#include "nat/linux-waitpid.h"
 #include "gdb_wait.h"
 #include <stdio.h>
 #include <sys/ptrace.h>
@@ -75,14 +77,6 @@
 #define __SIGRTMIN 32
 #endif
 
-#ifdef __UCLIBC__
-#if !(defined(__UCLIBC_HAS_MMU__) || defined(__ARCH_HAS_MMU__))
-/* PTRACE_TEXT_ADDR and friends.  */
-#include <asm/ptrace.h>
-#define HAS_NOMMU
-#endif
-#endif
-
 /* Some targets did not define these ptrace constants from the start,
    so gdbserver defines them locally here.  In the future, these may
    be removed after they are added to asm/ptrace.h.  */
@@ -236,7 +230,6 @@ 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 void linux_enable_event_reporting (int pid);
 
 /* True if the low target can hardware single-step.  Such targets
    don't need a BREAKPOINT_REINSERT_ADDR callback.  */
@@ -376,81 +369,6 @@ linux_add_process (int pid, int attached)
   return proc;
 }
 
-/* Wrapper function for waitpid which handles EINTR, and emulates
-   __WALL for systems where that is not available.  */
-
-static int
-my_waitpid (int pid, int *status, int flags)
-{
-  int ret, out_errno;
-
-  if (debug_threads)
-    fprintf (stderr, "my_waitpid (%d, 0x%x)\n", pid, flags);
-
-  if (flags & __WALL)
-    {
-      sigset_t block_mask, org_mask, wake_mask;
-      int wnohang;
-
-      wnohang = (flags & WNOHANG) != 0;
-      flags &= ~(__WALL | __WCLONE);
-      flags |= WNOHANG;
-
-      /* Block all signals while here.  This avoids knowing about
-	 LinuxThread's signals.  */
-      sigfillset (&block_mask);
-      sigprocmask (SIG_BLOCK, &block_mask, &org_mask);
-
-      /* ... except during the sigsuspend below.  */
-      sigemptyset (&wake_mask);
-
-      while (1)
-	{
-	  /* Since all signals are blocked, there's no need to check
-	     for EINTR here.  */
-	  ret = waitpid (pid, status, flags);
-	  out_errno = errno;
-
-	  if (ret == -1 && out_errno != ECHILD)
-	    break;
-	  else if (ret > 0)
-	    break;
-
-	  if (flags & __WCLONE)
-	    {
-	      /* We've tried both flavors now.  If WNOHANG is set,
-		 there's nothing else to do, just bail out.  */
-	      if (wnohang)
-		break;
-
-	      if (debug_threads)
-		fprintf (stderr, "blocking\n");
-
-	      /* Block waiting for signals.  */
-	      sigsuspend (&wake_mask);
-	    }
-
-	  flags ^= __WCLONE;
-	}
-
-      sigprocmask (SIG_SETMASK, &org_mask, NULL);
-    }
-  else
-    {
-      do
-	ret = waitpid (pid, status, flags);
-      while (ret == -1 && errno == EINTR);
-      out_errno = errno;
-    }
-
-  if (debug_threads)
-    fprintf (stderr, "my_waitpid (%d, 0x%x): status(%x), %d\n",
-	     pid, flags, status ? *status : -1, ret);
-
-  errno = out_errno;
-  return ret;
-}
-
 /* 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).  */
@@ -4659,168 +4577,6 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
   return 0;
 }
 
-/* Non-zero if the kernel supports PTRACE_O_TRACEFORK.  */
-static int linux_supports_tracefork_flag;
-
-static void
-linux_enable_event_reporting (int pid)
-{
-  if (!linux_supports_tracefork_flag)
-    return;
-
-  ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
-	  (PTRACE_TYPE_ARG4) PTRACE_O_TRACECLONE);
-}
-
-/* Helper functions for linux_test_for_tracefork, called via clone ().  */
-
-static int
-linux_tracefork_grandchild (void *arg)
-{
-  _exit (0);
-}
-
-#define STACK_SIZE 4096
-
-static int
-linux_tracefork_child (void *arg)
-{
-  ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
-  kill (getpid (), SIGSTOP);
-
-#if !(defined(__UCLIBC__) && defined(HAS_NOMMU))
-
-  if (fork () == 0)
-    linux_tracefork_grandchild (NULL);
-
-#else /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-
-#ifdef __ia64__
-  __clone2 (linux_tracefork_grandchild, arg, STACK_SIZE,
-	    CLONE_VM | SIGCHLD, NULL);
-#else
-  clone (linux_tracefork_grandchild, (char *) arg + STACK_SIZE,
-	 CLONE_VM | SIGCHLD, NULL);
-#endif
-
-#endif /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-
-  _exit (0);
-}
-
-/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events.  Make
-   sure that we can enable the option, and that it had the desired
-   effect.  */
-
-static void
-linux_test_for_tracefork (void)
-{
-  int child_pid, ret, status;
-  long second_pid;
-#if defined(__UCLIBC__) && defined(HAS_NOMMU)
-  char *stack = xmalloc (STACK_SIZE * 4);
-#endif /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-
-  linux_supports_tracefork_flag = 0;
-
-#if !(defined(__UCLIBC__) && defined(HAS_NOMMU))
-
-  child_pid = fork ();
-  if (child_pid == 0)
-    linux_tracefork_child (NULL);
-
-#else /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-
-  /* Use CLONE_VM instead of fork, to support uClinux (no MMU).  */
-#ifdef __ia64__
-  child_pid = __clone2 (linux_tracefork_child, stack, STACK_SIZE,
-			CLONE_VM | SIGCHLD, stack + STACK_SIZE * 2);
-#else /* !__ia64__ */
-  child_pid = clone (linux_tracefork_child, stack + STACK_SIZE,
-		     CLONE_VM | SIGCHLD, stack + STACK_SIZE * 2);
-#endif /* !__ia64__ */
-
-#endif /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-
-  if (child_pid == -1)
-    perror_with_name ("clone");
-
-  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, (PTRACE_TYPE_ARG3) 0,
-		(PTRACE_TYPE_ARG4) PTRACE_O_TRACEFORK);
-  if (ret != 0)
-    {
-      ret = ptrace (PTRACE_KILL, child_pid, (PTRACE_TYPE_ARG3) 0,
-		    (PTRACE_TYPE_ARG4) 0);
-      if (ret != 0)
-	{
-	  warning ("linux_test_for_tracefork: failed to kill child");
-	  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);
-
-      return;
-    }
-
-  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");
-
-  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, (PTRACE_TYPE_ARG3) 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, (PTRACE_TYPE_ARG3) 0,
-			(PTRACE_TYPE_ARG4) 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);
-
-  do
-    {
-      ret = ptrace (PTRACE_KILL, child_pid, (PTRACE_TYPE_ARG3) 0,
-		    (PTRACE_TYPE_ARG4) 0);
-      if (ret != 0)
-	warning ("linux_test_for_tracefork: failed to kill child");
-      my_waitpid (child_pid, &status, 0);
-    }
-  while (WIFSTOPPED (status));
-
-#if defined(__UCLIBC__) && defined(HAS_NOMMU)
-  free (stack);
-#endif /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-}
-
-
 static void
 linux_look_up_symbols (void)
 {
@@ -4830,10 +4586,10 @@ linux_look_up_symbols (void)
   if (proc->private->thread_db != NULL)
     return;
 
-  /* If the kernel supports tracing forks then it also supports tracing
-     clones, and then we don't need to use the magic thread event breakpoint
-     to learn about threads.  */
-  thread_db_init (!linux_supports_tracefork_flag);
+  /* If the kernel supports tracing clones, then we don't need to
+     use the magic thread event breakpoint to learn about
+     threads.  */
+  thread_db_init (!linux_supports_traceclone ());
 #endif
 }
 
@@ -6097,7 +5853,6 @@ initialize_low (void)
   set_breakpoint_data (the_low_target.breakpoint,
 		       the_low_target.breakpoint_len);
   linux_init_signals ();
-  linux_test_for_tracefork ();
   linux_ptrace_init_warnings ();
 
   sigchld_action.sa_handler = sigchld_handler;
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index e051ab6..4bf0dc3 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -22,8 +22,9 @@
 #include "gdbthread.h"
 #include "gdb_proc_service.h"
 
-#define PTRACE_TYPE_ARG3 void *
-#define PTRACE_TYPE_ARG4 void *
+/* Included for ptrace type definitions.  */
+#include "linux-ptrace.h"
+
 #define PTRACE_XFER_TYPE long
 
 #ifdef HAVE_LINUX_REGSETS
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index db23433..46e3dbf 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -20,6 +20,8 @@
 #include "defs.h"
 #include "inferior.h"
 #include "target.h"
+#include "nat/linux-nat.h"
+#include "nat/linux-waitpid.h"
 #include "gdb_string.h"
 #include "gdb_wait.h"
 #include "gdb_assert.h"
@@ -171,11 +173,6 @@ blocked.  */
 #define O_LARGEFILE 0
 #endif
 
-/* Unlike other extended result codes, WSTOPSIG (status) on
-   PTRACE_O_TRACESYSGOOD syscall events doesn't return SIGTRAP, but
-   instead SIGTRAP with bit 7 set.  */
-#define SYSCALL_SIGTRAP (SIGTRAP | 0x80)
-
 /* The single-threaded native GNU/Linux target_ops.  We save a pointer for
    the use of the multi-threaded target.  */
 static struct target_ops *linux_ops;
@@ -226,24 +223,6 @@ 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;
-
-/* This variable is a tri-state flag: -1 for unknown, 0 if
-   PTRACE_O_TRACESYSGOOD can not be used, 1 if it can.  */
-
-static int linux_supports_tracesysgood_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;
-
-/* Stores the current used ptrace() options.  */
-static int current_ptrace_options = 0;
-
 /* Async mode support.  */
 
 /* The read/write ends of the pipe registered as waitable file in the
@@ -349,244 +328,26 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
   return 0;
 }
 
-
-/* A helper function for linux_test_for_tracefork, called after fork ().  */
-
-static void
-linux_tracefork_child (void)
-{
-  ptrace (PTRACE_TRACEME, 0, 0, 0);
-  kill (getpid (), SIGSTOP);
-  fork ();
-  _exit (0);
-}
-
-/* Wrapper function for waitpid which handles EINTR.  */
-
-static int
-my_waitpid (int pid, int *statusp, int flags)
-{
-  int ret;
-
-  do
-    {
-      ret = waitpid (pid, statusp, 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.  */
+/* Initialize ptrace warnings and check for supported ptrace
+   features given PID.  */
 
 static void
-linux_test_for_tracefork (int original_pid)
+linux_init_ptrace (pid_t pid)
 {
-  int child_pid, ret, status;
-  long second_pid;
-
-  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"));
-	  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);
-
-      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);
-
-  do
-    {
-      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);
-    }
-  while (WIFSTOPPED (status));
-}
-
-/* Determine if PTRACE_O_TRACESYSGOOD can be used to follow syscalls.
-
-   We try to enable syscall 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.  */
-
-static void
-linux_test_for_tracesysgood (int original_pid)
-{
-  int ret;
-
-  linux_supports_tracesysgood_flag = 0;
-
-  ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACESYSGOOD);
-  if (ret != 0)
-    return;
-
-  linux_supports_tracesysgood_flag = 1;
-}
-
-/* Determine wether we support PTRACE_O_TRACESYSGOOD option available.
-   This function also sets linux_supports_tracesysgood_flag.  */
-
-static int
-linux_supports_tracesysgood (int pid)
-{
-  if (linux_supports_tracesysgood_flag == -1)
-    linux_test_for_tracesysgood (pid);
-  return linux_supports_tracesysgood_flag;
-}
-
-/* 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;
-}
-
-static int
-linux_supports_tracevforkdone (int pid)
-{
-  if (linux_supports_tracefork_flag == -1)
-    linux_test_for_tracefork (pid);
-  return linux_supports_tracevforkdone_flag;
-}
-
-static void
-linux_enable_tracesysgood (ptid_t ptid)
-{
-  int pid = ptid_get_lwp (ptid);
-
-  if (pid == 0)
-    pid = ptid_get_pid (ptid);
-
-  if (linux_supports_tracesysgood (pid) == 0)
-    return;
-
-  current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
-
-  ptrace (PTRACE_SETOPTIONS, pid, 0, current_ptrace_options);
-}
-
-
-void
-linux_enable_event_reporting (ptid_t ptid)
-{
-  int pid = ptid_get_lwp (ptid);
-
-  if (pid == 0)
-    pid = ptid_get_pid (ptid);
-
-  if (! linux_supports_tracefork (pid))
-    return;
-
-  current_ptrace_options |= PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK
-    | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE;
-
-  if (linux_supports_tracevforkdone (pid))
-    current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
-
-  /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support
-     read-only process state.  */
-
-  ptrace (PTRACE_SETOPTIONS, pid, 0, current_ptrace_options);
+  linux_enable_event_reporting (pid);
+  linux_ptrace_init_warnings ();
 }
 
 static void
 linux_child_post_attach (int pid)
 {
-  linux_enable_event_reporting (pid_to_ptid (pid));
-  linux_enable_tracesysgood (pid_to_ptid (pid));
-  linux_ptrace_init_warnings ();
+  linux_init_ptrace (pid);
 }
 
 static void
 linux_child_post_startup_inferior (ptid_t ptid)
 {
-  linux_enable_event_reporting (ptid);
-  linux_enable_tracesysgood (ptid);
-  linux_ptrace_init_warnings ();
+  linux_init_ptrace (ptid_get_pid (ptid));
 }
 
 /* Return the number of known LWPs in the tgid given by PID.  */
@@ -772,9 +533,9 @@ holding the child stopped.  Try \"set detach-on-fork\" or \
 	  parent_inf->pspace->breakpoints_not_allowed = detach_fork;
 
 	  parent_lp = find_lwp_pid (pid_to_ptid (parent_pid));
-	  gdb_assert (linux_supports_tracefork_flag >= 0);
+	  gdb_assert (linux_supports_tracefork () >= 0);
 
-	  if (linux_supports_tracevforkdone (0))
+	  if (linux_supports_tracevforkdone ())
 	    {
   	      if (debug_linux_nat)
   		fprintf_unfiltered (gdb_stdlog,
@@ -945,7 +706,7 @@ holding the child stopped.  Try \"set detach-on-fork\" or \
 static int
 linux_child_insert_fork_catchpoint (int pid)
 {
-  return !linux_supports_tracefork (pid);
+  return !linux_supports_tracefork ();
 }
 
 static int
@@ -957,7 +718,7 @@ linux_child_remove_fork_catchpoint (int pid)
 static int
 linux_child_insert_vfork_catchpoint (int pid)
 {
-  return !linux_supports_tracefork (pid);
+  return !linux_supports_tracefork ();
 }
 
 static int
@@ -969,7 +730,7 @@ linux_child_remove_vfork_catchpoint (int pid)
 static int
 linux_child_insert_exec_catchpoint (int pid)
 {
-  return !linux_supports_tracefork (pid);
+  return !linux_supports_tracefork ();
 }
 
 static int
@@ -982,7 +743,7 @@ static int
 linux_child_set_syscall_catchpoint (int pid, int needed, int any_count,
 				    int table_size, int *table)
 {
-  if (!linux_supports_tracesysgood (pid))
+  if (!linux_supports_tracesysgood ())
     return 1;
 
   /* On GNU/Linux, we ignore the arguments.  It means that we only
@@ -1429,7 +1190,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
 
       if (ptrace (PTRACE_ATTACH, lwpid, 0, 0) < 0)
 	{
-	  if (linux_supports_tracefork_flag)
+	  if (linux_supports_tracefork ())
 	    {
 	      /* If we haven't stopped all threads when we get here,
 		 we may have seen a thread listed in thread_db's list,
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index cb8f1da..044f646 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -127,9 +127,6 @@ extern void lin_thread_get_thread_signals (sigset_t *mask);
 void linux_proc_pending_signals (int pid, sigset_t *pending,
 				 sigset_t *blocked, sigset_t *ignored);
 
-/* linux-nat functions for handling fork events.  */
-extern void linux_enable_event_reporting (ptid_t ptid);
-
 extern int lin_lwp_attach_lwp (ptid_t ptid);
 
 extern void linux_stop_lwp (struct lwp_info *lwp);
diff --git a/gdb/nat/linux-nat.h b/gdb/nat/linux-nat.h
new file mode 100644
index 0000000..4d84aa5
--- /dev/null
+++ b/gdb/nat/linux-nat.h
@@ -0,0 +1,28 @@
+/* Code for native debugging support for GNU/Linux (LWP layer).
+
+   Copyright (C) 2000-2013 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 LINUX_NAT_H
+#define LINUX_NAT_H
+
+/* Unlike other extended result codes, WSTOPSIG (status) on
+   PTRACE_O_TRACESYSGOOD syscall events doesn't return SIGTRAP, but
+   instead SIGTRAP with bit 7 set.  */
+#define SYSCALL_SIGTRAP (SIGTRAP | 0x80)
+
+#endif /* LINUX_NAT_H */
diff --git a/gdb/nat/linux-waitpid.c b/gdb/nat/linux-waitpid.c
new file mode 100644
index 0000000..2debea4
--- /dev/null
+++ b/gdb/nat/linux-waitpid.c
@@ -0,0 +1,120 @@
+/* Wrapper implementation for waitpid for GNU/Linux (LWP layer).
+
+   Copyright (C) 2001-2013 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/>.  */
+
+#ifdef GDBSERVER
+#include "server.h"
+#else
+#include "defs.h"
+#include "signal.h"
+#endif
+
+#include "nat/linux-nat.h"
+#include "nat/linux-waitpid.h"
+#include "gdb_wait.h"
+
+/* Print debugging output based on the format string FORMAT and
+   its parameters.  */
+
+static inline void
+linux_debug (const char *format, ...)
+{
+#ifdef GDBSERVER
+  if (debug_threads)
+    {
+      va_list args;
+      va_start (args, format);
+      vfprintf (stderr, format, args);
+      fprintf (stderr, "\n");
+      va_end (args);
+    }
+#else
+  /* GDB-specific debugging output.  */
+#endif
+}
+
+/* Wrapper function for waitpid which handles EINTR, and emulates
+   __WALL for systems where that is not available.  */
+
+int
+my_waitpid (int pid, int *status, int flags)
+{
+  int ret, out_errno;
+
+  linux_debug ("my_waitpid (%d, 0x%x)\n", pid, flags);
+
+  if (flags & __WALL)
+    {
+      sigset_t block_mask, org_mask, wake_mask;
+      int wnohang;
+
+      wnohang = (flags & WNOHANG) != 0;
+      flags &= ~(__WALL | __WCLONE);
+      flags |= WNOHANG;
+
+      /* Block all signals while here.  This avoids knowing about
+	 LinuxThread's signals.  */
+      sigfillset (&block_mask);
+      sigprocmask (SIG_BLOCK, &block_mask, &org_mask);
+
+      /* ... except during the sigsuspend below.  */
+      sigemptyset (&wake_mask);
+
+      while (1)
+	{
+	  /* Since all signals are blocked, there's no need to check
+	     for EINTR here.  */
+	  ret = waitpid (pid, status, flags);
+	  out_errno = errno;
+
+	  if (ret == -1 && out_errno != ECHILD)
+	    break;
+	  else if (ret > 0)
+	    break;
+
+	  if (flags & __WCLONE)
+	    {
+	      /* We've tried both flavors now.  If WNOHANG is set,
+		 there's nothing else to do, just bail out.  */
+	      if (wnohang)
+		break;
+
+	      linux_debug ("blocking\n");
+
+	      /* Block waiting for signals.  */
+	      sigsuspend (&wake_mask);
+	    }
+	  flags ^= __WCLONE;
+	}
+
+      sigprocmask (SIG_SETMASK, &org_mask, NULL);
+    }
+  else
+    {
+      do
+	ret = waitpid (pid, status, flags);
+      while (ret == -1 && errno == EINTR);
+      out_errno = errno;
+    }
+
+  linux_debug ("my_waitpid (%d, 0x%x): status(%x), %d\n",
+	       pid, flags, status ? *status : -1, ret);
+
+  errno = out_errno;
+  return ret;
+}
diff --git a/gdb/nat/linux-waitpid.h b/gdb/nat/linux-waitpid.h
new file mode 100644
index 0000000..0df29d8
--- /dev/null
+++ b/gdb/nat/linux-waitpid.h
@@ -0,0 +1,27 @@
+/* Wrapper for waitpid for GNU/Linux (LWP layer).
+
+   Copyright (C) 2000-2013 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 LINUX_WAITPID_H
+#define LINUX_WAITPID_H
+
+/* Wrapper function for waitpid which handles EINTR, and emulates
+   __WALL for systems where that is not available.  */
+extern int my_waitpid (int pid, int *status, int flags);
+
+#endif /* LINUX_WAITPID_H */

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