2013-07-24 Luis Machado gdb/ * Makefile.in (SFILES): Add common/linux-nat-common.c. Add common/linux-nat-common.h to list of header files. (COMMON_OBS): Add linux-nat-common.o. (linux-nat-common.o): New target object file. common/linux-nat-common.c: New file. common/linux-nat-common.h: New file. * common/linux-ptrace.c: Include linux-nat-common.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 (linux_enable_event_reporting): New declaration. (linux_supports_tracefork): Likewise. (linux_supports_tracevforkdone): Likewise. (linux_supports_tracesysgood): Likewise. * linux-nat.c (SYSCALL_SIGTRAP): Moved to common/linux-nat-common.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. (block_child_signals): Moved to common/linux-nat-common.h. (restore_child_signals_mask): Likewise. (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_child_post_attach): Remove call to linux_enable_tracesysgood. (linux_child_post_startup_inferior): Remove call to linux_enable_tracesysgood. (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. (block_child_signals): Moved to common/linux-nat-common.c. (restore_child_signals_mask): Likewise. (lin_lwp_attach_lwp): Call linux_supports_tracefork. * linux-nat.h: Include linux-nat-common.h gdb/gdbserver/ * Makefile.in (SFILES): Add common/linux-nat-common.c. (server_h): Add $(srcdir)/../common/linux-nat-common.h. (linux-nat-common.o): New target object file. * linux-low.c: Include linux-nat-common.h (linux_enable_event_reporting): Remove declaration. (my_waitpid): Moved to common/linux-nat-common.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. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 2d574d4..bd3c760 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -775,7 +775,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ common/gdb_vecs.c common/common-utils.c common/xml-utils.c \ common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c \ common/format.c common/filestuff.c btrace.c record-btrace.c ctf.c \ - common/target-common.c + common/target-common.c common/linux-nat-common.c LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c @@ -854,7 +854,8 @@ common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \ 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 common/target-common.h +ctf.h common/i386-cpuid.h common/i386-gcc-cpuid.h common/target-common.h \ +common/linux-nat-common.h # Header files that already have srcdir in them, or which are in objdir. @@ -948,7 +949,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ inferior.o osdata.o gdb_usleep.o record.o record-full.o gcore.o \ gdb_vecs.o jit.o progspace.o skip.o probe.o \ common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \ - format.o registry.o btrace.o record-btrace.o target-common.o + format.o registry.o btrace.o record-btrace.o target-common.o \ + linux-nat-common.o TSOBS = inflow.o @@ -2026,6 +2028,10 @@ target-common.o: ${srcdir}/common/target-common.c $(COMPILE) $(srcdir)/common/target-common.c $(POSTCOMPILE) +linux-nat-common.o: ${srcdir}/common/linux-nat-common.c + $(COMPILE) $(srcdir)/common/linux-nat-common.c + $(POSTCOMPILE) + # # gdb/tui/ dependencies # diff --git a/gdb/common/linux-nat-common.c b/gdb/common/linux-nat-common.c new file mode 100644 index 0000000..8a6b2c2 --- /dev/null +++ b/gdb/common/linux-nat-common.c @@ -0,0 +1,134 @@ +/* GNU/Linux native-dependent code common to multiple platforms. + + 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 . */ + +#ifdef GDBSERVER +#include "server.h" +#else +#include "defs.h" +#include "signal.h" +#endif + +#include "linux-nat-common.h" +#include "linux-ptrace.h" +#include "gdb_wait.h" + +/* Signals to block to make that sigsuspend work. */ +static sigset_t blocked_mask; + +/* 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; + +#ifdef GDBSERVER + if (debug_threads) + fprintf (stderr, "my_waitpid (%d, 0x%x)\n", pid, flags); +#endif + + 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; + +#ifdef GDBSERVER + if (debug_threads) + fprintf (stderr, "blocking\n"); +#endif + + /* 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; + } + +#ifdef GDBSERVER + if (debug_threads) + fprintf (stderr, "my_waitpid (%d, 0x%x): status(%x), %d\n", + pid, flags, status ? *status : -1, ret); +#endif + + errno = out_errno; + return ret; +} + +/* Block child signals (SIGCHLD and linux threads signals), and store + the previous mask in PREV_MASK. */ + +void +block_child_signals (sigset_t *prev_mask) +{ + /* Make sure SIGCHLD is blocked. */ + if (!sigismember (&blocked_mask, SIGCHLD)) + sigaddset (&blocked_mask, SIGCHLD); + + sigprocmask (SIG_BLOCK, &blocked_mask, prev_mask); +} + +/* Restore child signals mask, previously returned by + block_child_signals. */ + +void +restore_child_signals_mask (sigset_t *prev_mask) +{ + sigprocmask (SIG_SETMASK, prev_mask, NULL); +} diff --git a/gdb/common/linux-nat-common.h b/gdb/common/linux-nat-common.h new file mode 100644 index 0000000..074af49 --- /dev/null +++ b/gdb/common/linux-nat-common.h @@ -0,0 +1,40 @@ +/* Common 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 . */ + +#ifndef LINUX_NAT_COMMON_H +#define LINUX_NAT_COMMON_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) + +/* 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); + +/* Block child signals (SIGCHLD and linux threads signals), and store + the previous mask in PREV_MASK. */ +extern void block_child_signals (sigset_t *prev_mask); + +/* Restore child signals mask, previously returned by + block_child_signals. */ +extern void restore_child_signals_mask (sigset_t *prev_mask); + +#endif /* LINUX_NAT_COMMON_H */ diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c index d5ac061..0f1c256 100644 --- a/gdb/common/linux-ptrace.c +++ b/gdb/common/linux-ptrace.c @@ -24,11 +24,17 @@ #endif #include "linux-ptrace.h" +#include "linux-nat-common.h" #include "linux-procfs.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,8 +228,270 @@ linux_ptrace_test_ret_to_nx (void) #endif /* defined __i386__ || defined __x86_64__ */ } -/* Display possible problems on this system. Display them only once per GDB - execution. */ +/* 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, 0, 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; + sigset_t prev_mask; + + /* Initialize the options. */ + current_ptrace_options = 0; + + /* We don't want those ptrace calls to be interrupted. */ + block_child_signals (&prev_mask); + + /* 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, 0, PTRACE_O_TRACEFORK); + if (ret != 0) + { + ret = ptrace (PTRACE_KILL, child_pid, 0, 0); + if (ret != 0) + { + warning (_("linux_check_ptrace_features: failed to kill child")); + restore_child_signals_mask (&prev_mask); + 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); + + restore_child_signals_mask (&prev_mask); + return; + } + + /* Check if the target supports PTRACE_O_TRACESYSGOOD. */ + ret = ptrace (PTRACE_SETOPTIONS, child_pid, 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, 0, + PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE); + current_ptrace_options |= (ret == 0)? PTRACE_O_TRACEVFORKDONE : 0; + + /* 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, 0, 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, 0, &second_pid); + if (ret == 0 && second_pid != 0) + { + int second_status; + + /* We got the PID from the grandchild, which means fork tracing + is supported. */ + current_ptrace_options |= PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC; + + /* Do some cleanup and kill the grandchild. */ + my_waitpid (second_pid, &second_status, 0); + ret = ptrace (PTRACE_KILL, second_pid, 0, 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, 0, 0); + if (ret != 0) + warning ("linux_check_ptrace_features: failed to kill child"); + my_waitpid (child_pid, &status, 0); + } + while (WIFSTOPPED (status)); + + restore_child_signals_mask (&prev_mask); +} + +/* 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. */ + +#ifdef GDBSERVER + /* Disable these options for now since gdbserver does not properly support + them. */ + current_ptrace_options &= ~(PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEVFORKDONE + | PTRACE_O_TRACESYSGOOD); +#endif + + /* Set the options. */ + ptrace (PTRACE_SETOPTIONS, pid, 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) != 0); +} + +/* 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); +} void linux_ptrace_init_warnings (void) diff --git a/gdb/common/linux-ptrace.h b/gdb/common/linux-ptrace.h index 8f02c82..7a50f9e 100644 --- a/gdb/common/linux-ptrace.h +++ b/gdb/common/linux-ptrace.h @@ -70,4 +70,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/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index b28f743..bdb83e3 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -157,7 +157,8 @@ 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)/common/target-common.c + $(srcdir)/common/filestuff.c $(srcdir)/common/target-common.c \ + $(srcdir)/common/linux-nat-common.c DEPFILES = @GDBSERVER_DEPFILES@ @@ -167,8 +168,8 @@ SOURCES = $(SFILES) TAGFILES = $(SOURCES) ${HFILES} ${ALLPARAM} ${POSSLIBS} OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o \ - target.o target-common.o utils.o version.o vec.o gdb_vecs.o \ - mem-break.o hostio.o event-loop.o tracepoint.o xml-utils.o \ + target.o target-common.o linux-nat-common.o utils.o version.o vec.o \ + gdb_vecs.o mem-break.o hostio.o event-loop.o tracepoint.o xml-utils.o \ common-utils.o ptid.o buffer.o format.o filestuff.o dll.o notif.o \ tdesc.o $(XML_BUILTIN) $(DEPFILES) $(LIBOBJS) GDBREPLAY_OBS = gdbreplay.o version.o @@ -437,6 +438,7 @@ server_h = $(srcdir)/server.h $(regcache_h) $(srcdir)/target.h \ $(srcdir)/../common/gdb_assert.h \ $(srcdir)/../common/gdb_locale.h \ $(srcdir)/../common/target-common.h \ + $(srcdir)/../common/linux-nat-common.h \ $(ptid_h) \ $(signals_h) \ $(libiberty_h) \ @@ -550,6 +552,9 @@ agent.o: ../common/agent.c target-common.o: ../common/target-common.c $(COMPILE) $< $(POSTCOMPILE) +linux-nat-common.o: ../common/linux-nat-common.c + $(COMPILE) $< + $(POSTCOMPILE) linux-btrace.o: ../common/linux-btrace.c $(linux_btrace_h) $(server_h) $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 47ea76d..2675b78 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -17,6 +17,7 @@ along with this program. If not, see . */ #include "server.h" +#include "linux-nat-common.h" #include "linux-low.h" #include "linux-osdata.h" #include "agent.h" @@ -236,7 +237,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 +376,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 +1923,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 +4584,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_ARG3_TYPE) 0, - (PTRACE_ARG4_TYPE) 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_ARG3_TYPE) 0, (PTRACE_ARG4_TYPE) 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_ARG3_TYPE) 0, - (PTRACE_ARG4_TYPE) PTRACE_O_TRACEFORK); - if (ret != 0) - { - ret = ptrace (PTRACE_KILL, child_pid, (PTRACE_ARG3_TYPE) 0, - (PTRACE_ARG4_TYPE) 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_ARG3_TYPE) 0, - (PTRACE_ARG4_TYPE) 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_ARG3_TYPE) 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_ARG3_TYPE) 0, - (PTRACE_ARG4_TYPE) 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_ARG3_TYPE) 0, - (PTRACE_ARG4_TYPE) 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 +4596,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 +5860,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/linux-nat.c b/gdb/linux-nat.c index 45a6e5f..077639d 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -171,11 +171,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 +221,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 @@ -297,9 +274,6 @@ static int kill_lwp (int lwpid, int signo); static int stop_callback (struct lwp_info *lp, void *data); -static void block_child_signals (sigset_t *prev_mask); -static void restore_child_signals_mask (sigset_t *prev_mask); - struct lwp_info; static struct lwp_info *add_lwp (ptid_t ptid); static void purge_lwp_list (int pid); @@ -349,248 +323,10 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp) return 0; } - -/* A helper function for linux_test_for_tracefork, called after fork (). */ - -static void -linux_tracefork_child (void) -{ - ptrace (PTRACE_TRACEME, 0, 0, 0); - kill (getpid (), SIGSTOP); - fork (); - _exit (0); -} - -/* Wrapper function for waitpid which handles EINTR. */ - -static int -my_waitpid (int pid, int *statusp, int flags) -{ - int ret; - - do - { - ret = waitpid (pid, statusp, flags); - } - while (ret == -1 && errno == EINTR); - - return ret; -} - -/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events. - - First, we try to enable fork tracing on ORIGINAL_PID. If this fails, - we know that the feature is not available. This may change the tracing - options for ORIGINAL_PID, but we'll be setting them shortly anyway. - - However, if it succeeds, we don't know for sure that the feature is - available; old versions of PTRACE_SETOPTIONS ignored unknown options. We - create a child process, attach to it, use PTRACE_SETOPTIONS to enable - fork tracing, and let it fork. If the process exits, we assume that we - can't use TRACEFORK; if we get the fork notification, and we can extract - the new child's PID, then we assume that we can. */ - -static void -linux_test_for_tracefork (int original_pid) -{ - int child_pid, ret, status; - long second_pid; - sigset_t prev_mask; - - /* We don't want those ptrace calls to be interrupted. */ - block_child_signals (&prev_mask); - - linux_supports_tracefork_flag = 0; - linux_supports_tracevforkdone_flag = 0; - - ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACEFORK); - if (ret != 0) - { - restore_child_signals_mask (&prev_mask); - 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")); - restore_child_signals_mask (&prev_mask); - 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); - - restore_child_signals_mask (&prev_mask); - 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); - - 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); - - restore_child_signals_mask (&prev_mask); -} - -/* 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; - sigset_t prev_mask; - - /* We don't want those ptrace calls to be interrupted. */ - block_child_signals (&prev_mask); - - linux_supports_tracesysgood_flag = 0; - - ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACESYSGOOD); - if (ret != 0) - goto out; - - linux_supports_tracesysgood_flag = 1; -out: - restore_child_signals_mask (&prev_mask); -} - -/* Determine wether we support PTRACE_O_TRACESYSGOOD option available. - This function also sets linux_supports_tracesysgood_flag. */ - -static int -linux_supports_tracesysgood (int pid) -{ - if (linux_supports_tracesysgood_flag == -1) - linux_test_for_tracesysgood (pid); - return linux_supports_tracesysgood_flag; -} - -/* Return non-zero iff we have tracefork functionality available. - This function also sets linux_supports_tracefork_flag. */ - -static int -linux_supports_tracefork (int pid) -{ - if (linux_supports_tracefork_flag == -1) - linux_test_for_tracefork (pid); - return linux_supports_tracefork_flag; -} - -static int -linux_supports_tracevforkdone (int pid) -{ - if (linux_supports_tracefork_flag == -1) - linux_test_for_tracefork (pid); - return linux_supports_tracevforkdone_flag; -} - -static void -linux_enable_tracesysgood (ptid_t ptid) -{ - int pid = ptid_get_lwp (ptid); - - if (pid == 0) - pid = ptid_get_pid (ptid); - - if (linux_supports_tracesysgood (pid) == 0) - return; - - current_ptrace_options |= PTRACE_O_TRACESYSGOOD; - - ptrace (PTRACE_SETOPTIONS, pid, 0, current_ptrace_options); -} - - -void -linux_enable_event_reporting (ptid_t ptid) -{ - int pid = ptid_get_lwp (ptid); - - if (pid == 0) - pid = ptid_get_pid (ptid); - - if (! linux_supports_tracefork (pid)) - return; - - current_ptrace_options |= PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK - | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE; - - if (linux_supports_tracevforkdone (pid)) - current_ptrace_options |= PTRACE_O_TRACEVFORKDONE; - - /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support - read-only process state. */ - - ptrace (PTRACE_SETOPTIONS, pid, 0, current_ptrace_options); -} - 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 (); } @@ -598,7 +334,6 @@ static void linux_child_post_startup_inferior (ptid_t ptid) { linux_enable_event_reporting (ptid); - linux_enable_tracesysgood (ptid); linux_ptrace_init_warnings (); } @@ -788,9 +523,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, @@ -962,7 +697,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 @@ -974,7 +709,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 @@ -986,7 +721,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 @@ -999,7 +734,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 @@ -1060,28 +795,6 @@ static sigset_t blocked_mask; /* SIGCHLD action. */ struct sigaction sigchld_action; -/* Block child signals (SIGCHLD and linux threads signals), and store - the previous mask in PREV_MASK. */ - -static void -block_child_signals (sigset_t *prev_mask) -{ - /* Make sure SIGCHLD is blocked. */ - if (!sigismember (&blocked_mask, SIGCHLD)) - sigaddset (&blocked_mask, SIGCHLD); - - sigprocmask (SIG_BLOCK, &blocked_mask, prev_mask); -} - -/* Restore child signals mask, previously returned by - block_child_signals. */ - -static void -restore_child_signals_mask (sigset_t *prev_mask) -{ - sigprocmask (SIG_SETMASK, prev_mask, NULL); -} - /* Mask of signals to pass directly to the inferior. */ static sigset_t pass_mask; @@ -1449,7 +1162,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..3473e23 100644 --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -18,6 +18,7 @@ along with this program. If not, see . */ #include "target.h" +#include "linux-nat-common.h" #include