PARAMS elimination.
[deliverable/binutils-gdb.git] / gdb / i386-linux-nat.c
index 8b930077516a25029adb4637e8616121dea1e18a..6a3755891753d3ba232ac04464e7687916045137 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"
 
@@ -35,6 +35,9 @@
 #include <sys/reg.h>
 #endif
 
+/* Prototypes for supply_gregset etc. */
+#include "gregset.h"
+
 /* 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
@@ -112,6 +115,8 @@ int have_ptrace_getxfpregs =
 ;
 
 \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
@@ -121,6 +126,13 @@ int have_ptrace_getxfpregs =
    the GETREGS request.  I want to avoid changing `infptrace.c' right
    now.  */
 
+#ifndef PT_READ_U
+#define PT_READ_U PTRACE_PEEKUSR
+#endif
+#ifndef PT_WRITE_U
+#define PT_WRITE_U PTRACE_POKEUSR
+#endif
+
 /* Default the type of the ptrace transfer to int.  */
 #ifndef PTRACE_XFER_TYPE
 #define PTRACE_XFER_TYPE int
@@ -393,28 +405,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
@@ -434,19 +451,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
@@ -934,6 +960,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.03027 seconds and 4 git commands to generate.