2000-04-30 Mark Kettenis <kettenis@gnu.org>
[deliverable/binutils-gdb.git] / gdb / i386-linux-nat.c
index bf5e821a27936e9a22f882006b1b0f702d4eac61..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"
 
@@ -950,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
@@ -1043,263 +1128,6 @@ i386_linux_skip_solib_resolver (CORE_ADDR pc)
   return 0;
 }
 
-\f
-/* Recognizing signal handler frames.  */
-
-/* Linux has two flavors of signals.  Normal signal handlers, and
-   "realtime" (RT) signals.  The RT signals can provide additional
-   information to the signal handler if the SA_SIGINFO flag is set
-   when establishing a signal handler using `sigaction'.  It is not
-   unlikely that future versions of Linux will support SA_SIGINFO for
-   normal signals too.  */
-
-/* When the i386 Linux kernel calls a signal handler and the
-   SA_RESTORER flag isn't set, the return address points to a bit of
-   code on the stack.  This function returns whether the PC appears to
-   be within this bit of code.
-
-   The instruction sequence for normal signals is
-       pop    %eax
-       mov    $0x77,%eax
-       int    $0x80
-   or 0x58 0xb8 0x77 0x00 0x00 0x00 0xcd 0x80.
-
-   Checking for the code sequence should be somewhat reliable, because
-   the effect is to call the system call sigreturn.  This is unlikely
-   to occur anywhere other than a signal trampoline.
-
-   It kind of sucks that we have to read memory from the process in
-   order to identify a signal trampoline, but there doesn't seem to be
-   any other way.  The IN_SIGTRAMP macro in tm-linux.h arranges to
-   only call us if no function name could be identified, which should
-   be the case since the code is on the stack.
-
-   Detection of signal trampolines for handlers that set the
-   SA_RESTORER flag is in general not possible.  Unfortunately this is
-   what the GNU C Library has been doing for quite some time now.
-   However, as of version 2.1.2, the GNU C Library uses signal
-   trampolines (named __restore and __restore_rt) that are identical
-   to the ones used by the kernel.  Therefore, these trampolines are
-   supported too.  */
-
-#define LINUX_SIGTRAMP_INSN0 (0x58)    /* pop %eax */
-#define LINUX_SIGTRAMP_OFFSET0 (0)
-#define LINUX_SIGTRAMP_INSN1 (0xb8)    /* mov $NNNN,%eax */
-#define LINUX_SIGTRAMP_OFFSET1 (1)
-#define LINUX_SIGTRAMP_INSN2 (0xcd)    /* int */
-#define LINUX_SIGTRAMP_OFFSET2 (6)
-
-static const unsigned char linux_sigtramp_code[] =
-{
-  LINUX_SIGTRAMP_INSN0,                                        /* pop %eax */
-  LINUX_SIGTRAMP_INSN1, 0x77, 0x00, 0x00, 0x00,                /* mov $0x77,%eax */
-  LINUX_SIGTRAMP_INSN2, 0x80                           /* int $0x80 */
-};
-
-#define LINUX_SIGTRAMP_LEN (sizeof linux_sigtramp_code)
-
-/* If PC is in a sigtramp routine, return the address of the start of
-   the routine.  Otherwise, return 0.  */
-
-static CORE_ADDR
-i386_linux_sigtramp_start (CORE_ADDR pc)
-{
-  unsigned char buf[LINUX_SIGTRAMP_LEN];
-
-  /* We only recognize a signal trampoline if PC is at the start of
-     one of the three instructions.  We optimize for finding the PC at
-     the start, as will be the case when the trampoline is not the
-     first frame on the stack.  We assume that in the case where the
-     PC is not at the start of the instruction sequence, there will be
-     a few trailing readable bytes on the stack.  */
-
-  if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0)
-    return 0;
-
-  if (buf[0] != LINUX_SIGTRAMP_INSN0)
-    {
-      int adjust;
-
-      switch (buf[0])
-       {
-       case LINUX_SIGTRAMP_INSN1:
-         adjust = LINUX_SIGTRAMP_OFFSET1;
-         break;
-       case LINUX_SIGTRAMP_INSN2:
-         adjust = LINUX_SIGTRAMP_OFFSET2;
-         break;
-       default:
-         return 0;
-       }
-
-      pc -= adjust;
-
-      if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0)
-       return 0;
-    }
-
-  if (memcmp (buf, linux_sigtramp_code, LINUX_SIGTRAMP_LEN) != 0)
-    return 0;
-
-  return pc;
-}
-
-/* This function does the same for RT signals.  Here the instruction
-   sequence is
-       mov    $0xad,%eax
-       int    $0x80
-   or 0xb8 0xad 0x00 0x00 0x00 0xcd 0x80.
-
-   The effect is to call the system call rt_sigreturn.  */
-
-#define LINUX_RT_SIGTRAMP_INSN0 (0xb8) /* mov $NNNN,%eax */
-#define LINUX_RT_SIGTRAMP_OFFSET0 (0)
-#define LINUX_RT_SIGTRAMP_INSN1 (0xcd) /* int */
-#define LINUX_RT_SIGTRAMP_OFFSET1 (5)
-
-static const unsigned char linux_rt_sigtramp_code[] =
-{
-  LINUX_RT_SIGTRAMP_INSN0, 0xad, 0x00, 0x00, 0x00,     /* mov $0xad,%eax */
-  LINUX_RT_SIGTRAMP_INSN1, 0x80                                /* int $0x80 */
-};
-
-#define LINUX_RT_SIGTRAMP_LEN (sizeof linux_rt_sigtramp_code)
-
-/* If PC is in a RT sigtramp routine, return the address of the start
-   of the routine.  Otherwise, return 0.  */
-
-static CORE_ADDR
-i386_linux_rt_sigtramp_start (CORE_ADDR pc)
-{
-  unsigned char buf[LINUX_RT_SIGTRAMP_LEN];
-
-  /* We only recognize a signal trampoline if PC is at the start of
-     one of the two instructions.  We optimize for finding the PC at
-     the start, as will be the case when the trampoline is not the
-     first frame on the stack.  We assume that in the case where the
-     PC is not at the start of the instruction sequence, there will be
-     a few trailing readable bytes on the stack.  */
-
-  if (read_memory_nobpt (pc, (char *) buf, LINUX_RT_SIGTRAMP_LEN) != 0)
-    return 0;
-
-  if (buf[0] != LINUX_RT_SIGTRAMP_INSN0)
-    {
-      if (buf[0] != LINUX_RT_SIGTRAMP_INSN1)
-       return 0;
-
-      pc -= LINUX_RT_SIGTRAMP_OFFSET1;
-
-      if (read_memory_nobpt (pc, (char *) buf, LINUX_RT_SIGTRAMP_LEN) != 0)
-       return 0;
-    }
-
-  if (memcmp (buf, linux_rt_sigtramp_code, LINUX_RT_SIGTRAMP_LEN) != 0)
-    return 0;
-
-  return pc;
-}
-
-/* Return whether PC is in a Linux sigtramp routine.  */
-
-int
-i386_linux_in_sigtramp (CORE_ADDR pc, char *name)
-{
-  if (name)
-    return STREQ ("__restore", name) || STREQ ("__restore_rt", name);
-  
-  return (i386_linux_sigtramp_start (pc) != 0
-         || i386_linux_rt_sigtramp_start (pc) != 0);
-}
-
-/* Assuming FRAME is for a Linux sigtramp routine, return the address
-   of the associated sigcontext structure.  */
-
-CORE_ADDR
-i386_linux_sigcontext_addr (struct frame_info *frame)
-{
-  CORE_ADDR pc;
-
-  pc = i386_linux_sigtramp_start (frame->pc);
-  if (pc)
-    {
-      CORE_ADDR sp;
-
-      if (frame->next)
-       /* If this isn't the top frame, the next frame must be for the
-          signal handler itself.  The sigcontext structure lives on
-          the stack, right after the signum argument.  */
-       return frame->next->frame + 12;
-
-      /* This is the top frame.  We'll have to find the address of the
-        sigcontext structure by looking at the stack pointer.  Keep
-        in mind that the first instruction of the sigtramp code is
-        "pop %eax".  If the PC is at this instruction, adjust the
-        returned value accordingly.  */
-      sp = read_register (SP_REGNUM);
-      if (pc == frame->pc)
-       return sp + 4;
-      return sp;
-    }
-
-  pc = i386_linux_rt_sigtramp_start (frame->pc);
-  if (pc)
-    {
-      if (frame->next)
-       /* If this isn't the top frame, the next frame must be for the
-          signal handler itself.  The sigcontext structure is part of
-          the user context.  A pointer to the user context is passed
-          as the third argument to the signal handler.  */
-       return read_memory_integer (frame->next->frame + 16, 4) + 20;
-
-      /* This is the top frame.  Again, use the stack pointer to find
-        the address of the sigcontext structure.  */
-      return read_memory_integer (read_register (SP_REGNUM) + 8, 4) + 20;
-    }
-
-  error ("Couldn't recognize signal trampoline.");
-  return 0;
-}
-
-/* Offset to saved PC in sigcontext, from <asm/sigcontext.h>.  */
-#define LINUX_SIGCONTEXT_PC_OFFSET (56)
-
-/* Assuming FRAME is for a Linux sigtramp routine, return the saved
-   program counter.  */
-
-CORE_ADDR
-i386_linux_sigtramp_saved_pc (struct frame_info *frame)
-{
-  CORE_ADDR addr;
-  addr = i386_linux_sigcontext_addr (frame);
-  return read_memory_integer (addr + LINUX_SIGCONTEXT_PC_OFFSET, 4);
-}
-
-/* Offset to saved SP in sigcontext, from <asm/sigcontext.h>.  */
-#define LINUX_SIGCONTEXT_SP_OFFSET (28)
-
-/* Assuming FRAME is for a Linux sigtramp routine, return the saved
-   stack pointer.  */
-
-CORE_ADDR
-i386_linux_sigtramp_saved_sp (struct frame_info *frame)
-{
-  CORE_ADDR addr;
-  addr = i386_linux_sigcontext_addr (frame);
-  return read_memory_integer (addr + LINUX_SIGCONTEXT_SP_OFFSET, 4);
-}
-
-/* Immediately after a function call, return the saved pc.  */
-
-CORE_ADDR
-i386_linux_saved_pc_after_call (struct frame_info *frame)
-{
-  if (frame->signal_handler_caller)
-    return i386_linux_sigtramp_saved_pc (frame);
-
-  return read_memory_integer (read_register (SP_REGNUM), 4);
-}
-
 \f
 /* Register that we are able to handle Linux ELF core file formats.  */
 
This page took 0.026265 seconds and 4 git commands to generate.