Blob Blame History Raw
2001-05-02  Jakub Jelinek  <jakub@redhat.com>

	* libgcc2.c (__frame_state_for): Make it a macro on alpha.
	(next_stack_level): Turn into macro. Clear indirect
	field, abort() if it was set instead of looking up cfa indirectly.
	* frame-dwarf2.c (__frame_state_for): Only fill in base_offset
	and indirect fields if we detect we are called from gcc-2.96-54
	up to gcc-2.96-82's next_stack_level.
	* frame.h (__frame_state_for): Pass 3 arguments on alpha instead
	of two.

--- gcc/libgcc2.c.jj	Tue Jul 11 10:32:15 2000
+++ gcc/libgcc2.c	Mon Apr 30 21:04:20 2001
@@ -3696,28 +3696,31 @@ put_return_addr (void *val, frame_state 
 
 /* Given the current frame UDATA and its return address PC, return the
    information about the calling frame in CALLER_UDATA.  */
+#if defined(__linux__) && defined(__alpha__)
+#define __frame_state_for(x, y) (__frame_state_for)(x, y, 0)
+#endif
 
-static void *
-next_stack_level (void *pc, frame_state *udata, frame_state *caller_udata)
-{
-  caller_udata = __frame_state_for (pc, caller_udata);
-  if (! caller_udata)
-    return 0;
-
-  /* Now go back to our caller's stack frame.  If our caller's CFA register
-     was saved in our stack frame, restore it; otherwise, assume the CFA
-     register is SP and restore it to our CFA value.  */
-  if (udata->saved[caller_udata->cfa_reg])
-    caller_udata->cfa = get_reg (caller_udata->cfa_reg, udata, 0);
-  else
-    caller_udata->cfa = udata->cfa;
-  if (caller_udata->indirect)
-    caller_udata->cfa = * (void **) ((unsigned char *)caller_udata->cfa 
-				     + caller_udata->base_offset);
-  caller_udata->cfa += caller_udata->cfa_offset;
-
-  return caller_udata;
-}
+#define next_stack_level(pc, udata, caller_udata)				\
+({										\
+  frame_state *ret;								\
+										\
+  caller_udata->indirect = 0;							\
+  ret = __frame_state_for (pc, caller_udata);					\
+  if (ret)									\
+    {										\
+      /* Now go back to our caller's stack frame.  If our caller's CFA register	\
+	 was saved in our stack frame, restore it; otherwise, assume the CFA	\
+	 register is SP and restore it to our CFA value.  */			\
+      if (udata->saved[ret->cfa_reg])						\
+	ret->cfa = get_reg (ret->cfa_reg, udata, 0);				\
+      else									\
+	ret->cfa = udata->cfa;							\
+      if (ret->indirect)							\
+	abort ();								\
+      ret->cfa += ret->cfa_offset;						\
+    }										\
+  ret;										\
+})
 
 /* Hook to call before __terminate if only cleanup handlers remain. */
 void 
--- gcc/frame-dwarf2.c.jj	Sun Jul  2 10:37:05 2000
+++ gcc/frame-dwarf2.c	Mon Apr 30 18:40:54 2001
@@ -733,12 +733,33 @@ execute_cfa_insn (void *p, struct frame_
    PC_TARGET.  The caller should allocate a local variable of `struct
    frame_state' (declared in frame.h) and pass its address to STATE_IN.  */
 
+#if defined(__linux__) && defined(__sparc__) && !defined(__arch64__)
+/* Grab some info from parent register window so that we don't
+   have to flush register windows and look it up on the stack.  */
+asm ("	.text					\n\
+	.globl __frame_state_for		\n\
+	.type __frame_state_for, #function	\n\
+__frame_state_for:				\n\
+	mov	%fp, %o2			\n\
+	b	___frame_state_for		\n\
+	 mov	%i0, %o3			\n\
+     ");
+
+static struct frame_state *
+___frame_state_for (void *pc_target, struct frame_state *state_in, void *pfp,
+		    int i0)
+#elif defined(__linux__) && defined(__alpha__)
+struct frame_state *
+__frame_state_for (void *pc_target, struct frame_state *state_in, long a2)
+#else
 struct frame_state *
 __frame_state_for (void *pc_target, struct frame_state *state_in)
+#endif
 {
   fde *f;
   void *insn, *end, *pc;
   struct cie_info info;
+  size_t s;
   struct frame_state_internal state;
 
   f = find_fde (pc_target);
@@ -773,7 +794,108 @@ __frame_state_for (void *pc_target, stru
   while (insn < end && pc <= pc_target)
     insn = execute_cfa_insn (insn, &state, &info, &pc);
 
-  memcpy (state_in, &state.s, sizeof (state.s));
+#ifdef __linux__
+  /* Binary compatibility problem: In June 2000, 2 fields
+     were added at the end of struct frame_state. If for some reason
+     __throw (or __rethrow) comes from binary/shared lib compiled
+     with egcs 1.x.y or gcc-2.95.x and __frame_state_for comes from
+     glibc compiled with gcc-2.96-RH up (gcc-3_0-branch in Apr 2001
+     works that way still), then we can overflow __throw's stack.
+     To fix this, we try to find out who calls us.
+     __frame_state_for is called by next_stack_level and __throw/__rethrow.
+     Of these, __throw/__rethrow does not care about indirect/base_offset
+     fields and next_stack_level can be found out because that's the only
+     routine which where state_in does not point into its stackframe.  */
+  s = (size_t) &((struct frame_state *)0)->base_offset;
+# ifdef __i386__
+  {
+    unsigned long pbp, bp;
+    unsigned char *ppc;
+
+    asm ("movl (%%ebp), %0; movl 4(%%ebp), %1; movl %%ebp, %2"
+	 : "=r" (pbp), "=r" (ppc), "=r" (bp));
+    if (pbp < (unsigned long)state_in && pbp - bp == 0x40)
+      /* We've been called from next_stack_level function.
+	 All gcc-2.96-RH rpms shipped by Red Hat satisfy pbp - bp == 64,
+	 none egcs 1.x.y Red Hat shipped do.  */
+      {
+	unsigned char *end;
+	int indirect = 0;
+
+	/* Skim next_stack_level to see if it compares 0x74(base),0
+	   and reads from 0x70(base).  Stop at ret instruction.  */
+	for (end = ppc + 512; ppc < end && *ppc != 0xc3 && indirect < 2;
+	     ppc++)
+	  {
+	    if (*ppc == 0x74 && ppc[1] == 0)
+	      indirect++;
+	    else if (*ppc == 0x70)
+	      indirect++;
+	  }
+	if (indirect == 2)
+	  s = sizeof (state.s);
+      }
+  }
+# elif defined(__sparc__) && !defined(__arch64__)
+  if (pfp < (unsigned long)state_in && i0 == 0)
+    /* We've been called from next_stack_level function.
+       All gcc-2.96-RH rpms shipped by Red Hat clear %i0 in next_stack_level
+       before calling __frame_state_for, all egcs 1.x.y just copy
+       %i0 into %o0, so it is guaranteed to be non-NULL.  */
+    {
+      unsigned int *ppc, *end;
+      int indirect = 0;
+
+      ppc = (unsigned int *) __builtin_return_address (0);
+
+      /* Skim next_stack_level to see if it does a ld?b [? + 0x218]
+	 and ld?w [? + 0x214]. Stop at ret instruction.  */
+      for (end = ppc + 128; ppc < end && indirect < 2
+	   && (*ppc | 0x00080000) != 0x81cfe008; ppc++)
+	switch (*ppc & 0xc1b83fff)
+	  {
+	    case 0xc0082218: /* ld?b [? + 0x218], ? */
+	    case 0xc0002214: /* ld?w [? + 0x214], ? */
+	      indirect++;
+	      break;
+	  }
+	if (indirect == 2)
+	  s = sizeof (state.s);
+    }
+# elif defined(__alpha__)
+  if ((long)state_in == a2)
+    /* We've been called most probably from next_stack_level (it has 3 arguments
+       and passes its third argument as second argument to __frame_state_for).  */
+    {
+      unsigned int *ppc, *end;
+      int indirect = 0;
+
+      ppc = (unsigned int *) __builtin_return_address (0);
+
+      /* Skim next_stack_level to see if it does a ldq ?,624(?)
+	 and ldl ?,632(?) resp. ldbu ?,632(?). Stop at ret instruction.  */
+      for (end = ppc + 128; ppc < end && indirect < 2
+	   && *ppc != 0x6bfa8001; ppc++)
+	switch (*ppc & 0xfc00ffff)
+	  {
+	    case 0xa4000270: /* ldq ?,624(?) */
+	    case 0xa0000278: /* ldl ?,632(?) */
+	    case 0xa8000278: /* ldbu ?,632(?) */
+	      indirect++;
+	      break;
+	  }
+	if (indirect == 2)
+	  s = sizeof (state.s);
+    }
+# else
+  s = sizeof (state.s);
+# endif
+#else
+  s = sizeof (state.s);
+#endif
+  if (state.s.indirect && s < sizeof (state.s))
+    abort ();
+  memcpy (state_in, &state.s, s);
   return state_in;
 }
 #endif /* DWARF2_UNWIND_INFO */
--- gcc/frame.h.jj	Fri Jul  7 21:39:58 2000
+++ gcc/frame.h	Mon Apr 30 17:18:31 2001
@@ -94,7 +94,11 @@ extern void *__deregister_frame_info (vo
    frame_state' (declared in frame.h) and pass its address to STATE_IN.
    Returns NULL on failure, otherwise returns STATE_IN.  */
 
+#if defined(__linux__) && defined(__alpha__)
+extern struct frame_state *__frame_state_for (void *, struct frame_state *, long);
+#else
 extern struct frame_state *__frame_state_for (void *, struct frame_state *);
+#endif
 
 #ifdef IA64_UNWIND_INFO