daily update
[external/binutils.git] / gdb / mips-linux-nat.c
index 985ff27..b2133b0 100644 (file)
@@ -1,13 +1,13 @@
 /* Native-dependent code for GNU/Linux on MIPS processors.
 
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
+   Copyright (C) 2001, 2002, 2003, 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., 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
 #include "inferior.h"
 #include "mips-tdep.h"
 #include "target.h"
+#include "regcache.h"
 #include "linux-nat.h"
 #include "mips-linux-tdep.h"
+#include "target-descriptions.h"
 
 #include "gdb_proc_service.h"
+#include "gregset.h"
 
+#include <sgidefs.h>
 #include <sys/ptrace.h>
 
+#include "features/mips-linux.c"
+#include "features/mips64-linux.c"
+
 #ifndef PTRACE_GET_THREAD_AREA
 #define PTRACE_GET_THREAD_AREA 25
 #endif
@@ -42,48 +47,84 @@ 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);
+void (*super_fetch_registers) (struct regcache *, int);
+void (*super_store_registers) (struct regcache *, int);
+
+/* Map gdb internal register number to ptrace ``address''.
+   These ``addresses'' are normally defined in <asm/ptrace.h>. 
 
-/* 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
-   setting MIPS_ZERO_REGNUM.  We also can not set BADVADDR, CAUSE, or
-   FCRIR via ptrace().  */
+   ptrace does not provide a way to read (or set) MIPS_PS_REGNUM,
+   and there's no point in reading or setting MIPS_ZERO_REGNUM.
+   We also can not set BADVADDR, CAUSE, or FCRIR via ptrace().  */
 
-int
-mips_linux_cannot_fetch_register (int regno)
+static CORE_ADDR
+mips_linux_register_addr (struct gdbarch *gdbarch, int regno, int store)
 {
+  CORE_ADDR regaddr;
+
+  if (regno < 0 || regno >= gdbarch_num_regs (gdbarch))
+    error (_("Bogon register number %d."), regno);
+
   if (regno > MIPS_ZERO_REGNUM && regno < MIPS_ZERO_REGNUM + 32)
-    return 0;
-  else if (regno >= mips_regnum (current_gdbarch)->fp0
-          && regno <= mips_regnum (current_gdbarch)->fp0 + 32)
-    return 0;
-  else if (regno == mips_regnum (current_gdbarch)->lo
-          || regno == mips_regnum (current_gdbarch)->hi
-          || regno == mips_regnum (current_gdbarch)->badvaddr
-          || regno == mips_regnum (current_gdbarch)->cause
-          || regno == mips_regnum (current_gdbarch)->pc
-          || regno == mips_regnum (current_gdbarch)->fp_control_status
-          || regno == mips_regnum (current_gdbarch)->fp_implementation_revision)
-    return 0;
+    regaddr = regno;
+  else if ((regno >= mips_regnum (gdbarch)->fp0)
+          && (regno < mips_regnum (gdbarch)->fp0 + 32))
+    regaddr = FPR_BASE + (regno - mips_regnum (gdbarch)->fp0);
+  else if (regno == mips_regnum (gdbarch)->pc)
+    regaddr = PC;
+  else if (regno == mips_regnum (gdbarch)->cause)
+    regaddr = store? (CORE_ADDR) -1 : CAUSE;
+  else if (regno == mips_regnum (gdbarch)->badvaddr)
+    regaddr = store? (CORE_ADDR) -1 : BADVADDR;
+  else if (regno == mips_regnum (gdbarch)->lo)
+    regaddr = MMLO;
+  else if (regno == mips_regnum (gdbarch)->hi)
+    regaddr = MMHI;
+  else if (regno == mips_regnum (gdbarch)->fp_control_status)
+    regaddr = FPC_CSR;
+  else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
+    regaddr = store? (CORE_ADDR) -1 : FPC_EIR;
+  else if (mips_linux_restart_reg_p (gdbarch) && regno == MIPS_RESTART_REGNUM)
+    regaddr = 0;
   else
-    return 1;
+    regaddr = (CORE_ADDR) -1;
+
+  return regaddr;
 }
 
-int
-mips_linux_cannot_store_register (int regno)
+static CORE_ADDR
+mips64_linux_register_addr (struct gdbarch *gdbarch, int regno, int store)
 {
+  CORE_ADDR regaddr;
+
+  if (regno < 0 || regno >= gdbarch_num_regs (gdbarch))
+    error (_("Bogon register number %d."), regno);
+
   if (regno > MIPS_ZERO_REGNUM && regno < MIPS_ZERO_REGNUM + 32)
-    return 0;
-  else if (regno >= FP0_REGNUM && regno <= FP0_REGNUM + 32)
-    return 0;
-  else if (regno == mips_regnum (current_gdbarch)->lo
-          || regno == mips_regnum (current_gdbarch)->hi
-          || regno == mips_regnum (current_gdbarch)->pc
-          || regno == mips_regnum (current_gdbarch)->fp_control_status)
-    return 0;
+    regaddr = regno;
+  else if ((regno >= mips_regnum (gdbarch)->fp0)
+          && (regno < mips_regnum (gdbarch)->fp0 + 32))
+    regaddr = MIPS64_FPR_BASE + (regno - gdbarch_fp0_regnum (gdbarch));
+  else if (regno == mips_regnum (gdbarch)->pc)
+    regaddr = MIPS64_PC;
+  else if (regno == mips_regnum (gdbarch)->cause)
+    regaddr = store? (CORE_ADDR) -1 : MIPS64_CAUSE;
+  else if (regno == mips_regnum (gdbarch)->badvaddr)
+    regaddr = store? (CORE_ADDR) -1 : MIPS64_BADVADDR;
+  else if (regno == mips_regnum (gdbarch)->lo)
+    regaddr = MIPS64_MMLO;
+  else if (regno == mips_regnum (gdbarch)->hi)
+    regaddr = MIPS64_MMHI;
+  else if (regno == mips_regnum (gdbarch)->fp_control_status)
+    regaddr = MIPS64_FPC_CSR;
+  else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
+    regaddr = store? (CORE_ADDR) -1 : MIPS64_FPC_EIR;
+  else if (mips_linux_restart_reg_p (gdbarch) && regno == MIPS_RESTART_REGNUM)
+    regaddr = 0;
   else
-    return 1;
+    regaddr = (CORE_ADDR) -1;
+
+  return regaddr;
 }
 
 /* Fetch the thread-local storage pointer for libthread_db.  */
@@ -103,21 +144,63 @@ ps_get_thread_area (const struct ps_prochandle *ph,
   return PS_OK;
 }
 
+/* Wrapper functions.  These are only used by libthread_db.  */
+
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
+{
+  if (mips_isa_regsize (get_regcache_arch (regcache)) == 4)
+    mips_supply_gregset (regcache, (const mips_elf_gregset_t *) gregsetp);
+  else
+    mips64_supply_gregset (regcache, (const mips64_elf_gregset_t *) gregsetp);
+}
+
+void
+fill_gregset (const struct regcache *regcache,
+             gdb_gregset_t *gregsetp, int regno)
+{
+  if (mips_isa_regsize (get_regcache_arch (regcache)) == 4)
+    mips_fill_gregset (regcache, (mips_elf_gregset_t *) gregsetp, regno);
+  else
+    mips64_fill_gregset (regcache, (mips64_elf_gregset_t *) gregsetp, regno);
+}
+
+void
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp)
+{
+  if (mips_isa_regsize (get_regcache_arch (regcache)) == 4)
+    mips_supply_fpregset (regcache, (const mips_elf_fpregset_t *) fpregsetp);
+  else
+    mips64_supply_fpregset (regcache, (const mips64_elf_fpregset_t *) fpregsetp);
+}
+
+void
+fill_fpregset (const struct regcache *regcache,
+              gdb_fpregset_t *fpregsetp, int regno)
+{
+  if (mips_isa_regsize (get_regcache_arch (regcache)) == 4)
+    mips_fill_fpregset (regcache, (mips_elf_fpregset_t *) fpregsetp, regno);
+  else
+    mips64_fill_fpregset (regcache, (mips64_elf_fpregset_t *) fpregsetp, regno);
+}
+
+
 /* 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)
+mips64_linux_regsets_fetch_registers (struct regcache *regcache, int regno)
 {
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
   int is_fp;
   int tid;
 
-  if (regno >= mips_regnum (current_gdbarch)->fp0
-      && regno <= mips_regnum (current_gdbarch)->fp0 + 32)
+  if (regno >= mips_regnum (gdbarch)->fp0
+      && regno <= mips_regnum (gdbarch)->fp0 + 32)
     is_fp = 1;
-  else if (regno == mips_regnum (current_gdbarch)->fp_control_status)
+  else if (regno == mips_regnum (gdbarch)->fp_control_status)
     is_fp = 1;
-  else if (regno == mips_regnum (current_gdbarch)->fp_implementation_revision)
+  else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
     is_fp = 1;
   else
     is_fp = 0;
@@ -140,7 +223,8 @@ mips64_linux_regsets_fetch_registers (int regno)
          perror_with_name (_("Couldn't get registers"));
        }
 
-      mips64_supply_gregset (&regs);
+      mips64_supply_gregset (regcache,
+                            (const mips64_elf_gregset_t *) &regs);
     }
 
   if (regno == -1 || is_fp)
@@ -158,7 +242,8 @@ mips64_linux_regsets_fetch_registers (int regno)
          perror_with_name (_("Couldn't get FP registers"));
        }
 
-      mips64_supply_fpregset (&fp_regs);
+      mips64_supply_fpregset (regcache,
+                             (const mips64_elf_fpregset_t *) &fp_regs);
     }
 }
 
@@ -166,17 +251,18 @@ mips64_linux_regsets_fetch_registers (int regno)
    using PTRACE_SETREGS et al.  */
 
 static void
-mips64_linux_regsets_store_registers (int regno)
+mips64_linux_regsets_store_registers (const struct regcache *regcache, int regno)
 {
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
   int is_fp;
   int tid;
 
-  if (regno >= mips_regnum (current_gdbarch)->fp0
-      && regno <= mips_regnum (current_gdbarch)->fp0 + 32)
+  if (regno >= mips_regnum (gdbarch)->fp0
+      && regno <= mips_regnum (gdbarch)->fp0 + 32)
     is_fp = 1;
-  else if (regno == mips_regnum (current_gdbarch)->fp_control_status)
+  else if (regno == mips_regnum (gdbarch)->fp_control_status)
     is_fp = 1;
-  else if (regno == mips_regnum (current_gdbarch)->fp_implementation_revision)
+  else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
     is_fp = 1;
   else
     is_fp = 0;
@@ -192,7 +278,7 @@ mips64_linux_regsets_store_registers (int regno)
       if (ptrace (PTRACE_GETREGS, tid, 0L, (PTRACE_TYPE_ARG3) &regs) == -1)
        perror_with_name (_("Couldn't get registers"));
 
-      mips64_fill_gregset (&regs, regno);
+      mips64_fill_gregset (regcache, &regs, regno);
 
       if (ptrace (PTRACE_SETREGS, tid, 0L, (PTRACE_TYPE_ARG3) &regs) == -1)
        perror_with_name (_("Couldn't set registers"));
@@ -206,7 +292,7 @@ mips64_linux_regsets_store_registers (int regno)
                  (PTRACE_TYPE_ARG3) &fp_regs) == -1)
        perror_with_name (_("Couldn't get FP registers"));
 
-      mips64_fill_fpregset (&fp_regs, regno);
+      mips64_fill_fpregset (regcache, &fp_regs, regno);
 
       if (ptrace (PTRACE_SETFPREGS, tid, 0L,
                  (PTRACE_TYPE_ARG3) &fp_regs) == -1)
@@ -218,32 +304,55 @@ mips64_linux_regsets_store_registers (int regno)
    using any working method.  */
 
 static void
-mips64_linux_fetch_registers (int regnum)
+mips64_linux_fetch_registers (struct regcache *regcache, int regnum)
 {
   /* Unless we already know that PTRACE_GETREGS does not work, try it.  */
   if (have_ptrace_regsets)
-    mips64_linux_regsets_fetch_registers (regnum);
+    mips64_linux_regsets_fetch_registers (regcache, 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);
+    super_fetch_registers (regcache, regnum);
 }
 
 /* Store REGNO (or all registers if REGNO == -1) to the target
    using any working method.  */
 
 static void
-mips64_linux_store_registers (int regnum)
+mips64_linux_store_registers (struct regcache *regcache, int regnum)
 {
   /* Unless we already know that PTRACE_GETREGS does not work, try it.  */
   if (have_ptrace_regsets)
-    mips64_linux_regsets_store_registers (regnum);
+    mips64_linux_regsets_store_registers (regcache, 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);
+    super_store_registers (regcache, regnum);
+}
+
+/* Return the address in the core dump or inferior of register
+   REGNO.  */
+
+static CORE_ADDR
+mips_linux_register_u_offset (struct gdbarch *gdbarch, int regno, int store_p)
+{
+  if (mips_abi_regsize (gdbarch) == 8)
+    return mips64_linux_register_addr (gdbarch, regno, store_p);
+  else
+    return mips_linux_register_addr (gdbarch, regno, store_p);
+}
+
+static const struct target_desc *
+mips_linux_read_description (struct target_ops *ops)
+{
+  /* Report that target registers are a size we know for sure
+     that we can get from ptrace.  */
+  if (_MIPS_SIM == _ABIO32)
+    return tdesc_mips_linux;
+  else
+    return tdesc_mips64_linux;
 }
 
 void _initialize_mips_linux_nat (void);
@@ -251,7 +360,7 @@ void _initialize_mips_linux_nat (void);
 void
 _initialize_mips_linux_nat (void)
 {
-  struct target_ops *t = linux_target ();
+  struct target_ops *t = linux_trad_target (mips_linux_register_u_offset);
 
   super_fetch_registers = t->to_fetch_registers;
   super_store_registers = t->to_store_registers;
@@ -259,5 +368,11 @@ _initialize_mips_linux_nat (void)
   t->to_fetch_registers = mips64_linux_fetch_registers;
   t->to_store_registers = mips64_linux_store_registers;
 
-  add_target (t);
+  t->to_read_description = mips_linux_read_description;
+
+  linux_nat_add_target (t);
+
+  /* Initialize the standard target descriptions.  */
+  initialize_tdesc_mips_linux ();
+  initialize_tdesc_mips64_linux ();
 }