This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH] Fix unwinding through SA_ONSTACK signal frames on IA-64
- From: Jakub Jelinek <jakub at redhat dot com>
- To: Jim Wilson <wilson at specifixinc dot com>
- Cc: Richard Henderson <rth at redhat dot com>, gcc-patches at gcc dot gnu dot org, David Mosberger <davidm at hpl dot hp dot com>, Ulrich Drepper <drepper at redhat dot com>
- Date: Fri, 19 Dec 2003 13:04:30 +0100
- Subject: Re: [PATCH] Fix unwinding through SA_ONSTACK signal frames on IA-64
- References: <20031218150528.GB12344@sunsite.ms.mff.cuni.cz> <1071819158.1058.107.camel@leaf.tuliptree.org>
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
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