This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


On Thu, Dec 18, 2003 at 11:32:37PM -0800, Jim Wilson wrote:
> On Thu, 2003-12-18 at 07:05, Jakub Jelinek wrote:
> This looks OK to me with some comments added in.

Below is what I've commited with comments added.

> You didn't mention testing, I am assuming you did the usual regression
> testing.

Before posting it was tested with glibc testsuite on ia64-redhat-linux,
before commiting by make bootstrap/make check on ia64-redhat-linux.

2003-12-19  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-19 09:16:32.000000000 +0100
@@ -182,7 +182,8 @@ struct _Unwind_Context
 {
   /* Initial frame info.  */
   unsigned long rnat;		/* rse nat collection */
-  unsigned long regstk_top;	/* bsp for first frame */
+  unsigned long regstk_top;	/* lowest address of rbs stored register
+				   which uses context->rnat collection */
 
   /* Current frame info.  */
   unsigned long bsp;		/* backing store pointer value
@@ -1490,6 +1491,80 @@ ia64_rse_skip_regs (unsigned long *addr,
 }
 
 
+/* Copy register backing store from SRC to DST, LEN words
+   (which include both saved registers and nat collections).
+   DST_RNAT is a partial nat collection for DST.  SRC and DST
+   don't have to be equal modulo 64 slots, so it cannot be
+   done with a simple memcpy as the nat collections will be
+   at different relative offsets and need to be combined together.  */
+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;
+    }
+  /* Set info->regstk_top to lowest rbs address which will use
+     info->rnat collection.  */
+  info->regstk_top = dst & ~0x1ffUL;
+  info->rnat = dst_rnat;
+}
+
 /* Unwind accessors.  */
 
 static void
@@ -1549,9 +1624,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 +1637,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 +1799,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 +2074,31 @@ 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 ();
+  /* Flush the register stack to memory so that we can access it.
+     Get rse nat collection for the last incomplete rbs chunk of
+     registers at the same time.  For this RSE needs to be turned
+     into the mandatory only mode.  */
+  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;
+  /* Set context->regstk_top to lowest rbs address which will use
+     context->rnat collection.  */
+  context->regstk_top = context->bsp & ~0x1ffULL;
+  context->rnat = rnat;
   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?  */
 
   if (uw_frame_state_for (context, &fs) != _URC_NO_REASON)
     abort ();
@@ -2048,6 +2139,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 +2343,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 +2357,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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]