On 02/16/2012 11:54 AM, Richard Henderson wrote:
> I'll work on a proper fix to the unwinder; we can then decide
> whether and when to apply it...

Something like this.  Not the best of solutions, but we've run out
of bits in the exception structure, and thus we can't use this 
solution universally.

It shouldn't matter for most CISC targets, like x86, which always
push the return address onto the stack; one never winds up with
the situation you saw on mips where the return address is held 
only in a (special) register.


r~
diff --git a/gcc/config/alpha/alpha.h b/gcc/config/alpha/alpha.h
index 07ffa9f..77e1ee5 100644
--- a/gcc/config/alpha/alpha.h
+++ b/gcc/config/alpha/alpha.h
@@ -1290,3 +1290,8 @@ do {                                                      
\
 
 /* The system headers under Alpha systems are generally C++-aware.  */
 #define NO_IMPLICIT_EXTERN_C
+
+/* Bits 0 and 1 are always zero in jump targets.  */
+#ifdef IN_LIBGCC2
+# define UNWIND_FORCE_UNWIND_MASK 1
+#endif
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index 1c19f8b..59d4560 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -2920,3 +2920,15 @@ extern GTY(()) struct target_globals *mips16_globals;
    with arguments ARGS.  */
 #define PMODE_INSN(NAME, ARGS) \
   (Pmode == SImode ? NAME ## _si ARGS : NAME ## _di ARGS)
+
+/* For mips32 mode we have bits 0 and 1 zero free, but for mips16 mode,
+   bit 0 indicates mips16 mode, and bit 1 is thence meaningful.  Thus
+   the only "free" bits would be at the top of the address space.
+   Can we trust that we'll never try to unwind in kernel mode?  */
+#ifdef IN_LIBGCC2
+# if TARGET_64BIT
+#  define UNWIND_FORCE_UNWIND_MASK     0x8000000000000000ull
+# else
+#  define UNWIND_FORCE_UNWIND_MASK     0x80000000u
+# endif
+#endif
diff --git a/libgcc/unwind-sjlj.c b/libgcc/unwind-sjlj.c
index 1fc1c5d..4b32328 100644
--- a/libgcc/unwind-sjlj.c
+++ b/libgcc/unwind-sjlj.c
@@ -322,6 +322,9 @@ uw_identify_context (struct _Unwind_Context *context)
 #define _Unwind_Resume                 _Unwind_SjLj_Resume
 #define _Unwind_Resume_or_Rethrow      _Unwind_SjLj_Resume_or_Rethrow
 
+/* Always use uw_identify_context to identify frames.  */
+#undef UNWIND_FORCE_UNWIND_MASK
+
 #include "unwind.inc"
 
 #endif /* USING_SJLJ_EXCEPTIONS */
diff --git a/libgcc/unwind.inc b/libgcc/unwind.inc
index 5e2ec29..1374f9d 100644
--- a/libgcc/unwind.inc
+++ b/libgcc/unwind.inc
@@ -1,5 +1,5 @@
 /* Exception handling and frame unwind runtime interface routines. -*- C -*-
-   Copyright (C) 2001, 2003, 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2003, 2008, 2009, 2012 Free Software Foundation, Inc.
 
    This file is part of GCC.
 
@@ -27,6 +27,64 @@
    This file is included from unwind-dw2.c, unwind-sjlj.c or
    unwind-ia64.c.  */
 
+/* This is a bit value within a function pointer or function return address
+   that is never set.  E.g. most RISC targets have instructions aligned to
+   four bytes, meaning bits 0 and 1 are "free" and thus either 1 or 2 would
+   be appropriate values to use here.
+
+   If non-zero, we will use this bit to distinguish forced unwind from normal
+   unwind, and in addition use both the IP+CFA to identify the target context
+   during unwinding.
+
+   If zero, i.e. there is no "free" bit, then we will only be able to use the
+   CFA to identify the target context during unwinding.  The consequence of
+   this is that we cannot reliably propagate an exception through a sequence
+   of functions that do not have a stack frame.  Normally this is not a
+   problem, in that most functions that do not have stack frames are leaf
+   functions, and thus do not throw exceptions.  */
+
+#include <stdbool.h>
+
+#ifndef UNWIND_FORCE_UNWIND_MASK
+# define UNWIND_FORCE_UNWIND_MASK 0
+#endif
+
+static inline bool
+is_forced_unwind (struct _Unwind_Exception *exc)
+{
+  if (UNWIND_FORCE_UNWIND_MASK != 0)
+    return (exc->private_1 & UNWIND_FORCE_UNWIND_MASK) != 0;
+  else
+    return exc->private_1 != 0;
+}
+
+static inline bool
+is_handler_match (struct _Unwind_Exception *exc,
+                 struct _Unwind_Context *context)
+{
+  if (UNWIND_FORCE_UNWIND_MASK != 0)
+    return (_Unwind_GetIP (context) == exc->private_1
+           && _Unwind_GetCFA (context) == exc->private_2);
+  else
+    return uw_identify_context (context) == exc->private_2;
+}
+
+static inline void
+record_handler_match (struct _Unwind_Exception *exc,
+                     struct _Unwind_Context *context)
+{
+  if (UNWIND_FORCE_UNWIND_MASK != 0)
+    {
+      exc->private_1 = _Unwind_GetIP (context);
+      exc->private_2 = _Unwind_GetCFA (context);
+    }
+  else
+    {
+      exc->private_1 = 0;
+      exc->private_2 = uw_identify_context (context);
+    }
+}
+
 /* Subroutine of _Unwind_RaiseException also invoked from _Unwind_Resume. 
 
    Unwind the stack calling the personality routine to find both the
@@ -48,8 +106,7 @@ _Unwind_RaiseException_Phase2(struct _Unwind_Exception *exc,
       code = uw_frame_state_for (context, &fs);
 
       /* Identify when we've reached the designated handler context.  */
-      match_handler = (uw_identify_context (context) == exc->private_2
-                      ? _UA_HANDLER_FRAME : 0);
+      match_handler = is_handler_match (exc, context) ? _UA_HANDLER_FRAME : 0;
 
       if (code != _URC_NO_REASON)
        /* Some error encountered.  Usually the unwinder doesn't
@@ -124,8 +181,7 @@ _Unwind_RaiseException(struct _Unwind_Exception *exc)
 
   /* Indicate to _Unwind_Resume and associated subroutines that this
      is not a forced unwind.  Further, note where we found a handler.  */
-  exc->private_1 = 0;
-  exc->private_2 = uw_identify_context (&cur_context);
+  record_handler_match (exc, &cur_context);
 
   cur_context = this_context;
   code = _Unwind_RaiseException_Phase2 (exc, &cur_context);
@@ -142,7 +198,8 @@ static _Unwind_Reason_Code
 _Unwind_ForcedUnwind_Phase2 (struct _Unwind_Exception *exc,
                             struct _Unwind_Context *context)
 {
-  _Unwind_Stop_Fn stop = (_Unwind_Stop_Fn) (_Unwind_Ptr) exc->private_1;
+  _Unwind_Stop_Fn stop = (_Unwind_Stop_Fn)
+    (_Unwind_Ptr) (exc->private_1 & ~UNWIND_FORCE_UNWIND_MASK);
   void *stop_argument = (void *) (_Unwind_Ptr) exc->private_2;
   _Unwind_Reason_Code code, stop_code;
 
@@ -201,7 +258,7 @@ _Unwind_ForcedUnwind (struct _Unwind_Exception *exc,
   uw_init_context (&this_context);
   cur_context = this_context;
 
-  exc->private_1 = (_Unwind_Ptr) stop;
+  exc->private_1 = (_Unwind_Ptr) stop | UNWIND_FORCE_UNWIND_MASK;
   exc->private_2 = (_Unwind_Ptr) stop_argument;
 
   code = _Unwind_ForcedUnwind_Phase2 (exc, &cur_context);
@@ -226,10 +283,10 @@ _Unwind_Resume (struct _Unwind_Exception *exc)
 
   /* Choose between continuing to process _Unwind_RaiseException
      or _Unwind_ForcedUnwind.  */
-  if (exc->private_1 == 0)
-    code = _Unwind_RaiseException_Phase2 (exc, &cur_context);
-  else
+  if (is_forced_unwind (exc))
     code = _Unwind_ForcedUnwind_Phase2 (exc, &cur_context);
+  else
+    code = _Unwind_RaiseException_Phase2 (exc, &cur_context);
 
   gcc_assert (code == _URC_INSTALL_CONTEXT);
 
@@ -248,7 +305,7 @@ _Unwind_Resume_or_Rethrow (struct _Unwind_Exception *exc)
 
   /* Choose between continuing to process _Unwind_RaiseException
      or _Unwind_ForcedUnwind.  */
-  if (exc->private_1 == 0)
+  if (!is_forced_unwind (exc))
     return _Unwind_RaiseException (exc);
 
   uw_init_context (&this_context);

Reply via email to