[newlib-cygwin] x86_64: Handle myfault exceptions when running on alternate signal stack

Corinna Vinschen corinna@sourceware.org
Tue Jul 7 18:45:00 GMT 2015


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=60f10c64aa57d613471446ea3ec1b3ce6d3b9500

commit 60f10c64aa57d613471446ea3ec1b3ce6d3b9500
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Tue Jul 7 20:45:06 2015 +0200

    x86_64: Handle myfault exceptions when running on alternate signal stack
    
            x86_64 only:
            * cygtls.cc (san::leave): Restore _my_tls.andreas.
            * cygtls.h (class san):  Add _clemente as in 32 bit case.  Add ret and
            frame members.
            (san::san): Handle _my_tls.andreas as on 32 bit.  Take parameter and
            write it to new member ret.  Store current stack pointer in frame.
            (san::~san): New destructor to restore _my_tls.andreas.
            (__try): Use __l_except address as parameter to san::san.
            * dcrt0.cc (dll_crt0_0): Add myfault_altstack_handler as vectored
            continuation handler.
            * exception.h (myfault_altstack_handler): Declare.
            * exceptions.cc (myfault_altstack_handler): New function.  Explain what
            it's good for.
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/cygtls.cc     |  1 +
 winsup/cygwin/cygtls.h      | 19 +++++++++++++++++--
 winsup/cygwin/dcrt0.cc      |  5 +++++
 winsup/cygwin/exception.h   |  2 ++
 winsup/cygwin/exceptions.cc | 46 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/winsup/cygwin/cygtls.cc b/winsup/cygwin/cygtls.cc
index dc9a698..cb8c24e 100644
--- a/winsup/cygwin/cygtls.cc
+++ b/winsup/cygwin/cygtls.cc
@@ -224,5 +224,6 @@ void san::leave ()
 {
   /* Restore tls_pathbuf counters in case of error. */
   _my_tls.locals.pathbufs._counters = _cnt;
+  _my_tls.andreas = _clemente;
 }
 #endif
diff --git a/winsup/cygwin/cygtls.h b/winsup/cygwin/cygtls.h
index 3dfffbb..91ed4d4 100644
--- a/winsup/cygwin/cygtls.h
+++ b/winsup/cygwin/cygtls.h
@@ -301,11 +301,26 @@ extern _cygtls *_sig_tls;
 #ifdef __x86_64__
 class san
 {
+  san *_clemente;
   uint64_t _cnt;
 public:
-  san () __attribute__ ((always_inline))
+  DWORD64 ret;
+  DWORD64 frame;
+
+  san (PVOID _ret) __attribute__ ((always_inline))
   {
+    _clemente = _my_tls.andreas;
+    _my_tls.andreas = this;
     _cnt = _my_tls.locals.pathbufs._counters;
+    /* myfault_altstack_handler needs the current stack pointer and the
+       address of the _except block to restore the context correctly.
+       See comment preceeding myfault_altstack_handler in exception.cc. */
+    ret = (DWORD64) _ret;
+    __asm__ volatile ("movq %%rsp,%0": "=o" (frame));
+  }
+  ~san () __attribute__ ((always_inline))
+  {
+    _my_tls.andreas = _clemente;
   }
   /* This is the first thing called in the __except handler.  The attribute
      "returns_twice" makes sure that GCC disregards any register value set
@@ -363,7 +378,7 @@ public:
   { \
     __label__ __l_try, __l_except, __l_endtry; \
     __mem_barrier; \
-    san __sebastian; \
+    san __sebastian (&&__l_except); \
     __asm__ goto ("\n" \
       "  .seh_handler _ZN9exception7myfaultEP17_EXCEPTION_RECORDPvP8_CONTEXTP19_DISPATCHER_CONTEXT, @except						\n" \
       "  .seh_handlerdata						\n" \
diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
index f80ee36..1874fe5 100644
--- a/winsup/cygwin/dcrt0.cc
+++ b/winsup/cygwin/dcrt0.cc
@@ -800,6 +800,11 @@ dll_crt0_0 ()
   if (!dynamically_loaded)
     sigproc_init ();
 
+#ifdef __x86_64__
+  /* See comment preceeding myfault_altstack_handler in exception.cc. */
+  AddVectoredContinueHandler (0, myfault_altstack_handler);
+#endif
+
   debug_printf ("finished dll_crt0_0 initialization");
 }
 
diff --git a/winsup/cygwin/exception.h b/winsup/cygwin/exception.h
index 0478daf..13160fe 100644
--- a/winsup/cygwin/exception.h
+++ b/winsup/cygwin/exception.h
@@ -160,6 +160,8 @@ public:
   }
 };
 
+LONG CALLBACK myfault_altstack_handler (EXCEPTION_POINTERS *);
+
 #endif /* !__x86_64__ */
 
 class cygwin_exception
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index 0ce22d9..2ba2f49 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -588,6 +588,50 @@ exception::myfault (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
   /* NOTREACHED, make gcc happy. */
   return ExceptionContinueSearch;
 }
+
+/* If another exception occurs while running a signal handler on an alternate
+   signal stack, the normal SEH handlers are skipped, because the OS exception
+   handling considers the current (alternate) stack "broken".  However, it
+   still calls vectored exception handlers.
+
+   TODO: What we do here is to handle only __try/__except blocks in Cygwin.
+         "Normal" exceptions will simply exit the process.  Still, better
+	 than nothing... */
+LONG WINAPI
+myfault_altstack_handler (EXCEPTION_POINTERS *exc)
+{
+  _cygtls& me = _my_tls;
+
+  if (me.andreas)
+    {
+      PRUNTIME_FUNCTION f;
+      ULONG64 imagebase;
+      UNWIND_HISTORY_TABLE hist;
+      DWORD64 establisher;
+      PVOID hdl;
+      CONTEXT *c = exc->ContextRecord;
+
+      /* Unwind the stack manually and call RtlRestoreContext.  This
+	 is necessary because RtlUnwindEx checks the stack for validity,
+	 which, as outlined above, fails for the alternate stack. */
+      while (c->Rsp < me.andreas->frame)
+	{
+	  f = RtlLookupFunctionEntry (c->Rip, &imagebase, &hist);
+	  if (f)
+	    RtlVirtualUnwind (0, imagebase, c->Rip, f, c, &hdl, &establisher,
+			      NULL);
+	  else
+	    {
+	      c->Rip = *(ULONG_PTR *) c->Rsp;
+	      c->Rsp += 8;
+	    }
+	}
+      c->Rip = me.andreas->ret;
+      RtlRestoreContext (c, NULL);
+    }
+  return EXCEPTION_CONTINUE_SEARCH;
+}
+
 #endif
 
 /* Main exception handler. */
@@ -697,11 +741,13 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
       break;
 
     case STATUS_STACK_OVERFLOW:
+#if 0
       /* If we encounter a stack overflow, and if the thread has no alternate
          stack, don't even try to call a signal handler.  This is in line with
 	 Linux behaviour and also makes a lot of sense on Windows. */
       if (me.altstack.ss_flags)
 	global_sigs[SIGSEGV].sa_handler = SIG_DFL;
+#endif
       /*FALLTHRU*/
     case STATUS_ARRAY_BOUNDS_EXCEEDED:
     case STATUS_IN_PAGE_ERROR:



More information about the Cygwin-cvs mailing list