From 59693f60249a2ae2500344ac2ee1cb923e55587c Mon Sep 17 00:00:00 2001 From: Daniel Lehman Date: Fri, 19 May 2017 10:16:07 -0700 Subject: [PATCH 2/2] msvcrt: Translate SEH exception on each call to __CxxFrameHandler. v2: using feedback from Piotr (https://www.winehq.org/pipermail/wine-devel/2017-May/117946.html) - use 'se_translator_ctx' for struct - use __EXCEPT_CTX macro - calling terminate() if EXCEPTION_RECORD passed to handler isn't C++ (can happen if SEH translator has SEGV) 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 | 46 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/dlls/msvcrt/except_x86_64.c b/dlls/msvcrt/except_x86_64.c index e891567..57fce93 100644 --- a/dlls/msvcrt/except_x86_64.c +++ b/dlls/msvcrt/except_x86_64.c @@ -108,6 +108,14 @@ typedef struct __cxx_function_descr UINT flags; } cxx_function_descr; +typedef struct +{ + ULONG64 dest_frame; + ULONG64 orig_frame; + DISPATCHER_CONTEXT *dispatch; + const cxx_function_descr *descr; +} se_translator_ctx; + static inline void* rva_to_ptr(UINT rva, ULONG64 base) { return rva ? (void*)(base+rva) : NULL; @@ -429,6 +437,26 @@ static inline void find_catch_block(EXCEPTION_RECORD *rec, ULONG64 frame, TRACE("no matching catch block found\n"); } +static LONG CALLBACK se_translation_filter(EXCEPTION_POINTERS *ep, void *c) +{ + se_translator_ctx *ctx = (se_translator_ctx *)c; + EXCEPTION_RECORD *rec = ep->ExceptionRecord; + cxx_exception_type *exc_type; + + if (rec->ExceptionCode != CXX_EXCEPTION) + { + TRACE("non-c++ exception thrown in SEH handler: %x\n", rec->ExceptionCode); + MSVCRT_terminate(); + } + + exc_type = (cxx_exception_type *)rec->ExceptionInformation[2]; + find_catch_block(rec, ctx->dest_frame, ctx->dispatch, + ctx->descr, exc_type, ctx->orig_frame); + + __DestructExceptionObject(rec); + return ExceptionContinueSearch; +} + static DWORD cxx_frame_handler(EXCEPTION_RECORD *rec, ULONG64 frame, CONTEXT *context, DISPATCHER_CONTEXT *dispatch, const cxx_function_descr *descr) @@ -554,10 +582,22 @@ static DWORD cxx_frame_handler(EXCEPTION_RECORD *rec, ULONG64 frame, if (data->se_translator) { EXCEPTION_POINTERS except_ptrs; + se_translator_ctx ctx; - except_ptrs.ExceptionRecord = rec; - except_ptrs.ContextRecord = context; - data->se_translator(rec->ExceptionCode, &except_ptrs); + ctx.dest_frame = frame; + ctx.orig_frame = orig_frame; + ctx.dispatch = dispatch; + ctx.descr = descr; + __TRY + { + except_ptrs.ExceptionRecord = rec; + except_ptrs.ContextRecord = context; + data->se_translator(rec->ExceptionCode, &except_ptrs); + } + __EXCEPT_CTX(se_translation_filter, &ctx) + { + } + __ENDTRY } } -- 1.9.5