/* Low-level child interface to ptrace.
- Copyright 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
- 1998, 1999, 2000, 2001, 2002, 2004, 2005
+ Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998,
+ 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008
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 2 of the License, or
+ 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,
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, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#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 <signal.h>
#include "inf-child.h"
+#include "gdbthread.h"
-/* HACK: Save the ptrace ops returned by inf_ptrace_target. */
-static struct target_ops *ptrace_ops_hack;
\f
#ifdef PT_GET_PROCESS_STATE
{
pid_t pid, fpid;
ptrace_state_t pe;
+ struct thread_info *last_tp = NULL;
/* FIXME: kettenis/20050720: This stuff should really be passed as
an argument by our caller. */
gdb_assert (status.kind == TARGET_WAITKIND_FORKED);
pid = ptid_get_pid (ptid);
+ last_tp = find_thread_pid (ptid);
}
if (ptrace (PT_GET_PROCESS_STATE, pid,
if (follow_child)
{
- inferior_ptid = pid_to_ptid (fpid);
- detach_breakpoints (pid);
+ /* Copy user stepping state to the new inferior thread. */
+ struct breakpoint *step_resume_breakpoint = last_tp->step_resume_breakpoint;
+ CORE_ADDR step_range_start = last_tp->step_range_start;
+ CORE_ADDR step_range_end = last_tp->step_range_end;
+ struct frame_id step_frame_id = last_tp->step_frame_id;
- /* Reset breakpoints in the child as appropriate. */
- follow_inferior_reset_breakpoints ();
+ struct thread_info *tp;
+
+ /* Otherwise, deleting the parent would get rid of this
+ breakpoint. */
+ last_tp->step_resume_breakpoint = NULL;
+
+ /* Before detaching from the parent, remove all breakpoints from
+ it. */
+ detach_breakpoints (pid);
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 the child. */
+ add_inferior (fpid);
+ tp = add_thread_silent (inferior_ptid);
+
+ tp->step_resume_breakpoint = step_resume_breakpoint;
+ tp->step_range_start = step_range_start;
+ tp->step_range_end = step_range_end;
+ tp->step_frame_id = step_frame_id;
+
+ /* Reset breakpoints in the child as appropriate. */
+ follow_inferior_reset_breakpoints ();
}
else
{
if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
perror_with_name (("ptrace"));
+ detach_inferior (pid);
}
return 0;
ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3)0, 0);
}
-/* Start tracing PID. */
+/* Start a new inferior Unix child process. EXEC_FILE is the file to
+ run, ALLARGS is a string containing the arguments to the program.
+ ENV is the environment vector to pass. If FROM_TTY is non-zero, be
+ chatty about it. */
static void
-inf_ptrace_him (int pid)
+inf_ptrace_create_inferior (struct target_ops *ops,
+ char *exec_file, char *allargs, char **env,
+ int from_tty)
{
- push_target (ptrace_ops_hack);
+ int pid;
+
+ pid = fork_inferior (exec_file, allargs, env, inf_ptrace_me, NULL,
+ NULL, NULL);
+
+ push_target (ops);
/* On some targets, there must be some explicit synchronization
between the parent and child processes after the debugger
target_post_startup_inferior (pid_to_ptid (pid));
}
-/* Start a new inferior Unix child process. EXEC_FILE is the file to
- run, ALLARGS is a string containing the arguments to the program.
- ENV is the environment vector to pass. If FROM_TTY is non-zero, be
- chatty about it. */
-
-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);
-
- /* We are at the first instruction we care about. */
- observer_notify_inferior_created (¤t_target, from_tty);
-
- /* Pedal to the metal... */
- proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0);
-}
-
#ifdef PT_GET_PROCESS_STATE
static void
/* Clean up a rotting corpse of an inferior after it died. */
static void
-inf_ptrace_mourn_inferior (void)
+inf_ptrace_mourn_inferior (struct target_ops *ops)
{
int status;
/* Wait just one more time to collect the inferior's exit status.
- Don not check whether this succeeds though, since we may be
+ Do not check whether this succeeds though, since we may be
dealing with a process that we attached to. Such a process will
- only report its exit status to its origional parent. */
+ only report its exit status to its original parent. */
waitpid (ptid_get_pid (inferior_ptid), &status, 0);
- unpush_target (ptrace_ops_hack);
+ unpush_target (ops);
generic_mourn_inferior ();
}
be chatty about it. */
static void
-inf_ptrace_attach (char *args, int from_tty)
+inf_ptrace_attach (struct target_ops *ops, char *args, int from_tty)
{
char *exec_file;
pid_t pid;
char *dummy;
+ struct inferior *inf;
if (!args)
error_no_arg (_("process-id to attach"));
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);
- /* Do this first, before anything has had a chance to query the
- inferior's symbol table or similar. */
- observer_notify_inferior_created (¤t_target, from_tty);
+ inf = add_inferior (pid);
+ inf->attach_flag = 1;
+
+ /* Always add a main thread. If some target extends the ptrace
+ target, it should decorate the ptid later with more info. */
+ add_thread_silent (inferior_ptid);
+
+ push_target(ops);
}
#ifdef PT_GET_PROCESS_STATE
#endif
/* Detach from the inferior, optionally passing it the signal
- specified ARGS. If FROM_TTY is non-zero, be chatty about it. */
+ specified by ARGS. If FROM_TTY is non-zero, be chatty about it. */
static void
-inf_ptrace_detach (char *args, int from_tty)
+inf_ptrace_detach (struct target_ops *ops, char *args, int from_tty)
{
pid_t pid = ptid_get_pid (inferior_ptid);
int sig = 0;
#ifdef PT_DETACH
/* We'd better not have left any breakpoints in the program or it'll
- die when it hits one. Alsno note that this may only work if we
+ die when it hits one. Also note that this may only work if we
previously attached to the inferior. It *might* work if we
started the process ourselves. */
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);
+ detach_inferior (pid);
+
+ if (!have_inferiors ())
+ unpush_target (ops);
}
/* Kill the inferior. */
/* Stop the inferior. */
static void
-inf_ptrace_stop (void)
+inf_ptrace_stop (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
do
{
set_sigint_trap ();
- set_sigio_trap ();
do
{
}
while (pid == -1 && errno == EINTR);
- clear_sigio_trap ();
clear_sigint_trap ();
if (pid == -1)
/* Claim it exited with unknown signal. */
ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
- return minus_one_ptid;
+ return inferior_ptid;
}
/* Ignore terminated detached child processes. */
{
case PTRACE_FORK:
ourstatus->kind = TARGET_WAITKIND_FORKED;
- ourstatus->value.related_pid = pe.pe_other_pid;
+ ourstatus->value.related_pid = pid_to_ptid (pe.pe_other_pid);
/* Make sure the other end of the fork is stopped too. */
fpid = waitpid (pe.pe_other_pid, &status, 0);
gdb_assert (pe.pe_other_pid == pid);
if (fpid == ptid_get_pid (inferior_ptid))
{
- ourstatus->value.related_pid = pe.pe_other_pid;
+ ourstatus->value.related_pid = pid_to_ptid (pe.pe_other_pid);
return pid_to_ptid (fpid);
}
< rounded_offset + sizeof (PTRACE_TYPE_RET)))
/* Need part of initial word -- fetch it. */
buffer.word = ptrace (PT_READ_I, pid,
- (PTRACE_TYPE_ARG3)(long)rounded_offset, 0);
+ (PTRACE_TYPE_ARG3)(uintptr_t)
+ rounded_offset, 0);
/* Copy data to be written over corresponding part of
buffer. */
errno = 0;
ptrace (PT_WRITE_D, pid,
- (PTRACE_TYPE_ARG3)(long)rounded_offset, buffer.word);
+ (PTRACE_TYPE_ARG3)(uintptr_t)rounded_offset,
+ buffer.word);
if (errno)
{
/* Using the appropriate one (I or D) is necessary for
Gould NP1, at least. */
errno = 0;
ptrace (PT_WRITE_I, pid,
- (PTRACE_TYPE_ARG3)(long)rounded_offset, buffer.word);
+ (PTRACE_TYPE_ARG3)(uintptr_t)rounded_offset,
+ buffer.word);
if (errno)
return 0;
}
{
errno = 0;
buffer.word = ptrace (PT_READ_I, pid,
- (PTRACE_TYPE_ARG3)(long)rounded_offset, 0);
+ (PTRACE_TYPE_ARG3)(uintptr_t)rounded_offset,
+ 0);
if (errno)
return 0;
/* Copy appropriate bytes out of the buffer. */
static void
inf_ptrace_files_info (struct target_ops *ignore)
{
+ struct inferior *inf = current_inferior ();
+
printf_filtered (_("\tUsing the running image of %s %s.\n"),
- attach_flag ? "attached" : "child",
+ inf->attach_flag ? "attached" : "child",
target_pid_to_str (inferior_ptid));
}
t->to_stop = inf_ptrace_stop;
t->to_xfer_partial = inf_ptrace_xfer_partial;
- ptrace_ops_hack = t;
return t;
}
\f
/* Pointer to a function that returns the offset within the user area
where a particular register is stored. */
-static CORE_ADDR (*inf_ptrace_register_u_offset)(int);
+static CORE_ADDR (*inf_ptrace_register_u_offset)(struct gdbarch *, int, int);
/* Fetch register REGNUM from the inferior. */
static void
-inf_ptrace_fetch_register (int regnum)
+inf_ptrace_fetch_register (struct regcache *regcache, int regnum)
{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
CORE_ADDR addr;
size_t size;
PTRACE_TYPE_RET *buf;
int pid, i;
- if (CANNOT_FETCH_REGISTER (regnum))
+ /* This isn't really an address, but ptrace thinks of it as one. */
+ addr = inf_ptrace_register_u_offset (gdbarch, regnum, 0);
+ if (addr == (CORE_ADDR)-1
+ || gdbarch_cannot_fetch_register (gdbarch, regnum))
{
- regcache_raw_supply (current_regcache, regnum, NULL);
+ regcache_raw_supply (regcache, regnum, NULL);
return;
}
if (pid == 0)
pid = ptid_get_pid (inferior_ptid);
- /* This isn't really an address, but ptrace thinks of it as one. */
- addr = inf_ptrace_register_u_offset (regnum);
- size = register_size (current_gdbarch, regnum);
-
+ size = register_size (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);
+ buf[i] = ptrace (PT_READ_U, pid, (PTRACE_TYPE_ARG3)(uintptr_t)addr, 0);
if (errno != 0)
error (_("Couldn't read register %s (#%d): %s."),
- REGISTER_NAME (regnum), regnum, safe_strerror (errno));
+ gdbarch_register_name (gdbarch, regnum),
+ regnum, safe_strerror (errno));
addr += sizeof (PTRACE_TYPE_RET);
}
- regcache_raw_supply (current_regcache, regnum, buf);
+ regcache_raw_supply (regcache, regnum, buf);
}
/* Fetch register REGNUM from the inferior. If REGNUM is -1, do this
for all registers. */
static void
-inf_ptrace_fetch_registers (int regnum)
+inf_ptrace_fetch_registers (struct regcache *regcache, int regnum)
{
if (regnum == -1)
- for (regnum = 0; regnum < NUM_REGS; regnum++)
- inf_ptrace_fetch_register (regnum);
+ for (regnum = 0;
+ regnum < gdbarch_num_regs (get_regcache_arch (regcache));
+ regnum++)
+ inf_ptrace_fetch_register (regcache, regnum);
else
- inf_ptrace_fetch_register (regnum);
+ inf_ptrace_fetch_register (regcache, regnum);
}
/* Store register REGNUM into the inferior. */
static void
-inf_ptrace_store_register (int regnum)
+inf_ptrace_store_register (const struct regcache *regcache, int regnum)
{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
CORE_ADDR addr;
size_t size;
PTRACE_TYPE_RET *buf;
int pid, i;
- if (CANNOT_STORE_REGISTER (regnum))
+ /* This isn't really an address, but ptrace thinks of it as one. */
+ addr = inf_ptrace_register_u_offset (gdbarch, regnum, 1);
+ if (addr == (CORE_ADDR)-1
+ || gdbarch_cannot_store_register (gdbarch, regnum))
return;
/* Cater for systems like GNU/Linux, that implement threads as
if (pid == 0)
pid = ptid_get_pid (inferior_ptid);
- /* This isn't really an address, but ptrace thinks of it as one. */
- addr = inf_ptrace_register_u_offset (regnum);
- size = register_size (current_gdbarch, regnum);
-
+ size = register_size (gdbarch, regnum);
gdb_assert ((size % sizeof (PTRACE_TYPE_RET)) == 0);
buf = alloca (size);
/* Write the register contents into the inferior a chunk at a time. */
- regcache_raw_collect (current_regcache, regnum, buf);
+ regcache_raw_collect (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]);
+ ptrace (PT_WRITE_U, pid, (PTRACE_TYPE_ARG3)(uintptr_t)addr, buf[i]);
if (errno != 0)
error (_("Couldn't write register %s (#%d): %s."),
- REGISTER_NAME (regnum), regnum, safe_strerror (errno));
+ gdbarch_register_name (gdbarch, regnum),
+ regnum, safe_strerror (errno));
addr += sizeof (PTRACE_TYPE_RET);
}
this for all registers. */
void
-inf_ptrace_store_registers (int regnum)
+inf_ptrace_store_registers (struct regcache *regcache, int regnum)
{
if (regnum == -1)
- for (regnum = 0; regnum < NUM_REGS; regnum++)
- inf_ptrace_store_register (regnum);
+ for (regnum = 0;
+ regnum < gdbarch_num_regs (get_regcache_arch (regcache));
+ regnum++)
+ inf_ptrace_store_register (regcache, regnum);
else
- inf_ptrace_store_register (regnum);
+ inf_ptrace_store_register (regcache, regnum);
}
/* Create a "traditional" ptrace target. REGISTER_U_OFFSET should be
particular register is stored. */
struct target_ops *
-inf_ptrace_trad_target (CORE_ADDR (*register_u_offset)(int))
+inf_ptrace_trad_target (CORE_ADDR (*register_u_offset)
+ (struct gdbarch *, int, int))
{
struct target_ops *t = inf_ptrace_target();