/* DWARF2 EH unwinding support for PowerPC and PowerPC64 Linux.
- Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
This file is part of GCC.
these structs elsewhere; Many fields are missing, particularly
from the end of the structures. */
+#define R_LR 65
+#define R_CR2 70
+#define R_VR0 77
+#define R_VRSAVE 109
+#define R_VSCR 110
+
struct gcc_vregs
{
__attribute__ ((vector_size (16))) int vr[32];
enum { SIGNAL_FRAMESIZE = 128 };
-/* If the current unwind info (FS) does not contain explicit info
- saving R2, then we have to do a minor amount of code reading to
- figure out if it was saved. The big problem here is that the
- code that does the save/restore is generated by the linker, so
- we have no good way to determine at compile time what to do. */
-
-#define MD_FROB_UPDATE_CONTEXT frob_update_context
-
-static void
-frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
-{
- if (fs->regs.reg[2].how == REG_UNSAVED)
- {
- unsigned int *insn
- = (unsigned int *) _Unwind_GetGR (context, LINK_REGISTER_REGNUM);
- if (*insn == 0xE8410028)
- _Unwind_SetGRPtr (context, 2, context->cfa + 40);
- }
-}
-
/* If PC is at a sigreturn trampoline, return a pointer to the
regs. Otherwise return NULL. */
return _URC_END_OF_STACK;
new_cfa = regs->gpr[STACK_POINTER_REGNUM];
- fs->cfa_how = CFA_REG_OFFSET;
- fs->cfa_reg = STACK_POINTER_REGNUM;
- fs->cfa_offset = new_cfa - (long) context->cfa;
+ fs->regs.cfa_how = CFA_REG_OFFSET;
+ fs->regs.cfa_reg = STACK_POINTER_REGNUM;
+ fs->regs.cfa_offset = new_cfa - (long) context->cfa;
for (i = 0; i < 32; i++)
if (i != STACK_POINTER_REGNUM)
fs->regs.reg[i].loc.offset = (long) ®s->gpr[i] - new_cfa;
}
- fs->regs.reg[CR2_REGNO].how = REG_SAVED_OFFSET;
- fs->regs.reg[CR2_REGNO].loc.offset = (long) ®s->ccr - new_cfa;
+ fs->regs.reg[R_CR2].how = REG_SAVED_OFFSET;
+ fs->regs.reg[R_CR2].loc.offset = (long) ®s->ccr - new_cfa;
- fs->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET;
- fs->regs.reg[LINK_REGISTER_REGNUM].loc.offset = (long) ®s->link - new_cfa;
+ fs->regs.reg[R_LR].how = REG_SAVED_OFFSET;
+ fs->regs.reg[R_LR].loc.offset = (long) ®s->link - new_cfa;
fs->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET;
fs->regs.reg[ARG_POINTER_REGNUM].loc.offset = (long) ®s->nip - new_cfa;
fs->retaddr_column = ARG_POINTER_REGNUM;
+ fs->signal_frame = 1;
if (hwcap == 0)
{
hwcap = ppc_linux_aux_vector (16);
- /* These will already be set if we found AT_HWCAP. A non-zero
+ /* These will already be set if we found AT_HWCAP. A nonzero
value stops us looking again if for some reason we couldn't
find AT_HWCAP. */
#ifdef __powerpc64__
{
for (i = 0; i < 32; i++)
{
- fs->regs.reg[i + FIRST_ALTIVEC_REGNO].how = REG_SAVED_OFFSET;
- fs->regs.reg[i + FIRST_ALTIVEC_REGNO].loc.offset
+ fs->regs.reg[i + R_VR0].how = REG_SAVED_OFFSET;
+ fs->regs.reg[i + R_VR0].loc.offset
= (long) &vregs[i] - new_cfa;
}
- fs->regs.reg[VSCR_REGNO].how = REG_SAVED_OFFSET;
- fs->regs.reg[VSCR_REGNO].loc.offset = (long) &vregs->vscr - new_cfa;
+ fs->regs.reg[R_VSCR].how = REG_SAVED_OFFSET;
+ fs->regs.reg[R_VSCR].loc.offset = (long) &vregs->vscr - new_cfa;
}
- fs->regs.reg[VRSAVE_REGNO].how = REG_SAVED_OFFSET;
- fs->regs.reg[VRSAVE_REGNO].loc.offset = (long) &vregs->vsave - new_cfa;
+ fs->regs.reg[R_VRSAVE].how = REG_SAVED_OFFSET;
+ fs->regs.reg[R_VRSAVE].loc.offset = (long) &vregs->vsave - new_cfa;
}
+ /* If we have SPE register high-parts... we check at compile-time to
+ avoid expanding the code for all other PowerPC. */
+#ifdef __SPE__
+ for (i = 0; i < 32; i++)
+ {
+ fs->regs.reg[i + FIRST_PSEUDO_REGISTER - 1].how = REG_SAVED_OFFSET;
+ fs->regs.reg[i + FIRST_PSEUDO_REGISTER - 1].loc.offset
+ = (long) ®s->vregs - new_cfa + 4 * i;
+ }
+#endif
+
return _URC_NO_REASON;
}
+
+#define MD_FROB_UPDATE_CONTEXT frob_update_context
+
+static void
+frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs ATTRIBUTE_UNUSED)
+{
+ const unsigned int *pc = (const unsigned int *) context->ra;
+
+ /* Fix up for 2.6.12 - 2.6.16 Linux kernels that have vDSO, but don't
+ have S flag in it. */
+#ifdef __powerpc64__
+ /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */
+ /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */
+ if (pc[0] == 0x38210000 + SIGNAL_FRAMESIZE
+ && (pc[1] == 0x38000077 || pc[1] == 0x380000AC)
+ && pc[2] == 0x44000002)
+ _Unwind_SetSignalFrame (context, 1);
+#else
+ /* li r0, 0x7777; sc (sigreturn old) */
+ /* li r0, 0x0077; sc (sigreturn new) */
+ /* li r0, 0x6666; sc (rt_sigreturn old) */
+ /* li r0, 0x00AC; sc (rt_sigreturn new) */
+ if ((pc[0] == 0x38007777 || pc[0] == 0x38000077
+ || pc[0] == 0x38006666 || pc[0] == 0x380000AC)
+ && pc[1] == 0x44000002)
+ _Unwind_SetSignalFrame (context, 1);
+#endif
+
+#ifdef __powerpc64__
+ if (fs->regs.reg[2].how == REG_UNSAVED)
+ {
+ /* If the current unwind info (FS) does not contain explicit info
+ saving R2, then we have to do a minor amount of code reading to
+ figure out if it was saved. The big problem here is that the
+ code that does the save/restore is generated by the linker, so
+ we have no good way to determine at compile time what to do. */
+ unsigned int *insn
+ = (unsigned int *) _Unwind_GetGR (context, R_LR);
+ if (*insn == 0xE8410028)
+ _Unwind_SetGRPtr (context, 2, context->cfa + 40);
+ }
+#endif
+}