diff options
author | Alan Modra <amodra@bigpond.net.au> | 2005-01-31 13:37:37 +0000 |
---|---|---|
committer | Alan Modra <amodra@gcc.gnu.org> | 2005-02-01 00:07:37 +1030 |
commit | a7a52913cf19685b6b0e9cbc61015e41ec2f82b1 (patch) | |
tree | 2711321640270fb38a9c4b9736f14000fd02abb6 /gcc | |
parent | 261ba0c8dbb450cc88a56e6869f962964d6e9e91 (diff) | |
download | gcc-a7a52913cf19685b6b0e9cbc61015e41ec2f82b1.zip gcc-a7a52913cf19685b6b0e9cbc61015e41ec2f82b1.tar.gz gcc-a7a52913cf19685b6b0e9cbc61015e41ec2f82b1.tar.bz2 |
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
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 13 | ||||
-rw-r--r-- | gcc/config/rs6000/linux-unwind.h | 218 |
2 files changed, 184 insertions, 47 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1a42864..2a39cfd 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +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 diff --git a/gcc/config/rs6000/linux-unwind.h b/gcc/config/rs6000/linux-unwind.h index 4212dfe..3e12cc6 100644 --- a/gcc/config/rs6000/linux-unwind.h +++ b/gcc/config/rs6000/linux-unwind.h @@ -1,5 +1,5 @@ /* 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) ®s->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) ®s->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) ®s->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) ®s->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) ®s->fpr[i] - new_cfa; + } + + /* If we have a VMX unit... */ + 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; } |