]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/mips-linux-nat.c
Copyright updates for 2007.
[thirdparty/binutils-gdb.git] / gdb / mips-linux-nat.c
index 835ba58785b9f88abf37376a8619438d845a6941..15b23a2ffcb6ec7ff1383d07ef037a43a5fcf8b8 100644 (file)
@@ -1,6 +1,7 @@
 /* Native-dependent code for GNU/Linux on MIPS processors.
 
-   Copyright 2001, 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 
    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.  */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
 
 #include "defs.h"
+#include "inferior.h"
 #include "mips-tdep.h"
+#include "target.h"
+#include "linux-nat.h"
+#include "mips-linux-tdep.h"
+
+#include "gdb_proc_service.h"
+
+#include <sys/ptrace.h>
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
+/* Assume that we have PTRACE_GETREGS et al. support.  If we do not,
+   we'll clear this and use PTRACE_PEEKUSER instead.  */
+static int have_ptrace_regsets = 1;
+
+/* Saved function pointers to fetch and store a single register using
+   PTRACE_PEEKUSER and PTRACE_POKEUSER.  */
+
+void (*super_fetch_registers) (int);
+void (*super_store_registers) (int);
 
 /* Pseudo registers can not be read.  ptrace does not provide a way to
    read (or set) MIPS_PS_REGNUM, and there's no point in reading or
@@ -62,3 +85,179 @@ mips_linux_cannot_store_register (int regno)
   else
     return 1;
 }
+
+/* Fetch the thread-local storage pointer for libthread_db.  */
+
+ps_err_e
+ps_get_thread_area (const struct ps_prochandle *ph,
+                    lwpid_t lwpid, int idx, void **base)
+{
+  if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
+    return PS_ERR;
+
+  /* IDX is the bias from the thread pointer to the beginning of the
+     thread descriptor.  It has to be subtracted due to implementation
+     quirks in libthread_db.  */
+  *base = (void *) ((char *)*base - idx);
+
+  return PS_OK;
+}
+
+/* Fetch REGNO (or all registers if REGNO == -1) from the target
+   using PTRACE_GETREGS et al.  */
+
+static void
+mips64_linux_regsets_fetch_registers (int regno)
+{
+  int is_fp;
+  int tid;
+
+  if (regno >= mips_regnum (current_gdbarch)->fp0
+      && regno <= mips_regnum (current_gdbarch)->fp0 + 32)
+    is_fp = 1;
+  else if (regno == mips_regnum (current_gdbarch)->fp_control_status)
+    is_fp = 1;
+  else if (regno == mips_regnum (current_gdbarch)->fp_implementation_revision)
+    is_fp = 1;
+  else
+    is_fp = 0;
+
+  tid = ptid_get_lwp (inferior_ptid);
+  if (tid == 0)
+    tid = ptid_get_pid (inferior_ptid);
+
+  if (regno == -1 || !is_fp)
+    {
+      mips64_elf_gregset_t regs;
+
+      if (ptrace (PTRACE_GETREGS, tid, 0L, (PTRACE_TYPE_ARG3) &regs) == -1)
+       {
+         if (errno == EIO)
+           {
+             have_ptrace_regsets = 0;
+             return;
+           }
+         perror_with_name (_("Couldn't get registers"));
+       }
+
+      mips64_supply_gregset (&regs);
+    }
+
+  if (regno == -1 || is_fp)
+    {
+      mips64_elf_fpregset_t fp_regs;
+
+      if (ptrace (PTRACE_GETFPREGS, tid, 0L,
+                 (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+       {
+         if (errno == EIO)
+           {
+             have_ptrace_regsets = 0;
+             return;
+           }
+         perror_with_name (_("Couldn't get FP registers"));
+       }
+
+      mips64_supply_fpregset (&fp_regs);
+    }
+}
+
+/* Store REGNO (or all registers if REGNO == -1) to the target
+   using PTRACE_SETREGS et al.  */
+
+static void
+mips64_linux_regsets_store_registers (int regno)
+{
+  int is_fp;
+  int tid;
+
+  if (regno >= mips_regnum (current_gdbarch)->fp0
+      && regno <= mips_regnum (current_gdbarch)->fp0 + 32)
+    is_fp = 1;
+  else if (regno == mips_regnum (current_gdbarch)->fp_control_status)
+    is_fp = 1;
+  else if (regno == mips_regnum (current_gdbarch)->fp_implementation_revision)
+    is_fp = 1;
+  else
+    is_fp = 0;
+
+  tid = ptid_get_lwp (inferior_ptid);
+  if (tid == 0)
+    tid = ptid_get_pid (inferior_ptid);
+
+  if (regno == -1 || !is_fp)
+    {
+      mips64_elf_gregset_t regs;
+
+      if (ptrace (PTRACE_GETREGS, tid, 0L, (PTRACE_TYPE_ARG3) &regs) == -1)
+       perror_with_name (_("Couldn't get registers"));
+
+      mips64_fill_gregset (&regs, regno);
+
+      if (ptrace (PTRACE_SETREGS, tid, 0L, (PTRACE_TYPE_ARG3) &regs) == -1)
+       perror_with_name (_("Couldn't set registers"));
+    }
+
+  if (regno == -1 || is_fp)
+    {
+      mips64_elf_fpregset_t fp_regs;
+
+      if (ptrace (PTRACE_GETFPREGS, tid, 0L,
+                 (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+       perror_with_name (_("Couldn't get FP registers"));
+
+      mips64_fill_fpregset (&fp_regs, regno);
+
+      if (ptrace (PTRACE_SETFPREGS, tid, 0L,
+                 (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+       perror_with_name (_("Couldn't set FP registers"));
+    }
+}
+
+/* Fetch REGNO (or all registers if REGNO == -1) from the target
+   using any working method.  */
+
+static void
+mips64_linux_fetch_registers (int regnum)
+{
+  /* Unless we already know that PTRACE_GETREGS does not work, try it.  */
+  if (have_ptrace_regsets)
+    mips64_linux_regsets_fetch_registers (regnum);
+
+  /* If we know, or just found out, that PTRACE_GETREGS does not work, fall
+     back to PTRACE_PEEKUSER.  */
+  if (!have_ptrace_regsets)
+    super_fetch_registers (regnum);
+}
+
+/* Store REGNO (or all registers if REGNO == -1) to the target
+   using any working method.  */
+
+static void
+mips64_linux_store_registers (int regnum)
+{
+  /* Unless we already know that PTRACE_GETREGS does not work, try it.  */
+  if (have_ptrace_regsets)
+    mips64_linux_regsets_store_registers (regnum);
+
+  /* If we know, or just found out, that PTRACE_GETREGS does not work, fall
+     back to PTRACE_PEEKUSER.  */
+  if (!have_ptrace_regsets)
+    super_store_registers (regnum);
+}
+
+void _initialize_mips_linux_nat (void);
+
+void
+_initialize_mips_linux_nat (void)
+{
+  struct target_ops *t = linux_target ();
+
+  super_fetch_registers = t->to_fetch_registers;
+  super_store_registers = t->to_store_registers;
+
+  t->to_fetch_registers = mips64_linux_fetch_registers;
+  t->to_store_registers = mips64_linux_store_registers;
+
+  linux_nat_add_target (t);
+}