2000-04-30 Mark Kettenis <kettenis@gnu.org>
[deliverable/binutils-gdb.git] / gdb / i386-linux-nat.c
index 88b6ba22157eba9a2fa36d55b24ebe9a9047805e..2bfac295efe35688effb8c27882224a3c164a534 100644 (file)
@@ -1,4 +1,5 @@
 /* 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.
 
@@ -23,7 +24,6 @@
 
 /* For i386_linux_skip_solib_resolver.  */
 #include "symtab.h"
-#include "frame.h"
 #include "symfile.h"
 #include "objfiles.h"
 
@@ -79,6 +79,15 @@ static int regmap[] =
 #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.
@@ -102,6 +111,159 @@ int have_ptrace_getxfpregs =
 #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). */
+
+void
+old_fetch_inferior_registers (regno)
+     int regno;
+{
+  if (regno >= 0)
+    {
+      fetch_register (regno);
+    }
+  else
+    {
+      for (regno = 0; regno < ARCH_NUM_REGS; regno++)
+       {
+         fetch_register (regno);
+       }
+    }
+}
+
+/* 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))
+    {
+      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);
+       }
+    }
+}
+
+/* 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
+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.  */
@@ -158,6 +320,8 @@ fill_gregset (elf_gregset_t *gregsetp, int regno)
     }
 }
 
+#ifdef HAVE_PTRACE_GETREGS
+
 /* Fetch all general-purpose registers from process/thread TID and
    store their values in GDB's register array.  */
 
@@ -170,6 +334,14 @@ fetch_regs (int tid)
   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;
     }
@@ -203,6 +375,13 @@ store_regs (int tid)
     }
 }
 
+#else
+
+static void fetch_regs (int tid) {}
+static void store_regs (int tid) {}
+
+#endif
+
 \f
 /* Transfering floating-point registers between GDB, inferiors and cores.  */
 
@@ -216,28 +395,33 @@ void
 supply_fpregset (elf_fpregset_t *fpregsetp)
 {
   int reg;
+  long l;
 
   /* Supply the floating-point registers.  */
   for (reg = 0; reg < 8; reg++)
     supply_register (FP0_REGNUM + reg, FPREG_ADDR (fpregsetp, reg));
 
-  supply_register (FCTRL_REGNUM, (char *) &fpregsetp->cwd);
-  supply_register (FSTAT_REGNUM, (char *) &fpregsetp->swd);
-  supply_register (FTAG_REGNUM,  (char *) &fpregsetp->twd);
+  /* 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
@@ -257,19 +441,28 @@ convert_to_fpregset (elf_fpregset_t *fpregsetp, signed char *valid)
              &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
@@ -307,6 +500,8 @@ fill_fpregset (elf_fpregset_t *fpregsetp, int regno)
     }
 }
 
+#ifdef HAVE_PTRACE_GETREGS
+
 /* Fetch all floating-point registers from process/thread TID and store
    thier values in GDB's register array.  */
 
@@ -352,6 +547,13 @@ store_fpregs (int tid)
     }
 }
 
+#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.  */
 
@@ -561,6 +763,14 @@ fetch_inferior_registers (int regno)
 {
   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.  */
@@ -572,6 +782,14 @@ fetch_inferior_registers (int regno)
   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);
@@ -612,6 +830,14 @@ 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;
+    }
+
   /* Linux LWP ID's are process ID's.  */
   if ((tid = TIDGET (inferior_pid)) == 0)
     tid = inferior_pid;                /* Not a threaded program.  */
@@ -724,6 +950,91 @@ fetch_core_registers (char *core_reg_sect, unsigned core_reg_size,
     }
 }
 
+\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
+child_resume (int pid, int step, enum target_signal signal)
+{
+  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 (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
This page took 0.028778 seconds and 4 git commands to generate.