2000-04-30 Mark Kettenis <kettenis@gnu.org>
[deliverable/binutils-gdb.git] / gdb / i386-linux-nat.c
index 12fbe3e280c611c2c76c167c041d71a6844531ec..2bfac295efe35688effb8c27882224a3c164a534 100644 (file)
@@ -1,28 +1,29 @@
 /* Native-dependent code for Linux running on i386's, for GDB.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
 
-This file is part of GDB.
+   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
-(at your option) any later version.
+   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
+   (at your option) any later version.
 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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.  */
+   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.  */
 
 #include "defs.h"
 #include "inferior.h"
 #include "gdbcore.h"
 
-/* For i386_linux_skip_solib_resolver */
+/* For i386_linux_skip_solib_resolver */
 #include "symtab.h"
-#include "frame.h"
 #include "symfile.h"
 #include "objfiles.h"
 
@@ -34,140 +35,434 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include <sys/reg.h>
 #endif
 
-/* This is a duplicate of the table in i386-xdep.c. */
+/* On Linux, threads are implemented as pseudo-processes, in which
+   case we may be tracing more than one process at a time.  In that
+   case, inferior_pid will contain the main process ID and the
+   individual thread (process) ID mashed together.  These macros are
+   used to separate them out.  These definitions should be overridden
+   if thread support is included.  */
 
+#if !defined (PIDGET)  /* Default definition for PIDGET/TIDGET.  */
+#define PIDGET(PID)    PID
+#define TIDGET(PID)    0
+#endif
+
+
+/* The register sets used in Linux ELF core-dumps are identical to the
+   register sets in `struct user' that is used for a.out core-dumps,
+   and is also used by `ptrace'.  The corresponding types are
+   `elf_gregset_t' for the general-purpose registers (with
+   `elf_greg_t' the type of a single GP register) and `elf_fpregset_t'
+   for the floating-point registers.
+
+   Those types used to be available under the names `gregset_t' and
+   `fpregset_t' too, and this file used those names in the past.  But
+   those names are now used for the register sets used in the
+   `mcontext_t' type, and have a different size and layout.  */
+
+/* Mapping between the general-purpose registers in `struct user'
+   format and GDB's register array layout.  */
 static int regmap[] = 
 {
   EAX, ECX, EDX, EBX,
   UESP, EBP, ESI, EDI,
   EIP, EFL, CS, SS,
-  DS, ES, FS, GS,
+  DS, ES, FS, GS
 };
 
+/* Which ptrace request retrieves which registers?
+   These apply to the corresponding SET requests as well.  */
+#define GETREGS_SUPPLIES(regno) \
+  (0 <= (regno) && (regno) <= 15)
+#define GETFPREGS_SUPPLIES(regno) \
+  (FP0_REGNUM <= (regno) && (regno) <= LAST_FPU_CTRL_REGNUM)
+#define GETXFPREGS_SUPPLIES(regno) \
+  (FP0_REGNUM <= (regno) && (regno) <= MXCSR_REGNUM)
+
+/* Does the current host support the GETREGS request?  */
+int have_ptrace_getregs =
+#ifdef HAVE_PTRACE_GETREGS
+  1
+#else
+  0
+#endif
+;
+
+/* Does the current host support the GETXFPREGS request?  The header
+   file may or may not define it, and even if it is defined, the
+   kernel will return EIO if it's running on a pre-SSE processor.
+
+   PTRACE_GETXFPREGS is a Cygnus invention, since we wrote our own
+   Linux kernel patch for SSE support.  That patch may or may not
+   actually make it into the official distribution.  If you find that
+   years have gone by since this stuff was added, and Linux isn't
+   using PTRACE_GETXFPREGS, that means that our patch didn't make it,
+   and you can delete this, and the related code.
+
+   My instinct is to attach this to some architecture- or
+   target-specific data structure, but really, a particular GDB
+   process can only run on top of one kernel at a time.  So it's okay
+   for this to be a simple variable.  */
+int have_ptrace_getxfpregs =
+#ifdef HAVE_PTRACE_GETXFPREGS
+  1
+#else
+  0
+#endif
+;
+
+\f
+/* Fetching registers directly from the U area, one at a time.  */
+
+/* FIXME: kettenis/2000-03-05: This duplicates code from `inptrace.c'.
+   The problem is that we define FETCH_INFERIOR_REGISTERS since we
+   want to use our own versions of {fetch,store}_inferior_registers
+   that use the GETREGS request.  This means that the code in
+   `infptrace.c' is #ifdef'd out.  But we need to fall back on that
+   code when GDB is running on top of a kernel that doesn't support
+   the GETREGS request.  I want to avoid changing `infptrace.c' right
+   now.  */
+
+/* Default the type of the ptrace transfer to int.  */
+#ifndef PTRACE_XFER_TYPE
+#define PTRACE_XFER_TYPE int
+#endif
+
+/* Registers we shouldn't try to fetch.  */
+#if !defined (CANNOT_FETCH_REGISTER)
+#define CANNOT_FETCH_REGISTER(regno) 0
+#endif
+
+/* Fetch one register.  */
+
+static void
+fetch_register (regno)
+     int regno;
+{
+  /* This isn't really an address.  But ptrace thinks of it as one.  */
+  CORE_ADDR regaddr;
+  char mess[128];              /* For messages */
+  register int i;
+  unsigned int offset;         /* Offset of registers within the u area.  */
+  char buf[MAX_REGISTER_RAW_SIZE];
+  int tid;
+
+  if (CANNOT_FETCH_REGISTER (regno))
+    {
+      memset (buf, '\0', REGISTER_RAW_SIZE (regno));   /* Supply zeroes */
+      supply_register (regno, buf);
+      return;
+    }
+
+  /* Overload thread id onto process id */
+  if ((tid = TIDGET (inferior_pid)) == 0)
+    tid = inferior_pid;                /* no thread id, just use process id */
+
+  offset = U_REGS_OFFSET;
+
+  regaddr = register_addr (regno, offset);
+  for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE))
+    {
+      errno = 0;
+      *(PTRACE_XFER_TYPE *) & buf[i] = ptrace (PT_READ_U, tid,
+                                              (PTRACE_ARG3_TYPE) regaddr, 0);
+      regaddr += sizeof (PTRACE_XFER_TYPE);
+      if (errno != 0)
+       {
+         sprintf (mess, "reading register %s (#%d)", 
+                  REGISTER_NAME (regno), regno);
+         perror_with_name (mess);
+       }
+    }
+  supply_register (regno, buf);
+}
+
+/* Fetch register values from the inferior.
+   If REGNO is negative, do this for all registers.
+   Otherwise, REGNO specifies which register (so we can save time). */
 
-/* Given a pointer to a general register set in struct user format
-   (gregset_t *), unpack the register contents and supply them as
-   gdb's idea of the current register values. */
 void
-supply_gregset (gregsetp)
-     gregset_t *gregsetp;
+old_fetch_inferior_registers (regno)
+     int regno;
 {
-  register int regi;
-  register greg_t *regp = (greg_t *) gregsetp;
+  if (regno >= 0)
+    {
+      fetch_register (regno);
+    }
+  else
+    {
+      for (regno = 0; regno < ARCH_NUM_REGS; regno++)
+       {
+         fetch_register (regno);
+       }
+    }
+}
 
-  for (regi = 0; regi < NUM_GREGS; regi++)
+/* Registers we shouldn't try to store.  */
+#if !defined (CANNOT_STORE_REGISTER)
+#define CANNOT_STORE_REGISTER(regno) 0
+#endif
+
+/* Store one register. */
+
+static void
+store_register (regno)
+     int regno;
+{
+  /* This isn't really an address.  But ptrace thinks of it as one.  */
+  CORE_ADDR regaddr;
+  char mess[128];              /* For messages */
+  register int i;
+  unsigned int offset;         /* Offset of registers within the u area.  */
+  int tid;
+
+  if (CANNOT_STORE_REGISTER (regno))
+    {
+      return;
+    }
+
+  /* Overload thread id onto process id */
+  if ((tid = TIDGET (inferior_pid)) == 0)
+    tid = inferior_pid;                /* no thread id, just use process id */
+
+  offset = U_REGS_OFFSET;
+
+  regaddr = register_addr (regno, offset);
+  for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE))
     {
-      supply_register (regi, (char *) (regp + regmap[regi]));
+      errno = 0;
+      ptrace (PT_WRITE_U, tid, (PTRACE_ARG3_TYPE) regaddr,
+             *(PTRACE_XFER_TYPE *) & registers[REGISTER_BYTE (regno) + i]);
+      regaddr += sizeof (PTRACE_XFER_TYPE);
+      if (errno != 0)
+       {
+         sprintf (mess, "writing register %s (#%d)", 
+                  REGISTER_NAME (regno), regno);
+         perror_with_name (mess);
+       }
     }
 }
 
-/* Fill in a gregset_t object with selected data from a gdb-format
-   register file.
-   - GREGSETP points to the gregset_t object to be filled.
-   - GDB_REGS points to the GDB-style register file providing the data.
-   - VALID is an array indicating which registers in GDB_REGS are
-     valid; the parts of *GREGSETP that would hold registers marked
-     invalid in GDB_REGS are left unchanged.  If VALID is zero, all
-     registers are assumed to be valid.  */
+/* Store our register values back into the inferior.
+   If REGNO is negative, do this for all registers.
+   Otherwise, REGNO specifies which register (so we can save time).  */
+
 void
-convert_to_gregset (gregset_t *gregsetp,
-                   char *gdb_regs,
-                   signed char *valid)
+old_store_inferior_registers (regno)
+     int regno;
 {
+  if (regno >= 0)
+    {
+      store_register (regno);
+    }
+  else
+    {
+      for (regno = 0; regno < ARCH_NUM_REGS; regno++)
+       {
+         store_register (regno);
+       }
+    }
+}
+
+\f
+/* Transfering the general-purpose registers between GDB, inferiors
+   and core files.  */
+
+/* Fill GDB's register array with the genereal-purpose register values
+   in *GREGSETP.  */
+
+void
+supply_gregset (elf_gregset_t *gregsetp)
+{
+  elf_greg_t *regp = (elf_greg_t *) gregsetp;
+  int regi;
+
+  for (regi = 0; regi < NUM_GREGS; regi++)
+    supply_register (regi, (char *) (regp + regmap[regi]));
+}
+
+/* Convert the valid general-purpose register values in GDB's register
+   array to `struct user' format and store them in *GREGSETP.  The
+   array VALID indicates which register values are valid.  If VALID is
+   NULL, all registers are assumed to be valid.  */
+
+static void
+convert_to_gregset (elf_gregset_t *gregsetp, signed char *valid)
+{
+  elf_greg_t *regp = (elf_greg_t *) gregsetp;
   int regi;
-  register greg_t *regp = (greg_t *) gregsetp;
 
   for (regi = 0; regi < NUM_GREGS; regi++)
     if (! valid || valid[regi])
       *(regp + regmap[regi]) = * (int *) &registers[REGISTER_BYTE (regi)];
 }
 
+/* Fill register REGNO (if it is a general-purpose register) in
+   *GREGSETPS with the value in GDB's register array.  If REGNO is -1,
+   do this for all registers.  */
 void
-fill_gregset (gregset_t *gregsetp,
-             int regno)
+fill_gregset (elf_gregset_t *gregsetp, int regno)
 {
   if (regno == -1)
-    convert_to_gregset (gregsetp, registers, 0);
-  else
+    {
+      convert_to_gregset (gregsetp, NULL);
+      return;
+    }
+
+  if (GETREGS_SUPPLIES (regno))
     {
       signed char valid[NUM_GREGS];
+
       memset (valid, 0, sizeof (valid));
       valid[regno] = 1;
-      convert_to_gregset (gregsetp, valid, valid);
+
+      convert_to_gregset (gregsetp, valid);
+    }
+}
+
+#ifdef HAVE_PTRACE_GETREGS
+
+/* Fetch all general-purpose registers from process/thread TID and
+   store their values in GDB's register array.  */
+
+static void
+fetch_regs (int tid)
+{
+  elf_gregset_t regs;
+  int ret;
+
+  ret = ptrace (PTRACE_GETREGS, tid, 0, (int) &regs);
+  if (ret < 0)
+    {
+      if (errno == EIO)
+       {
+         /* The kernel we're running on doesn't support the GETREGS
+             request.  Reset `have_ptrace_getregs'.  */
+         have_ptrace_getregs = 0;
+         return;
+       }
+
+      warning ("Couldn't get registers.");
+      return;
     }
+
+  supply_gregset (&regs);
 }
 
+/* Store all valid general-purpose registers in GDB's register array
+   into the process/thread specified by TID.  */
+
+static void
+store_regs (int tid)
+{
+  elf_gregset_t regs;
+  int ret;
+
+  ret = ptrace (PTRACE_GETREGS, tid, 0, (int) &regs);
+  if (ret < 0)
+    {
+      warning ("Couldn't get registers.");
+      return;
+    }
+
+  convert_to_gregset (&regs, register_valid);
+
+  ret = ptrace (PTRACE_SETREGS, tid, 0, (int) &regs);
+  if (ret < 0)
+    {
+      warning ("Couldn't write registers.");
+      return;
+    }
+}
+
+#else
+
+static void fetch_regs (int tid) {}
+static void store_regs (int tid) {}
+
+#endif
+
+\f
+/* Transfering floating-point registers between GDB, inferiors and cores.  */
 
-/* Where does st(N) start in the fpregset_t structure F?  */
-#define FPREGSET_T_FPREG_OFFSET(f, n) \
-  ((char *) &(f)->st_space + (n) * 10)
+/* What is the address of st(N) within the floating-point register set F?  */
+#define FPREG_ADDR(f, n) ((char *) &(f)->st_space + (n) * 10)
 
-/* Fill GDB's register file with the floating-point register values in
+/* Fill GDB's register array with the floating-point register values in
    *FPREGSETP.  */
+
 void 
-supply_fpregset (fpregset_t *fpregsetp)
+supply_fpregset (elf_fpregset_t *fpregsetp)
 {
-  int i;
+  int reg;
+  long l;
 
   /* Supply the floating-point registers.  */
-  for (i = 0; i < 8; i++)
-    supply_register (FP0_REGNUM + i, FPREGSET_T_FPREG_OFFSET (fpregsetp, i));
-
-  supply_register (FCTRL_REGNUM, (char *) &fpregsetp->cwd);
-  supply_register (FSTAT_REGNUM, (char *) &fpregsetp->swd);
-  supply_register (FTAG_REGNUM,  (char *) &fpregsetp->twd);
+  for (reg = 0; reg < 8; reg++)
+    supply_register (FP0_REGNUM + reg, FPREG_ADDR (fpregsetp, reg));
+
+  /* We have to mask off the reserved bits in *FPREGSETP before
+     storing the values in GDB's register file.  */
+#define supply(REGNO, MEMBER)                                           \
+  l = fpregsetp->MEMBER & 0xffff;                                       \
+  supply_register (REGNO, (char *) &l)
+
+  supply (FCTRL_REGNUM, cwd);
+  supply (FSTAT_REGNUM, swd);
+  supply (FTAG_REGNUM, twd);
   supply_register (FCOFF_REGNUM, (char *) &fpregsetp->fip);
-  supply_register (FDS_REGNUM,   (char *) &fpregsetp->fos);
+  supply (FDS_REGNUM, fos);
   supply_register (FDOFF_REGNUM, (char *) &fpregsetp->foo);
-  
-  /* Extract the code segment and opcode from the  "fcs" member.  */
-  {
-    long l;
 
-    l = fpregsetp->fcs & 0xffff;
-    supply_register (FCS_REGNUM, (char *) &l);
+#undef supply
 
-    l = (fpregsetp->fcs >> 16) & ((1 << 11) - 1);
-    supply_register (FOP_REGNUM, (char *) &l);
-  }
+  /* Extract the code segment and opcode from the  "fcs" member.  */
+  l = fpregsetp->fcs & 0xffff;
+  supply_register (FCS_REGNUM, (char *) &l);
+
+  l = (fpregsetp->fcs >> 16) & ((1 << 11) - 1);
+  supply_register (FOP_REGNUM, (char *) &l);
 }
 
+/* Convert the valid floating-point register values in GDB's register
+   array to `struct user' format and store them in *FPREGSETP.  The
+   array VALID indicates which register values are valid.  If VALID is
+   NULL, all registers are assumed to be valid.  */
 
-/* Fill in an fpregset_t structure with selected data from a
-   gdb-format register file.
-   - FPREGSETP points to the structure to be filled. 
-   - GDB_REGS points to the GDB-style register file providing the data.
-   - VALID is an array indicating which registers in GDB_REGS are
-     valid; the parts of *FPREGSETP that would hold registers marked
-     invalid in GDB_REGS are left unchanged.  If VALID is zero, all
-     registers are assumed to be valid.  */
-void
-convert_to_fpregset (fpregset_t *fpregsetp,
-                    char *gdb_regs,
-                    signed char *valid)
+static void
+convert_to_fpregset (elf_fpregset_t *fpregsetp, signed char *valid)
 {
-  int i;
+  int reg;
 
   /* Fill in the floating-point registers.  */
-  for (i = 0; i < 8; i++)
-    if (!valid || valid[i])
-      memcpy (FPREGSET_T_FPREG_OFFSET (fpregsetp, i),
-             &registers[REGISTER_BYTE (FP0_REGNUM + i)],
-             REGISTER_RAW_SIZE(FP0_REGNUM + i));
+  for (reg = 0; reg < 8; reg++)
+    if (!valid || valid[reg])
+      memcpy (FPREG_ADDR (fpregsetp, reg),
+             &registers[REGISTER_BYTE (FP0_REGNUM + reg)],
+             REGISTER_RAW_SIZE(FP0_REGNUM + reg));
+
+  /* We're not supposed to touch the reserved bits in *FPREGSETP.  */
 
 #define fill(MEMBER, REGNO)                                            \
   if (! valid || valid[(REGNO)])                                       \
-    memcpy (&fpregsetp->MEMBER, &registers[REGISTER_BYTE (REGNO)],     \
-           sizeof (fpregsetp->MEMBER))
+    fpregsetp->MEMBER                                                   \
+      = ((fpregsetp->MEMBER & ~0xffff)                                  \
+         | (* (int *) &registers[REGISTER_BYTE (REGNO)] & 0xffff))
+
+#define fill_register(MEMBER, REGNO)                                    \
+  if (! valid || valid[(REGNO)])                                        \
+    memcpy (&fpregsetp->MEMBER, &registers[REGISTER_BYTE (REGNO)],      \
+            sizeof (fpregsetp->MEMBER))
 
   fill (cwd, FCTRL_REGNUM);
   fill (swd, FSTAT_REGNUM);
   fill (twd, FTAG_REGNUM);
-  fill (fip, FCOFF_REGNUM);
+  fill_register (fip, FCOFF_REGNUM);
   fill (foo, FDOFF_REGNUM);
-  fill (fos, FDS_REGNUM);
+  fill_register (fos, FDS_REGNUM);
 
 #undef fill
+#undef fill_register
 
   if (! valid || valid[FCS_REGNUM])
     fpregsetp->fcs
@@ -181,144 +476,571 @@ convert_to_fpregset (fpregset_t *fpregsetp,
            << 16));
 }
 
-
-/* Given a pointer to a floating point register set in (fpregset_t *)
-   format, update all of the registers from gdb's idea of the current
-   floating point register set.  */
+/* Fill register REGNO (if it is a floating-point register) in
+   *FPREGSETP with the value in GDB's register array.  If REGNO is -1,
+   do this for all registers.  */
 
 void
-fill_fpregset (fpregset_t *fpregsetp,
-              int regno)
+fill_fpregset (elf_fpregset_t *fpregsetp, int regno)
 {
-  convert_to_fpregset (fpregsetp, registers, 0);
+  if (regno == -1)
+    {
+      convert_to_fpregset (fpregsetp, NULL);
+      return;
+    }
+
+  if (GETFPREGS_SUPPLIES(regno))
+    {
+      signed char valid[MAX_NUM_REGS];
+      
+      memset (valid, 0, sizeof (valid));
+      valid[regno] = 1;
+             
+      convert_to_fpregset (fpregsetp, valid);
+    }
 }
 
+#ifdef HAVE_PTRACE_GETREGS
+
+/* Fetch all floating-point registers from process/thread TID and store
+   thier values in GDB's register array.  */
 
-/* Get the whole floating point state of the process and store the
-   floating point stack into registers[].  */
 static void
-fetch_fpregs ()
+fetch_fpregs (int tid)
 {
-  int ret, regno;
-  fpregset_t buf;
+  elf_fpregset_t fpregs;
+  int ret;
 
-  ret = ptrace (PTRACE_GETFPREGS, inferior_pid,        0, (int) &buf);
+  ret = ptrace (PTRACE_GETFPREGS, tid, 0, (int) &fpregs);
   if (ret < 0)
     {
-      warning ("Couldn't get floating point status");
+      warning ("Couldn't get floating point status.");
       return;
     }
 
-  /* ptrace fills an fpregset_t, so we can use the same function we do
-     for core files.  */
-  supply_fpregset (&buf);
+  supply_fpregset (&fpregs);
 }
 
+/* Store all valid floating-point registers in GDB's register array
+   into the process/thread specified by TID.  */
 
-/* Set the inferior's floating-point registers to the values in
-   registers[] --- but only those registers marked valid.  */
 static void
-store_fpregs ()
+store_fpregs (int tid)
 {
+  elf_fpregset_t fpregs;
   int ret;
-  fpregset_t buf;
 
-  ret = ptrace (PTRACE_GETFPREGS, inferior_pid,        0, (int) &buf);
+  ret = ptrace (PTRACE_GETFPREGS, tid, 0, (int) &fpregs);
   if (ret < 0)
     {
-      warning ("Couldn't get floating point status");
+      warning ("Couldn't get floating point status.");
       return;
     }
 
-  convert_to_fpregset (&buf, registers, register_valid);
+  convert_to_fpregset (&fpregs, register_valid);
 
-  ret = ptrace (PTRACE_SETFPREGS, inferior_pid, 0, (int) &buf);
+  ret = ptrace (PTRACE_SETFPREGS, tid, 0, (int) &fpregs);
   if (ret < 0)
     {
-      warning ("Couldn't write floating point status");
+      warning ("Couldn't write floating point status.");
       return;
     }
 }
 
+#else
+
+static void fetch_fpregs (int tid) {}
+static void store_fpregs (int tid) {}
+
+#endif
+
+\f
+/* Transfering floating-point and SSE registers to and from GDB.  */
+
+/* PTRACE_GETXFPREGS is a Cygnus invention, since we wrote our own
+   Linux kernel patch for SSE support.  That patch may or may not
+   actually make it into the official distribution.  If you find that
+   years have gone by since this code was added, and Linux isn't using
+   PTRACE_GETXFPREGS, that means that our patch didn't make it, and
+   you can delete this code.  */
+
+#ifdef HAVE_PTRACE_GETXFPREGS
+
+/* Fill GDB's register array with the floating-point and SSE register
+   values in *XFPREGS.  */
 
-/* Read the general registers from the process, and store them
-   in registers[].  */
 static void
-fetch_regs ()
+supply_xfpregset (struct user_xfpregs_struct *xfpregs)
 {
-  int ret, regno;
-  gregset_t buf;
+  int reg;
 
-  ret = ptrace (PTRACE_GETREGS, inferior_pid, 0, (int) &buf);
-  if (ret < 0)
+  /* Supply the floating-point registers.  */
+  for (reg = 0; reg < 8; reg++)
+    supply_register (FP0_REGNUM + reg, (char *) &xfpregs->st_space[reg]);
+
+  {
+    supply_register (FCTRL_REGNUM, (char *) &xfpregs->cwd);
+    supply_register (FSTAT_REGNUM, (char *) &xfpregs->swd);
+    supply_register (FTAG_REGNUM,  (char *) &xfpregs->twd);
+    supply_register (FCOFF_REGNUM, (char *) &xfpregs->fip);
+    supply_register (FDS_REGNUM,   (char *) &xfpregs->fos);
+    supply_register (FDOFF_REGNUM, (char *) &xfpregs->foo);
+  
+    /* Extract the code segment and opcode from the  "fcs" member.  */
     {
-      warning ("Couldn't get registers");
-      return;
+      long l;
+      
+      l = xfpregs->fcs & 0xffff;
+      supply_register (FCS_REGNUM, (char *) &l);
+
+      l = (xfpregs->fcs >> 16) & ((1 << 11) - 1);
+      supply_register (FOP_REGNUM, (char *) &l);
     }
+  }
 
-  supply_gregset (&buf);
+  /* Supply the SSE registers.  */
+  for (reg = 0; reg < 8; reg++)
+    supply_register (XMM0_REGNUM + reg, (char *) &xfpregs->xmm_space[reg]);
+  supply_register (MXCSR_REGNUM, (char *) &xfpregs->mxcsr);
 }
 
+/* Convert the valid floating-point and SSE registers in GDB's
+   register array to `struct user' format and store them in *XFPREGS.
+   The array VALID indicates which registers are valid.  If VALID is
+   NULL, all registers are assumed to be valid.  */
 
-/* Set the inferior's general registers to the values in registers[]
-   --- but only those registers marked as valid.  */
 static void
-store_regs ()
+convert_to_xfpregset (struct user_xfpregs_struct *xfpregs,
+                     signed char *valid)
 {
-  int ret, regno;
-  gregset_t buf;
+  int reg;
 
-  ret = ptrace (PTRACE_GETREGS, inferior_pid, 0, (int) &buf);
-  if (ret < 0)
+  /* Fill in the floating-point registers.  */
+  for (reg = 0; reg < 8; reg++)
+    if (!valid || valid[reg])
+      memcpy (&xfpregs->st_space[reg],
+             &registers[REGISTER_BYTE (FP0_REGNUM + reg)],
+             REGISTER_RAW_SIZE(FP0_REGNUM + reg));
+
+#define fill(MEMBER, REGNO)                                            \
+  if (! valid || valid[(REGNO)])                                       \
+    memcpy (&xfpregs->MEMBER, &registers[REGISTER_BYTE (REGNO)],       \
+           sizeof (xfpregs->MEMBER))
+
+  fill (cwd, FCTRL_REGNUM);
+  fill (swd, FSTAT_REGNUM);
+  fill (twd, FTAG_REGNUM);
+  fill (fip, FCOFF_REGNUM);
+  fill (foo, FDOFF_REGNUM);
+  fill (fos, FDS_REGNUM);
+
+#undef fill
+
+  if (! valid || valid[FCS_REGNUM])
+    xfpregs->fcs
+      = ((xfpregs->fcs & ~0xffff)
+        | (* (int *) &registers[REGISTER_BYTE (FCS_REGNUM)] & 0xffff));
+
+  if (! valid || valid[FOP_REGNUM])
+    xfpregs->fcs
+      = ((xfpregs->fcs & 0xffff)
+        | ((*(int *) &registers[REGISTER_BYTE (FOP_REGNUM)] & ((1 << 11) - 1))
+           << 16));
+
+  /* Fill in the XMM registers.  */
+  for (reg = 0; reg < 8; reg++)
+    if (! valid || valid[reg])
+      memcpy (&xfpregs->xmm_space[reg],
+             &registers[REGISTER_BYTE (XMM0_REGNUM + reg)],
+             REGISTER_RAW_SIZE (XMM0_REGNUM + reg));
+}
+
+/* Fetch all registers covered by the PTRACE_SETXFPREGS request from
+   process/thread TID and store their values in GDB's register array.
+   Return non-zero if successful, zero otherwise.  */
+
+static int
+fetch_xfpregs (int tid)
+{
+  struct user_xfpregs_struct xfpregs;
+  int ret;
+
+  if (! have_ptrace_getxfpregs) 
+    return 0;
+
+  ret = ptrace (PTRACE_GETXFPREGS, tid, 0, &xfpregs);
+  if (ret == -1)
     {
-      warning ("Couldn't get registers");
-      return;
+      if (errno == EIO)
+       {
+         have_ptrace_getxfpregs = 0;
+         return 0;
+       }
+
+      warning ("Couldn't read floating-point and SSE registers.");
+      return 0;
     }
 
-  convert_to_gregset (&buf, registers, register_valid);
+  supply_xfpregset (&xfpregs);
+  return 1;
+}
 
-  ret = ptrace (PTRACE_SETREGS, inferior_pid, 0, (int)buf);
-  if (ret < 0)
+/* Store all valid registers in GDB's register array covered by the
+   PTRACE_SETXFPREGS request into the process/thread specified by TID.
+   Return non-zero if successful, zero otherwise.  */
+
+static int
+store_xfpregs (int tid)
+{
+  struct user_xfpregs_struct xfpregs;
+  int ret;
+
+  if (! have_ptrace_getxfpregs)
+    return 0;
+
+  ret = ptrace (PTRACE_GETXFPREGS, tid, 0, &xfpregs);
+  if (ret == -1)
     {
-      warning ("Couldn't write registers");
-      return;
+      if (errno == EIO)
+       {
+         have_ptrace_getxfpregs = 0;
+         return 0;
+       }
+
+      warning ("Couldn't read floating-point and SSE registers.");
+      return 0;
+    }
+
+  convert_to_xfpregset (&xfpregs, register_valid);
+
+  if (ptrace (PTRACE_SETXFPREGS, tid, 0, &xfpregs) < 0)
+    {
+      warning ("Couldn't write floating-point and SSE registers.");
+      return 0;
     }
+
+  return 1;
+}
+
+/* Fill the XMM registers in the register array with dummy values.  For
+   cases where we don't have access to the XMM registers.  I think
+   this is cleaner than printing a warning.  For a cleaner solution,
+   we should gdbarchify the i386 family.  */
+
+static void
+dummy_sse_values (void)
+{
+  /* C doesn't have a syntax for NaN's, so write it out as an array of
+     longs.  */
+  static long dummy[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff };
+  static long mxcsr = 0x1f80;
+  int reg;
+
+  for (reg = 0; reg < 8; reg++)
+    supply_register (XMM0_REGNUM + reg, (char *) dummy);
+  supply_register (MXCSR_REGNUM, (char *) &mxcsr);
 }
 
+#else
 
-/* Fetch registers from the child process.
-   Fetch all if regno == -1, otherwise fetch all ordinary
-   registers or all floating point registers depending
-   upon the value of regno. */
+/* Stub versions of the above routines, for systems that don't have
+   PTRACE_GETXFPREGS.  */
+static int store_xfpregs (int tid) { return 0; }
+static int fetch_xfpregs (int tid) { return 0; }
+static void dummy_sse_values (void) {}
+
+#endif
+
+\f
+/* Transferring arbitrary registers between GDB and inferior.  */
+
+/* Fetch register REGNO from the child process.  If REGNO is -1, do
+   this for all registers (including the floating point and SSE
+   registers).  */
 
 void
 fetch_inferior_registers (int regno)
 {
-  if (regno < NUM_GREGS || regno == -1)
-    fetch_regs ();
+  int tid;
+
+  /* Use the old method of peeking around in `struct user' if the
+     GETREGS request isn't available.  */
+  if (! have_ptrace_getregs)
+    {
+      old_fetch_inferior_registers (regno);
+      return;
+    }
+
+  /* Linux LWP ID's are process ID's.  */
+  if ((tid = TIDGET (inferior_pid)) == 0)
+    tid = inferior_pid;                /* Not a threaded program.  */
+
+  /* Use the PTRACE_GETXFPREGS request whenever possible, since it
+     transfers more registers in one system call, and we'll cache the
+     results.  But remember that fetch_xfpregs can fail, and return
+     zero.  */
+  if (regno == -1)
+    {
+      fetch_regs (tid);
+
+      /* The call above might reset `have_ptrace_getregs'.  */
+      if (! have_ptrace_getregs)
+       {
+         old_fetch_inferior_registers (-1);
+         return;
+       }
+
+      if (fetch_xfpregs (tid))
+       return;
+      fetch_fpregs (tid);
+      return;
+    }
+
+  if (GETREGS_SUPPLIES (regno))
+    {
+      fetch_regs (tid);
+      return;
+    }
+
+  if (GETXFPREGS_SUPPLIES (regno))
+    {
+      if (fetch_xfpregs (tid))
+       return;
+
+      /* Either our processor or our kernel doesn't support the SSE
+        registers, so read the FP registers in the traditional way,
+        and fill the SSE registers with dummy values.  It would be
+        more graceful to handle differences in the register set using
+        gdbarch.  Until then, this will at least make things work
+        plausibly.  */
+      fetch_fpregs (tid);
+      dummy_sse_values ();
+      return;
+    }
+
+  internal_error ("i386-linux-nat.c (fetch_inferior_registers): "
+                 "got request for bad register number %d", regno);
+}
+
+/* Store register REGNO back into the child process.  If REGNO is -1,
+   do this for all registers (including the floating point and SSE
+   registers).  */
+void
+store_inferior_registers (int regno)
+{
+  int tid;
+
+  /* Use the old method of poking around in `struct user' if the
+     SETREGS request isn't available.  */
+  if (! have_ptrace_getregs)
+    {
+      old_store_inferior_registers (regno);
+      return;
+    }
 
-  if (regno >= NUM_GREGS || regno == -1)
-    fetch_fpregs ();
+  /* Linux LWP ID's are process ID's.  */
+  if ((tid = TIDGET (inferior_pid)) == 0)
+    tid = inferior_pid;                /* Not a threaded program.  */
+
+  /* Use the PTRACE_SETXFPREGS requests whenever possibl, since it
+     transfers more registers in one system call.  But remember that
+     store_xfpregs can fail, and return zero.  */
+  if (regno == -1)
+    {
+      store_regs (tid);
+      if (store_xfpregs (tid))
+       return;
+      store_fpregs (tid);
+      return;
+    }
+
+  if (GETREGS_SUPPLIES (regno))
+    {
+      store_regs (tid);
+      return;
+    }
+
+  if (GETXFPREGS_SUPPLIES (regno))
+    {
+      if (store_xfpregs (tid))
+       return;
+
+      /* Either our processor or our kernel doesn't support the SSE
+        registers, so just write the FP registers in the traditional
+        way.  */
+      store_fpregs (tid);
+      return;
+    }
+
+  internal_error ("Got request to store bad register number %d.", regno);
 }
 
+\f
+/* Interpreting register set info found in core files.  */
 
-/* Store our register values back into the inferior.
-   If REGNO is -1, do this for all registers.
-   Otherwise, REGNO specifies which register, which
-   then determines whether we store all ordinary
-   registers or all of the floating point registers. */
+/* Provide registers to GDB from a core file.
+
+   (We can't use the generic version of this function in
+   core-regset.c, because Linux has *three* different kinds of
+   register set notes.  core-regset.c would have to call
+   supply_xfpregset, which most platforms don't have.)
+
+   CORE_REG_SECT points to an array of bytes, which are the contents
+   of a `note' from a core file which BFD thinks might contain
+   register contents.  CORE_REG_SIZE is its size.
+
+   WHICH says which register set corelow suspects this is:
+     0 --- the general-purpose register set, in elf_gregset_t format
+     2 --- the floating-point register set, in elf_fpregset_t format
+     3 --- the extended floating-point register set, in struct
+           user_xfpregs_struct format
+
+   REG_ADDR isn't used on Linux.  */
+
+static void
+fetch_core_registers (char *core_reg_sect, unsigned core_reg_size,
+                     int which, CORE_ADDR reg_addr)
+{
+  elf_gregset_t gregset;
+  elf_fpregset_t fpregset;
+
+  switch (which)
+    {
+    case 0:
+      if (core_reg_size != sizeof (gregset))
+       warning ("Wrong size gregset in core file.");
+      else
+       {
+         memcpy (&gregset, core_reg_sect, sizeof (gregset));
+         supply_gregset (&gregset);
+       }
+      break;
+
+    case 2:
+      if (core_reg_size != sizeof (fpregset))
+       warning ("Wrong size fpregset in core file.");
+      else
+       {
+         memcpy (&fpregset, core_reg_sect, sizeof (fpregset));
+         supply_fpregset (&fpregset);
+       }
+      break;
+
+#ifdef HAVE_PTRACE_GETXFPREGS
+      {
+       struct user_xfpregs_struct xfpregset;
+
+      case 3:
+       if (core_reg_size != sizeof (xfpregset))
+         warning ("Wrong size user_xfpregs_struct in core file.");
+       else
+         {
+           memcpy (&xfpregset, core_reg_sect, sizeof (xfpregset));
+           supply_xfpregset (&xfpregset);
+         }
+       break;
+      }
+#endif
+
+    default:
+      /* We've covered all the kinds of registers we know about here,
+         so this must be something we wouldn't know what to do with
+         anyway.  Just ignore it.  */
+      break;
+    }
+}
+
+\f
+/* The instruction for a Linux system call is:
+       int $0x80
+   or 0xcd 0x80.  */
+
+static const unsigned char linux_syscall[] = { 0xcd, 0x80 };
+
+#define LINUX_SYSCALL_LEN (sizeof linux_syscall)
+
+/* The system call number is stored in the %eax register.  */
+#define LINUX_SYSCALL_REGNUM 0 /* %eax */
+
+/* We are specifically interested in the sigreturn and rt_sigreturn
+   system calls.  */
+
+#ifndef SYS_sigreturn
+#define SYS_sigreturn          0x77
+#endif
+#ifndef SYS_rt_sigreturn
+#define SYS_rt_sigreturn       0xad
+#endif
+
+/* Offset to saved processor flags, from <asm/sigcontext.h>.  */
+#define LINUX_SIGCONTEXT_EFLAGS_OFFSET (64)
+
+/* Resume execution of the inferior process.
+   If STEP is nonzero, single-step it.
+   If SIGNAL is nonzero, give it that signal.  */
 
 void
-store_inferior_registers (regno)
-     int regno;
+child_resume (int pid, int step, enum target_signal signal)
 {
-  if (regno < NUM_GREGS || regno == -1)
-    store_regs ();
+  int request = PTRACE_CONT;
+
+  if (pid == -1)
+    /* Resume all threads.  */
+    /* I think this only gets used in the non-threaded case, where "resume
+       all threads" and "resume inferior_pid" are the same.  */
+    pid = inferior_pid;
+
+  if (step)
+    {
+      CORE_ADDR pc = read_pc_pid (pid);
+      unsigned char buf[LINUX_SYSCALL_LEN];
+
+      request = PTRACE_SINGLESTEP;
+
+      /* Returning from a signal trampoline is done by calling a
+         special system call (sigreturn or rt_sigreturn, see
+         i386-linux-tdep.c for more information).  This system call
+         restores the registers that were saved when the signal was
+         raised, including %eflags.  That means that single-stepping
+         won't work.  Instead, we'll have to modify the signal context
+         that's about to be restored, and set the trace flag there.  */
+
+      /* First check if PC is at a system call.  */
+      if (read_memory_nobpt (pc, (char *) buf, LINUX_SYSCALL_LEN) == 0
+         && memcmp (buf, linux_syscall, LINUX_SYSCALL_LEN) == 0)
+       {
+         int syscall = read_register_pid (LINUX_SYSCALL_REGNUM, pid);
+
+         /* Then check the system call number.  */
+         if (syscall == SYS_sigreturn || syscall == SYS_rt_sigreturn)
+           {
+             CORE_ADDR sp = read_register (SP_REGNUM);
+             CORE_ADDR addr = sp;
+             unsigned long int eflags;
+             
+             if (syscall == SYS_rt_sigreturn)
+               addr = read_memory_integer (sp + 8, 4) + 20;
+
+             /* Set the trace flag in the context that's about to be
+                 restored.  */
+             addr += LINUX_SIGCONTEXT_EFLAGS_OFFSET;
+             read_memory (addr, (char *) &eflags, 4);
+             eflags |= 0x0100;
+             write_memory (addr, (char *) &eflags, 4);
+           }
+       }
+    }
 
-  if (regno >= NUM_GREGS || regno == -1)
-    store_fpregs ();
+  if (ptrace (request, pid, 0, target_signal_to_host (signal)) == -1)
+    perror_with_name ("ptrace");
 }
 
+\f
+/* Calling functions in shared libraries.  */
+/* FIXME: kettenis/2000-03-05: Doesn't this belong in a
+   target-dependent file?  The function
+   `i386_linux_skip_solib_resolver' is mentioned in
+   `config/i386/tm-linux.h'.  */
 
 /* Find the minimal symbol named NAME, and return both the minsym
    struct and its objfile.  This probably ought to be in minsym.c, but
@@ -386,7 +1108,6 @@ skip_hurd_resolver (CORE_ADDR pc)
   return 0;
 }      
 
-
 /* See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c.
    This function:
    1) decides whether a PLT has sent us into the linker to resolve
@@ -406,3 +1127,21 @@ i386_linux_skip_solib_resolver (CORE_ADDR pc)
 
   return 0;
 }
+
+\f
+/* Register that we are able to handle Linux ELF core file formats.  */
+
+static struct core_fns linux_elf_core_fns =
+{
+  bfd_target_elf_flavour,              /* core_flavour */
+  default_check_format,                        /* check_format */
+  default_core_sniffer,                        /* core_sniffer */
+  fetch_core_registers,                        /* core_read_registers */
+  NULL                                 /* next */
+};
+
+void
+_initialize_i386_linux_nat ()
+{
+  add_core_fns (&linux_elf_core_fns);
+}
This page took 0.034453 seconds and 4 git commands to generate.