gdb/nat/linux-ptrace.c - gdb
Global variables defined
Functions defined
Macros defined
Source code
- #include "common-defs.h"
- #include "linux-ptrace.h"
- #include "linux-procfs.h"
- #include "linux-waitpid.h"
- #include "buffer.h"
- #include "gdb_wait.h"
- #include <stdint.h>
- static int current_ptrace_options = -1;
- static int additional_flags;
- void
- linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer)
- {
- pid_t tracerpid;
- tracerpid = linux_proc_get_tracerpid_nowarn (pid);
- if (tracerpid > 0)
- buffer_xml_printf (buffer, _("process %d is already traced "
- "by process %d"),
- (int) pid, (int) tracerpid);
- if (linux_proc_pid_is_zombie_nowarn (pid))
- buffer_xml_printf (buffer, _("process %d is a zombie "
- "- the process has already terminated"),
- (int) pid);
- }
- char *
- linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
- {
- static char *reason_string;
- struct buffer buffer;
- char *warnings;
- long lwpid = ptid_get_lwp (ptid);
- xfree (reason_string);
- buffer_init (&buffer);
- linux_ptrace_attach_fail_reason (lwpid, &buffer);
- buffer_grow_str0 (&buffer, "");
- warnings = buffer_finish (&buffer);
- if (warnings[0] != '\0')
- reason_string = xstrprintf ("%s (%d), %s",
- strerror (err), err, warnings);
- else
- reason_string = xstrprintf ("%s (%d)",
- strerror (err), err);
- xfree (warnings);
- return reason_string;
- }
- #if defined __i386__ || defined __x86_64__
- extern void (linux_ptrace_test_ret_to_nx_instr) (void);
- #include <sys/reg.h>
- #include <sys/mman.h>
- #include <signal.h>
- #endif
- static void
- linux_ptrace_test_ret_to_nx (void)
- {
- #if defined __i386__ || defined __x86_64__
- pid_t child, got_pid;
- gdb_byte *return_address, *pc;
- long l;
- int status, kill_status;
- return_address = mmap (NULL, 2, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (return_address == MAP_FAILED)
- {
- warning (_("linux_ptrace_test_ret_to_nx: Cannot mmap: %s"),
- strerror (errno));
- return;
- }
-
- *return_address = 0xcc;
- child = fork ();
- switch (child)
- {
- case -1:
- warning (_("linux_ptrace_test_ret_to_nx: Cannot fork: %s"),
- strerror (errno));
- return;
- case 0:
- 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));
- else
- {
- #if defined __i386__
- asm volatile ("pushl %0;"
- ".globl linux_ptrace_test_ret_to_nx_instr;"
- "linux_ptrace_test_ret_to_nx_instr:"
- "ret"
- : : "r" (return_address) : "%esp", "memory");
- #elif defined __x86_64__
- asm volatile ("pushq %0;"
- ".globl linux_ptrace_test_ret_to_nx_instr;"
- "linux_ptrace_test_ret_to_nx_instr:"
- "ret"
- : : "r" ((uint64_t) (uintptr_t) return_address)
- : "%rsp", "memory");
- #else
- # error "!__i386__ && !__x86_64__"
- #endif
- gdb_assert_not_reached ("asm block did not terminate");
- }
- _exit (1);
- }
- errno = 0;
- got_pid = waitpid (child, &status, 0);
- if (got_pid != child)
- {
- warning (_("linux_ptrace_test_ret_to_nx: waitpid returned %ld: %s"),
- (long) got_pid, strerror (errno));
- return;
- }
- if (WIFSIGNALED (status))
- {
- if (WTERMSIG (status) != SIGKILL)
- warning (_("linux_ptrace_test_ret_to_nx: WTERMSIG %d is not SIGKILL!"),
- (int) WTERMSIG (status));
- else
- warning (_("Cannot call inferior functions, Linux kernel PaX "
- "protection forbids return to non-executable pages!"));
- return;
- }
- if (!WIFSTOPPED (status))
- {
- warning (_("linux_ptrace_test_ret_to_nx: status %d is not WIFSTOPPED!"),
- status);
- return;
- }
-
- if (WSTOPSIG (status) != SIGTRAP && WSTOPSIG (status) != SIGSEGV)
- {
- warning (_("linux_ptrace_test_ret_to_nx: "
- "WSTOPSIG %d is neither SIGTRAP nor SIGSEGV!"),
- (int) WSTOPSIG (status));
- return;
- }
- errno = 0;
- #if defined __i386__
- 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, (PTRACE_TYPE_ARG3) (uintptr_t) (RIP * 8),
- (PTRACE_TYPE_ARG4) NULL);
- #else
- # error "!__i386__ && !__x86_64__"
- #endif
- if (errno != 0)
- {
- warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_PEEKUSER: %s"),
- strerror (errno));
- return;
- }
- pc = (void *) (uintptr_t) l;
- 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: "
- "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;
- }
-
- if (WSTOPSIG (status) == SIGTRAP && pc == return_address + 1)
- {
-
- return;
- }
-
- if (WSTOPSIG (status) == SIGSEGV && pc == return_address)
- {
-
- return;
- }
- if ((void (*) (void)) pc != &linux_ptrace_test_ret_to_nx_instr)
- warning (_("linux_ptrace_test_ret_to_nx: PC %p is neither near return "
- "address %p nor is the return instruction %p!"),
- pc, return_address, &linux_ptrace_test_ret_to_nx_instr);
- else
- warning (_("Cannot call inferior functions on this system - "
- "Linux kernel with broken i386 NX (non-executable pages) "
- "support detected!"));
- #endif
- }
- static int
- linux_fork_to_function (gdb_byte *child_stack, void (*function) (gdb_byte *))
- {
- int child_pid;
-
- gdb_assert (function != NULL);
- #if defined(__UCLIBC__) && defined(HAS_NOMMU)
- #define STACK_SIZE 4096
- if (child_stack == NULL)
- child_stack = xmalloc (STACK_SIZE * 4);
-
- #ifdef __ia64__
- child_pid = __clone2 (function, child_stack, STACK_SIZE,
- CLONE_VM | SIGCHLD, child_stack + STACK_SIZE * 2);
- #else
- child_pid = clone (function, child_stack + STACK_SIZE,
- CLONE_VM | SIGCHLD, child_stack + STACK_SIZE * 2);
- #endif
- #else
- child_pid = fork ();
- if (child_pid == 0)
- function (NULL);
- #endif
- if (child_pid == -1)
- perror_with_name (("fork"));
- return child_pid;
- }
- static void
- linux_grandchild_function (gdb_byte *child_stack)
- {
-
- xfree (child_stack);
-
- _exit (0);
- }
- static void
- linux_child_function (gdb_byte *child_stack)
- {
- ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
- kill (getpid (), SIGSTOP);
-
- linux_fork_to_function (child_stack, linux_grandchild_function);
-
- _exit (0);
- }
- static void linux_test_for_tracesysgood (int child_pid);
- static void linux_test_for_tracefork (int child_pid);
- static void linux_test_for_exitkill (int child_pid);
- static void
- linux_check_ptrace_features (void)
- {
- int child_pid, ret, status;
-
- current_ptrace_options = 0;
-
- 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);
- linux_test_for_exitkill (child_pid);
-
- 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));
- }
- static void
- linux_test_for_tracesysgood (int child_pid)
- {
- int ret;
- if ((additional_flags & PTRACE_O_TRACESYSGOOD) == 0)
- return;
- 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;
- }
- static void
- linux_test_for_tracefork (int child_pid)
- {
- int ret, status;
- long second_pid;
-
- ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
- (PTRACE_TYPE_ARG4) PTRACE_O_TRACEFORK);
- if (ret != 0)
- return;
- if ((additional_flags & PTRACE_O_TRACEVFORKDONE) != 0)
- {
-
- 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;
- }
-
- 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)
- && linux_ptrace_get_extended_event (status) == PTRACE_EVENT_FORK)
- {
-
- 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;
-
- current_ptrace_options |= PTRACE_O_TRACECLONE;
- current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEEXEC));
-
- 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);
- }
- static void
- linux_test_for_exitkill (int child_pid)
- {
- int ret;
- ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
- (PTRACE_TYPE_ARG4) PTRACE_O_EXITKILL);
- if (ret == 0)
- current_ptrace_options |= PTRACE_O_EXITKILL;
- }
- void
- linux_enable_event_reporting (pid_t pid, int attached)
- {
- int ptrace_options;
-
- if (current_ptrace_options == -1)
- linux_check_ptrace_features ();
- ptrace_options = current_ptrace_options;
- if (attached)
- {
-
- ptrace_options &= ~PTRACE_O_EXITKILL;
- }
-
- ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
- (PTRACE_TYPE_ARG4) (uintptr_t) ptrace_options);
- }
- void
- linux_disable_event_reporting (pid_t pid)
- {
-
- ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0, 0);
- }
- static int
- ptrace_supports_feature (int ptrace_options)
- {
- if (current_ptrace_options == -1)
- linux_check_ptrace_features ();
- return ((current_ptrace_options & ptrace_options) == ptrace_options);
- }
- int
- linux_supports_tracefork (void)
- {
- return ptrace_supports_feature (PTRACE_O_TRACEFORK);
- }
- int
- linux_supports_traceclone (void)
- {
- return ptrace_supports_feature (PTRACE_O_TRACECLONE);
- }
- int
- linux_supports_tracevforkdone (void)
- {
- return ptrace_supports_feature (PTRACE_O_TRACEVFORKDONE);
- }
- int
- linux_supports_tracesysgood (void)
- {
- return ptrace_supports_feature (PTRACE_O_TRACESYSGOOD);
- }
- void
- linux_ptrace_init_warnings (void)
- {
- static int warned = 0;
- if (warned)
- return;
- warned = 1;
- linux_ptrace_test_ret_to_nx ();
- }
- void
- linux_ptrace_set_additional_flags (int flags)
- {
- additional_flags = flags;
- }
- int
- linux_ptrace_get_extended_event (int wstat)
- {
- return (wstat >> 16);
- }
- int
- linux_is_extended_waitstatus (int wstat)
- {
- return (linux_ptrace_get_extended_event (wstat) != 0);
- }