From bd2d10715165b89d30e46c5075aed725705dfe5b Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Fri, 15 Sep 2023 05:47:29 +0200 Subject: [PATCH] Windows: Call C++ destructors without compiling with /EHa. Thanks to Peter Cawley. #593 --- doc/extensions.html | 4 +--- src/lj_err.c | 35 +++++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/doc/extensions.html b/doc/extensions.html index eb591d1e..a4f20841 100644 --- a/doc/extensions.html +++ b/doc/extensions.html @@ -426,9 +426,7 @@ the toolchain used to compile LuaJIT: on the C stack. The contents of the C++ exception object pass through unmodified.
  • Lua errors can be caught on the C++ side with catch(...). -The corresponding Lua error message can be retrieved from the Lua stack.
    -For MSVC for Windows 64 bit this requires compilation of your C++ code -with /EHa.
  • +The corresponding Lua error message can be retrieved from the Lua stack.
  • Throwing Lua errors across C++ frames is safe. C++ destructors will be called.
  • diff --git a/src/lj_err.c b/src/lj_err.c index 9677a1b0..cadc76bd 100644 --- a/src/lj_err.c +++ b/src/lj_err.c @@ -209,11 +209,6 @@ static void *err_unwind(lua_State *L, void *stopcf, int errcode) ** from 3rd party docs or must be found by trial-and-error. They really ** don't want you to write your own language-specific exception handler ** or to interact gracefully with MSVC. :-( -** -** Apparently MSVC doesn't call C++ destructors for foreign exceptions -** unless you compile your C++ code with /EHa. Unfortunately this means -** catch (...) also catches things like access violations. The use of -** _set_se_translator doesn't really help, because it requires /EHa, too. */ #define WIN32_LEAN_AND_MEAN @@ -270,11 +265,25 @@ LJ_FUNCA int lj_err_unwind_win(EXCEPTION_RECORD *rec, int errcode = LJ_EXCODE_CHECK(rec->ExceptionCode) ? LJ_EXCODE_ERRCODE(rec->ExceptionCode) : LUA_ERRRUN; if ((rec->ExceptionFlags & 6)) { /* EH_UNWINDING|EH_EXIT_UNWIND */ + if (rec->ExceptionCode == STATUS_LONGJUMP && + rec->ExceptionRecord && + LJ_EXCODE_CHECK(rec->ExceptionRecord->ExceptionCode)) { + errcode = LJ_EXCODE_ERRCODE(rec->ExceptionRecord->ExceptionCode); + if ((rec->ExceptionFlags & 0x20)) { /* EH_TARGET_UNWIND */ + /* Unwinding is about to finish; revert the ExceptionCode so that + ** RtlRestoreContext does not try to restore from a _JUMP_BUFFER. + */ + rec->ExceptionCode = 0; + } + } /* Unwind internal frames. */ err_unwind(L, cf, errcode); } else { void *cf2 = err_unwind(L, cf, 0); if (cf2) { /* We catch it, so start unwinding the upper frames. */ +#if !LJ_TARGET_X86 + EXCEPTION_RECORD rec2; +#endif if (rec->ExceptionCode == LJ_MSVC_EXCODE || rec->ExceptionCode == LJ_GCC_EXCODE) { #if !LJ_TARGET_CYGWIN @@ -285,8 +294,8 @@ LJ_FUNCA int lj_err_unwind_win(EXCEPTION_RECORD *rec, /* Don't catch access violations etc. */ return 1; /* ExceptionContinueSearch */ } - UNUSED(ctx); #if LJ_TARGET_X86 + UNUSED(ctx); UNUSED(dispatch); /* Call all handlers for all lower C frames (including ourselves) again ** with EH_UNWINDING set. Then call the specified function, passing cf @@ -297,6 +306,20 @@ LJ_FUNCA int lj_err_unwind_win(EXCEPTION_RECORD *rec, (void *)lj_vm_unwind_ff : (void *)lj_vm_unwind_c, errcode); /* lj_vm_rtlunwind does not return. */ #else + if (LJ_EXCODE_CHECK(rec->ExceptionCode)) { + /* For unwind purposes, wrap the EXCEPTION_RECORD in something that + ** looks like a longjmp, so that MSVC will execute C++ destructors in + ** the frames we unwind over. ExceptionInformation[0] should really + ** contain a _JUMP_BUFFER*, but hopefully nobody is looking too closely + ** at this point. + */ + rec2.ExceptionCode = STATUS_LONGJUMP; + rec2.ExceptionRecord = rec; + rec2.ExceptionAddress = 0; + rec2.NumberParameters = 1; + rec2.ExceptionInformation[0] = (ULONG_PTR)ctx; + rec = &rec2; + } /* Unwind the stack and call all handlers for all lower C frames ** (including ourselves) again with EH_UNWINDING set. Then set ** stack pointer = f, result = errcode and jump to the specified target.