#include "server.h"
#include "linux-low.h"
-#include <sys/ptrace.h>
+#include "nat/gdb_ptrace.h"
#include <endian.h>
#include "nat/mips-linux-watch.h"
static const unsigned int mips_breakpoint = 0x0005000d;
#define mips_breakpoint_len 4
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+mips_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = mips_breakpoint_len;
+ return (const gdb_byte *) &mips_breakpoint;
+}
+
/* We only place breakpoints in empty marker functions, and thread locking
is outside of the function. So rather than importing software single-step,
we can just run until exit. */
static struct arch_process_info *
mips_linux_new_process (void)
{
- struct arch_process_info *info = xcalloc (1, sizeof (*info));
+ struct arch_process_info *info = XCNEW (struct arch_process_info);
return info;
}
static void
mips_linux_new_thread (struct lwp_info *lwp)
{
- struct arch_lwp_info *info = xcalloc (1, sizeof (*info));
+ struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
info->watch_registers_changed = 1;
lwp->arch_private = info;
}
+/* Create a new mips_watchpoint and add it to the list. */
+
+static void
+mips_add_watchpoint (struct arch_process_info *priv, CORE_ADDR addr, int len,
+ enum target_hw_bp_type watch_type)
+{
+ struct mips_watchpoint *new_watch;
+ struct mips_watchpoint **pw;
+
+ new_watch = XNEW (struct mips_watchpoint);
+ new_watch->addr = addr;
+ new_watch->len = len;
+ new_watch->type = watch_type;
+ new_watch->next = NULL;
+
+ pw = &priv->current_watches;
+ while (*pw != NULL)
+ pw = &(*pw)->next;
+ *pw = new_watch;
+}
+
+/* Hook to call when a new fork is attached. */
+
+static void
+mips_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ struct arch_process_info *parent_private;
+ struct arch_process_info *child_private;
+ struct mips_watchpoint *wp;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->priv != NULL
+ && parent->priv->arch_private != NULL);
+ gdb_assert (child->priv != NULL
+ && child->priv->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ parent_private = parent->priv->arch_private;
+ child_private = child->priv->arch_private;
+
+ child_private->watch_readback_valid = parent_private->watch_readback_valid;
+ child_private->watch_readback = parent_private->watch_readback;
+
+ for (wp = parent_private->current_watches; wp != NULL; wp = wp->next)
+ mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type);
+
+ child_private->watch_mirror = parent_private->watch_mirror;
+}
/* This is the implementation of linux_target_ops method
prepare_to_resume. If the watch regs have changed, update the
thread's copies. */
int tid = ptid_get_lwp (ptid);
if (-1 == ptrace (PTRACE_SET_WATCH_REGS, tid,
- &priv->watch_mirror))
+ &priv->watch_mirror, NULL))
perror_with_name ("Couldn't write watch register");
}
struct process_info *proc = current_process ();
struct arch_process_info *priv = proc->priv->arch_private;
struct pt_watch_regs regs;
- struct mips_watchpoint *new_watch;
- struct mips_watchpoint **pw;
int pid;
long lwpid;
enum target_hw_bp_type watch_type;
return -1;
/* It fit. Stick it on the end of the list. */
- new_watch = xmalloc (sizeof (struct mips_watchpoint));
- new_watch->addr = addr;
- new_watch->len = len;
- new_watch->type = watch_type;
- new_watch->next = NULL;
-
- pw = &priv->current_watches;
- while (*pw != NULL)
- pw = &(*pw)->next;
- *pw = new_watch;
+ mips_add_watchpoint (priv, addr, len, watch_type);
priv->watch_mirror = regs;
static void
mips_fill_gregset (struct regcache *regcache, void *buf)
{
- union mips_register *regset = buf;
+ union mips_register *regset = (union mips_register *) buf;
int i, use_64bit;
const struct target_desc *tdesc = regcache->tdesc;
static void
mips_store_gregset (struct regcache *regcache, const void *buf)
{
- const union mips_register *regset = buf;
+ const union mips_register *regset = (const union mips_register *) buf;
int i, use_64bit;
use_64bit = (register_size (regcache->tdesc, 0) == 8);
static void
mips_fill_fpregset (struct regcache *regcache, void *buf)
{
- union mips_register *regset = buf;
+ union mips_register *regset = (union mips_register *) buf;
int i, use_64bit, first_fp, big_endian;
use_64bit = (register_size (regcache->tdesc, 0) == 8);
static void
mips_store_fpregset (struct regcache *regcache, const void *buf)
{
- const union mips_register *regset = buf;
+ const union mips_register *regset = (const union mips_register *) buf;
int i, use_64bit, first_fp, big_endian;
use_64bit = (register_size (regcache->tdesc, 0) == 8);
{ PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, 33 * 8, FP_REGS,
mips_fill_fpregset, mips_store_fpregset },
#endif /* HAVE_PTRACE_GETREGS */
- { 0, 0, 0, -1, -1, NULL, NULL }
+ NULL_REGSET
};
static struct regsets_info mips_regsets_info =
NULL, /* fetch_register */
mips_get_pc,
mips_set_pc,
- (const unsigned char *) &mips_breakpoint,
- mips_breakpoint_len,
+ NULL, /* breakpoint_kind_from_pc */
+ mips_sw_breakpoint_from_kind,
mips_reinsert_addr,
0,
mips_breakpoint_at,
NULL, /* siginfo_fixup */
mips_linux_new_process,
mips_linux_new_thread,
+ mips_linux_new_fork,
mips_linux_prepare_to_resume
};