struct gcc_vregs
{
__attribute__ ((vector_size (16))) int vr[32];
#ifdef __powerpc64__
unsigned int pad1[3];
unsigned int vscr;
unsigned int vsave;
unsigned int pad2[3];
#else
unsigned int vsave;
unsigned int pad[2];
unsigned int vscr;
#endif
};
struct gcc_regs
{
unsigned long gpr[32];
unsigned long nip;
unsigned long msr;
unsigned long orig_gpr3;
unsigned long ctr;
unsigned long link;
unsigned long xer;
unsigned long ccr;
unsigned long softe;
unsigned long trap;
unsigned long dar;
unsigned long dsisr;
unsigned long result;
unsigned long pad1[4];
double fpr[32];
unsigned int pad2;
unsigned int fpscr;
#ifdef __powerpc64__
struct gcc_vregs *vp;
#else
unsigned int pad3[2];
#endif
struct gcc_vregs vregs;
};
struct gcc_ucontext
{
#ifdef __powerpc64__
unsigned long pad[28];
#else
unsigned long pad[12];
#endif
struct gcc_regs *regs;
struct gcc_regs rsave;
};
#ifdef __powerpc64__
enum { SIGNAL_FRAMESIZE = 128 };
#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);
}
}
static struct gcc_regs *
get_regs (struct _Unwind_Context *context)
{
const unsigned char *pc = context->ra;
if (*(unsigned int *) (pc + 0) != 0x38210000 + SIGNAL_FRAMESIZE
|| *(unsigned int *) (pc + 8) != 0x44000002)
return NULL;
if (*(unsigned int *) (pc + 4) == 0x38000077)
{
struct sigframe {
char gap[SIGNAL_FRAMESIZE];
unsigned long pad[7];
struct gcc_regs *regs;
} *frame = (struct sigframe *) context->cfa;
return frame->regs;
}
else if (*(unsigned int *) (pc + 4) == 0x380000AC)
{
struct rt_sigframe_24 {
int tramp[6];
void *pinfo;
struct gcc_ucontext *puc;
} *frame24 = (struct rt_sigframe_24 *) pc;
if ((long) frame24->puc != -21 * 8)
return frame24->puc->regs;
else
{
struct rt_sigframe {
char gap[SIGNAL_FRAMESIZE];
struct gcc_ucontext uc;
unsigned long pad[2];
int tramp[6];
void *pinfo;
struct gcc_ucontext *puc;
} *frame = (struct rt_sigframe *) context->cfa;
return frame->uc.regs;
}
}
return NULL;
}
#else
enum { SIGNAL_FRAMESIZE = 64 };
static struct gcc_regs *
get_regs (struct _Unwind_Context *context)
{
const unsigned char *pc = context->ra;
if (*(unsigned int *) (pc + 4) != 0x44000002)
return NULL;
if (*(unsigned int *) (pc + 0) == 0x38007777
|| *(unsigned int *) (pc + 0) == 0x38000077)
{
struct sigframe {
char gap[SIGNAL_FRAMESIZE];
unsigned long pad[7];
struct gcc_regs *regs;
} *frame = (struct sigframe *) context->cfa;
return frame->regs;
}
else if (*(unsigned int *) (pc + 0) == 0x38006666
|| *(unsigned int *) (pc + 0) == 0x380000AC)
{
struct rt_sigframe {
char gap[SIGNAL_FRAMESIZE + 16];
char siginfo[128];
struct gcc_ucontext uc;
} *frame = (struct rt_sigframe *) context->cfa;
return frame->uc.regs;
}
return NULL;
}
#endif
static long
ppc_linux_aux_vector (long which)
{
extern long *__libc_stack_end;
long argc;
char **argv;
char **envp;
struct auxv
{
long a_type;
long a_val;
} *auxp;
argc = __libc_stack_end[0];
argv = (char **) __libc_stack_end + 1;
envp = argv + argc + 1;
while (*envp++)
continue;
for (auxp = (struct auxv *) envp; auxp->a_type != 0; ++auxp)
if (auxp->a_type == which)
return auxp->a_val;
return 0;
}
#define MD_FALLBACK_FRAME_STATE_FOR ppc_fallback_frame_state
static _Unwind_Reason_Code
ppc_fallback_frame_state (struct _Unwind_Context *context,
_Unwind_FrameState *fs)
{
static long hwcap = 0;
struct gcc_regs *regs = get_regs (context);
long new_cfa;
int i;
if (regs == 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;
for (i = 0; i < 32; i++)
if (i != STACK_POINTER_REGNUM)
{
fs->regs.reg[i].how = REG_SAVED_OFFSET;
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[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET;
fs->regs.reg[LINK_REGISTER_REGNUM].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;
if (hwcap == 0)
{
hwcap = ppc_linux_aux_vector (16);
#ifdef __powerpc64__
hwcap |= 0xc0000000;
#else
hwcap |= 0x80000000;
#endif
}
if (hwcap & 0x08000000)
for (i = 0; i < 32; i++)
{
fs->regs.reg[i + 32].how = REG_SAVED_OFFSET;
fs->regs.reg[i + 32].loc.offset = (long) ®s->fpr[i] - new_cfa;
}
if (hwcap & 0x10000000)
{
struct gcc_vregs *vregs;
#ifdef __powerpc64__
vregs = regs->vp;
#else
vregs = ®s->vregs;
#endif
if (regs->msr & (1 << 25))
{
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
= (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[VRSAVE_REGNO].how = REG_SAVED_OFFSET;
fs->regs.reg[VRSAVE_REGNO].loc.offset = (long) &vregs->vsave - new_cfa;
}
return _URC_NO_REASON;
}