Windows: Call C++ destructors without compiling with /EHa.

Thanks to Peter Cawley. #593
This commit is contained in:
Mike Pall 2023-09-15 05:47:29 +02:00
parent 7a1c139569
commit bd2d107151
2 changed files with 30 additions and 9 deletions

View File

@ -426,9 +426,7 @@ the toolchain used to compile LuaJIT:
on the C stack. The contents of the C++ exception object on the C stack. The contents of the C++ exception object
pass through unmodified.</li> pass through unmodified.</li>
<li>Lua errors can be caught on the C++ side with <tt>catch(...)</tt>. <li>Lua errors can be caught on the C++ side with <tt>catch(...)</tt>.
The corresponding Lua error message can be retrieved from the Lua stack.<br> The corresponding Lua error message can be retrieved from the Lua stack.</li>
For MSVC for Windows 64 bit this requires compilation of your C++ code
with <tt>/EHa</tt>.</li>
<li>Throwing Lua errors across C++ frames is safe. C++ destructors <li>Throwing Lua errors across C++ frames is safe. C++ destructors
will be called.</li> will be called.</li>
</ul> </ul>

View File

@ -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 ** 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 ** don't want you to write your own language-specific exception handler
** or to interact gracefully with MSVC. :-( ** 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 #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) ? int errcode = LJ_EXCODE_CHECK(rec->ExceptionCode) ?
LJ_EXCODE_ERRCODE(rec->ExceptionCode) : LUA_ERRRUN; LJ_EXCODE_ERRCODE(rec->ExceptionCode) : LUA_ERRRUN;
if ((rec->ExceptionFlags & 6)) { /* EH_UNWINDING|EH_EXIT_UNWIND */ 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. */ /* Unwind internal frames. */
err_unwind(L, cf, errcode); err_unwind(L, cf, errcode);
} else { } else {
void *cf2 = err_unwind(L, cf, 0); void *cf2 = err_unwind(L, cf, 0);
if (cf2) { /* We catch it, so start unwinding the upper frames. */ 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 || if (rec->ExceptionCode == LJ_MSVC_EXCODE ||
rec->ExceptionCode == LJ_GCC_EXCODE) { rec->ExceptionCode == LJ_GCC_EXCODE) {
#if !LJ_TARGET_CYGWIN #if !LJ_TARGET_CYGWIN
@ -285,8 +294,8 @@ LJ_FUNCA int lj_err_unwind_win(EXCEPTION_RECORD *rec,
/* Don't catch access violations etc. */ /* Don't catch access violations etc. */
return 1; /* ExceptionContinueSearch */ return 1; /* ExceptionContinueSearch */
} }
UNUSED(ctx);
#if LJ_TARGET_X86 #if LJ_TARGET_X86
UNUSED(ctx);
UNUSED(dispatch); UNUSED(dispatch);
/* Call all handlers for all lower C frames (including ourselves) again /* Call all handlers for all lower C frames (including ourselves) again
** with EH_UNWINDING set. Then call the specified function, passing cf ** 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); (void *)lj_vm_unwind_ff : (void *)lj_vm_unwind_c, errcode);
/* lj_vm_rtlunwind does not return. */ /* lj_vm_rtlunwind does not return. */
#else #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 /* Unwind the stack and call all handlers for all lower C frames
** (including ourselves) again with EH_UNWINDING set. Then set ** (including ourselves) again with EH_UNWINDING set. Then set
** stack pointer = f, result = errcode and jump to the specified target. ** stack pointer = f, result = errcode and jump to the specified target.