X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fnat%2Flinux-ptrace.c;h=5335d6909228b01ab960a8c57a007d0f909898c5;hb=1ee1a363454d88a87ad2ade7530b2a7fb670021e;hp=3ad2113d7d6268576d796a94db02bee7aa6c13c2;hpb=125f8a3ddedd413a2290dae011f0bed9ffc78278;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c index 3ad2113d7d..5335d69092 100644 --- a/gdb/nat/linux-ptrace.c +++ b/gdb/nat/linux-ptrace.c @@ -1,5 +1,5 @@ /* Linux-specific ptrace manipulation routines. - Copyright (C) 2012-2014 Free Software Foundation, Inc. + Copyright (C) 2012-2020 Free Software Foundation, Inc. This file is part of GDB. @@ -16,52 +16,62 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifdef GDBSERVER -#include "server.h" -#else -#include "defs.h" -#include -#endif - +#include "gdbsupport/common-defs.h" #include "linux-ptrace.h" #include "linux-procfs.h" #include "linux-waitpid.h" -#include "buffer.h" -#include "gdb_assert.h" -#include "gdb_wait.h" - -#include +#include "gdbsupport/buffer.h" +#ifdef HAVE_SYS_PROCFS_H +#include +#endif -/* 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; +/* Stores the ptrace options supported by the running kernel. + A value of -1 means we did not check for features yet. A value + of 0 means there are no supported features. */ +static int supported_ptrace_options = -1; -/* Find all possible reasons we could fail to attach PID and append - these as strings to the already initialized BUFFER. '\0' - termination of BUFFER must be done by the caller. */ +/* Find all possible reasons we could fail to attach PID and return these + as a string. An empty string is returned if we didn't find any reason. */ -void -linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer) +std::string +linux_ptrace_attach_fail_reason (pid_t pid) { - pid_t tracerpid; + pid_t tracerpid = linux_proc_get_tracerpid_nowarn (pid); + std::string result; - tracerpid = linux_proc_get_tracerpid (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 (pid)) - buffer_xml_printf (buffer, _("process %d is a zombie " - "- the process has already terminated"), - (int) pid); + string_appendf (result, + _("process %d is already traced by process %d"), + (int) pid, (int) tracerpid); + + if (linux_proc_pid_is_zombie_nowarn (pid)) + string_appendf (result, + _("process %d is a zombie - the process has already " + "terminated"), + (int) pid); + + return result; +} + +/* See linux-ptrace.h. */ + +std::string +linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err) +{ + long lwpid = ptid.lwp (); + std::string reason = linux_ptrace_attach_fail_reason (lwpid); + + if (!reason.empty ()) + return string_printf ("%s (%d), %s", safe_strerror (err), err, + reason.c_str ()); + else + return string_printf ("%s (%d)", safe_strerror (err), err); } #if defined __i386__ || defined __x86_64__ /* Address of the 'ret' instruction in asm code block below. */ -extern void (linux_ptrace_test_ret_to_nx_instr) (void); +EXTERN_C void linux_ptrace_test_ret_to_nx_instr (void); #include #include @@ -69,6 +79,39 @@ extern void (linux_ptrace_test_ret_to_nx_instr) (void); #endif /* defined __i386__ || defined __x86_64__ */ +/* Kill CHILD. WHO is used to report warnings. */ + +static void +kill_child (pid_t child, const char *who) +{ + pid_t got_pid; + int kill_status; + + if (kill (child, SIGKILL) != 0) + { + warning (_("%s: failed to kill child pid %ld %s"), + who, (long) child, safe_strerror (errno)); + return; + } + + errno = 0; + got_pid = my_waitpid (child, &kill_status, 0); + if (got_pid != child) + { + warning (_("%s: " + "kill waitpid returned %ld: %s"), + who, (long) got_pid, safe_strerror (errno)); + return; + } + if (!WIFSIGNALED (kill_status)) + { + warning (_("%s: " + "kill status %d is not WIFSIGNALED!"), + who, kill_status); + return; + } +} + /* Test broken off-trunk Linux kernel patchset for NX support on i386. It was removed in Fedora kernel 88fa1f0332d188795ed73d7ac2b1564e11a0b4cd. @@ -81,14 +124,16 @@ linux_ptrace_test_ret_to_nx (void) pid_t child, got_pid; gdb_byte *return_address, *pc; long l; - int status, kill_status; + int status; + elf_gregset_t regs; - return_address = mmap (NULL, 2, PROT_READ | PROT_WRITE, + return_address + = (gdb_byte *) 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)); + safe_strerror (errno)); return; } @@ -100,7 +145,7 @@ linux_ptrace_test_ret_to_nx (void) { case -1: warning (_("linux_ptrace_test_ret_to_nx: Cannot fork: %s"), - strerror (errno)); + safe_strerror (errno)); return; case 0: @@ -108,7 +153,7 @@ linux_ptrace_test_ret_to_nx (void) (PTRACE_TYPE_ARG4) NULL); if (l != 0) warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: %s"), - strerror (errno)); + safe_strerror (errno)); else { #if defined __i386__ @@ -116,14 +161,14 @@ 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) : "%esp", "memory"); + : : "r" (return_address) : "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"); + : "memory"); #else # error "!__i386__ && !__x86_64__" #endif @@ -138,7 +183,7 @@ linux_ptrace_test_ret_to_nx (void) if (got_pid != child) { warning (_("linux_ptrace_test_ret_to_nx: waitpid returned %ld: %s"), - (long) got_pid, strerror (errno)); + (long) got_pid, safe_strerror (errno)); return; } @@ -157,6 +202,7 @@ linux_ptrace_test_ret_to_nx (void) { warning (_("linux_ptrace_test_ret_to_nx: status %d is not WIFSTOPPED!"), status); + kill_child (child, "linux_ptrace_test_ret_to_nx"); return; } @@ -166,47 +212,25 @@ linux_ptrace_test_ret_to_nx (void) warning (_("linux_ptrace_test_ret_to_nx: " "WSTOPSIG %d is neither SIGTRAP nor SIGSEGV!"), (int) WSTOPSIG (status)); + kill_child (child, "linux_ptrace_test_ret_to_nx"); return; } - errno = 0; + if (ptrace (PTRACE_GETREGS, child, (PTRACE_TYPE_ARG3) 0, + (PTRACE_TYPE_ARG4) ®s) < 0) + { + warning (_("linux_ptrace_test_ret_to_nx: Cannot PTRACE_GETREGS: %s"), + safe_strerror (errno)); + } #if defined __i386__ - l = ptrace (PTRACE_PEEKUSER, child, (PTRACE_TYPE_ARG3) (uintptr_t) (EIP * 4), - (PTRACE_TYPE_ARG4) NULL); + pc = (gdb_byte *) (uintptr_t) regs[EIP]; #elif defined __x86_64__ - l = ptrace (PTRACE_PEEKUSER, child, (PTRACE_TYPE_ARG3) (uintptr_t) (RIP * 8), - (PTRACE_TYPE_ARG4) NULL); + pc = (gdb_byte *) (uintptr_t) regs[RIP]; #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; - } + kill_child (child, "linux_ptrace_test_ret_to_nx"); /* + 1 is there as x86* stops after the 'int3' instruction. */ if (WSTOPSIG (status) == SIGTRAP && pc == return_address + 1) @@ -242,7 +266,7 @@ linux_ptrace_test_ret_to_nx (void) FUNCTION). For MMU targets, CHILD_STACK is ignored. */ static int -linux_fork_to_function (gdb_byte *child_stack, void (*function) (gdb_byte *)) +linux_fork_to_function (gdb_byte *child_stack, int (*function) (void *)) { int child_pid; @@ -253,7 +277,7 @@ linux_fork_to_function (gdb_byte *child_stack, void (*function) (gdb_byte *)) #define STACK_SIZE 4096 if (child_stack == NULL) - child_stack = xmalloc (STACK_SIZE * 4); + child_stack = (gdb_byte *) xmalloc (STACK_SIZE * 4); /* Use CLONE_VM instead of fork, to support uClinux (no MMU). */ #ifdef __ia64__ @@ -279,8 +303,8 @@ linux_fork_to_function (gdb_byte *child_stack, void (*function) (gdb_byte *)) /* A helper function for linux_check_ptrace_features, called after the child forks a grandchild. */ -static void -linux_grandchild_function (gdb_byte *child_stack) +static int +linux_grandchild_function (void *child_stack) { /* Free any allocated stack. */ xfree (child_stack); @@ -294,14 +318,14 @@ linux_grandchild_function (gdb_byte *child_stack) 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) +static int +linux_child_function (void *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); + linux_fork_to_function ((gdb_byte *) child_stack, linux_grandchild_function); /* This code is only reacheable by the child (grandchild's parent) process. */ @@ -310,16 +334,17 @@ linux_child_function (gdb_byte *child_stack) 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); /* Determine ptrace features available on this target. */ -static void +void linux_check_ptrace_features (void) { int child_pid, ret, status; /* Initialize the options. */ - current_ptrace_options = 0; + supported_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 @@ -341,16 +366,10 @@ linux_check_ptrace_features (void) 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)); + linux_test_for_exitkill (child_pid); + + /* Kill child_pid. */ + kill_child (child_pid, "linux_check_ptrace_features"); } /* Determine if PTRACE_O_TRACESYSGOOD can be used to catch @@ -359,16 +378,13 @@ linux_check_ptrace_features (void) 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 + supported_ptrace_options |= PTRACE_O_TRACESYSGOOD; } /* Determine if PTRACE_O_TRACEFORK can be used to follow fork @@ -388,16 +404,12 @@ linux_test_for_tracefork (int child_pid) 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 + supported_ptrace_options |= PTRACE_O_TRACEVFORKDONE; /* Setting PTRACE_O_TRACEFORK did not cause an error, however we don't know for sure that the feature is available; old @@ -420,7 +432,7 @@ linux_test_for_tracefork (int child_pid) /* Check if we received a fork event notification. */ if (ret == child_pid && WIFSTOPPED (status) - && status >> 16 == PTRACE_EVENT_FORK) + && linux_ptrace_get_extended_event (status) == PTRACE_EVENT_FORK) { /* We did receive a fork event notification. Make sure its PID is reported. */ @@ -433,27 +445,14 @@ linux_test_for_tracefork (int child_pid) /* 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 + supported_ptrace_options |= PTRACE_O_TRACECLONE; + supported_ptrace_options |= (PTRACE_O_TRACEFORK + | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEEXEC); /* 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); + kill_child (second_pid, "linux_test_for_tracefork"); } } else @@ -461,19 +460,42 @@ linux_test_for_tracefork (int child_pid) "(%d, status 0x%x)"), ret, status); } -/* Enable reporting of all currently supported ptrace events. */ +/* Determine if PTRACE_O_EXITKILL can be used. */ + +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) + supported_ptrace_options |= PTRACE_O_EXITKILL; +} + +/* Enable reporting of all currently supported ptrace events. + OPTIONS is a bit mask of extended features we want enabled, + if supported by the kernel. PTRACE_O_TRACECLONE is always + enabled, if supported. */ void -linux_enable_event_reporting (pid_t pid) +linux_enable_event_reporting (pid_t pid, int options) { /* Check if we have initialized the ptrace features for this target. If not, do it now. */ - if (current_ptrace_options == -1) + if (supported_ptrace_options == -1) linux_check_ptrace_features (); + /* We always want clone events. */ + options |= PTRACE_O_TRACECLONE; + + /* Filter out unsupported options. */ + options &= supported_ptrace_options; + /* Set the options. */ ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0, - (PTRACE_TYPE_ARG4) (uintptr_t) current_ptrace_options); + (PTRACE_TYPE_ARG4) (uintptr_t) options); } /* Disable reporting of all currently supported ptrace events. */ @@ -486,15 +508,16 @@ linux_disable_event_reporting (pid_t pid) } /* Returns non-zero if PTRACE_OPTIONS is contained within - CURRENT_PTRACE_OPTIONS, therefore supported. Returns 0 + SUPPORTED_PTRACE_OPTIONS, therefore supported. Returns 0 otherwise. */ static int ptrace_supports_feature (int ptrace_options) { - gdb_assert (current_ptrace_options >= 0); + if (supported_ptrace_options == -1) + linux_check_ptrace_features (); - return ((current_ptrace_options & ptrace_options) == ptrace_options); + return ((supported_ptrace_options & ptrace_options) == ptrace_options); } /* Returns non-zero if PTRACE_EVENT_FORK is supported by ptrace, @@ -508,6 +531,17 @@ linux_supports_tracefork (void) return ptrace_supports_feature (PTRACE_O_TRACEFORK); } +/* Returns non-zero if PTRACE_EVENT_EXEC is supported by ptrace, + 0 otherwise. Note that if PTRACE_EVENT_FORK is supported so is + PTRACE_EVENT_CLONE, PTRACE_EVENT_FORK and PTRACE_EVENT_VFORK, + since they were all added to the kernel at the same time. */ + +int +linux_supports_traceexec (void) +{ + return ptrace_supports_feature (PTRACE_O_TRACEEXEC); +} + /* 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, @@ -551,3 +585,32 @@ linux_ptrace_init_warnings (void) linux_ptrace_test_ret_to_nx (); } + +/* Extract extended ptrace event from wait status. */ + +int +linux_ptrace_get_extended_event (int wstat) +{ + return (wstat >> 16); +} + +/* Determine whether wait status denotes an extended event. */ + +int +linux_is_extended_waitstatus (int wstat) +{ + return (linux_ptrace_get_extended_event (wstat) != 0); +} + +/* Return true if the event in LP may be caused by breakpoint. */ + +int +linux_wstatus_maybe_breakpoint (int wstat) +{ + return (WIFSTOPPED (wstat) + && (WSTOPSIG (wstat) == SIGTRAP + /* SIGILL and SIGSEGV are also treated as traps in case a + breakpoint is inserted at the current PC. */ + || WSTOPSIG (wstat) == SIGILL + || WSTOPSIG (wstat) == SIGSEGV)); +}