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