Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
@ 2013-08-19 23:27 Luis Machado
  2013-08-20 15:38 ` Tom Tromey
  2013-08-20 16:39 ` Pedro Alves
  0 siblings, 2 replies; 13+ messages in thread
From: Luis Machado @ 2013-08-19 23:27 UTC (permalink / raw)
  To: 'gdb-patches@sourceware.org', Pedro Alves, Tom Tromey

[-- Attachment #1: Type: text/plain, Size: 826 bytes --]

Hi,

This is the second iteration of the patch to do some sharing of ptrace 
and native linux stuff between GDB and gdbserver.

I've included fixes based on reviews, so hopefully this is in better 
shape now.

This patch also breaks the shared native linux code into more meaningful 
modules, stored inside the new subdirectory gdb/nat.

Configure and Makefile bits have been updated. The configure bits have 
been adjusted to define PTRACE_TYPE_ARG4 for GDB as well, since this was 
defined for gdbserver already.

Testsuite looks clean, as well as a build with --enable-targets=all and 
--enable-64-bit-bfd.

I decided not to go with a more elegant solution to the config/* changes 
in this patch since it seems to be outside the scope of this work. We 
can probably address that via a different patch set.

Thoughts?

Luis

[-- Attachment #2: share_ptrace.diff --]
[-- Type: text/x-patch, Size: 64021 bytes --]

2013-08-19  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_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_tracevforkdone): Likewise.
          (linux_supports_tracesysgood): Likewise.
          * common/linux-ptrace.h (HAS_NOMMU): Moved from
          gdbserver/linux-low.c.
          (PTRACE_TYPE_ARG3): Conditionally define.  Moved from
          gdbserver/linux-low.h.
          (PTRACE_TYPE_ARG4): Likewise.
          (linux_enable_event_reporting): New declaration.
          (linux_supports_tracefork): Likewise.
          (linux_supports_tracevforkdone): Likewise.
          (linux_supports_tracesysgood): Likewise.
          * config.in (PTRACE_TYPE_ARG4): Undefine.
          * 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.
          (SFILES): Reorder and add $(srcdir)/nat/linux-waitpid.c.
          (linux_low_h): Add $(srcdir)/../nat/linux-nat.h and
          $(srcdir)/../nat/linux-waitpid.h.
          (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_tracefork.
          (initialize_low): Remove call to linux_test_for_tracefork.
          * linux-low.h (PTRACE_TYPE_ARG3): Moved 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..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 *))
+{
+  int child_pid;
+
+  gdb_byte *stack = (gdb_byte *) arg;
+
+  /* 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);
+
+    /* 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);
+    #else /* !__ia64__ */
+      child_pid = clone (function, stack + STACK_SIZE,
+			 CLONE_VM | SIGCHLD, stack + STACK_SIZE * 2);
+  #endif /* !__ia64__ */
+#else /* !defined(__UCLIBC) && defined(HAS_NOMMU) */
+  child_pid = fork ();
+
+  if (child_pid == 0)
+    function (stack);
+#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 (void *arg)
+{
+  /* Free any allocated stack.  */
+  xfree (arg);
+
+  /* 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 (void *arg)
+{
+  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);
+
+  /* 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);
+
+#ifdef GDBSERVER
+  /* gdbserver does not support PTRACE_O_TRACEFORK yet.  */
+#else
+  /* 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);
+#endif
+
+  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_O_TRACESYSGOOD);
+  current_ptrace_options |= (ret == 0)? PTRACE_O_TRACESYSGOOD : 0;
+
+  /* Check if the target supports PTRACE_O_TRACEVFORKDONE.  */
+  ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
+		PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE);
+  current_ptrace_options |= (ret == 0)? PTRACE_O_TRACEVFORKDONE : 0;
+#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;
+#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 childs.  */
+  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 (ptid_t ptid)
+{
+  int pid = ptid_get_lwp (ptid);
+
+  if (pid == 0)
+    pid = ptid_get_pid (ptid);
+
+  /* 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 ();
+
+  /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
+     support read-only process state.  */
+
+  /* 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_O_TRACEFORK, PTRACE_O_TRACEVFORK,
+   PTRACE_O_TRACECLONE and PTRACE_O_TRACEEXEC are supported by
+   ptrace, 0 otherwise  */
+
+int
+linux_supports_tracefork (void)
+{
+  return ptrace_supports_feature (PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK
+				  | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC);
+}
+
+/* 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..005da3d 100644
--- a/gdb/common/linux-ptrace.h
+++ b/gdb/common/linux-ptrace.h
@@ -22,6 +22,24 @@ 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
+
+#ifdef GDBSERVER
+#if !defined (PTRACE_TYPE_ARG3)
+#define PTRACE_TYPE_ARG3 void *
+#endif
+
+#if !defined (PTRACE_TYPE_ARG4)
+#define PTRACE_TYPE_ARG4 void *
+#endif
+#endif /* GDBSERVER */
+
 #ifndef PTRACE_GETSIGINFO
 # define PTRACE_GETSIGINFO 0x4202
 # define PTRACE_SETSIGINFO 0x4203
@@ -70,4 +88,19 @@ struct buffer;
 extern void linux_ptrace_attach_warnings (pid_t pid, struct buffer *buffer);
 extern void linux_ptrace_init_warnings (void);
 
+/* Enable reporting of all currently supported ptrace events.  */
+extern void linux_enable_event_reporting (ptid_t ptid);
+
+/* Returns non-zero if PTRACE_EVENT_FORK, PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC
+   and PTRACE_EVENT_VFORK are supported by ptrace, 0 otherwise  */
+extern int linux_supports_tracefork (void);
+
+/* Returns non-zero if PTRACE_O_TRACEVFORKDONE is supported by ptrace,
+   0 otherwise  */
+extern int linux_supports_tracevforkdone (void);
+
+/* Returns non-zero if PTRACE_O_TRACESYSGOOD is supported by ptrace,
+   0 otherwise  */
+extern int linux_supports_tracesysgood (void);
+
 #endif /* COMMON_LINUX_PTRACE_H */
diff --git a/gdb/config.in b/gdb/config.in
index 76abd04..5a80001 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 3 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..55425f6 100644
--- a/gdb/config/aarch64/linux.mh
+++ b/gdb/config/aarch64/linux.mh
@@ -19,7 +19,7 @@
 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o aarch64-linux-nat.o \
+NATDEPFILES= linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/config/alpha/alpha-linux.mh b/gdb/config/alpha/alpha-linux.mh
index 9eb9e4b..ba46ec8 100644
--- a/gdb/config/alpha/alpha-linux.mh
+++ b/gdb/config/alpha/alpha-linux.mh
@@ -1,6 +1,6 @@
 # Host: Little-endian Alpha running Linux
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o alpha-linux-nat.o \
+NATDEPFILES= linux-waitpid.o linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/config/arm/linux.mh b/gdb/config/arm/linux.mh
index c0a1c66..af42783 100644
--- a/gdb/config/arm/linux.mh
+++ b/gdb/config/arm/linux.mh
@@ -1,7 +1,7 @@
 # Host: ARM based machine running GNU/Linux
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o arm-linux-nat.o \
+NATDEPFILES= linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/config/i386/linux.mh b/gdb/config/i386/linux.mh
index 7c64e83..d5f64e5 100644
--- a/gdb/config/i386/linux.mh
+++ b/gdb/config/i386/linux.mh
@@ -1,7 +1,7 @@
 # Host: Intel 386 running GNU/Linux.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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 \
diff --git a/gdb/config/i386/linux64.mh b/gdb/config/i386/linux64.mh
index 8d782c1..8d35a31 100644
--- a/gdb/config/i386/linux64.mh
+++ b/gdb/config/i386/linux64.mh
@@ -1,5 +1,5 @@
 # Host: GNU/Linux x86-64
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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 \
diff --git a/gdb/config/ia64/linux.mh b/gdb/config/ia64/linux.mh
index 1a4c68e..0917fc3 100644
--- a/gdb/config/ia64/linux.mh
+++ b/gdb/config/ia64/linux.mh
@@ -1,7 +1,7 @@
 # Host: Intel IA-64 running GNU/Linux
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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 \
diff --git a/gdb/config/m32r/linux.mh b/gdb/config/m32r/linux.mh
index b461806..e8c7caa 100644
--- a/gdb/config/m32r/linux.mh
+++ b/gdb/config/m32r/linux.mh
@@ -1,7 +1,7 @@
 # Host: M32R based machine running GNU/Linux
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o				\
+NATDEPFILES= linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/config/m68k/linux.mh b/gdb/config/m68k/linux.mh
index e3aaf38..40405ca 100644
--- a/gdb/config/m68k/linux.mh
+++ b/gdb/config/m68k/linux.mh
@@ -1,7 +1,7 @@
 # Host: Motorola m68k running GNU/Linux.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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
diff --git a/gdb/config/mips/linux.mh b/gdb/config/mips/linux.mh
index a4f23e3..3dca2db 100644
--- a/gdb/config/mips/linux.mh
+++ b/gdb/config/mips/linux.mh
@@ -1,6 +1,6 @@
 # Host: Linux/MIPS
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o mips-linux-nat.o \
+NATDEPFILES= linux-waitpid.o 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
diff --git a/gdb/config/pa/linux.mh b/gdb/config/pa/linux.mh
index fa46db6..53331e1 100644
--- a/gdb/config/pa/linux.mh
+++ b/gdb/config/pa/linux.mh
@@ -1,6 +1,6 @@
 # Host: Hewlett-Packard PA-RISC machine, running Linux
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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
diff --git a/gdb/config/powerpc/linux.mh b/gdb/config/powerpc/linux.mh
index b0d4ce7..9d4f496 100644
--- a/gdb/config/powerpc/linux.mh
+++ b/gdb/config/powerpc/linux.mh
@@ -3,7 +3,7 @@
 XM_CLIBS=
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/config/powerpc/ppc64-linux.mh b/gdb/config/powerpc/ppc64-linux.mh
index 367a818..24f3287 100644
--- a/gdb/config/powerpc/ppc64-linux.mh
+++ b/gdb/config/powerpc/ppc64-linux.mh
@@ -3,7 +3,7 @@
 XM_CLIBS=
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
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
 
diff --git a/gdb/config/sparc/linux.mh b/gdb/config/sparc/linux.mh
index 6a2cefd..537ecf7 100644
--- a/gdb/config/sparc/linux.mh
+++ b/gdb/config/sparc/linux.mh
@@ -1,7 +1,7 @@
 # Host: GNU/Linux SPARC
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= sparc-nat.o sparc-linux-nat.o \
-	core-regset.o fork-child.o inf-ptrace.o \
+	core-regset.o fork-child.o linux-waitpid.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
diff --git a/gdb/config/sparc/linux64.mh b/gdb/config/sparc/linux64.mh
index d1e1a97..f021864 100644
--- a/gdb/config/sparc/linux64.mh
+++ b/gdb/config/sparc/linux64.mh
@@ -2,7 +2,7 @@
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= sparc-nat.o sparc64-nat.o sparc64-linux-nat.o \
 	core-regset.o \
-	fork-child.o inf-ptrace.o \
+	fork-child.o linux-waitpid.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
diff --git a/gdb/config/tilegx/linux.mh b/gdb/config/tilegx/linux.mh
index 56ef694..c70b452 100644
--- a/gdb/config/tilegx/linux.mh
+++ b/gdb/config/tilegx/linux.mh
@@ -1,7 +1,7 @@
 # Host: Tilera TILE-Gx running GNU/Linux.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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 \
diff --git a/gdb/config/xtensa/linux.mh b/gdb/config/xtensa/linux.mh
index deffe25..afa0043 100644
--- a/gdb/config/xtensa/linux.mh
+++ b/gdb/config/xtensa/linux.mh
@@ -2,7 +2,7 @@
 
 NAT_FILE= config/nm-linux.h
 
-NATDEPFILES= inf-ptrace.o fork-child.o xtensa-linux-nat.o \
+NATDEPFILES= linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/configure b/gdb/configure
index 8067825..5993256 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -10399,6 +10399,10 @@ 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..04c061a 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)
@@ -157,8 +162,10 @@ SFILES=	$(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
 	$(srcdir)/common/common-utils.c $(srcdir)/common/xml-utils.c \
 	$(srcdir)/common/linux-osdata.c $(srcdir)/common/ptid.c \
 	$(srcdir)/common/buffer.c $(srcdir)/common/linux-btrace.c \
-	$(srcdir)/common/filestuff.c $(srcdir)/target/waitstatus.c \
-    $(srcdir)/common/mips-linux-watch.c
+	$(srcdir)/common/filestuff.c $(srcdir)/common/linux-ptrace.c \
+	$(srcdir)/common/mips-linux-watch.c \
+	$(srcdir)/target/waitstatus.c \
+	$(srcdir)/nat/linux-waitpid.c \
 
 DEPFILES = @GDBSERVER_DEPFILES@
 
@@ -447,7 +454,8 @@ server_h = $(srcdir)/server.h $(regcache_h) $(srcdir)/target.h \
 		$(generated_files)
 
 gdbthread_h = $(srcdir)/gdbthread.h $(target_h) $(srcdir)/server.h
-linux_low_h = $(srcdir)/linux-low.h $(gdbthread_h)
+linux_low_h = $(srcdir)/linux-low.h $(srcdir)/../nat/linux-nat.h \
+	      $(srcdir)/../nat/linux-waitpid.h $(gdbthread_h)
 
 linux_ptrace_h = $(srcdir)/../common/linux-ptrace.h
 
@@ -562,6 +570,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) $<
 
+# Shared 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..b5b878c 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -39,15 +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"
 
+
+# Native 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"
+
 # 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} $srv_native_linux_obj"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_xmlfiles="aarch64.xml"
 			srv_xmlfiles="${srv_xmlfiles} aarch64-core.xml"
@@ -60,7 +63,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_native_linux_obj linux-arm-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_xmlfiles="arm-with-iwmmxt.xml"
 			srv_xmlfiles="${srv_xmlfiles} arm-with-vfpv2.xml"
@@ -83,19 +86,19 @@ 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_native_linux_obj linux-bfin-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.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_native_linux_obj linux-crisv32-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.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_native_linux_obj linux-cris-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_linux_usrregs=yes
 			srv_linux_thread_db=yes
@@ -110,7 +113,7 @@ 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_native_linux_obj linux-x86-low.o i386-low.o i387-fp.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o linux-btrace.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
@@ -146,12 +149,12 @@ 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_native_linux_obj linux-ia64-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.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_native_linux_obj linux-m32r-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_linux_usrregs=yes
  			srv_linux_thread_db=yes
@@ -161,7 +164,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_native_linux_obj linux-m68k-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
@@ -172,7 +175,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_native_linux_obj linux-m68k-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
@@ -182,7 +185,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_native_linux_obj linux-mips-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_tgtobj="${srv_tgtobj} mips-linux-watch.o"
 			srv_xmlfiles="mips-linux.xml"
@@ -202,7 +205,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_native_linux_obj linux-nios2-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_xmlfiles="nios2-linux.xml"
 			srv_xmlfiles="${srv_xmlfiles} nios2-cpu.xml"
@@ -225,7 +228,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_native_linux_obj linux-ppc-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_xmlfiles="rs6000/powerpc-32l.xml"
 			srv_xmlfiles="${srv_xmlfiles} rs6000/powerpc-altivec32l.xml"
@@ -271,7 +274,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_native_linux_obj linux-s390-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_xmlfiles="s390-linux32.xml"
 			srv_xmlfiles="${srv_xmlfiles} s390-linux32v1.xml"
@@ -292,14 +295,14 @@ 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_native_linux_obj linux-sh-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.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_native_linux_obj linux-sparc-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o"
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
@@ -316,14 +319,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_native_linux_obj linux-tic6x-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.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_native_linux_obj linux-x86-low.o i386-low.o i387-fp.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.o linux-btrace.o"
 			srv_xmlfiles="$srv_i386_linux_xmlfiles $srv_amd64_linux_xmlfiles"
 			srv_linux_usrregs=yes # This is for i386 progs.
@@ -343,13 +346,13 @@ 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_native_linux_obj linux-xtensa-low.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.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_native_linux_obj linux-osdata.o"
 			srv_tgtobj="${srv_tgtobj} linux-ptrace.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..75ccc1f 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).  */
@@ -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 (ptid_of (event_child));
 	  event_child->must_set_ptrace_flags = 0;
 	}
 
@@ -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)
 {
@@ -4833,7 +4589,7 @@ linux_look_up_symbols (void)
   /* 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);
+  thread_db_init (!linux_supports_tracefork ());
 #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..c545108 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;
 }
 
-\f
-/* A helper function for linux_test_for_tracefork, called after fork ().  */
+/* Initialize ptrace warnings and check for supported ptrace
+   features given PTID.  */
 
 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)
+linux_init_ptrace (ptid_t ptid)
 {
-  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.  */
-
-static void
-linux_test_for_tracefork (int original_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);
-}
-
-\f
-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 (ptid);
+  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_to_ptid (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);
 }
 
 /* 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/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 */

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-19 23:27 [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver Luis Machado
@ 2013-08-20 15:38 ` Tom Tromey
  2013-08-20 15:46   ` Luis Machado
  2013-08-20 16:39 ` Pedro Alves
  1 sibling, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2013-08-20 15:38 UTC (permalink / raw)
  To: lgustavo; +Cc: 'gdb-patches@sourceware.org', Pedro Alves

>>>>> "Luis" == Luis Machado <lgustavo@codesourcery.com> writes:

Luis> This is the second iteration of the patch to do some sharing of ptrace
Luis> and native linux stuff between GDB and gdbserver.

Luis> I've included fixes based on reviews, so hopefully this is in better
Luis> shape now.

Thanks.  I like it.  I have a few nits though.

Luis>           * common/linux-ptrace.c: Include nat/linux-waitpid.h.

I suppose at some point this file ought to be moved.

Luis>           * config.in (PTRACE_TYPE_ARG4): Undefine.

Usually for config.in I just write:

	* config.in: Rebuild.

... since presumably you just used autoheader.

Luis>           * configure.ac (AC_CACHE_CHECK): Add void * to the list of
Luis>           ptrace's 4th argument's types.
Luis>           Check the type of PTRACE_TYPE_ARG4.

AC_CACHE_CHECK isn't the right function name.  Perhaps no name is best
here.

Also, doesn't this check need to be done in gdbserver as well?

Tom


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-20 15:38 ` Tom Tromey
@ 2013-08-20 15:46   ` Luis Machado
  2013-08-20 16:14     ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Luis Machado @ 2013-08-20 15:46 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 'gdb-patches@sourceware.org', Pedro Alves

On 08/20/2013 12:38 PM, Tom Tromey wrote:
>>>>>> "Luis" == Luis Machado <lgustavo@codesourcery.com> writes:
>
> Luis> This is the second iteration of the patch to do some sharing of ptrace
> Luis> and native linux stuff between GDB and gdbserver.
>
> Luis> I've included fixes based on reviews, so hopefully this is in better
> Luis> shape now.
>
> Thanks.  I like it.  I have a few nits though.
>
> Luis>           * common/linux-ptrace.c: Include nat/linux-waitpid.h.
>
> I suppose at some point this file ought to be moved.

Right. Should i move it now as part of this cleanup or should i move it 
in a follow up?

>
> Luis>           * config.in (PTRACE_TYPE_ARG4): Undefine.
>
> Usually for config.in I just write:
>
> 	* config.in: Rebuild.
>
> ... since presumably you just used autoheader.
>

Oops. I should probably rebuild it then.

> Luis>           * configure.ac (AC_CACHE_CHECK): Add void * to the list of
> Luis>           ptrace's 4th argument's types.
> Luis>           Check the type of PTRACE_TYPE_ARG4.
>
> AC_CACHE_CHECK isn't the right function name.  Perhaps no name is best
> here.

Got it. Let's go with no name.

>
> Also, doesn't this check need to be done in gdbserver as well?

For gdbserver we explicitly define the correct types for each argument.

Since this is a runtime check, we may run into issues if we attempt to 
run a binary on a build machine if the target machine is from a 
different architecture. Does it make sense?

Luis


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-20 15:46   ` Luis Machado
@ 2013-08-20 16:14     ` Tom Tromey
  0 siblings, 0 replies; 13+ messages in thread
From: Tom Tromey @ 2013-08-20 16:14 UTC (permalink / raw)
  To: lgustavo; +Cc: 'gdb-patches@sourceware.org', Pedro Alves

>>>>> "Luis" == Luis Machado <lgustavo@codesourcery.com> writes:

Luis> Right. Should i move it now as part of this cleanup or should i move
Luis> it in a follow up?

Probably preferable as a follow-up.
Or a prequel.

>> Also, doesn't this check need to be done in gdbserver as well?

Luis> For gdbserver we explicitly define the correct types for each argument.

Ok, I see.

Luis> Since this is a runtime check, we may run into issues if we attempt to
Luis> run a binary on a build machine if the target machine is from a
Luis> different architecture. Does it make sense?

I looks like an AC_TRY_COMPILE check to me.  That means it is safe.
However, if it isn't needed for gdbserver, then the point is moot.

Tom


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-19 23:27 [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver Luis Machado
  2013-08-20 15:38 ` Tom Tromey
@ 2013-08-20 16:39 ` Pedro Alves
  2013-08-20 16:44   ` Pedro Alves
                     ` (2 more replies)
  1 sibling, 3 replies; 13+ messages in thread
From: Pedro Alves @ 2013-08-20 16:39 UTC (permalink / raw)
  To: lgustavo; +Cc: 'gdb-patches@sourceware.org', Tom Tromey

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.

> +
> +    /* 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);
> +    #else /* !__ia64__ */
> +      child_pid = clone (function, stack + STACK_SIZE,
> +			 CLONE_VM | SIGCHLD, stack + STACK_SIZE * 2);
> +  #endif /* !__ia64__ */
> +#else /* !defined(__UCLIBC) && defined(HAS_NOMMU) */
> +  child_pid = fork ();
> +
> +  if (child_pid == 0)
> +    function (stack);
> +#endif /* defined(__UCLIBC) && defined(HAS_NOMMU) */
> +
> +  if (child_pid == -1)
> +    perror_with_name (("fork"));
> +
> +  return child_pid;
> +}
> +


> +
> +/* 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);
> +
> +#ifdef GDBSERVER
> +  /* gdbserver does not support PTRACE_O_TRACEFORK yet.  */


This here doesn't look right ...

> +#else
> +  /* 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);
> +#endif
> +
> +  if (ret != 0)

... as this looks like will always be reached for gdbserver.
The PTRACE_O_TRACEFORK testing is being used in gdbserver as
proxy for close tracing support.

> +    {
> +      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_O_TRACESYSGOOD);
> +  current_ptrace_options |= (ret == 0)? PTRACE_O_TRACESYSGOOD : 0;

Space before '?'.

Arguably,

  if (ret == 0)
    current_ptrace_options |= PTRACE_O_TRACESYSGOOD;

would be more straightforward to read.

> +
> +  /* Check if the target supports PTRACE_O_TRACEVFORKDONE.  */
> +  ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
> +		PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE);
> +  current_ptrace_options |= (ret == 0)? PTRACE_O_TRACEVFORKDONE : 0;

Ditto missing space.

> +#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;
> +#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 childs.  */

s/childs/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 (ptid_t ptid)

Could you preserve gdbserver's prototype here, please?  That
is, take a single integer pid rather than a ptid.

> +{
> +  int pid = ptid_get_lwp (ptid);
> +
> +  if (pid == 0)
> +    pid = ptid_get_pid (ptid);

This dance is only really necessary for GDB.  Given
this is working at the ptrace level, passing in the pid
only is more to the point.  (E.g., passing in a ptid makes
the reader wonder whether PTID can be represent a whole
inferior, and thus event reporting enablement gets applied to
all threads).

> +
> +  /* 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 ();
> +
> +  /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
> +     support read-only process state.  */

This comment really belongs close to where current_ptrace_options
is set.  That in, in the original code, read it as attached to
the "current_ptrace_options |= " bits above, not the ptrace call
below.

> +
> +  /* 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_O_TRACEFORK, PTRACE_O_TRACEVFORK,
> +   PTRACE_O_TRACECLONE and PTRACE_O_TRACEEXEC are supported by
> +   ptrace, 0 otherwise  */

Missing period.  Given the name of the function, I'd suggest instead:

/* 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 | 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?

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.

> +}
> +
> +/* 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..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 ?

> +#define PTRACE_TYPE_ARG3 void *
> +#endif
> +
> +#if !defined (PTRACE_TYPE_ARG4)
> +#define PTRACE_TYPE_ARG4 void *
> +#endif
> +#endif /* GDBSERVER */


> +
>  #ifndef PTRACE_GETSIGINFO
>  # define PTRACE_GETSIGINFO 0x4202
>  # define PTRACE_SETSIGINFO 0x4203
> @@ -70,4 +88,19 @@ struct buffer;
>  extern void linux_ptrace_attach_warnings (pid_t pid, struct buffer *buffer);
>  extern void linux_ptrace_init_warnings (void);
>  
> +/* Enable reporting of all currently supported ptrace events.  */
> +extern void linux_enable_event_reporting (ptid_t ptid);
> +
> +/* Returns non-zero if PTRACE_EVENT_FORK, PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC
> +   and PTRACE_EVENT_VFORK are supported by ptrace, 0 otherwise  */
> +extern int linux_supports_tracefork (void);
> +
> +/* Returns non-zero if PTRACE_O_TRACEVFORKDONE is supported by ptrace,
> +   0 otherwise  */
> +extern int linux_supports_tracevforkdone (void);
> +
> +/* Returns non-zero if PTRACE_O_TRACESYSGOOD is supported by ptrace,
> +   0 otherwise  */
> +extern int linux_supports_tracesysgood (void);
> +
>  #endif /* COMMON_LINUX_PTRACE_H */
> diff --git a/gdb/config.in b/gdb/config.in
> index 76abd04..5a80001 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 3 for ptrace. */
> +#undef PTRACE_TYPE_ARG4

Off by one in comment...

> +
>  /* Define to the type of arg 5 for ptrace. */
>  #undef PTRACE_TYPE_ARG5
>  

> 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.])

... but here the comment looks right, so I think you just
forgot to regenerate config.in.

>  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..04c061a 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
> +#

Missing final quote, and probably period:

  # E.g.: "target/wait.h".

>  INCLUDE_CFLAGS = -I. -I${srcdir} -I$(srcdir)/../common \
>  	-I$(srcdir)/../regformats -I$(srcdir)/../ -I$(INCLUDE_DIR) \
>  	$(INCGNU)
> @@ -157,8 +162,10 @@ SFILES=	$(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
>  	$(srcdir)/common/common-utils.c $(srcdir)/common/xml-utils.c \
>  	$(srcdir)/common/linux-osdata.c $(srcdir)/common/ptid.c \
>  	$(srcdir)/common/buffer.c $(srcdir)/common/linux-btrace.c \
> -	$(srcdir)/common/filestuff.c $(srcdir)/target/waitstatus.c \
> -    $(srcdir)/common/mips-linux-watch.c
> +	$(srcdir)/common/filestuff.c $(srcdir)/common/linux-ptrace.c \
> +	$(srcdir)/common/mips-linux-watch.c \
> +	$(srcdir)/target/waitstatus.c \
> +	$(srcdir)/nat/linux-waitpid.c \

Hmm, so linux-ptrace.c was missing?  (It's a bit harder than expected to
see what's doing on in this hunk, given the order's been changed.)
It seems linux-procfs.c is missing too, and probably others.  Please do that
as separate patch...  This is penance for forgetting that backslash in the
last line, and forcing me to look closer.  ;-)

>  
>  DEPFILES = @GDBSERVER_DEPFILES@
>  
> @@ -447,7 +454,8 @@ server_h = $(srcdir)/server.h $(regcache_h) $(srcdir)/target.h \
>  		$(generated_files)
>  
>  gdbthread_h = $(srcdir)/gdbthread.h $(target_h) $(srcdir)/server.h
> -linux_low_h = $(srcdir)/linux-low.h $(gdbthread_h)
> +linux_low_h = $(srcdir)/linux-low.h $(srcdir)/../nat/linux-nat.h \
> +	      $(srcdir)/../nat/linux-waitpid.h $(gdbthread_h)

Is this really necessary, given we now have automatic dependencies
in gdbserver?  $linux_low_h even used anywhere at all.  Looks
like these variables are just waiting to be garbage collected...

>  
>  linux_ptrace_h = $(srcdir)/../common/linux-ptrace.h
>  
> @@ -562,6 +570,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) $<
>  
> +# Shared 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..b5b878c 100644
> --- a/gdb/gdbserver/configure.srv
> +++ b/gdb/gdbserver/configure.srv
> @@ -39,15 +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"
>  
> +
> +# Native 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"

Let's just drop the "native" bit.  All objects in this file are native:

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

Thanks,
-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-20 16:39 ` Pedro Alves
@ 2013-08-20 16:44   ` Pedro Alves
  2013-08-20 16:48   ` Tom Tromey
  2013-08-21  3:09   ` Luis Machado
  2 siblings, 0 replies; 13+ messages in thread
From: Pedro Alves @ 2013-08-20 16:44 UTC (permalink / raw)
  Cc: lgustavo, 'gdb-patches@sourceware.org', Tom Tromey

On 08/20/2013 05:39 PM, Pedro Alves wrote:
> ... as this looks like will always be reached for gdbserver.
> The PTRACE_O_TRACEFORK testing is being used in gdbserver as
> proxy for close tracing support.

Sorry, typo, s/close/clone/ , in case it wasn't clear...

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-20 16:39 ` Pedro Alves
  2013-08-20 16:44   ` Pedro Alves
@ 2013-08-20 16:48   ` Tom Tromey
  2013-08-20 17:03     ` Pedro Alves
  2013-08-21  3:09   ` Luis Machado
  2 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2013-08-20 16:48 UTC (permalink / raw)
  To: Pedro Alves; +Cc: lgustavo, 'gdb-patches@sourceware.org'

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

>> +void
>> +linux_enable_event_reporting (ptid_t ptid)

Pedro> Could you preserve gdbserver's prototype here, please?  That
Pedro> is, take a single integer pid rather than a ptid.

Just a nit -- but why int and not pid_t?
(Not a typo for ptid_t, that one I understand :)

Tom


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-20 16:48   ` Tom Tromey
@ 2013-08-20 17:03     ` Pedro Alves
  0 siblings, 0 replies; 13+ messages in thread
From: Pedro Alves @ 2013-08-20 17:03 UTC (permalink / raw)
  To: Tom Tromey; +Cc: lgustavo, 'gdb-patches@sourceware.org'

On 08/20/2013 05:48 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
> 
>>> +void
>>> +linux_enable_event_reporting (ptid_t ptid)
> 
> Pedro> Could you preserve gdbserver's prototype here, please?  That
> Pedro> is, take a single integer pid rather than a ptid.
> 
> Just a nit -- but why int and not pid_t?

Hysterical raisins, I guess.

Eheh, I purposedly said integer to avoid implying "int".  :-)
The current gdbserver prototype does use int, but I don't
care whether it's int or pid_t as long as it's a single
integer.
On the core side, ptid_t holds an int for pid, so code that
faces the core would tend to use int, and we extract pids from ptids
in the target code all the time, so int tends to Just Be Used.
In this particular case, given this is native code, there's
nothing stopping it from using pid_t.

> (Not a typo for ptid_t, that one I understand :)

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-20 16:39 ` Pedro Alves
  2013-08-20 16:44   ` Pedro Alves
  2013-08-20 16:48   ` Tom Tromey
@ 2013-08-21  3:09   ` Luis Machado
  2013-08-21 21:29     ` Pedro Alves
  2 siblings, 1 reply; 13+ messages in thread
From: Luis Machado @ 2013-08-21  3:09 UTC (permalink / raw)
  To: Pedro Alves; +Cc: 'gdb-patches@sourceware.org', Tom Tromey

[-- Attachment #1: Type: text/plain, Size: 11475 bytes --]

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.

>> +
>> +    /* 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);
>> +    #else /* !__ia64__ */
>> +      child_pid = clone (function, stack + STACK_SIZE,
>> +			 CLONE_VM | SIGCHLD, stack + STACK_SIZE * 2);
>> +  #endif /* !__ia64__ */
>> +#else /* !defined(__UCLIBC) && defined(HAS_NOMMU) */
>> +  child_pid = fork ();
>> +
>> +  if (child_pid == 0)
>> +    function (stack);
>> +#endif /* defined(__UCLIBC) && defined(HAS_NOMMU) */
>> +
>> +  if (child_pid == -1)
>> +    perror_with_name (("fork"));
>> +
>> +  return child_pid;
>> +}
>> +
>
>
>> +
>> +/* 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);
>> +
>> +#ifdef GDBSERVER
>> +  /* gdbserver does not support PTRACE_O_TRACEFORK yet.  */
>
>
> This here doesn't look right ...
>
>> +#else
>> +  /* 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);
>> +#endif
>> +
>> +  if (ret != 0)
>
> ... as this looks like will always be reached for gdbserver.
> The PTRACE_O_TRACEFORK testing is being used in gdbserver as
> proxy for close tracing support.
>

I've removed the guards now. gdbserver should be able to run this chunk 
of code and lie about not supporting PTRACE_O_TRACEFORK later on.

>> +#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_O_TRACESYSGOOD);
>> +  current_ptrace_options |= (ret == 0)? PTRACE_O_TRACESYSGOOD : 0;
>
> Space before '?'.
>
> Arguably,
>
>    if (ret == 0)
>      current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
>
> would be more straightforward to read.
>

I've used your suggestion now.

>> +/* Enable reporting of all currently supported ptrace events.  */
>> +
>> +void
>> +linux_enable_event_reporting (ptid_t ptid)
>
> Could you preserve gdbserver's prototype here, please?  That
> is, take a single integer pid rather than a ptid.
>

Done.

>> +  /* 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 ();
>> +
>> +  /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
>> +     support read-only process state.  */
>
> This comment really belongs close to where current_ptrace_options
> is set.  That in, in the original code, read it as attached to
> the "current_ptrace_options |= " bits above, not the ptrace call
> below.
>

Moved closer to where it belongs.


>> +/* Returns non-zero if PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK,
>> +   PTRACE_O_TRACECLONE and PTRACE_O_TRACEEXEC are supported by
>> +   ptrace, 0 otherwise  */
>
> Missing period.  Given the name of the function, I'd suggest instead:
>
> /* 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.
>

Done. 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?

>
> 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.

Thanks Tom.

>> +#define PTRACE_TYPE_ARG3 void *
>> +#endif
>> +
>> +#if !defined (PTRACE_TYPE_ARG4)
>> +#define PTRACE_TYPE_ARG4 void *
>> +#endif
>> +#endif /* GDBSERVER */
>
>

These are gone now.

>> diff --git a/gdb/config.in b/gdb/config.in
>> index 76abd04..5a80001 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 3 for ptrace. */
>> +#undef PTRACE_TYPE_ARG4
>
> Off by one in comment...
>
>> +
>>   /* Define to the type of arg 5 for ptrace. */
>>   #undef PTRACE_TYPE_ARG5
>>
>
>> 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.])
>
> ... but here the comment looks right, so I think you just
> forgot to regenerate config.in.
>

Fixed by regenerating the required files.

>>   INCLUDE_CFLAGS = -I. -I${srcdir} -I$(srcdir)/../common \
>>   	-I$(srcdir)/../regformats -I$(srcdir)/../ -I$(INCLUDE_DIR) \
>>   	$(INCGNU)
>> @@ -157,8 +162,10 @@ SFILES=	$(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
>>   	$(srcdir)/common/common-utils.c $(srcdir)/common/xml-utils.c \
>>   	$(srcdir)/common/linux-osdata.c $(srcdir)/common/ptid.c \
>>   	$(srcdir)/common/buffer.c $(srcdir)/common/linux-btrace.c \
>> -	$(srcdir)/common/filestuff.c $(srcdir)/target/waitstatus.c \
>> -    $(srcdir)/common/mips-linux-watch.c
>> +	$(srcdir)/common/filestuff.c $(srcdir)/common/linux-ptrace.c \
>> +	$(srcdir)/common/mips-linux-watch.c \
>> +	$(srcdir)/target/waitstatus.c \
>> +	$(srcdir)/nat/linux-waitpid.c \
>
> Hmm, so linux-ptrace.c was missing?  (It's a bit harder than expected to
> see what's doing on in this hunk, given the order's been changed.)
> It seems linux-procfs.c is missing too, and probably others.  Please do that
> as separate patch...  This is penance for forgetting that backslash in the
> last line, and forcing me to look closer.  ;-)
>

The identation problem with the mips-linux-watch.c entry bothered me, 
but turns out the inclusion of linux-ptrace.c and linux-waitpid.c isn't 
needed at all. I have a successful build without any change to SFILES.

>>
>>   DEPFILES = @GDBSERVER_DEPFILES@
>>
>> @@ -447,7 +454,8 @@ server_h = $(srcdir)/server.h $(regcache_h) $(srcdir)/target.h \
>>   		$(generated_files)
>>
>>   gdbthread_h = $(srcdir)/gdbthread.h $(target_h) $(srcdir)/server.h
>> -linux_low_h = $(srcdir)/linux-low.h $(gdbthread_h)
>> +linux_low_h = $(srcdir)/linux-low.h $(srcdir)/../nat/linux-nat.h \
>> +	      $(srcdir)/../nat/linux-waitpid.h $(gdbthread_h)
>
> Is this really necessary, given we now have automatic dependencies
> in gdbserver?  $linux_low_h even used anywhere at all.  Looks
> like these variables are just waiting to be garbage collected...
>

Probably not. I removed this change now.

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.

Thanks!
Luis

[-- Attachment #2: ptrace_share.diff --]
[-- Type: text/x-patch, Size: 71430 bytes --]

2013-08-20  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_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_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_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_tracefork.
	(initialize_low): Remove call to linux_test_for_tracefork.
	* linux-low.h (PTRACE_TYPE_ARG3): Remove.
	(PTRACE_TYPE_ARG4): Likewise.
	Include linux-ptrace.h.
	* configure.ac: Check ptrace return and argument types.
	* configure: Regenerate.
	* config.in: Regenerate.

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..bcd3c94 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,283 @@ 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.
+
+   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 *))
+{
+  int child_pid;
+#if defined(__UCLIBC__) && defined(HAS_NOMMU)
+  gdb_byte *stack;
+#endif
+
+  /* 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);
+    else
+      stack = (gdb_byte *) arg;
+
+    /* 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);
+    #else /* !__ia64__ */
+      child_pid = clone (function, stack + STACK_SIZE,
+			 CLONE_VM | SIGCHLD, stack + STACK_SIZE * 2);
+  #endif /* !__ia64__ */
+#else /* !defined(__UCLIBC) && defined(HAS_NOMMU) */
+  child_pid = fork ();
+
+  if (child_pid == 0)
+    function (arg);
+#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 (void *arg)
+{
+  /* Free any allocated stack.  */
+  xfree (arg);
+
+  /* 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 (void *arg)
+{
+  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);
+
+  /* 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_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_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_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,
+	  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  */
+
+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..4f7456f 100644
--- a/gdb/common/linux-ptrace.h
+++ b/gdb/common/linux-ptrace.h
@@ -22,6 +22,14 @@ 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
+
 #ifndef PTRACE_GETSIGINFO
 # define PTRACE_GETSIGINFO 0x4202
 # define PTRACE_SETSIGINFO 0x4203
@@ -69,5 +77,9 @@ 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_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..55425f6 100644
--- a/gdb/config/aarch64/linux.mh
+++ b/gdb/config/aarch64/linux.mh
@@ -19,7 +19,7 @@
 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o aarch64-linux-nat.o \
+NATDEPFILES= linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/config/alpha/alpha-linux.mh b/gdb/config/alpha/alpha-linux.mh
index 9eb9e4b..ba46ec8 100644
--- a/gdb/config/alpha/alpha-linux.mh
+++ b/gdb/config/alpha/alpha-linux.mh
@@ -1,6 +1,6 @@
 # Host: Little-endian Alpha running Linux
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o alpha-linux-nat.o \
+NATDEPFILES= linux-waitpid.o linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/config/arm/linux.mh b/gdb/config/arm/linux.mh
index c0a1c66..af42783 100644
--- a/gdb/config/arm/linux.mh
+++ b/gdb/config/arm/linux.mh
@@ -1,7 +1,7 @@
 # Host: ARM based machine running GNU/Linux
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o arm-linux-nat.o \
+NATDEPFILES= linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/config/i386/linux.mh b/gdb/config/i386/linux.mh
index 7c64e83..d5f64e5 100644
--- a/gdb/config/i386/linux.mh
+++ b/gdb/config/i386/linux.mh
@@ -1,7 +1,7 @@
 # Host: Intel 386 running GNU/Linux.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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 \
diff --git a/gdb/config/i386/linux64.mh b/gdb/config/i386/linux64.mh
index 8d782c1..8d35a31 100644
--- a/gdb/config/i386/linux64.mh
+++ b/gdb/config/i386/linux64.mh
@@ -1,5 +1,5 @@
 # Host: GNU/Linux x86-64
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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 \
diff --git a/gdb/config/ia64/linux.mh b/gdb/config/ia64/linux.mh
index 1a4c68e..0917fc3 100644
--- a/gdb/config/ia64/linux.mh
+++ b/gdb/config/ia64/linux.mh
@@ -1,7 +1,7 @@
 # Host: Intel IA-64 running GNU/Linux
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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 \
diff --git a/gdb/config/m32r/linux.mh b/gdb/config/m32r/linux.mh
index b461806..e8c7caa 100644
--- a/gdb/config/m32r/linux.mh
+++ b/gdb/config/m32r/linux.mh
@@ -1,7 +1,7 @@
 # Host: M32R based machine running GNU/Linux
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o				\
+NATDEPFILES= linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/config/m68k/linux.mh b/gdb/config/m68k/linux.mh
index e3aaf38..40405ca 100644
--- a/gdb/config/m68k/linux.mh
+++ b/gdb/config/m68k/linux.mh
@@ -1,7 +1,7 @@
 # Host: Motorola m68k running GNU/Linux.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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
diff --git a/gdb/config/mips/linux.mh b/gdb/config/mips/linux.mh
index a4f23e3..3dca2db 100644
--- a/gdb/config/mips/linux.mh
+++ b/gdb/config/mips/linux.mh
@@ -1,6 +1,6 @@
 # Host: Linux/MIPS
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o mips-linux-nat.o \
+NATDEPFILES= linux-waitpid.o 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
diff --git a/gdb/config/pa/linux.mh b/gdb/config/pa/linux.mh
index fa46db6..53331e1 100644
--- a/gdb/config/pa/linux.mh
+++ b/gdb/config/pa/linux.mh
@@ -1,6 +1,6 @@
 # Host: Hewlett-Packard PA-RISC machine, running Linux
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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
diff --git a/gdb/config/powerpc/linux.mh b/gdb/config/powerpc/linux.mh
index b0d4ce7..9d4f496 100644
--- a/gdb/config/powerpc/linux.mh
+++ b/gdb/config/powerpc/linux.mh
@@ -3,7 +3,7 @@
 XM_CLIBS=
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
diff --git a/gdb/config/powerpc/ppc64-linux.mh b/gdb/config/powerpc/ppc64-linux.mh
index 367a818..24f3287 100644
--- a/gdb/config/powerpc/ppc64-linux.mh
+++ b/gdb/config/powerpc/ppc64-linux.mh
@@ -3,7 +3,7 @@
 XM_CLIBS=
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
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
 
diff --git a/gdb/config/sparc/linux.mh b/gdb/config/sparc/linux.mh
index 6a2cefd..537ecf7 100644
--- a/gdb/config/sparc/linux.mh
+++ b/gdb/config/sparc/linux.mh
@@ -1,7 +1,7 @@
 # Host: GNU/Linux SPARC
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= sparc-nat.o sparc-linux-nat.o \
-	core-regset.o fork-child.o inf-ptrace.o \
+	core-regset.o fork-child.o linux-waitpid.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
diff --git a/gdb/config/sparc/linux64.mh b/gdb/config/sparc/linux64.mh
index d1e1a97..f021864 100644
--- a/gdb/config/sparc/linux64.mh
+++ b/gdb/config/sparc/linux64.mh
@@ -2,7 +2,7 @@
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= sparc-nat.o sparc64-nat.o sparc64-linux-nat.o \
 	core-regset.o \
-	fork-child.o inf-ptrace.o \
+	fork-child.o linux-waitpid.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
diff --git a/gdb/config/tilegx/linux.mh b/gdb/config/tilegx/linux.mh
index 56ef694..c70b452 100644
--- a/gdb/config/tilegx/linux.mh
+++ b/gdb/config/tilegx/linux.mh
@@ -1,7 +1,7 @@
 # Host: Tilera TILE-Gx running GNU/Linux.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= linux-waitpid.o 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 \
diff --git a/gdb/config/xtensa/linux.mh b/gdb/config/xtensa/linux.mh
index deffe25..afa0043 100644
--- a/gdb/config/xtensa/linux.mh
+++ b/gdb/config/xtensa/linux.mh
@@ -2,7 +2,7 @@
 
 NAT_FILE= config/nm-linux.h
 
-NATDEPFILES= inf-ptrace.o fork-child.o xtensa-linux-nat.o \
+NATDEPFILES= linux-waitpid.o 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
 NAT_CDEPS = $(srcdir)/proc-service.list
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/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
+
 /* Define if the target supports branch tracing. */
 #undef HAVE_LINUX_BTRACE
 
@@ -256,6 +263,18 @@
 /* Additional package description */
 #undef PKGVERSION
 
+/* 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
+
+/* Define as the return type of ptrace. */
+#undef PTRACE_TYPE_RET
+
 /* Bug reporting address */
 #undef REPORT_BUGS_TO
 
diff --git a/gdb/gdbserver/configure b/gdb/gdbserver/configure
index 193aedc..7e61fa9 100755
--- a/gdb/gdbserver/configure
+++ b/gdb/gdbserver/configure
@@ -5222,6 +5222,194 @@ _ACEOF
 fi
 
 
+#
+# Check the return and argument types of ptrace.  No canned test for
+# this, so roll our own.
+#
+gdb_ptrace_headers='
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_PTRACE_H
+# include <sys/ptrace.h>
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+'
+# There is no point in checking if we don't have a prototype.
+ac_fn_c_check_decl "$LINENO" "ptrace" "ac_cv_have_decl_ptrace" "$gdb_ptrace_headers
+"
+if test "x$ac_cv_have_decl_ptrace" = x""yes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_PTRACE $ac_have_decl
+_ACEOF
+if test $ac_have_decl = 1; then :
+
+else
+
+  : ${gdb_cv_func_ptrace_ret='int'}
+  : ${gdb_cv_func_ptrace_args='int,int,long,long'}
+
+fi
+
+# Check return type.  Varargs (used on GNU/Linux) conflict with the
+# empty argument list, so check for that explicitly.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of ptrace" >&5
+$as_echo_n "checking return type of ptrace... " >&6; }
+if test "${gdb_cv_func_ptrace_ret+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$gdb_ptrace_headers
+int
+main ()
+{
+extern long ptrace (enum __ptrace_request, ...);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  gdb_cv_func_ptrace_ret='long'
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$gdb_ptrace_headers
+int
+main ()
+{
+extern int ptrace ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  gdb_cv_func_ptrace_ret='int'
+else
+  gdb_cv_func_ptrace_ret='long'
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_func_ptrace_ret" >&5
+$as_echo "$gdb_cv_func_ptrace_ret" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define PTRACE_TYPE_RET $gdb_cv_func_ptrace_ret
+_ACEOF
+
+# Check argument types.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking types of arguments for ptrace" >&5
+$as_echo_n "checking types of arguments for ptrace... " >&6; }
+if test "${gdb_cv_func_ptrace_args+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$gdb_ptrace_headers
+int
+main ()
+{
+extern long ptrace (enum __ptrace_request, ...);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  gdb_cv_func_ptrace_args='int,int,long,long'
+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' 'void *'; do
+     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$gdb_ptrace_headers
+int
+main ()
+{
+
+extern $gdb_cv_func_ptrace_ret
+  ptrace ($gdb_arg1, $gdb_arg2, $gdb_arg3, $gdb_arg4);
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  gdb_cv_func_ptrace_args="$gdb_arg1,$gdb_arg2,$gdb_arg3,$gdb_arg4";
+    break 4;
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    for gdb_arg5 in 'int *' 'int' 'long'; do
+     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$gdb_ptrace_headers
+int
+main ()
+{
+
+extern $gdb_cv_func_ptrace_ret
+  ptrace ($gdb_arg1, $gdb_arg2, $gdb_arg3, $gdb_arg4, $gdb_arg5);
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+gdb_cv_func_ptrace_args="$gdb_arg1,$gdb_arg2,$gdb_arg3,$gdb_arg4,$gdb_arg5";
+    break 5;
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    done
+   done
+  done
+ done
+done
+# Provide a safe default value.
+: ${gdb_cv_func_ptrace_args='int,int,long,long'}
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_func_ptrace_args" >&5
+$as_echo "$gdb_cv_func_ptrace_args" >&6; }
+ac_save_IFS=$IFS; IFS=','
+set dummy `echo "$gdb_cv_func_ptrace_args" | sed 's/\*/\*/g'`
+IFS=$ac_save_IFS
+shift
+
+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
+#define PTRACE_TYPE_ARG5 $5
+_ACEOF
+
+fi
+#
+# End of ptrace type checks
+#
+
 
 
 # Check whether --with-pkgversion was given.
diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac
index 456a1f7..84ff9ea 100644
--- a/gdb/gdbserver/configure.ac
+++ b/gdb/gdbserver/configure.ac
@@ -214,6 +214,83 @@ AC_CHECK_TYPES([Elf32_auxv_t, Elf64_auxv_t], [], [],
 #include <elf.h>
 )
 
+#
+# Check the return and argument types of ptrace.  No canned test for
+# this, so roll our own.
+#
+gdb_ptrace_headers='
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_PTRACE_H
+# include <sys/ptrace.h>
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+'
+# There is no point in checking if we don't have a prototype.
+AC_CHECK_DECLS(ptrace, [], [
+  : ${gdb_cv_func_ptrace_ret='int'}
+  : ${gdb_cv_func_ptrace_args='int,int,long,long'}
+], $gdb_ptrace_headers)
+# Check return type.  Varargs (used on GNU/Linux) conflict with the
+# empty argument list, so check for that explicitly.
+AC_CACHE_CHECK([return type of ptrace], gdb_cv_func_ptrace_ret,
+  AC_TRY_COMPILE($gdb_ptrace_headers,
+    [extern long ptrace (enum __ptrace_request, ...);],
+    gdb_cv_func_ptrace_ret='long',
+    AC_TRY_COMPILE($gdb_ptrace_headers,
+      [extern int ptrace ();],
+      gdb_cv_func_ptrace_ret='int',
+      gdb_cv_func_ptrace_ret='long')))
+AC_DEFINE_UNQUOTED(PTRACE_TYPE_RET, $gdb_cv_func_ptrace_ret,
+  [Define as the return type of ptrace.])
+# Check argument types.
+AC_CACHE_CHECK([types of arguments for ptrace], gdb_cv_func_ptrace_args, [
+  AC_TRY_COMPILE($gdb_ptrace_headers,
+    [extern long ptrace (enum __ptrace_request, ...);],
+    [gdb_cv_func_ptrace_args='int,int,long,long'],[
+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' 'void *'; do
+     AC_TRY_COMPILE($gdb_ptrace_headers, [
+extern $gdb_cv_func_ptrace_ret
+  ptrace ($gdb_arg1, $gdb_arg2, $gdb_arg3, $gdb_arg4);
+], [gdb_cv_func_ptrace_args="$gdb_arg1,$gdb_arg2,$gdb_arg3,$gdb_arg4";
+    break 4;])
+    for gdb_arg5 in 'int *' 'int' 'long'; do
+     AC_TRY_COMPILE($gdb_ptrace_headers, [
+extern $gdb_cv_func_ptrace_ret
+  ptrace ($gdb_arg1, $gdb_arg2, $gdb_arg3, $gdb_arg4, $gdb_arg5);
+], [
+gdb_cv_func_ptrace_args="$gdb_arg1,$gdb_arg2,$gdb_arg3,$gdb_arg4,$gdb_arg5";
+    break 5;])
+    done
+   done
+  done
+ done
+done
+# Provide a safe default value.
+: ${gdb_cv_func_ptrace_args='int,int,long,long'}
+])])
+ac_save_IFS=$IFS; IFS=','
+set dummy `echo "$gdb_cv_func_ptrace_args" | sed 's/\*/\*/g'`
+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.])
+fi
+#
+# End of ptrace type checks
+#
+
 ACX_PKGVERSION([GDB])
 ACX_BUGURL([http://www.gnu.org/software/gdb/bugs/])
 AC_DEFINE_UNQUOTED([PKGVERSION], ["$PKGVERSION"], [Additional package description])
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"
+
 # 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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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_native_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..ffc145e 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).  */
@@ -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));
 	  event_child->must_set_ptrace_flags = 0;
 	}
 
@@ -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)
 {
@@ -4833,7 +4589,7 @@ linux_look_up_symbols (void)
   /* 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);
+  thread_db_init (!linux_supports_tracefork ());
 #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..0aac73a 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;
 }
 
-\f
-/* 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 PTID.  */
 
 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);
-}
-
-\f
-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..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);
-
 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 */

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-21  3:09   ` Luis Machado
@ 2013-08-21 21:29     ` Pedro Alves
  2013-08-22  2:42       ` Luis Machado
  0 siblings, 1 reply; 13+ messages in thread
From: Pedro Alves @ 2013-08-21 21:29 UTC (permalink / raw)
  To: lgustavo; +Cc: 'gdb-patches@sourceware.org', Tom Tromey

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.)

>>> +
>>> +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).

> 
>>
>> 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'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.

> +
> +/* 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.

> +
> +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.

> 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.  ;-)

> 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.

> 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/.

> @@ -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.


> 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.

> @@ -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.

>  extern int lin_lwp_attach_lwp (ptid_t ptid);
>  
>  extern void linux_stop_lwp (struct lwp_info *lwp);
-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-21 21:29     ` Pedro Alves
@ 2013-08-22  2:42       ` Luis Machado
  2013-08-22 17:06         ` Pedro Alves
  0 siblings, 1 reply; 13+ messages in thread
From: Luis Machado @ 2013-08-22  2:42 UTC (permalink / raw)
  To: Pedro Alves; +Cc: 'gdb-patches@sourceware.org', Tom Tromey

[-- Attachment #1: Type: text/plain, Size: 19718 bytes --]

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

[-- Attachment #2: ptrace_share_v3.diff --]
[-- Type: text/x-patch, Size: 65148 bytes --]

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;
 }
 
-\f
-/* 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);
-}
-
-\f
-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 */

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-22  2:42       ` Luis Machado
@ 2013-08-22 17:06         ` Pedro Alves
  2013-08-22 18:02           ` Luis Machado
  0 siblings, 1 reply; 13+ messages in thread
From: Pedro Alves @ 2013-08-22 17:06 UTC (permalink / raw)
  To: lgustavo; +Cc: 'gdb-patches@sourceware.org', Tom Tromey

On 08/22/2013 03:42 AM, Luis Machado wrote:

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

OOC, for tracing ptrace calls, or something else?

> How does this third iteration look?

Looks great, thanks.

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver
  2013-08-22 17:06         ` Pedro Alves
@ 2013-08-22 18:02           ` Luis Machado
  0 siblings, 0 replies; 13+ messages in thread
From: Luis Machado @ 2013-08-22 18:02 UTC (permalink / raw)
  To: Pedro Alves; +Cc: 'gdb-patches@sourceware.org', Tom Tromey

On 08/22/2013 02:05 PM, Pedro Alves wrote:
> On 08/22/2013 03:42 AM, Luis Machado wrote:
>
>> My intention is to pursue a ptrace wrapper at a later stage, so i can
>> probably address this by then.
>
> OOC, for tracing ptrace calls, or something else?
>

To enforce proper casting of types like Tom suggested the other day on 
IRC. We currently have type conversion assumptions that may not always 
be correct for the third and forth ptrace parameters.

Tracing ptrace calls may be interesting as well now that you mentioned.

>> How does this third iteration look?
>
> Looks great, thanks.
>

Great. I'll check it in later today.

Thanks,
Luis


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2013-08-22 18:02 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-19 23:27 [PATCH, v2] Share ptrace options discovery/linux native code between GDB and gdbserver Luis Machado
2013-08-20 15:38 ` Tom Tromey
2013-08-20 15:46   ` Luis Machado
2013-08-20 16:14     ` Tom Tromey
2013-08-20 16:39 ` Pedro Alves
2013-08-20 16:44   ` Pedro Alves
2013-08-20 16:48   ` Tom Tromey
2013-08-20 17:03     ` Pedro Alves
2013-08-21  3:09   ` Luis Machado
2013-08-21 21:29     ` Pedro Alves
2013-08-22  2:42       ` Luis Machado
2013-08-22 17:06         ` Pedro Alves
2013-08-22 18:02           ` Luis Machado

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox