Patchwork [RS6000] PR55341 linux unwind fixes

login
register
mail settings
Submitter Alan Modra
Date Feb. 14, 2013, 10:33 p.m.
Message ID <20130214223357.GA9150@bubble.grove.modra.org>
Download mbox | patch
Permalink /patch/220535/
State New
Headers show

Comments

Alan Modra - Feb. 14, 2013, 10:33 p.m.
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.
David Edelsohn - Feb. 15, 2013, 1:07 a.m.
On Thu, Feb 14, 2013 at 5:33 PM, Alan Modra <amodra@gmail.com> wrote:
> 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.

Okay.

Thanks, David

Patch

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) &regs->gpr[i] - new_cfa;
-      }
+#ifdef __powerpc64__
+  fs->regs.reg[2].how = REG_SAVED_OFFSET;
+  fs->regs.reg[2].loc.offset = (long) &regs->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) &regs->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) &regs->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) &regs->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 = &regs->vregs;
+  vregs = &regs->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