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>
* tree-vectorizer.c (vect_analyze_offset_expr): Use ssizetype instead
......
/* 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.
......@@ -24,7 +24,22 @@
these structs elsewhere; Many fields are missing, particularly
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 nip;
......@@ -34,22 +49,32 @@ struct gcc_pt_regs
unsigned long link;
unsigned long xer;
unsigned long ccr;
};
struct gcc_sigcontext
{
unsigned long pad[7];
struct gcc_pt_regs *regs;
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[21];
unsigned long pad[28];
#else
unsigned long pad[5];
unsigned long pad[12];
#endif
struct gcc_sigcontext uc_mcontext;
struct gcc_regs *regs;
struct gcc_regs rsave;
};
#ifdef __powerpc64__
......@@ -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
sigcontext. Otherwise return NULL. */
regs. Otherwise return NULL. */
static struct gcc_sigcontext *
get_sigcontext (struct _Unwind_Context *context)
static struct gcc_regs *
get_regs (struct _Unwind_Context *context)
{
const unsigned char *pc = context->ra;
/* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */
/* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */
if (*(unsigned int *) (pc+0) != 0x38210000 + SIGNAL_FRAMESIZE
|| *(unsigned int *) (pc+8) != 0x44000002)
if (*(unsigned int *) (pc + 0) != 0x38210000 + SIGNAL_FRAMESIZE
|| *(unsigned int *) (pc + 8) != 0x44000002)
return NULL;
if (*(unsigned int *) (pc+4) == 0x38000077)
if (*(unsigned int *) (pc + 4) == 0x38000077)
{
struct sigframe {
char gap[SIGNAL_FRAMESIZE];
struct gcc_sigcontext sigctx;
} *rt_ = context->cfa;
return &rt_->sigctx;
unsigned long pad[7];
struct gcc_regs *regs;
} *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];
void *pinfo;
struct gcc_ucontext *puc;
} *rt_ = (struct rt_sigframe *) pc;
return &rt_->puc->uc_mcontext;
} *frame24 = (struct rt_sigframe_24 *) pc;
/* 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;
}
......@@ -113,8 +159,8 @@ get_sigcontext (struct _Unwind_Context *context)
enum { SIGNAL_FRAMESIZE = 64 };
static struct gcc_sigcontext *
get_sigcontext (struct _Unwind_Context *context)
static struct gcc_regs *
get_regs (struct _Unwind_Context *context)
{
const unsigned char *pc = context->ra;
......@@ -122,31 +168,64 @@ get_sigcontext (struct _Unwind_Context *context)
/* li r0, 0x0077; sc (sigreturn new) */
/* li r0, 0x6666; sc (rt_sigreturn old) */
/* li r0, 0x00AC; sc (rt_sigreturn new) */
if (*(unsigned int *) (pc+4) != 0x44000002)
if (*(unsigned int *) (pc + 4) != 0x44000002)
return NULL;
if (*(unsigned int *) (pc+0) == 0x38007777
|| *(unsigned int *) (pc+0) == 0x38000077)
if (*(unsigned int *) (pc + 0) == 0x38007777
|| *(unsigned int *) (pc + 0) == 0x38000077)
{
struct sigframe {
char gap[SIGNAL_FRAMESIZE];
struct gcc_sigcontext sigctx;
} *rt_ = context->cfa;
return &rt_->sigctx;
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)
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;
} *rt_ = context->cfa;
return &rt_->uc.uc_mcontext;
} *frame = (struct rt_sigframe *) context->cfa;
return frame->uc.regs;
}
return NULL;
}
#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
state data appropriately. See unwind-dw2.c for the structs. */
......@@ -156,14 +235,15 @@ static _Unwind_Reason_Code
ppc_fallback_frame_state (struct _Unwind_Context *context,
_Unwind_FrameState *fs)
{
struct gcc_sigcontext *sc = get_sigcontext (context);
static long hwcap = 0;
struct gcc_regs *regs = get_regs (context);
long new_cfa;
int i;
if (sc == NULL)
if (regs == NULL)
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_reg = STACK_POINTER_REGNUM;
fs->cfa_offset = new_cfa - (long) context->cfa;
......@@ -172,21 +252,65 @@ ppc_fallback_frame_state (struct _Unwind_Context *context,
if (i != STACK_POINTER_REGNUM)
{
fs->regs.reg[i].how = REG_SAVED_OFFSET;
fs->regs.reg[i].loc.offset
= (long)&(sc->regs->gpr[i]) - new_cfa;
fs->regs.reg[i].loc.offset = (long) &regs->gpr[i] - new_cfa;
}
fs->regs.reg[CR2_REGNO].how = REG_SAVED_OFFSET;
fs->regs.reg[CR2_REGNO].loc.offset
= (long)&(sc->regs->ccr) - new_cfa;
fs->regs.reg[CR2_REGNO].loc.offset = (long) &regs->ccr - new_cfa;
fs->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET;
fs->regs.reg[LINK_REGISTER_REGNUM].loc.offset
= (long)&(sc->regs->link) - new_cfa;
fs->regs.reg[LINK_REGISTER_REGNUM].loc.offset = (long) &regs->link - new_cfa;
fs->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET;
fs->regs.reg[ARG_POINTER_REGNUM].loc.offset
= (long)&(sc->regs->nip) - new_cfa;
fs->regs.reg[ARG_POINTER_REGNUM].loc.offset = (long) &regs->nip - new_cfa;
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;
}
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