[PATCH] Fix unwinding through SA_ONSTACK signal frames on IA-64

Jakub Jelinek jakub@redhat.com
Thu Dec 18 17:21:00 GMT 2003


Hi!

This patch fixes the attached cleanup-1{0,1}.c tests working on IA-64
(and tst-cancelx2{0,1} NPTL tests).
I think sigaltstack is rare enough that it is not worth polluting lots
of unwind-ia64.c with handling of it, instead the MD_* macros just
copy the backing store (emulate what would a loadrs; change rbs; flushrs
sequence do).

Ok to commit? What about 3.3?

2003-12-18  Jakub Jelinek  <jakub@redhat.com>

	* config/ia64/unwind-ia64.c (ia64_copy_rbs): New function.
	(unw_access_gr): Only call ia64_rse_rnat_addr if addr is above
	regstk_top.
	(uw_frame_state_for): Handle locations inside bundles.
	(uw_init_context_1): Initialize context->rnat.
	Set context->regstk_top to lowest rbs address which has nat collection
	in context->rnat.
	(uw_install_context): Fix rnat restoring.
	Restore ar.rsc to previous state.
	* config/ia64/linux.h (MD_FALLBACK_FRAME_STATE_FOR,
	MD_HANDLE_UNWABI): Handle unwinding through SA_ONSTACK frames.

	* gcc.dg/cleanup-10.c: New test.
	* gcc.dg/cleanup-11.c: New test.

--- gcc/config/ia64/unwind-ia64.c.jj	2003-12-10 21:05:09.000000000 +0100
+++ gcc/config/ia64/unwind-ia64.c	2003-12-18 00:19:01.000000000 +0100
@@ -1466,7 +1466,7 @@ ia64_rse_rnat_addr (unsigned long *slot_
   return (unsigned long *) ((unsigned long) slot_addr | (0x3f << 3));
 }
 
-/* Calcuate the number of registers in the dirty partition starting at
+/* Calculate the number of registers in the dirty partition starting at
    BSPSTORE with a size of DIRTY bytes.  This isn't simply DIRTY
    divided by eight because the 64th slot is used to store ar.rnat.  */
 static inline unsigned long
@@ -1490,6 +1490,72 @@ ia64_rse_skip_regs (unsigned long *addr,
 }
 
 
+static void
+ia64_copy_rbs (struct _Unwind_Context *info, unsigned long dst,
+               unsigned long src, long len, unsigned long dst_rnat)
+{
+  long count;
+  unsigned long src_rnat;
+  unsigned long shift1, shift2;
+
+  len <<= 3;
+  dst_rnat &= (1UL << ((dst >> 3) & 0x3f)) - 1;
+  src_rnat = src >= info->regstk_top
+	     ? info->rnat : *(unsigned long *) (src | 0x1f8);
+  src_rnat &= ~((1UL << ((src >> 3) & 0x3f)) - 1);
+  /* Just to make sure.  */
+  src_rnat &= ~(1UL << 63);
+  shift1 = ((dst - src) >> 3) & 0x3f;
+  if ((dst & 0x1f8) < (src & 0x1f8))
+    shift1--;
+  shift2 = 0x3f - shift1;
+  if ((dst & 0x1f8) >= (src & 0x1f8))
+    {
+      count = ~dst & 0x1f8;
+      goto first;
+    }
+  count = ~src & 0x1f8;
+  goto second;
+  while (len > 0)
+    {
+      src_rnat = src >= info->regstk_top
+		 ? info->rnat : *(unsigned long *) (src | 0x1f8);
+      /* Just to make sure.  */
+      src_rnat &= ~(1UL << 63);
+      count = shift2 << 3;
+first:
+      if (count > len)
+        count = len;
+      memcpy ((char *) dst, (char *) src, count);
+      dst += count;
+      src += count;
+      len -= count;
+      dst_rnat |= (src_rnat << shift1) & ~(1UL << 63);
+      if (len <= 0)
+        break;
+      *(long *) dst = dst_rnat;
+      dst += 8;
+      dst_rnat = 0;
+      count = shift1 << 3;
+second:
+      if (count > len)
+        count = len;
+      memcpy ((char *) dst, (char *) src, count);
+      dst += count;
+      src += count + 8;
+      len -= count + 8;
+      dst_rnat |= (src_rnat >> shift2);
+    }
+  if ((dst & 0x1f8) == 0x1f8)
+    {
+      *(long *) dst = dst_rnat;
+      dst += 8;
+      dst_rnat = 0;
+    }
+  info->regstk_top = dst & ~0x1ffUL;
+  info->rnat = dst_rnat;
+}
+
 /* Unwind accessors.  */
 
 static void
@@ -1549,9 +1615,10 @@ unw_access_gr (struct _Unwind_Context *i
 	      break;
 
 	    case UNW_NAT_REGSTK:
-	      nat_addr = ia64_rse_rnat_addr (addr);
-	      if ((unsigned long) nat_addr >= info->regstk_top)
+	      if ((unsigned long) addr >= info->regstk_top)
 		nat_addr = &info->rnat;
+	      else
+		nat_addr = ia64_rse_rnat_addr (addr);
 	      nat_mask = 1UL << ia64_rse_slot_num (addr);
 	      break;
 	    }
@@ -1561,9 +1628,10 @@ unw_access_gr (struct _Unwind_Context *i
     {
       /* Access a stacked register.  */
       addr = ia64_rse_skip_regs ((unsigned long *) info->bsp, regnum - 32);
-      nat_addr = ia64_rse_rnat_addr (addr);
-      if ((unsigned long) nat_addr >= info->regstk_top)
+      if ((unsigned long) addr >= info->regstk_top)
 	nat_addr = &info->rnat;
+      else
+	nat_addr = ia64_rse_rnat_addr (addr);
       nat_mask = 1UL << ia64_rse_slot_num (addr);
     }
 
@@ -1722,7 +1790,8 @@ uw_frame_state_for (struct _Unwind_Conte
     }
 
   context->region_start = ent->start_offset + segment_base;
-  fs->when_target = (context->rp - context->region_start) / 16 * 3;
+  fs->when_target = ((context->rp & -16) - context->region_start) / 16 * 3
+		    + (context->rp & 15);
 
   unw = (unsigned long *) (ent->info_offset + segment_base);
   header = *unw;
@@ -1996,18 +2065,26 @@ uw_init_context_1 (struct _Unwind_Contex
   /* Set psp to the caller's stack pointer.  */
   void *psp = __builtin_dwarf_cfa () - 16;
   _Unwind_FrameState fs;
+  unsigned long rnat, tmp1, tmp2;
 
   /* Flush the register stack to memory so that we can access it.  */
-  __builtin_ia64_flushrs ();
+  asm ("mov.m %1 = ar.rsc;;\n\t"
+       "and %2 = 0x1c, %1;;\n\t"
+       "mov.m ar.rsc = %2;;\n\t"
+       "flushrs;;\n\t"
+       "mov.m %0 = ar.rnat;;\n\t"
+       "mov.m ar.rsc = %1\n\t"
+       : "=r" (rnat), "=r" (tmp1), "=r" (tmp2));
 
   memset (context, 0, sizeof (struct _Unwind_Context));
-  context->bsp = context->regstk_top = (unsigned long) bsp;
+  context->bsp = (unsigned long) bsp;
+  context->regstk_top = context->bsp & ~0x1ffULL;
   context->psp = (unsigned long) psp;
   context->rp = (unsigned long) rp;
   asm ("mov %0 = sp" : "=r" (context->sp));
   asm ("mov %0 = pr" : "=r" (context->pr));
   context->pri_unat_loc = &context->initial_unat;	/* ??? */
-  /* ??? Get rnat.  Don't we have to turn off the rse for that?  */
+  context->rnat = rnat;
 
   if (uw_frame_state_for (context, &fs) != _URC_NO_REASON)
     abort ();
@@ -2048,6 +2125,9 @@ uw_install_context (struct _Unwind_Conte
     ia64_rse_skip_regs ((unsigned long *)target->bsp,
 			(*target->pfs_loc >> 7) & 0x7f);
 
+  if (target->bsp < target->regstk_top)
+    target->rnat = *ia64_rse_rnat_addr ((unsigned long *) target->bsp);
+
   /* Provide assembly with the offsets into the _Unwind_Context.  */
   asm volatile ("uc_rnat = %0"
 		: : "i"(offsetof (struct _Unwind_Context, rnat)));
@@ -2249,10 +2329,10 @@ uw_install_context (struct _Unwind_Conte
 	"mov.m r25 = ar.rsc			\n\t"
 	"(p6) mov.m ar.fpsr = r30		\n\t"
 	";;					\n\t"
-	"and r25 = 0x1c, r25			\n\t"
+	"and r29 = 0x1c, r25			\n\t"
 	"mov b0 = r26				\n\t"
 	";;					\n\t"
-	"mov.m ar.rsc = r25			\n\t"
+	"mov.m ar.rsc = r29			\n\t"
 	";;					\n\t"
 	/* This must be done before setting AR.BSPSTORE, otherwise 
 	   AR.BSP will be initialized with a random displacement
@@ -2263,7 +2343,6 @@ uw_install_context (struct _Unwind_Conte
 	";;					\n\t"
 	"mov.m ar.bspstore = r23		\n\t"
 	";;					\n\t"
-	"or r25 = 0x3, r25			\n\t"
 	"mov.m ar.rnat = r22			\n\t"
 	";;					\n\t"
 	"mov.m ar.rsc = r25			\n\t"
--- gcc/config/ia64/linux.h.jj	2003-12-12 12:09:15.000000000 +0100
+++ gcc/config/ia64/linux.h	2003-12-17 23:33:03.000000000 +0100
@@ -106,7 +106,6 @@ do {						\
       (CONTEXT)->br_loc[0] = &(sc_->sc_br[0]);				\
       (CONTEXT)->br_loc[6] = &(sc_->sc_br[6]);				\
       (CONTEXT)->br_loc[7] = &(sc_->sc_br[7]);				\
-      (CONTEXT)->bsp = sc_->sc_ar_bsp;					\
       (CONTEXT)->pr = sc_->sc_pr;					\
       (CONTEXT)->psp = sc_->sc_gr[12];					\
       (CONTEXT)->gp = sc_->sc_gr[1];					\
@@ -114,6 +113,22 @@ do {						\
          other than what we adjust for below.	  */			\
       (FS) -> no_reg_stack_frame = 1;					\
 									\
+      if (sc_->sc_rbs_base)						\
+	{								\
+	  /* Need to switch from alternate register backing store.  */	\
+	  long ndirty, loadrs = sc_->sc_loadrs >> 16;			\
+	  unsigned long alt_bspstore = (CONTEXT)->bsp - loadrs;		\
+	  unsigned long bspstore;					\
+	  unsigned long *ar_bsp = (unsigned long *)(sc_->sc_ar_bsp);	\
+									\
+	  ndirty = ia64_rse_num_regs ((unsigned long *) alt_bspstore,	\
+				      (unsigned long *) (CONTEXT)->bsp);\
+	  bspstore = (unsigned long)					\
+		     ia64_rse_skip_regs (ar_bsp, -ndirty);		\
+	  ia64_copy_rbs ((CONTEXT), bspstore, alt_bspstore, loadrs,	\
+			 sc_->sc_ar_rnat);				\
+	}								\
+									\
       /* Don't touch the branch registers o.t. b0, b6 and b7.		\
 	 The kernel doesn't pass the preserved branch registers		\
 	 in the sigcontext but leaves them intact, so there's no	\
@@ -163,13 +178,28 @@ do {						\
       (CONTEXT)->br_loc[0] = &(sc_->sc_br[0]);				\
       (CONTEXT)->br_loc[6] = &(sc_->sc_br[6]);				\
       (CONTEXT)->br_loc[7] = &(sc_->sc_br[7]);				\
-      (CONTEXT)->bsp = sc_->sc_ar_bsp;					\
       (CONTEXT)->pr = sc_->sc_pr;					\
       (CONTEXT)->gp = sc_->sc_gr[1];					\
       /* Signal frame doesn't have an associated reg. stack frame 	\
          other than what we adjust for below.	  */			\
       (FS) -> no_reg_stack_frame = 1;					\
 									\
+      if (sc_->sc_rbs_base)						\
+	{								\
+	  /* Need to switch from alternate register backing store.  */	\
+	  long ndirty, loadrs = sc_->sc_loadrs >> 16;			\
+	  unsigned long alt_bspstore = (CONTEXT)->bsp - loadrs;		\
+	  unsigned long bspstore;					\
+	  unsigned long *ar_bsp = (unsigned long *)(sc_->sc_ar_bsp);	\
+									\
+	  ndirty = ia64_rse_num_regs ((unsigned long *) alt_bspstore,	\
+				      (unsigned long *) (CONTEXT)->bsp);\
+	  bspstore = (unsigned long)					\
+		     ia64_rse_skip_regs (ar_bsp, -ndirty);		\
+	  ia64_copy_rbs ((CONTEXT), bspstore, alt_bspstore, loadrs,	\
+			 sc_->sc_ar_rnat);				\
+	}								\
+									\
       /* Don't touch the branch registers o.t. b0, b6 and b7.		\
 	 The kernel doesn't pass the preserved branch registers		\
 	 in the sigcontext but leaves them intact, so there's no	\
--- gcc/testsuite/gcc.dg/cleanup-10.c.jj	2003-12-18 16:02:28.000000000 +0100
+++ gcc/testsuite/gcc.dg/cleanup-10.c	2003-12-18 16:13:15.000000000 +0100
@@ -0,0 +1,114 @@
+/* { dg-do run { target i?86-*-linux* x86_64-*-linux* ia64-*-linux* alpha*-*-linux* powerpc*-*-linux* s390*-*-linux* sparc*-*-linux* mips*-*-linux* } } */
+/* { dg-options "-fasynchronous-unwind-tables -fexceptions -O2" } */
+/* Verify that cleanups work with exception handling through signal frames
+   on alternate stack.  */
+
+#include <unwind.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+static _Unwind_Reason_Code
+force_unwind_stop (int version, _Unwind_Action actions,
+                   _Unwind_Exception_Class exc_class,
+                   struct _Unwind_Exception *exc_obj,
+                   struct _Unwind_Context *context,
+                   void *stop_parameter)
+{
+  if (actions & _UA_END_OF_STACK)
+    abort ();
+  return _URC_NO_REASON;
+}
+
+static void force_unwind ()
+{
+  struct _Unwind_Exception *exc = malloc (sizeof (*exc));
+  exc->exception_class = 0;
+  exc->exception_cleanup = 0;
+                   
+#ifndef __USING_SJLJ_EXCEPTIONS__
+  _Unwind_ForcedUnwind (exc, force_unwind_stop, 0);
+#else
+  _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0);
+#endif
+                   
+  abort ();
+}
+
+int count;
+char *null;
+
+static void counter (void *p __attribute__((unused)))
+{
+  ++count;
+}
+
+static void handler (void *p __attribute__((unused)))
+{
+  if (count != 2)
+    abort ();
+  exit (0);
+}
+
+static int __attribute__((noinline)) fn5 ()
+{
+  char dummy __attribute__((cleanup (counter)));
+  force_unwind ();
+  return 0;
+}
+
+static void fn4 (int sig, siginfo_t *info, void *ctx)
+{
+  char dummy __attribute__((cleanup (counter)));
+  fn5 ();
+  null = NULL;
+}
+
+static void fn3 ()
+{
+  abort ();
+}
+
+static int __attribute__((noinline)) fn2 ()
+{
+  *null = 0;
+  fn3 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn1 ()
+{
+  stack_t ss;
+  struct sigaction s;
+
+  ss.ss_size = 4 * sysconf (_SC_PAGESIZE);
+  if (ss.ss_size < SIGSTKSZ)
+    ss.ss_size = SIGSTKSZ;
+  ss.ss_sp = malloc (ss.ss_size);
+  if (ss.ss_sp == NULL)
+    exit (1);
+  ss.ss_flags = 0;
+  if (sigaltstack (&ss, NULL) < 0)
+    exit (1);
+
+  sigemptyset (&s.sa_mask);
+  s.sa_sigaction = fn4;
+  s.sa_flags = SA_ONESHOT | SA_ONSTACK;
+  sigaction (SIGSEGV, &s, NULL);
+  fn2 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn0 ()
+{
+  char dummy __attribute__((cleanup (handler)));
+  fn1 ();
+  null = 0;
+  return 0;
+}
+
+int main()
+{ 
+  fn0 ();
+  abort ();
+}
--- gcc/testsuite/gcc.dg/cleanup-11.c.jj	2003-12-18 16:13:46.000000000 +0100
+++ gcc/testsuite/gcc.dg/cleanup-11.c	2003-12-18 16:14:51.000000000 +0100
@@ -0,0 +1,114 @@
+/* { dg-do run { target i?86-*-linux* x86_64-*-linux* ia64-*-linux* alpha*-*-linux* powerpc*-*-linux* s390*-*-linux* sparc*-*-linux* mips*-*-linux* } } */
+/* { dg-options "-fasynchronous-unwind-tables -fexceptions -O2" } */
+/* Verify that cleanups work with exception handling through realtime signal
+   frames on alternate stack.  */
+
+#include <unwind.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+static _Unwind_Reason_Code
+force_unwind_stop (int version, _Unwind_Action actions,
+                   _Unwind_Exception_Class exc_class,
+                   struct _Unwind_Exception *exc_obj,
+                   struct _Unwind_Context *context,
+                   void *stop_parameter)
+{
+  if (actions & _UA_END_OF_STACK)
+    abort ();
+  return _URC_NO_REASON;
+}
+
+static void force_unwind ()
+{
+  struct _Unwind_Exception *exc = malloc (sizeof (*exc));
+  exc->exception_class = 0;
+  exc->exception_cleanup = 0;
+                   
+#ifndef __USING_SJLJ_EXCEPTIONS__
+  _Unwind_ForcedUnwind (exc, force_unwind_stop, 0);
+#else
+  _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0);
+#endif
+                   
+  abort ();
+}
+
+int count;
+char *null;
+
+static void counter (void *p __attribute__((unused)))
+{
+  ++count;
+}
+
+static void handler (void *p __attribute__((unused)))
+{
+  if (count != 2)
+    abort ();
+  exit (0);
+}
+
+static int __attribute__((noinline)) fn5 ()
+{
+  char dummy __attribute__((cleanup (counter)));
+  force_unwind ();
+  return 0;
+}
+
+static void fn4 (int sig, siginfo_t *info, void *ctx)
+{
+  char dummy __attribute__((cleanup (counter)));
+  fn5 ();
+  null = NULL;
+}
+
+static void fn3 ()
+{
+  abort ();
+}
+
+static int __attribute__((noinline)) fn2 ()
+{
+  *null = 0;
+  fn3 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn1 ()
+{
+  stack_t ss;
+  struct sigaction s;
+
+  ss.ss_size = 4 * sysconf (_SC_PAGESIZE);
+  if (ss.ss_size < SIGSTKSZ)
+    ss.ss_size = SIGSTKSZ;
+  ss.ss_sp = malloc (ss.ss_size);
+  if (ss.ss_sp == NULL)
+    exit (1);
+  ss.ss_flags = 0;
+  if (sigaltstack (&ss, NULL) < 0)
+    exit (1);
+
+  sigemptyset (&s.sa_mask);
+  s.sa_sigaction = fn4;
+  s.sa_flags = SA_ONESHOT | SA_ONSTACK | SA_SIGINFO;
+  sigaction (SIGSEGV, &s, NULL);
+  fn2 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn0 ()
+{
+  char dummy __attribute__((cleanup (handler)));
+  fn1 ();
+  null = 0;
+  return 0;
+}
+
+int main()
+{ 
+  fn0 ();
+  abort ();
+}

	Jakub



More information about the Gcc-patches mailing list