PR55341 has two complaints, that powerpc gcc references a non-ABI symbol exported by glibc, and that the scheme used to find the aux vector can be broken by someone writing *environ = 0. Both true, but since this code is only exercised when running Linux kernels prior to 2.6.15 (7 years old!), I was inclined to "fix" the first problem by making __libc_stack_end weak and ignore the second problem entirely.
However, since I first looked at this bug, I've been delving into unwinding code due to thinking we had a problem in the kernel vdso with VSX registers. We didn't, but the exercise taught me that my concern about blindly setting up locations for altivec registers is unfounded. If the kernel doesn't support altivec and those locations point well past the kernel sigcontext, perhaps even to unmapped memory, we still don't have a problem since the normal course of unwinding won't reference those locations. The unwinder only looks at few regs like cfa and ra during unwinding, and the rest of the regs when copying into _Unwind_* callee save locations using uw_install_context. That means you'll only reference the altivec save locations if you're using a libgcc with altivec support. Running such a libgcc on a kernel without altivec support is crazy, and will cause all sorts of problems before you even consider exception handling, eg. sigill if the hardware doesn't support altivec, no process context swapping of altivec regs etc. So we can do without the AT_HWCAP tests. There is also no need to set up locations for call used regs. Bootstrapped and regression tested powerpc64-linux using a ld.so hack to prevent registration of the kernel vdso. OK to apply? PR target/55431 * config/rs6000/linux-unwind.h (ppc_linux_aux_vector): Delete. (ppc_fallback_frame_state): Always set up save locations for fp and altivec. Don't bother with non-callee-saved regs, r0-r13 except for r2 on ppc64, fr0-fr13, v0-v19, vscr. Index: libgcc/config/rs6000/linux-unwind.h =================================================================== --- libgcc/config/rs6000/linux-unwind.h (revision 195836) +++ libgcc/config/rs6000/linux-unwind.h (working copy) @@ -26,7 +26,6 @@ #define R_CR2 70 #define R_VR0 77 #define R_VRSAVE 109 -#define R_VSCR 110 struct gcc_vregs { @@ -175,38 +174,6 @@ } #endif -/* Find an entry in the process auxiliary 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. */ @@ -216,8 +183,8 @@ ppc_fallback_frame_state (struct _Unwind_Context *context, _Unwind_FrameState *fs) { - static long hwcap = 0; struct gcc_regs *regs = get_regs (context); + struct gcc_vregs *vregs; long new_cfa; int i; @@ -229,12 +196,15 @@ 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].how = REG_SAVED_OFFSET; - fs->regs.reg[i].loc.offset = (long) ®s->gpr[i] - new_cfa; - } +#ifdef __powerpc64__ + fs->regs.reg[2].how = REG_SAVED_OFFSET; + fs->regs.reg[2].loc.offset = (long) ®s->gpr[2] - new_cfa; +#endif + for (i = 14; i < 32; i++) + { + fs->regs.reg[i].how = REG_SAVED_OFFSET; + fs->regs.reg[i].loc.offset = (long) ®s->gpr[i] - new_cfa; + } fs->regs.reg[R_CR2].how = REG_SAVED_OFFSET; /* CR? regs are always 32-bit and PPC is big-endian, so in 64-bit @@ -250,57 +220,35 @@ fs->retaddr_column = ARG_POINTER_REGNUM; fs->signal_frame = 1; - if (hwcap == 0) + /* If we have a FPU... */ + for (i = 14; i < 32; i++) { - hwcap = ppc_linux_aux_vector (16); - /* 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__ - hwcap |= 0xc0000000; -#else - hwcap |= 0x80000000; -#endif + 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 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; + vregs = regs->vp; #else - vregs = ®s->vregs; + vregs = ®s->vregs; #endif - if (regs->msr & (1 << 25)) + if (regs->msr & (1 << 25)) + { + for (i = 20; i < 32; i++) { - for (i = 0; i < 32; i++) - { - fs->regs.reg[i + R_VR0].how = REG_SAVED_OFFSET; - fs->regs.reg[i + R_VR0].loc.offset - = (long) &vregs->vr[i] - 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[i + R_VR0].how = REG_SAVED_OFFSET; + fs->regs.reg[i + R_VR0].loc.offset = (long) &vregs->vr[i] - new_cfa; } - - fs->regs.reg[R_VRSAVE].how = REG_SAVED_OFFSET; - fs->regs.reg[R_VRSAVE].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++) + for (i = 14; 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 -- Alan Modra Australia Development Lab, IBM