[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