Commit a7a52913 by Alan Modra Committed by Alan Modra

linux-unwind.h (struct gcc_vregs): New.

	* config/rs6000/linux-unwind.h (struct gcc_vregs): New.
	(struct gcc_regs): Rename from gcc_pt_regs.  Add more regs.
	(struct gcc_sigcontext): Delete.  Merge contents to..
	(struct gcc_ucontext): ..here.
	(get_sigcontext): Delete.
	(get_regs): New function, like get_sigcontext but return regs ptr.
	64-bit version finds regs from r1 to support vdso.
	(ppc_linux_aux_vector): New function.
	(ppc_fallback_frame_state): Modify for get_regs.  Restore fprs
	and vector regs.

From-SVN: r94481
parent 261ba0c8
2005-01-31 Alan Modra <amodra@bigpond.net.au>
* config/rs6000/linux-unwind.h (struct gcc_vregs): New.
(struct gcc_regs): Rename from gcc_pt_regs. Add more regs.
(struct gcc_sigcontext): Delete. Merge contents to..
(struct gcc_ucontext): ..here.
(get_sigcontext): Delete.
(get_regs): New function, like get_sigcontext but return regs ptr.
64-bit version finds regs from r1 to support vdso.
(ppc_linux_aux_vector): New function.
(ppc_fallback_frame_state): Modify for get_regs. Restore fprs
and vector regs.
2005-01-31 Ira Rosen <irar@il.ibm.com> 2005-01-31 Ira Rosen <irar@il.ibm.com>
* tree-vectorizer.c (vect_analyze_offset_expr): Use ssizetype instead * tree-vectorizer.c (vect_analyze_offset_expr): Use ssizetype instead
......
/* DWARF2 EH unwinding support for PowerPC and PowerPC64 Linux. /* DWARF2 EH unwinding support for PowerPC and PowerPC64 Linux.
Copyright (C) 2004 Free Software Foundation, Inc. Copyright (C) 2004, 2005 Free Software Foundation, Inc.
This file is part of GCC. This file is part of GCC.
...@@ -24,7 +24,22 @@ ...@@ -24,7 +24,22 @@
these structs elsewhere; Many fields are missing, particularly these structs elsewhere; Many fields are missing, particularly
from the end of the structures. */ from the end of the structures. */
struct gcc_pt_regs 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 gpr[32];
unsigned long nip; unsigned long nip;
...@@ -34,22 +49,32 @@ struct gcc_pt_regs ...@@ -34,22 +49,32 @@ struct gcc_pt_regs
unsigned long link; unsigned long link;
unsigned long xer; unsigned long xer;
unsigned long ccr; unsigned long ccr;
}; unsigned long softe;
unsigned long trap;
struct gcc_sigcontext unsigned long dar;
{ unsigned long dsisr;
unsigned long pad[7]; unsigned long result;
struct gcc_pt_regs *regs; 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 struct gcc_ucontext
{ {
#ifdef __powerpc64__ #ifdef __powerpc64__
unsigned long pad[21]; unsigned long pad[28];
#else #else
unsigned long pad[5]; unsigned long pad[12];
#endif #endif
struct gcc_sigcontext uc_mcontext; struct gcc_regs *regs;
struct gcc_regs rsave;
}; };
#ifdef __powerpc64__ #ifdef __powerpc64__
...@@ -77,34 +102,55 @@ frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs) ...@@ -77,34 +102,55 @@ frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
} }
/* If PC is at a sigreturn trampoline, return a pointer to the /* If PC is at a sigreturn trampoline, return a pointer to the
sigcontext. Otherwise return NULL. */ regs. Otherwise return NULL. */
static struct gcc_sigcontext * static struct gcc_regs *
get_sigcontext (struct _Unwind_Context *context) get_regs (struct _Unwind_Context *context)
{ {
const unsigned char *pc = context->ra; const unsigned char *pc = context->ra;
/* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */ /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */
/* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */ /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */
if (*(unsigned int *) (pc+0) != 0x38210000 + SIGNAL_FRAMESIZE if (*(unsigned int *) (pc + 0) != 0x38210000 + SIGNAL_FRAMESIZE
|| *(unsigned int *) (pc+8) != 0x44000002) || *(unsigned int *) (pc + 8) != 0x44000002)
return NULL; return NULL;
if (*(unsigned int *) (pc+4) == 0x38000077) if (*(unsigned int *) (pc + 4) == 0x38000077)
{ {
struct sigframe { struct sigframe {
char gap[SIGNAL_FRAMESIZE]; char gap[SIGNAL_FRAMESIZE];
struct gcc_sigcontext sigctx; unsigned long pad[7];
} *rt_ = context->cfa; struct gcc_regs *regs;
return &rt_->sigctx; } *frame = (struct sigframe *) context->cfa;
return frame->regs;
} }
else if (*(unsigned int *) (pc+4) == 0x380000AC) else if (*(unsigned int *) (pc + 4) == 0x380000AC)
{ {
struct rt_sigframe { /* This works for 2.4 kernels, but not for 2.6 kernels with vdso
because pc isn't pointing into the stack. Can be removed when
no one is running 2.4.19 or 2.4.20, the first two ppc64
kernels released. */
struct rt_sigframe_24 {
int tramp[6]; int tramp[6];
void *pinfo; void *pinfo;
struct gcc_ucontext *puc; struct gcc_ucontext *puc;
} *rt_ = (struct rt_sigframe *) pc; } *frame24 = (struct rt_sigframe_24 *) pc;
return &rt_->puc->uc_mcontext;
/* Test for magic value in *puc of vdso. */
if ((long) frame24->puc != -21 * 8)
return frame24->puc->regs;
else
{
/* This works for 2.4.21 and later kernels. */
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; return NULL;
} }
...@@ -113,8 +159,8 @@ get_sigcontext (struct _Unwind_Context *context) ...@@ -113,8 +159,8 @@ get_sigcontext (struct _Unwind_Context *context)
enum { SIGNAL_FRAMESIZE = 64 }; enum { SIGNAL_FRAMESIZE = 64 };
static struct gcc_sigcontext * static struct gcc_regs *
get_sigcontext (struct _Unwind_Context *context) get_regs (struct _Unwind_Context *context)
{ {
const unsigned char *pc = context->ra; const unsigned char *pc = context->ra;
...@@ -122,31 +168,64 @@ get_sigcontext (struct _Unwind_Context *context) ...@@ -122,31 +168,64 @@ get_sigcontext (struct _Unwind_Context *context)
/* li r0, 0x0077; sc (sigreturn new) */ /* li r0, 0x0077; sc (sigreturn new) */
/* li r0, 0x6666; sc (rt_sigreturn old) */ /* li r0, 0x6666; sc (rt_sigreturn old) */
/* li r0, 0x00AC; sc (rt_sigreturn new) */ /* li r0, 0x00AC; sc (rt_sigreturn new) */
if (*(unsigned int *) (pc+4) != 0x44000002) if (*(unsigned int *) (pc + 4) != 0x44000002)
return NULL; return NULL;
if (*(unsigned int *) (pc+0) == 0x38007777 if (*(unsigned int *) (pc + 0) == 0x38007777
|| *(unsigned int *) (pc+0) == 0x38000077) || *(unsigned int *) (pc + 0) == 0x38000077)
{ {
struct sigframe { struct sigframe {
char gap[SIGNAL_FRAMESIZE]; char gap[SIGNAL_FRAMESIZE];
struct gcc_sigcontext sigctx; unsigned long pad[7];
} *rt_ = context->cfa; struct gcc_regs *regs;
return &rt_->sigctx; } *frame = (struct sigframe *) context->cfa;
return frame->regs;
} }
else if (*(unsigned int *) (pc+0) == 0x38006666 else if (*(unsigned int *) (pc + 0) == 0x38006666
|| *(unsigned int *) (pc+0) == 0x380000AC) || *(unsigned int *) (pc + 0) == 0x380000AC)
{ {
struct rt_sigframe { struct rt_sigframe {
char gap[SIGNAL_FRAMESIZE + 16]; char gap[SIGNAL_FRAMESIZE + 16];
char siginfo[128]; char siginfo[128];
struct gcc_ucontext uc; struct gcc_ucontext uc;
} *rt_ = context->cfa; } *frame = (struct rt_sigframe *) context->cfa;
return &rt_->uc.uc_mcontext; return frame->uc.regs;
} }
return NULL; return NULL;
} }
#endif #endif
/* Find an entry in the process auxilliary vector. The canonical way to
test for VMX is to look at AT_HWCAP. */
static long
ppc_linux_aux_vector (long which)
{
/* __libc_stack_end holds the original stack passed to a process. */
extern long *__libc_stack_end;
long argc;
char **argv;
char **envp;
struct auxv
{
long a_type;
long a_val;
} *auxp;
/* The Linux kernel puts argc first on the stack. */
argc = __libc_stack_end[0];
/* Followed by argv, NULL terminated. */
argv = (char **) __libc_stack_end + 1;
/* Followed by environment string pointers, NULL terminated. */
envp = argv + argc + 1;
while (*envp++)
continue;
/* Followed by the aux vector, zero terminated. */
for (auxp = (struct auxv *) envp; auxp->a_type != 0; ++auxp)
if (auxp->a_type == which)
return auxp->a_val;
return 0;
}
/* Do code reading to identify a signal frame, and set the frame /* Do code reading to identify a signal frame, and set the frame
state data appropriately. See unwind-dw2.c for the structs. */ state data appropriately. See unwind-dw2.c for the structs. */
...@@ -156,14 +235,15 @@ static _Unwind_Reason_Code ...@@ -156,14 +235,15 @@ static _Unwind_Reason_Code
ppc_fallback_frame_state (struct _Unwind_Context *context, ppc_fallback_frame_state (struct _Unwind_Context *context,
_Unwind_FrameState *fs) _Unwind_FrameState *fs)
{ {
struct gcc_sigcontext *sc = get_sigcontext (context); static long hwcap = 0;
struct gcc_regs *regs = get_regs (context);
long new_cfa; long new_cfa;
int i; int i;
if (sc == NULL) if (regs == NULL)
return _URC_END_OF_STACK; return _URC_END_OF_STACK;
new_cfa = sc->regs->gpr[STACK_POINTER_REGNUM]; new_cfa = regs->gpr[STACK_POINTER_REGNUM];
fs->cfa_how = CFA_REG_OFFSET; fs->cfa_how = CFA_REG_OFFSET;
fs->cfa_reg = STACK_POINTER_REGNUM; fs->cfa_reg = STACK_POINTER_REGNUM;
fs->cfa_offset = new_cfa - (long) context->cfa; fs->cfa_offset = new_cfa - (long) context->cfa;
...@@ -172,21 +252,65 @@ ppc_fallback_frame_state (struct _Unwind_Context *context, ...@@ -172,21 +252,65 @@ ppc_fallback_frame_state (struct _Unwind_Context *context,
if (i != STACK_POINTER_REGNUM) if (i != STACK_POINTER_REGNUM)
{ {
fs->regs.reg[i].how = REG_SAVED_OFFSET; fs->regs.reg[i].how = REG_SAVED_OFFSET;
fs->regs.reg[i].loc.offset fs->regs.reg[i].loc.offset = (long) &regs->gpr[i] - new_cfa;
= (long)&(sc->regs->gpr[i]) - new_cfa;
} }
fs->regs.reg[CR2_REGNO].how = REG_SAVED_OFFSET; fs->regs.reg[CR2_REGNO].how = REG_SAVED_OFFSET;
fs->regs.reg[CR2_REGNO].loc.offset fs->regs.reg[CR2_REGNO].loc.offset = (long) &regs->ccr - new_cfa;
= (long)&(sc->regs->ccr) - new_cfa;
fs->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET; fs->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET;
fs->regs.reg[LINK_REGISTER_REGNUM].loc.offset fs->regs.reg[LINK_REGISTER_REGNUM].loc.offset = (long) &regs->link - new_cfa;
= (long)&(sc->regs->link) - new_cfa;
fs->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET; fs->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET;
fs->regs.reg[ARG_POINTER_REGNUM].loc.offset fs->regs.reg[ARG_POINTER_REGNUM].loc.offset = (long) &regs->nip - new_cfa;
= (long)&(sc->regs->nip) - new_cfa;
fs->retaddr_column = ARG_POINTER_REGNUM; fs->retaddr_column = ARG_POINTER_REGNUM;
if (hwcap == 0)
{
hwcap = ppc_linux_aux_vector (16);
/* These will already be set if we found AT_HWCAP. A non-zero
value stops us looking again if for some reason we couldn't
find AT_HWCAP. */
#ifdef __powerpc64__
hwcap |= 0xc0000000;
#else
hwcap |= 0x80000000;
#endif
}
/* If we have a FPU... */
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) &regs->fpr[i] - new_cfa;
}
/* If we have a VMX unit... */
if (hwcap & 0x10000000)
{
struct gcc_vregs *vregs;
#ifdef __powerpc64__
vregs = regs->vp;
#else
vregs = &regs->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; return _URC_NO_REASON;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment