Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Unified Diff: gdb/common/linux-ptrace.c

Issue 124383005: GDB 7.6.50 (Closed) Base URL: http://git.chromium.org/native_client/nacl-gdb.git@upstream
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « gdb/common/linux-ptrace.h ('k') | gdb/common/mips-linux-watch.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gdb/common/linux-ptrace.c
diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c
index ae17786eb7a6485c4dc7a3f59db9d5d2391caaf5..2b12a6812143d49682ccc091092a2dd92a8362b9 100644
--- a/gdb/common/linux-ptrace.c
+++ b/gdb/common/linux-ptrace.c
@@ -1,5 +1,5 @@
/* Linux-specific ptrace manipulation routines.
- Copyright (C) 2012 Free Software Foundation, Inc.
+ Copyright (C) 2012-2013 Free Software Foundation, Inc.
This file is part of GDB.
@@ -20,13 +20,22 @@
#include "server.h"
#else
#include "defs.h"
-#include "gdb_string.h"
+#include <string.h>
#endif
#include "linux-ptrace.h"
#include "linux-procfs.h"
+#include "nat/linux-waitpid.h"
#include "buffer.h"
#include "gdb_assert.h"
+#include "gdb_wait.h"
+
+#include <stdint.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
@@ -57,8 +66,6 @@ extern void (linux_ptrace_test_ret_to_nx_instr) (void);
#include <sys/reg.h>
#include <sys/mman.h>
#include <signal.h>
-#include <sys/wait.h>
-#include <stdint.h>
#endif /* defined __i386__ || defined __x86_64__ */
@@ -74,7 +81,7 @@ linux_ptrace_test_ret_to_nx (void)
pid_t child, got_pid;
gdb_byte *return_address, *pc;
long l;
- int status;
+ int status, kill_status;
return_address = mmap (NULL, 2, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
@@ -97,7 +104,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));
@@ -114,7 +122,8 @@ linux_ptrace_test_ret_to_nx (void)
".globl linux_ptrace_test_ret_to_nx_instr;"
"linux_ptrace_test_ret_to_nx_instr:"
"ret"
- : : "r" (return_address) : "%rsp", "memory");
+ : : "r" ((uint64_t) (uintptr_t) return_address)
+ : "%rsp", "memory");
#else
# error "!__i386__ && !__x86_64__"
#endif
@@ -162,9 +171,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
@@ -176,32 +187,25 @@ linux_ptrace_test_ret_to_nx (void)
}
pc = (void *) (uintptr_t) l;
- if (ptrace (PTRACE_KILL, child, NULL, NULL) != 0)
+ kill (child, SIGKILL);
+ ptrace (PTRACE_KILL, child, (PTRACE_TYPE_ARG3) NULL,
+ (PTRACE_TYPE_ARG4) NULL);
+
+ errno = 0;
+ got_pid = waitpid (child, &kill_status, 0);
+ if (got_pid != child)
{
- warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_KILL: %s"),
- strerror (errno));
+ warning (_("linux_ptrace_test_ret_to_nx: "
+ "PTRACE_KILL waitpid returned %ld: %s"),
+ (long) got_pid, strerror (errno));
return;
}
- else
+ if (!WIFSIGNALED (kill_status))
{
- int kill_status;
-
- errno = 0;
- got_pid = waitpid (child, &kill_status, 0);
- if (got_pid != child)
- {
- warning (_("linux_ptrace_test_ret_to_nx: "
- "PTRACE_KILL waitpid returned %ld: %s"),
- (long) got_pid, strerror (errno));
- return;
- }
- if (!WIFSIGNALED (kill_status))
- {
- warning (_("linux_ptrace_test_ret_to_nx: "
- "PTRACE_KILL status %d is not WIFSIGNALED!"),
- status);
- return;
- }
+ warning (_("linux_ptrace_test_ret_to_nx: "
+ "PTRACE_KILL status %d is not WIFSIGNALED!"),
+ status);
+ return;
}
/* + 1 is there as x86* stops after the 'int3' instruction. */
@@ -223,11 +227,307 @@ linux_ptrace_test_ret_to_nx (void)
"address %p nor is the return instruction %p!"),
pc, return_address, &linux_ptrace_test_ret_to_nx_instr);
else
- warning (_("Cannot call inferior functions, you have broken "
- "Linux kernel i386 NX (non-executable pages) support!"));
+ warning (_("Cannot call inferior functions on this system - "
+ "Linux kernel with broken i386 NX (non-executable pages) "
+ "support detected!"));
#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);
+}
+
+static void linux_test_for_tracesysgood (int child_pid);
+static void linux_test_for_tracefork (int child_pid);
+
+/* Determine ptrace features available on this target. */
+
+static void
+linux_check_ptrace_features (void)
+{
+ int child_pid, ret, status;
+
+ /* 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);
+
+ linux_test_for_tracesysgood (child_pid);
+
+ linux_test_for_tracefork (child_pid);
+
+ /* 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));
+}
+
+/* Determine if PTRACE_O_TRACESYSGOOD can be used to catch
+ syscalls. */
+
+static void
+linux_test_for_tracesysgood (int child_pid)
+{
+#ifdef GDBSERVER
+ /* gdbserver does not support PTRACE_O_TRACESYSGOOD. */
+#else
+ int ret;
+
+ 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;
+#endif
+}
+
+/* Determine if PTRACE_O_TRACEFORK can be used to follow fork
+ events. */
+
+static void
+linux_test_for_tracefork (int child_pid)
+{
+ int ret, status;
+ long second_pid;
+
+ /* 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)
+ return;
+
+#ifdef GDBSERVER
+ /* gdbserver does not support PTRACE_O_TRACEVFORKDONE yet. */
+#else
+ /* 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_test_for_tracefork: 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_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);
+}
+
+/* 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. */
« no previous file with comments | « gdb/common/linux-ptrace.h ('k') | gdb/common/mips-linux-watch.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698