diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2015-07-07 20:45:06 +0200 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2015-07-07 20:45:06 +0200 |
commit | 60f10c64aa57d613471446ea3ec1b3ce6d3b9500 (patch) | |
tree | 0d7e41c913bb1d547cf3512dd3314dc332ca0585 /winsup/cygwin | |
parent | 29a12632278368f197f3270532e2f6c19642512e (diff) | |
download | newlib-60f10c64aa57d613471446ea3ec1b3ce6d3b9500.zip newlib-60f10c64aa57d613471446ea3ec1b3ce6d3b9500.tar.gz newlib-60f10c64aa57d613471446ea3ec1b3ce6d3b9500.tar.bz2 |
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>
Diffstat (limited to 'winsup/cygwin')
-rw-r--r-- | winsup/cygwin/cygtls.cc | 1 | ||||
-rw-r--r-- | winsup/cygwin/cygtls.h | 19 | ||||
-rw-r--r-- | winsup/cygwin/dcrt0.cc | 5 | ||||
-rw-r--r-- | winsup/cygwin/exception.h | 2 | ||||
-rw-r--r-- | 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: |