From 2ab03f3ab4838fc013b995cc303118fd6e3079ee Mon Sep 17 00:00:00 2001 From: Daniel Lehman Date: Fri, 19 May 2017 11:21:52 -0700 Subject: [PATCH 3/7] msvcrt: Unwind the catch block frame. this ties into the previous commit in support of nested c++ exceptions normally, unwinding past a frame calls the destructors for all objects from the ControlPc to the lowest state but there seems to be a special case for catch blocks. they are unwound separately, stopping where the handler is NULL the program below produces this on Windows: ... <- throw new obj inside of a's catch block klass::~klass: a catch <- unwinding past a's catch block klass::~klass: a obj <- destroying a's exception object klass::~klass: a <- finish unwinding the rest of a main catch <- how we handle the new exception klass::~klass: new obj when combined with the previous commit, this commit produces the same output as Windows when unwinding to the exception handler: a _CxxThrowException <- call '~klass: a' (cxx_frame_handler -> cxx_local_unwind) ... call_consolidate call_catch_block <- call '~klass: a obj' (cxx_catch_cleanup) catch$0 <- call '~klass: a catch' (cxx_frame_handler -> cxx_catch_unwind) =============================== // cl /Od /MD /EHs standalone.cpp include include struct klass { klass(const char *msg) : str(msg) {} ~klass() { printf("%s: %s\n", __FUNCTION__, str); } const char *str; }; void a(void) { klass foo("a"); try { throw klass("a obj"); } catch (klass &k) { klass foo("a catch"); throw klass("new obj"); } } int main(int argc, char **argv) { try { a(); } catch (klass &k) {printf("main catch\n");} return 0; } Signed-off-by: Daniel Lehman --- dlls/msvcrt/except_x86_64.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/dlls/msvcrt/except_x86_64.c b/dlls/msvcrt/except_x86_64.c index ec18746..68ea33a 100644 --- a/dlls/msvcrt/except_x86_64.c +++ b/dlls/msvcrt/except_x86_64.c @@ -473,6 +473,44 @@ static inline void find_catch_block(EXCEPTION_RECORD *rec, ULONG64 frame, TRACE("no matching catch block found\n"); } +static void cxx_catch_unwind(ULONG64 frame, DISPATCHER_CONTEXT *dispatch, + const cxx_function_descr *descr) +{ + const unwind_info *unwind_table = rva_to_ptr(descr->unwind_table, dispatch->ImageBase); + void (__cdecl *handler)(ULONG64 unk, ULONG64 rbp); + int *unwind_help = rva_to_ptr(descr->unwind_help, frame); + int trylevel; + + if (unwind_help[0] == -2) + { + trylevel = ip_to_state(rva_to_ptr(descr->ipmap, dispatch->ImageBase), + descr->ipmap_count, dispatch->ControlPc-dispatch->ImageBase); + } + else + { + trylevel = unwind_help[0]; + } + + TRACE("current level: %d\n", trylevel); + while (trylevel != -1) + { + if (trylevel<0 || trylevel>=descr->unwind_count) + { + ERR("invalid trylevel %d\n", trylevel); + MSVCRT_terminate(); + } + handler = rva_to_ptr(unwind_table[trylevel].handler, dispatch->ImageBase); + if (!handler) break; + + TRACE("handler: %p\n", handler); + handler(0, frame); + + trylevel = unwind_table[trylevel].prev; + } + unwind_help[0] = trylevel; + unwind_help[1] = trylevel; +} + extern void __wine_rtl_unwind( EXCEPTION_REGISTRATION_RECORD* frame, EXCEPTION_RECORD *record, void (*target)(void) ); extern void CDECL __DestructExceptionObject(EXCEPTION_RECORD *rec); @@ -572,11 +610,15 @@ static DWORD cxx_frame_handler(EXCEPTION_RECORD *rec, ULONG64 frame, } else if(frame == orig_frame) cxx_local_unwind(frame, dispatch, descr, -1); + else + cxx_catch_unwind(orig_frame, dispatch, descr); return ExceptionContinueSearch; } if (frame == orig_frame) cxx_local_unwind(frame, dispatch, descr, rec->ExceptionFlags & EH_TARGET_UNWIND ? trylevel : -1); + else + cxx_catch_unwind(orig_frame, dispatch, descr); return ExceptionContinueSearch; } if (!descr->tryblock_count) return ExceptionContinueSearch; -- 1.9.5