Unify ptrace options discovery code and make both GDB and

gdbserver use it.

	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.
This commit is contained in:
Luis Machado
2013-08-22 23:46:30 +00:00
parent a5829458a1
commit 96d7229d2a
34 changed files with 715 additions and 577 deletions

View File

@@ -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;
}
/* A helper function for linux_test_for_tracefork, called after fork (). */
/* Initialize ptrace warnings and check for supported ptrace
features given PID. */
static void
linux_tracefork_child (void)
linux_init_ptrace (pid_t pid)
{
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;
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);
}
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,