/* Low-level child interface to ptrace.
- Copyright (C) 1988-2013 Free Software Foundation, Inc.
+ Copyright (C) 1988-2015 Free Software Foundation, Inc.
This file is part of GDB.
#include "terminal.h"
#include "gdbcore.h"
#include "regcache.h"
-
-#include "gdb_assert.h"
-#include "gdb_string.h"
-#include "gdb_ptrace.h"
+#include "nat/gdb_ptrace.h"
#include "gdb_wait.h"
#include <signal.h>
#ifdef PT_GET_PROCESS_STATE
+/* Target hook for follow_fork. On entry and at return inferior_ptid is
+ the ptid of the followed inferior. */
+
static int
inf_ptrace_follow_fork (struct target_ops *ops, int follow_child,
int detach_fork)
{
- pid_t pid, fpid;
- ptrace_state_t pe;
-
- pid = ptid_get_pid (inferior_ptid);
-
- if (ptrace (PT_GET_PROCESS_STATE, pid,
- (PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
- perror_with_name (("ptrace"));
-
- gdb_assert (pe.pe_report_event == PTRACE_FORK);
- fpid = pe.pe_other_pid;
-
- if (follow_child)
+ if (!follow_child)
{
- struct inferior *parent_inf, *child_inf;
- struct thread_info *tp;
-
- parent_inf = find_inferior_pid (pid);
-
- /* Add the child. */
- child_inf = add_inferior (fpid);
- child_inf->attach_flag = parent_inf->attach_flag;
- copy_terminal_info (child_inf, parent_inf);
- child_inf->pspace = parent_inf->pspace;
- child_inf->aspace = parent_inf->aspace;
+ struct thread_info *tp = inferior_thread ();
+ pid_t child_pid = ptid_get_pid (tp->pending_follow.value.related_pid);
- /* Before detaching from the parent, remove all breakpoints from
- it. */
- remove_breakpoints ();
-
- if (ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
- perror_with_name (("ptrace"));
-
- /* Switch inferior_ptid out of the parent's way. */
- inferior_ptid = pid_to_ptid (fpid);
-
- /* Delete the parent. */
- detach_inferior (pid);
-
- add_thread_silent (inferior_ptid);
- }
- else
- {
/* Breakpoints have already been detached from the child by
infrun.c. */
- if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
+ if (ptrace (PT_DETACH, child_pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
perror_with_name (("ptrace"));
}
return 0;
}
+static int
+inf_ptrace_insert_fork_catchpoint (struct target_ops *self, int pid)
+{
+ return 0;
+}
+
+static int
+inf_ptrace_remove_fork_catchpoint (struct target_ops *self, int pid)
+{
+ return 0;
+}
+
#endif /* PT_GET_PROCESS_STATE */
\f
#ifdef PT_GET_PROCESS_STATE
static void
-inf_ptrace_post_startup_inferior (ptid_t pid)
+inf_ptrace_post_startup_inferior (struct target_ops *self, ptid_t pid)
{
ptrace_event_t pe;
only report its exit status to its original parent. */
waitpid (ptid_get_pid (inferior_ptid), &status, 0);
- generic_mourn_inferior ();
-
- if (!have_inferiors ())
- unpush_target (ops);
+ inf_child_mourn_inferior (ops);
}
/* Attach to the process specified by ARGS. If FROM_TTY is non-zero,
be chatty about it. */
static void
-inf_ptrace_attach (struct target_ops *ops, char *args, int from_tty)
+inf_ptrace_attach (struct target_ops *ops, const char *args, int from_tty)
{
char *exec_file;
pid_t pid;
#ifdef PT_GET_PROCESS_STATE
static void
-inf_ptrace_post_attach (int pid)
+inf_ptrace_post_attach (struct target_ops *self, int pid)
{
ptrace_event_t pe;
inferior_ptid = null_ptid;
detach_inferior (pid);
- if (!have_inferiors ())
- unpush_target (ops);
+ inf_child_maybe_unpush_target (ops);
}
/* Kill the inferior. */
target_mourn_inferior ();
}
-/* Stop the inferior. */
+/* Interrupt the inferior. */
static void
-inf_ptrace_stop (ptid_t ptid)
+inf_ptrace_interrupt (struct target_ops *self, ptid_t ptid)
{
/* Send a SIGINT to the process group. This acts just like the user
typed a ^C on the controlling terminal. Note that using a
kill (-inferior_process_group (), SIGINT);
}
+/* Return which PID to pass to ptrace in order to observe/control the
+ tracee identified by PTID. */
+
+static pid_t
+get_ptrace_pid (ptid_t ptid)
+{
+ pid_t pid;
+
+ /* If we have an LWPID to work with, use it. Otherwise, we're
+ dealing with a non-threaded program/target. */
+ pid = ptid_get_lwp (ptid);
+ if (pid == 0)
+ pid = ptid_get_pid (ptid);
+ return pid;
+}
+
/* Resume execution of thread PTID, or all threads if PTID is -1. If
STEP is nonzero, single-step it. If SIGNAL is nonzero, give it
that signal. */
inf_ptrace_resume (struct target_ops *ops,
ptid_t ptid, int step, enum gdb_signal signal)
{
- pid_t pid = ptid_get_pid (ptid);
+ pid_t pid;
int request;
- if (pid == -1)
+ if (ptid_equal (minus_one_ptid, ptid))
/* Resume all threads. Traditionally ptrace() only supports
single-threaded processes, so simply resume the inferior. */
pid = ptid_get_pid (inferior_ptid);
+ else
+ pid = get_ptrace_pid (ptid);
if (catch_syscall_enabled () > 0)
request = PT_SYSCALL;
return pid_to_ptid (pid);
}
-/* Attempt a transfer all LEN bytes starting at OFFSET between the
- inferior's OBJECT:ANNEX space and GDB's READBUF/WRITEBUF buffer.
- Return the number of bytes actually transferred. */
+/* Implement the to_xfer_partial target_ops method. */
-static LONGEST
+static enum target_xfer_status
inf_ptrace_xfer_partial (struct target_ops *ops, enum target_object object,
const char *annex, gdb_byte *readbuf,
const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
+ ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
{
pid_t pid = ptid_get_pid (inferior_ptid);
errno = 0;
if (ptrace (PT_IO, pid, (caddr_t)&piod, 0) == 0)
- /* Return the actual number of bytes read or written. */
- return piod.piod_len;
+ {
+ /* Return the actual number of bytes read or written. */
+ *xfered_len = piod.piod_len;
+ return (piod.piod_len == 0) ? TARGET_XFER_EOF : TARGET_XFER_OK;
+ }
/* If the PT_IO request is somehow not supported, fallback on
using PT_WRITE_D/PT_READ_D. Otherwise we will return zero
to indicate failure. */
if (errno != EINVAL)
- return 0;
+ return TARGET_XFER_EOF;
}
#endif
{
gdb_byte byte[sizeof (PTRACE_TYPE_RET)];
} buffer;
ULONGEST rounded_offset;
- LONGEST partial_len;
+ ULONGEST partial_len;
/* Round the start offset down to the next long word
boundary. */
(PTRACE_TYPE_ARG3)(uintptr_t)rounded_offset,
buffer.word);
if (errno)
- return 0;
+ return TARGET_XFER_EOF;
}
}
(PTRACE_TYPE_ARG3)(uintptr_t)rounded_offset,
0);
if (errno)
- return 0;
+ return TARGET_XFER_EOF;
/* Copy appropriate bytes out of the buffer. */
memcpy (readbuf, buffer.byte + (offset - rounded_offset),
partial_len);
}
- return partial_len;
+ *xfered_len = partial_len;
+ return TARGET_XFER_OK;
}
case TARGET_OBJECT_UNWIND_TABLE:
- return -1;
+ return TARGET_XFER_E_IO;
case TARGET_OBJECT_AUXV:
#if defined (PT_IO) && defined (PIOD_READ_AUXV)
struct ptrace_io_desc piod;
if (writebuf)
- return -1;
+ return TARGET_XFER_E_IO;
piod.piod_op = PIOD_READ_AUXV;
piod.piod_addr = readbuf;
piod.piod_offs = (void *) (long) offset;
errno = 0;
if (ptrace (PT_IO, pid, (caddr_t)&piod, 0) == 0)
- /* Return the actual number of bytes read or written. */
- return piod.piod_len;
+ {
+ /* Return the actual number of bytes read or written. */
+ *xfered_len = piod.piod_len;
+ return (piod.piod_len == 0) ? TARGET_XFER_EOF : TARGET_XFER_OK;
+ }
}
#endif
- return -1;
+ return TARGET_XFER_E_IO;
case TARGET_OBJECT_WCOOKIE:
- return -1;
+ return TARGET_XFER_E_IO;
default:
- return -1;
+ return TARGET_XFER_E_IO;
}
}
t->to_create_inferior = inf_ptrace_create_inferior;
#ifdef PT_GET_PROCESS_STATE
t->to_follow_fork = inf_ptrace_follow_fork;
+ t->to_insert_fork_catchpoint = inf_ptrace_insert_fork_catchpoint;
+ t->to_remove_fork_catchpoint = inf_ptrace_remove_fork_catchpoint;
t->to_post_startup_inferior = inf_ptrace_post_startup_inferior;
t->to_post_attach = inf_ptrace_post_attach;
#endif
t->to_mourn_inferior = inf_ptrace_mourn_inferior;
t->to_thread_alive = inf_ptrace_thread_alive;
t->to_pid_to_str = inf_ptrace_pid_to_str;
- t->to_stop = inf_ptrace_stop;
+ t->to_interrupt = inf_ptrace_interrupt;
t->to_xfer_partial = inf_ptrace_xfer_partial;
#if defined (PT_IO) && defined (PIOD_READ_AUXV)
t->to_auxv_parse = inf_ptrace_auxv_parse;