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>
This commit is contained in:
Corinna Vinschen 2015-07-07 20:45:06 +02:00
parent 29a1263227
commit 60f10c64aa
5 changed files with 71 additions and 2 deletions

View File

@ -224,5 +224,6 @@ void san::leave ()
{ {
/* Restore tls_pathbuf counters in case of error. */ /* Restore tls_pathbuf counters in case of error. */
_my_tls.locals.pathbufs._counters = _cnt; _my_tls.locals.pathbufs._counters = _cnt;
_my_tls.andreas = _clemente;
} }
#endif #endif

View File

@ -301,11 +301,26 @@ extern _cygtls *_sig_tls;
#ifdef __x86_64__ #ifdef __x86_64__
class san class san
{ {
san *_clemente;
uint64_t _cnt; uint64_t _cnt;
public: 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; _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 /* This is the first thing called in the __except handler. The attribute
"returns_twice" makes sure that GCC disregards any register value set "returns_twice" makes sure that GCC disregards any register value set
@ -363,7 +378,7 @@ public:
{ \ { \
__label__ __l_try, __l_except, __l_endtry; \ __label__ __l_try, __l_except, __l_endtry; \
__mem_barrier; \ __mem_barrier; \
san __sebastian; \ san __sebastian (&&__l_except); \
__asm__ goto ("\n" \ __asm__ goto ("\n" \
" .seh_handler _ZN9exception7myfaultEP17_EXCEPTION_RECORDPvP8_CONTEXTP19_DISPATCHER_CONTEXT, @except \n" \ " .seh_handler _ZN9exception7myfaultEP17_EXCEPTION_RECORDPvP8_CONTEXTP19_DISPATCHER_CONTEXT, @except \n" \
" .seh_handlerdata \n" \ " .seh_handlerdata \n" \

View File

@ -800,6 +800,11 @@ dll_crt0_0 ()
if (!dynamically_loaded) if (!dynamically_loaded)
sigproc_init (); 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"); debug_printf ("finished dll_crt0_0 initialization");
} }

View File

@ -160,6 +160,8 @@ public:
} }
}; };
LONG CALLBACK myfault_altstack_handler (EXCEPTION_POINTERS *);
#endif /* !__x86_64__ */ #endif /* !__x86_64__ */
class cygwin_exception class cygwin_exception

View File

@ -588,6 +588,50 @@ exception::myfault (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
/* NOTREACHED, make gcc happy. */ /* NOTREACHED, make gcc happy. */
return ExceptionContinueSearch; 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 #endif
/* Main exception handler. */ /* Main exception handler. */
@ -697,11 +741,13 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
break; break;
case STATUS_STACK_OVERFLOW: case STATUS_STACK_OVERFLOW:
#if 0
/* If we encounter a stack overflow, and if the thread has no alternate /* 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 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. */ Linux behaviour and also makes a lot of sense on Windows. */
if (me.altstack.ss_flags) if (me.altstack.ss_flags)
global_sigs[SIGSEGV].sa_handler = SIG_DFL; global_sigs[SIGSEGV].sa_handler = SIG_DFL;
#endif
/*FALLTHRU*/ /*FALLTHRU*/
case STATUS_ARRAY_BOUNDS_EXCEEDED: case STATUS_ARRAY_BOUNDS_EXCEEDED:
case STATUS_IN_PAGE_ERROR: case STATUS_IN_PAGE_ERROR: