]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/alpha-tdep.c
Changes from: David Mosberger-Tang <davidm@azstarnet.com>
[thirdparty/binutils-gdb.git] / gdb / alpha-tdep.c
index 7fbf6422e7003d29ba806f6ad7299c87e45241c0..d778bf22e2915e9a94645f104f22f2c51fab615b 100644 (file)
@@ -131,6 +131,110 @@ struct linked_proc_info
   struct linked_proc_info *next;
 } *linked_proc_desc_table = NULL;
 
+\f
+/* Under Linux, signal handler invocations can be identified by the
+   designated code sequence that is used to return from a signal
+   handler.  In particular, the return address of a signal handler
+   points to the following sequence (the first instruction is quadword
+   aligned):
+
+       bis $30,$30,$16
+       addq $31,0x67,$0
+       call_pal callsys
+
+   Each instruction has a unique encoding, so we simply attempt to
+   match the instruction the pc is pointing to with any of the above
+   instructions.  If there is a hit, we know the offset to the start
+   of the designated sequence and can then check whether we really are
+   executing in a designated sequence.  If not, -1 is returned,
+   otherwise the offset from the start of the desingated sequence is
+   returned.
+
+   There is a slight chance of false hits: code could jump into the
+   middle of the designated sequence, in which case there is no
+   guarantee that we are in the middle of a sigreturn syscall.  Don't
+   think this will be a problem in praxis, though.
+*/
+long
+alpha_linux_sigtramp_offset (CORE_ADDR pc)
+{
+  unsigned int i[3], w;
+  long off, res;
+
+  if (read_memory_nobpt(pc, (char *) &w, 4) != 0)
+    return -1;
+
+  off = -1;
+  switch (w)
+    {
+    case 0x47de0410: off = 0; break;   /* bis $30,$30,$16 */
+    case 0x43ecf400: off = 4; break;   /* addq $31,0x67,$0 */
+    case 0x00000083: off = 8; break;   /* call_pal callsys */
+    default:        return -1;
+    }
+  pc -= off;
+  if (pc & 0x7)
+    {
+      /* designated sequence is not quadword aligned */
+      return -1;
+    }
+
+  if (read_memory_nobpt(pc, (char *) i, sizeof(i)) != 0)
+    return -1;
+
+  if (i[0] == 0x47de0410 && i[1] == 0x43ecf400 && i[2] == 0x00000083)
+    return off;
+
+  return -1;
+}
+
+\f
+/* Under OSF/1, the __sigtramp routine is frameless and has a frame
+   size of zero, but we are able to backtrace through it.  */
+CORE_ADDR
+alpha_osf_skip_sigtramp_frame (frame, pc)
+     struct frame_info *frame;
+     CORE_ADDR pc;
+{
+  char *name;
+  find_pc_partial_function (pc, &name, (CORE_ADDR *)NULL, (CORE_ADDR *)NULL);
+  if (IN_SIGTRAMP (pc, name))
+    return frame->frame;
+  else
+    return 0;
+}
+
+\f
+/* Dynamically create a signal-handler caller procedure descriptor for
+   the signal-handler return code starting at address LOW_ADDR.  The
+   descriptor is added to the linked_proc_desc_table.  */
+
+alpha_extra_func_info_t
+push_sigtramp_desc (CORE_ADDR low_addr)
+{
+  struct linked_proc_info *link;
+  alpha_extra_func_info_t proc_desc;
+
+  link = (struct linked_proc_info *)
+    xmalloc (sizeof (struct linked_proc_info));
+  link->next = linked_proc_desc_table;
+  linked_proc_desc_table = link;
+
+  proc_desc = &link->info;
+
+  proc_desc->numargs = 0;
+  PROC_LOW_ADDR (proc_desc)    = low_addr;
+  PROC_HIGH_ADDR (proc_desc)   = low_addr + 3 * 4;
+  PROC_DUMMY_FRAME (proc_desc) = 0;
+  PROC_FRAME_OFFSET (proc_desc)        = 0x298; /* sizeof(struct sigcontext_struct) */
+  PROC_FRAME_REG (proc_desc)   = SP_REGNUM;
+  PROC_REG_MASK (proc_desc)    = 0xffff;
+  PROC_FREG_MASK (proc_desc)   = 0xffff;
+  PROC_PC_REG (proc_desc)      = 26;
+  PROC_LOCALOFF (proc_desc)    = 0;
+  SET_PROC_DESC_IS_DYN_SIGTRAMP (proc_desc);
+}
+
 \f
 /* Guaranteed to set frame->saved_regs to some values (it never leaves it
    NULL).  */
@@ -161,14 +265,9 @@ alpha_find_saved_regs (frame)
 #endif
   if (frame->signal_handler_caller)
     {
-      CORE_ADDR sigcontext_pointer_addr;
       CORE_ADDR sigcontext_addr;
 
-      if (frame->next)
-       sigcontext_pointer_addr = frame->next->frame;
-      else
-       sigcontext_pointer_addr = frame->frame;
-      sigcontext_addr = read_memory_integer(sigcontext_pointer_addr, 8);
+      sigcontext_addr = SIGCONTEXT_ADDR (frame);
       for (ireg = 0; ireg < 32; ireg++)
        {
          reg_position = sigcontext_addr + SIGFRAME_REGSAVE_OFF + ireg * 8;
@@ -285,7 +384,10 @@ alpha_saved_pc_after_call (frame)
   proc_desc = find_proc_desc (pc, frame->next);
   pcreg = proc_desc ? PROC_PC_REG (proc_desc) : RA_REGNUM;
 
-  return read_register (pcreg);
+  if (frame->signal_handler_caller)
+    return alpha_frame_saved_pc (frame);
+  else
+    return read_register (pcreg);
 }
 
 
@@ -471,6 +573,9 @@ after_prologue (pc, proc_desc)
 
   if (proc_desc)
     {
+      if (PROC_DESC_IS_DYN_SIGTRAMP (proc_desc))
+       return PROC_LOW_ADDR (proc_desc);       /* "prologue" is in kernel */
+
       /* If function is frameless, then we need to do it the hard way.  I
         strongly suspect that frameless always means prologueless... */
       if (PROC_FRAME_REG (proc_desc) == SP_REGNUM
@@ -493,7 +598,7 @@ after_prologue (pc, proc_desc)
 }
 
 /* Return non-zero if we *might* be in a function prologue.  Return zero if we
-   are definatly *not* in a function prologue.  */
+   are definitively *not* in a function prologue.  */
 
 static int
 alpha_in_prologue (pc, proc_desc)
@@ -599,6 +704,8 @@ find_proc_desc (pc, next_frame)
     }
   else
     {
+      long offset;
+
       /* Is linked_proc_desc_table really necessary?  It only seems to be used
         by procedure call dummys.  However, the procedures being called ought
         to have their own proc_descs, and even if they don't,
@@ -610,6 +717,12 @@ find_proc_desc (pc, next_frame)
              && PROC_HIGH_ADDR(&link->info) > pc)
              return &link->info;
 
+      /* If PC is inside a dynamically generated sigtramp handler,
+        create and push a procedure descriptor for that code: */
+      offset = DYNAMIC_SIGTRAMP_OFFSET (pc);
+      if (offset >= 0)
+       return push_sigtramp_desc (pc - offset);
+
       if (startaddr == 0)
        startaddr = heuristic_proc_start (pc);
 
@@ -650,17 +763,7 @@ alpha_frame_chain(frame)
        /* The previous frame from a sigtramp frame might be frameless
           and have frame size zero.  */
        && !frame->signal_handler_caller)
-      {
-       /* The alpha __sigtramp routine is frameless and has a frame size
-          of zero, but we are able to backtrace through it. */
-       char *name;
-       find_pc_partial_function (saved_pc, &name,
-                                 (CORE_ADDR *)NULL, (CORE_ADDR *)NULL);
-       if (IN_SIGTRAMP (saved_pc, name))
-         return frame->frame;
-       else
-         return 0;
-      }
+      return FRAME_PAST_SIGTRAMP_FRAME (frame, saved_pc);
     else
       return read_next_frame_reg(frame, PROC_FRAME_REG(proc_desc))
             + PROC_FRAME_OFFSET(proc_desc);
@@ -696,7 +799,8 @@ init_extra_frame_info (frame)
       /* This may not be quite right, if proc has a real frame register.
         Get the value of the frame relative sp, procedure might have been
         interrupted by a signal at it's very start.  */
-      else if (frame->pc == PROC_LOW_ADDR (proc_desc) && !PROC_DESC_IS_DUMMY (proc_desc))
+      else if (frame->pc == PROC_LOW_ADDR (proc_desc)
+              && !PROC_DESC_IS_DYN_SIGTRAMP (proc_desc))
        frame->frame = read_next_frame_reg (frame->next, SP_REGNUM);
       else
        frame->frame = read_next_frame_reg (frame->next, PROC_FRAME_REG (proc_desc))
@@ -984,7 +1088,8 @@ alpha_pop_frame()
   write_register (SP_REGNUM, new_sp);
   flush_cached_frames ();
 
-  if (proc_desc && PROC_DESC_IS_DUMMY(proc_desc))
+  if (proc_desc && (PROC_DESC_IS_DUMMY(proc_desc)
+                   || PROC_DESC_IS_DYN_SIGTRAMP (proc_desc)))
     {
       struct linked_proc_info *pi_ptr, *prev_ptr;