#include "defs.h"
#include "command.h"
#include "inferior.h"
#include "inflow.h"
#include "gdbcore.h"
#include "observer.h"
#include "regcache.h"
#include "gdb_assert.h"
#include "gdb_string.h"
#include "gdb_ptrace.h"
#include "gdb_wait.h"
#include <signal.h>
#include "inf-child.h"
static struct target_ops *ptrace_ops_hack;
#ifdef PT_GET_PROCESS_STATE
static int
inf_ptrace_follow_fork (int follow_child)
{
pid_t pid, fpid;
ptrace_state_t pe;
{
ptid_t ptid;
struct target_waitstatus status;
get_last_target_status (&ptid, &status);
gdb_assert (status.kind == TARGET_WAITKIND_FORKED);
pid = ptid_get_pid (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)
{
inferior_ptid = pid_to_ptid (fpid);
detach_breakpoints (pid);
follow_inferior_reset_breakpoints ();
if (ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
perror_with_name (("ptrace"));
}
else
{
inferior_ptid = pid_to_ptid (pid);
detach_breakpoints (fpid);
if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
perror_with_name (("ptrace"));
}
return 0;
}
#endif
static void
inf_ptrace_me (void)
{
ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3)0, 0);
}
static void
inf_ptrace_him (int pid)
{
push_target (ptrace_ops_hack);
target_acknowledge_created_inferior (pid);
startup_inferior (START_INFERIOR_TRAPS_EXPECTED);
target_post_startup_inferior (pid_to_ptid (pid));
}
static void
inf_ptrace_create_inferior (char *exec_file, char *allargs, char **env,
int from_tty)
{
fork_inferior (exec_file, allargs, env, inf_ptrace_me, inf_ptrace_him,
NULL, NULL);
observer_notify_inferior_created (¤t_target, from_tty);
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0);
}
#ifdef PT_GET_PROCESS_STATE
static void
inf_ptrace_post_startup_inferior (ptid_t pid)
{
ptrace_event_t pe;
memset (&pe, 0, sizeof pe);
pe.pe_set_event |= PTRACE_FORK;
if (ptrace (PT_SET_EVENT_MASK, ptid_get_pid (pid),
(PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
perror_with_name (("ptrace"));
}
#endif
static void
inf_ptrace_mourn_inferior (void)
{
int status;
waitpid (ptid_get_pid (inferior_ptid), &status, 0);
unpush_target (ptrace_ops_hack);
generic_mourn_inferior ();
}
static void
inf_ptrace_attach (char *args, int from_tty)
{
char *exec_file;
pid_t pid;
char *dummy;
if (!args)
error_no_arg (_("process-id to attach"));
dummy = args;
pid = strtol (args, &dummy, 0);
if (pid == 0 && args == dummy)
error (_("Illegal process-id: %s."), args);
if (pid == getpid ())
error (_("I refuse to debug myself!"));
if (from_tty)
{
exec_file = get_exec_file (0);
if (exec_file)
printf_unfiltered (_("Attaching to program: %s, %s\n"), exec_file,
target_pid_to_str (pid_to_ptid (pid)));
else
printf_unfiltered (_("Attaching to %s\n"),
target_pid_to_str (pid_to_ptid (pid)));
gdb_flush (gdb_stdout);
}
#ifdef PT_ATTACH
errno = 0;
ptrace (PT_ATTACH, pid, (PTRACE_TYPE_ARG3)0, 0);
if (errno != 0)
perror_with_name (("ptrace"));
attach_flag = 1;
#else
error (_("This system does not support attaching to a process"));
#endif
inferior_ptid = pid_to_ptid (pid);
push_target (ptrace_ops_hack);
observer_notify_inferior_created (¤t_target, from_tty);
}
#ifdef PT_GET_PROCESS_STATE
void
inf_ptrace_post_attach (int pid)
{
ptrace_event_t pe;
memset (&pe, 0, sizeof pe);
pe.pe_set_event |= PTRACE_FORK;
if (ptrace (PT_SET_EVENT_MASK, pid,
(PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
perror_with_name (("ptrace"));
}
#endif
static void
inf_ptrace_detach (char *args, int from_tty)
{
pid_t pid = ptid_get_pid (inferior_ptid);
int sig = 0;
if (from_tty)
{
char *exec_file = get_exec_file (0);
if (exec_file == 0)
exec_file = "";
printf_unfiltered (_("Detaching from program: %s, %s\n"), exec_file,
target_pid_to_str (pid_to_ptid (pid)));
gdb_flush (gdb_stdout);
}
if (args)
sig = atoi (args);
#ifdef PT_DETACH
errno = 0;
ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, sig);
if (errno != 0)
perror_with_name (("ptrace"));
attach_flag = 0;
#else
error (_("This system does not support detaching from a process"));
#endif
inferior_ptid = null_ptid;
unpush_target (ptrace_ops_hack);
}
static void
inf_ptrace_kill (void)
{
pid_t pid = ptid_get_pid (inferior_ptid);
int status;
if (pid == 0)
return;
ptrace (PT_KILL, pid, (PTRACE_TYPE_ARG3)0, 0);
waitpid (pid, &status, 0);
target_mourn_inferior ();
}
static void
inf_ptrace_stop (void)
{
kill (-inferior_process_group, SIGINT);
}
static void
inf_ptrace_resume (ptid_t ptid, int step, enum target_signal signal)
{
pid_t pid = ptid_get_pid (ptid);
int request = PT_CONTINUE;
if (pid == -1)
pid = ptid_get_pid (inferior_ptid);
if (step)
{
request = PT_STEP;
}
errno = 0;
ptrace (request, pid, (PTRACE_TYPE_ARG3)1, target_signal_to_host (signal));
if (errno != 0)
perror_with_name (("ptrace"));
}
static ptid_t
inf_ptrace_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
{
pid_t pid;
int status, save_errno;
do
{
set_sigint_trap ();
set_sigio_trap ();
do
{
pid = waitpid (ptid_get_pid (ptid), &status, 0);
save_errno = errno;
}
while (pid == -1 && errno == EINTR);
clear_sigio_trap ();
clear_sigint_trap ();
if (pid == -1)
{
fprintf_unfiltered (gdb_stderr,
_("Child process unexpectedly missing: %s.\n"),
safe_strerror (save_errno));
ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
return minus_one_ptid;
}
if (!WIFSTOPPED (status) && pid != ptid_get_pid (inferior_ptid))
pid = -1;
}
while (pid == -1);
#ifdef PT_GET_PROCESS_STATE
if (WIFSTOPPED (status))
{
ptrace_state_t pe;
pid_t fpid;
if (ptrace (PT_GET_PROCESS_STATE, pid,
(PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
perror_with_name (("ptrace"));
switch (pe.pe_report_event)
{
case PTRACE_FORK:
ourstatus->kind = TARGET_WAITKIND_FORKED;
ourstatus->value.related_pid = pe.pe_other_pid;
fpid = waitpid (pe.pe_other_pid, &status, 0);
if (fpid == -1)
perror_with_name (("waitpid"));
if (ptrace (PT_GET_PROCESS_STATE, fpid,
(PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
perror_with_name (("ptrace"));
gdb_assert (pe.pe_report_event == PTRACE_FORK);
gdb_assert (pe.pe_other_pid == pid);
if (fpid == ptid_get_pid (inferior_ptid))
{
ourstatus->value.related_pid = pe.pe_other_pid;
return pid_to_ptid (fpid);
}
return pid_to_ptid (pid);
}
}
#endif
store_waitstatus (ourstatus, status);
return pid_to_ptid (pid);
}
static LONGEST
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)
{
pid_t pid = ptid_get_pid (inferior_ptid);
switch (object)
{
case TARGET_OBJECT_MEMORY:
#ifdef PT_IO
{
struct ptrace_io_desc piod;
piod.piod_op = writebuf ? PIOD_WRITE_D : PIOD_READ_D;
piod.piod_addr = writebuf ? (void *) writebuf : readbuf;
piod.piod_offs = (void *) (long) offset;
piod.piod_len = len;
errno = 0;
if (ptrace (PT_IO, pid, (caddr_t)&piod, 0) == 0)
return piod.piod_len;
if (errno != EINVAL)
return 0;
}
#endif
{
union
{
PTRACE_TYPE_RET word;
gdb_byte byte[sizeof (PTRACE_TYPE_RET)];
} buffer;
ULONGEST rounded_offset;
LONGEST partial_len;
rounded_offset = offset & -(ULONGEST) sizeof (PTRACE_TYPE_RET);
partial_len = (rounded_offset + sizeof (PTRACE_TYPE_RET)) - offset;
if (partial_len > len)
partial_len = len;
if (writebuf)
{
if (rounded_offset < offset
|| (offset + partial_len
< rounded_offset + sizeof (PTRACE_TYPE_RET)))
buffer.word = ptrace (PT_READ_I, pid,
(PTRACE_TYPE_ARG3)(long)rounded_offset, 0);
memcpy (buffer.byte + (offset - rounded_offset),
writebuf, partial_len);
errno = 0;
ptrace (PT_WRITE_D, pid,
(PTRACE_TYPE_ARG3)(long)rounded_offset, buffer.word);
if (errno)
{
errno = 0;
ptrace (PT_WRITE_I, pid,
(PTRACE_TYPE_ARG3)(long)rounded_offset, buffer.word);
if (errno)
return 0;
}
}
if (readbuf)
{
errno = 0;
buffer.word = ptrace (PT_READ_I, pid,
(PTRACE_TYPE_ARG3)(long)rounded_offset, 0);
if (errno)
return 0;
memcpy (readbuf, buffer.byte + (offset - rounded_offset),
partial_len);
}
return partial_len;
}
case TARGET_OBJECT_UNWIND_TABLE:
return -1;
case TARGET_OBJECT_AUXV:
return -1;
case TARGET_OBJECT_WCOOKIE:
return -1;
default:
return -1;
}
}
static int
inf_ptrace_thread_alive (ptid_t ptid)
{
return (kill (ptid_get_pid (ptid), 0) != -1);
}
static void
inf_ptrace_files_info (struct target_ops *ignore)
{
printf_filtered (_("\tUsing the running image of %s %s.\n"),
attach_flag ? "attached" : "child",
target_pid_to_str (inferior_ptid));
}
struct target_ops *
inf_ptrace_target (void)
{
struct target_ops *t = inf_child_target ();
t->to_attach = inf_ptrace_attach;
t->to_detach = inf_ptrace_detach;
t->to_resume = inf_ptrace_resume;
t->to_wait = inf_ptrace_wait;
t->to_files_info = inf_ptrace_files_info;
t->to_kill = inf_ptrace_kill;
t->to_create_inferior = inf_ptrace_create_inferior;
#ifdef PT_GET_PROCESS_STATE
t->to_follow_fork = inf_ptrace_follow_fork;
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 = normal_pid_to_str;
t->to_stop = inf_ptrace_stop;
t->to_xfer_partial = inf_ptrace_xfer_partial;
ptrace_ops_hack = t;
return t;
}
static CORE_ADDR (*inf_ptrace_register_u_offset)(int);
static void
inf_ptrace_fetch_register (int regnum)
{
CORE_ADDR addr;
size_t size;
PTRACE_TYPE_RET *buf;
int pid, i;
pid = ptid_get_lwp (inferior_ptid);
if (pid == 0)
pid = ptid_get_pid (inferior_ptid);
addr = inf_ptrace_register_u_offset (regnum);
size = register_size (current_gdbarch, regnum);
gdb_assert ((size % sizeof (PTRACE_TYPE_RET)) == 0);
buf = alloca (size);
for (i = 0; i < size / sizeof (PTRACE_TYPE_RET); i++)
{
errno = 0;
buf[i] = ptrace (PT_READ_U, pid, (PTRACE_TYPE_ARG3)addr, 0);
if (errno != 0)
error (_("Couldn't read register %s (#%d): %s."),
REGISTER_NAME (regnum), regnum, safe_strerror (errno));
addr += sizeof (PTRACE_TYPE_RET);
}
regcache_raw_supply (current_regcache, regnum, buf);
}
static void
inf_ptrace_fetch_registers (int regnum)
{
if (regnum == -1)
for (regnum = 0; regnum < NUM_REGS; regnum++)
inf_ptrace_fetch_register (regnum);
else
inf_ptrace_fetch_register (regnum);
}
static void
inf_ptrace_store_register (int regnum)
{
CORE_ADDR addr;
size_t size;
PTRACE_TYPE_RET *buf;
int pid, i;
pid = ptid_get_lwp (inferior_ptid);
if (pid == 0)
pid = ptid_get_pid (inferior_ptid);
addr = inf_ptrace_register_u_offset (regnum);
size = register_size (current_gdbarch, regnum);
gdb_assert ((size % sizeof (PTRACE_TYPE_RET)) == 0);
buf = alloca (size);
regcache_raw_collect (current_regcache, regnum, buf);
for (i = 0; i < size / sizeof (PTRACE_TYPE_RET); i++)
{
errno = 0;
ptrace (PT_WRITE_U, pid, (PTRACE_TYPE_ARG3)addr, buf[i]);
if (errno != 0)
error (_("Couldn't write register %s (#%d): %s."),
REGISTER_NAME (regnum), regnum, safe_strerror (errno));
addr += sizeof (PTRACE_TYPE_RET);
}
}
void
inf_ptrace_store_registers (int regnum)
{
if (regnum == -1)
for (regnum = 0; regnum < NUM_REGS; regnum++)
inf_ptrace_store_register (regnum);
else
inf_ptrace_store_register (regnum);
}
struct target_ops *
inf_ptrace_trad_target (CORE_ADDR (*register_u_offset)(int))
{
struct target_ops *t = inf_ptrace_target();
gdb_assert (register_u_offset);
inf_ptrace_register_u_offset = register_u_offset;
t->to_fetch_registers = inf_ptrace_fetch_registers;
t->to_store_registers = inf_ptrace_store_registers;
return t;
}