From: Daniel Lehman Subject: [PATCH 5/7] msvcrt: Support rethrowing C++ exceptions. Message-Id: <8b1e0f491a2747458d2ecfc7be9c9707@RED-INF-MXMB-P4.esri.com> Date: Wed, 24 May 2017 00:55:12 +0000 From df8041130ef2704147a1582da891eb0813a57df5 Mon Sep 17 00:00:00 2001 From: Daniel Lehman Date: Fri, 19 May 2017 11:26:26 -0700 Subject: [PATCH 5/7] msvcrt: Support rethrowing C++ exceptions. this ties into the previous commits on the order of unwinding catch frames and destroying objects and into the following commits for rethrowing SEH exceptions in case of a rethrow, Windows appears to unwind the catch frame, then throw a new exception while searching for an exception handler during a rethrow clear the unwind helpers. this triggers the unwind code to use the ControlPc at the point of the rethrow the FIXME about unwinding in the catch block is handled by the previous commits the program below illustrates the difference between throwing a new exception and a rethrow: ------- throw new: klass::klass: old <- copy ctor called as catch block is found *** caught old <- inside catch block klass::klass: new <- copy ctor called as catch block found for new exception klass::~klass: catch block <- unwinding throwit() catch frame, local object klass::~klass: old <- unwinding throwit() catch frame, copy of exception object klass::~klass: old <- destroy old exception object *** caught new <- inside catch block for new klass::~klass: new <- unwinding main() catch frame klass::~klass: new <- destroy new exception object ------- rethrow: klass::klass: old <- copy ctor called as catch block is found *** caught old <- inside catch block klass::~klass: catch block <- unwinding throwit() catch frame, local object klass::~klass: old <- unwinding throwit() catch frame, copy of exception object klass::klass: old <- copy ctor called as catch block found for rethrow (note this is done after unwinding catch frame) *** caught rethrow old <- inside catch block for rethrow klass::~klass: old <- unwinding main() catch frame klass::~klass: old <- destroy copy of rethrow object // cl /Od /MD /EHs standalone.cpp include include struct klass { klass(const char *_str) : str(_str) {} klass(const klass ©) : str(copy.str) { printf("%s: %s\n", __FUNCTION__, str); } ~klass() { printf("%s: %s\n", __FUNCTION__, str); } const char *str; }; void throwit(bool rethrow) { try { throw klass("old"); } catch (klass x) { klass k("catch block"); printf("*** caught %s\n", x.str); rethrow ? throw : throw klass("new"); } } int main(int argc, char **argv) { printf("\n------- throw new:\n"); try { throwit(false); } catch (klass x) {printf("*** caught %s\n", x.str);} printf("\n------- rethrow:\n"); try { throwit(true); } catch (klass x) {printf("*** caught rethrow %s\n", x.str);} return 0; } Signed-off-by: Daniel Lehman --- dlls/msvcrt/except_x86_64.c | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/dlls/msvcrt/except_x86_64.c b/dlls/msvcrt/except_x86_64.c index 1992e75..64cf808 100644 --- a/dlls/msvcrt/except_x86_64.c +++ b/dlls/msvcrt/except_x86_64.c @@ -320,6 +320,20 @@ static void cxx_local_unwind(ULONG64 frame, DISPATCHER_CONTEXT *dispatch, unwind_help[0] = last_level; } +static inline BOOL cxx_is_rethrow(const EXCEPTION_RECORD *rec) +{ + return rec->ExceptionCode==CXX_EXCEPTION && + !rec->ExceptionInformation[1] && !rec->ExceptionInformation[2]; +} + +static LONG CALLBACK cxx_rethrow_filter(PEXCEPTION_POINTERS eptrs) +{ + EXCEPTION_RECORD *rec = eptrs->ExceptionRecord; + if (cxx_is_rethrow(rec)) + return EXCEPTION_EXECUTE_HANDLER; + return EXCEPTION_CONTINUE_SEARCH; +} + static void* WINAPI call_catch_block(EXCEPTION_RECORD *rec); static inline BOOL cxx_is_consolidate(const EXCEPTION_RECORD *rec) @@ -364,16 +378,24 @@ static void* WINAPI call_catch_block(EXCEPTION_RECORD *rec) int *unwind_help = rva_to_ptr(descr->unwind_help, frame); EXCEPTION_REGISTRATION_RECORD catch_frame; cxx_frame_info frame_info; - void *ret_addr; + void *ret_addr = NULL; TRACE("calling handler %p\n", handler); - /* FIXME: native does local_unwind here in case of exception rethrow */ catch_frame.Handler = cxx_catch_cleanup; __wine_push_frame(&catch_frame); - __CxxRegisterExceptionObject(&prev_rec, &frame_info); - ret_addr = handler(0, frame); - __CxxUnregisterExceptionObject(&frame_info, FALSE); + __TRY + { + __CxxRegisterExceptionObject(&prev_rec, &frame_info); + ret_addr = handler(0, frame); + __CxxUnregisterExceptionObject(&frame_info, FALSE); + } + __EXCEPT(cxx_rethrow_filter) + { + _CxxThrowException((exception *)prev_rec->ExceptionInformation[1], + (const cxx_exception_type *)prev_rec->ExceptionInformation[2]); + } + __ENDTRY __wine_pop_frame(&catch_frame); unwind_help[0] = -2; @@ -609,9 +631,16 @@ static DWORD cxx_frame_handler(EXCEPTION_RECORD *rec, ULONG64 frame, } if (!descr->tryblock_count) return ExceptionContinueSearch; - if (rec->ExceptionCode == CXX_EXCEPTION && - rec->ExceptionInformation[1] == 0 && rec->ExceptionInformation[2] == 0) + if (cxx_is_rethrow(rec)) { + if (frame != orig_frame) + { + int *unwind_help = rva_to_ptr(descr->unwind_help, orig_frame); + unwind_help[0] = -2; + unwind_help[1] = -1; + return ExceptionContinueSearch; + } + *rec = *msvcrt_get_thread_data()->exc_record; rec->ExceptionFlags &= ~EH_UNWINDING; if (TRACE_ON(seh)) { -- 1.9.5