From a4ffc050c04a78669dc971feb2ca8e537e7889f8 Mon Sep 17 00:00:00 2001 From: Daniel Lehman Date: Fri, 19 May 2017 10:16:07 -0700 Subject: [PATCH 1/7] msvcrt: Translate SEH exception on each call to __CxxFrameHandler. Windows seems to call the current seh translator on each call to __CxxFrameHandler this commit mimics the behavior if the translator doesn't throw a c++ exception, it falls through as before; a catch(...) can still catch an SEH if it does throw, the C++ object is caught and used to find a matching catch block if one is found, RtlUnwindEx is called to consolidate as before otherwise, it falls through to __DestructExceptionObject to destroy the unused object this program demonstrates the issue: ========================== standalone.cpp // cl /Od /MD /EHa standalone.cpp /link /out:async.exe include include include struct klass { klass(int _x) : x(_x), myid(++id) {printf("%i id = %i\n", x, myid);} ~klass(void) {printf("%i id = %i~\n", x, myid);} int x; int myid; static int id; }; int klass::id = 0; void trans_func(unsigned int u, EXCEPTION_POINTERS* ptrs) { printf("%s: %i: u = %x code = 0x%08x\n", __FILE__, __LINE__, u, ptrs->ExceptionRecord->ExceptionCode); throw klass(0x42); } void c(void) { int *p = NULL; *p = 0x42; } void b(void) { try { c(); } catch (float f) {} } void a(void) { try { b(); } catch (float f) {} } int main(int argc, char **argv) { _se_translator_function orig = _set_se_translator( trans_func ); try { a(); } catch (klass x) { printf("catch: %x\n", x.x); } _set_se_translator(orig); return 0; } On Windows: U:\>async.exe standalone.cpp: 15: u = c0000005 code = 0xc0000005 <- translator called on each frame 66 id = 1 <- C++ exception object created... 66 id = 1~ <- ... and destroyed if no matching catch standalone.cpp: 15: u = c0000005 code = 0xc0000005 66 id = 2 66 id = 2~ standalone.cpp: 15: u = c0000005 code = 0xc0000005 66 id = 3 catch: 42 66 id = 3~ 66 id = 3~ on Wine: $ wine async.exe standalone.cpp: 15: u = c0000005 code = 0xc0000005 <- translated once to C++ 66 id = 1 catch: 42 66 id = 1~ 66 id = 1~ Signed-off-by: Daniel Lehman --- dlls/msvcrt/except_x86_64.c | 61 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/dlls/msvcrt/except_x86_64.c b/dlls/msvcrt/except_x86_64.c index e891567..6b0557a 100644 --- a/dlls/msvcrt/except_x86_64.c +++ b/dlls/msvcrt/except_x86_64.c @@ -108,6 +108,16 @@ typedef struct __cxx_function_descr UINT flags; } cxx_function_descr; +typedef struct __seh_frame_info +{ + EXCEPTION_REGISTRATION_RECORD frame; + ULONG64 dest_frame; + ULONG64 orig_frame; + DISPATCHER_CONTEXT *dispatch; + const cxx_function_descr *descr; + sigjmp_buf jmp; +} seh_frame_info; + static inline void* rva_to_ptr(UINT rva, ULONG64 base) { return rva ? (void*)(base+rva) : NULL; @@ -429,6 +439,39 @@ static inline void find_catch_block(EXCEPTION_RECORD *rec, ULONG64 frame, TRACE("no matching catch block found\n"); } +extern void __wine_rtl_unwind( EXCEPTION_REGISTRATION_RECORD* frame, EXCEPTION_RECORD *record, + void (*target)(void) ); +extern void CDECL __DestructExceptionObject(EXCEPTION_RECORD *rec); + +static void DECLSPEC_NORETURN seh_unwind_target(void) +{ + seh_frame_info *seh_frame = (seh_frame_info *)__wine_get_frame(); + __wine_pop_frame( &seh_frame->frame ); + siglongjmp( seh_frame->jmp, 1 ); +} + +static DWORD seh_translation_handler(EXCEPTION_RECORD *rec ,struct _EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, struct _EXCEPTION_REGISTRATION_RECORD **reg) +{ + seh_frame_info *seh_frame; + cxx_exception_type *exc_type; + + if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) + return ExceptionContinueSearch; + + if (rec->ExceptionCode != CXX_EXCEPTION) + return ExceptionContinueSearch; + + exc_type = (cxx_exception_type *)rec->ExceptionInformation[2]; + seh_frame = (seh_frame_info *)frame; + + find_catch_block(rec, seh_frame->dest_frame, seh_frame->dispatch, + seh_frame->descr, exc_type, seh_frame->orig_frame); + + __DestructExceptionObject(rec); + __wine_rtl_unwind(&seh_frame->frame, rec, seh_unwind_target); +} + static DWORD cxx_frame_handler(EXCEPTION_RECORD *rec, ULONG64 frame, CONTEXT *context, DISPATCHER_CONTEXT *dispatch, const cxx_function_descr *descr) @@ -554,10 +597,20 @@ static DWORD cxx_frame_handler(EXCEPTION_RECORD *rec, ULONG64 frame, if (data->se_translator) { EXCEPTION_POINTERS except_ptrs; - - except_ptrs.ExceptionRecord = rec; - except_ptrs.ContextRecord = context; - data->se_translator(rec->ExceptionCode, &except_ptrs); + seh_frame_info seh_frame; + + seh_frame.frame.Handler = seh_translation_handler; + seh_frame.dest_frame = frame; + seh_frame.orig_frame = orig_frame; + seh_frame.dispatch = dispatch; + seh_frame.descr = descr; + __wine_push_frame(&seh_frame.frame); + if (!sigsetjmp( seh_frame.jmp, 0 )) { + except_ptrs.ExceptionRecord = rec; + except_ptrs.ContextRecord = context; + data->se_translator(rec->ExceptionCode, &except_ptrs); + } + __wine_pop_frame(&seh_frame.frame); } } -- 1.9.5