mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-11 15:22:16 +00:00
- removed the longjmp based exception catch/rethrow mechanism and instead force-terminate in case a user exception is thrown while the VM is executing JITed code on a non-Windows system
On Windows none of this is needed, because we can generate a proper unwind frame for the JITed functions, but even on Linux, it would require manual additions to each single piece of native code that ever gets called from inside a JIT compiled function. This is an utterly prohibitive proposition because it makes direct native calls a virtual impossibility So, in order to get the thrown error properly presented both I_Error and ThrowAbortException will now forward to I_FatalError if it is called from inside a JIT context.
This commit is contained in:
parent
b26b16e4b7
commit
42b9a41421
6 changed files with 112 additions and 174 deletions
|
@ -130,7 +130,7 @@ void I_Quit()
|
|||
extern FILE* Logfile;
|
||||
bool gameisdead;
|
||||
|
||||
void I_FatalError(const char* const error, ...)
|
||||
static void I_FatalError(const char* const error, va_list ap)
|
||||
{
|
||||
static bool alreadyThrown = false;
|
||||
gameisdead = true;
|
||||
|
@ -143,7 +143,7 @@ void I_FatalError(const char* const error, ...)
|
|||
int index;
|
||||
va_list argptr;
|
||||
va_start(argptr, error);
|
||||
index = vsnprintf(errortext, MAX_ERRORTEXT, error, argptr);
|
||||
index = vsnprintf(errortext, MAX_ERRORTEXT, error, ap);
|
||||
va_end(argptr);
|
||||
|
||||
extern void Mac_I_FatalError(const char*);
|
||||
|
@ -167,17 +167,35 @@ void I_FatalError(const char* const error, ...)
|
|||
}
|
||||
}
|
||||
|
||||
void I_Error(const char* const error, ...)
|
||||
void I_FatalError(const char* const error, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
va_start(argptr, error);
|
||||
I_FatalError(error, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
}
|
||||
|
||||
extern thread_local int jit_frames;
|
||||
void I_Error (const char *error, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char errortext[MAX_ERRORTEXT];
|
||||
|
||||
va_start(argptr, error);
|
||||
vsnprintf(errortext, MAX_ERRORTEXT, error, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
if (jit_frames == 0)
|
||||
{
|
||||
vsprintf (errortext, error, argptr);
|
||||
va_end (argptr);
|
||||
throw CRecoverableError(errortext);
|
||||
}
|
||||
else
|
||||
{
|
||||
I_FatalError(error, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void I_SetIWADInfo()
|
||||
|
|
|
@ -173,7 +173,7 @@ void Linux_I_FatalError(const char* errortext)
|
|||
}
|
||||
#endif
|
||||
|
||||
void I_FatalError (const char *error, ...)
|
||||
void I_FatalError (const char *error, va_list ap)
|
||||
{
|
||||
static bool alreadyThrown = false;
|
||||
gameisdead = true;
|
||||
|
@ -183,10 +183,7 @@ void I_FatalError (const char *error, ...)
|
|||
alreadyThrown = true;
|
||||
char errortext[MAX_ERRORTEXT];
|
||||
int index;
|
||||
va_list argptr;
|
||||
va_start (argptr, error);
|
||||
index = vsnprintf (errortext, MAX_ERRORTEXT, error, argptr);
|
||||
va_end (argptr);
|
||||
index = vsnprintf (errortext, MAX_ERRORTEXT, error, ap);
|
||||
|
||||
#ifdef __APPLE__
|
||||
Mac_I_FatalError(errortext);
|
||||
|
@ -214,17 +211,35 @@ void I_FatalError (const char *error, ...)
|
|||
}
|
||||
}
|
||||
|
||||
void I_FatalError(const char* const error, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
va_start(argptr, error);
|
||||
I_FatalError(error, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
}
|
||||
|
||||
extern thread_local int jit_frames;
|
||||
void I_Error (const char *error, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char errortext[MAX_ERRORTEXT];
|
||||
|
||||
va_start(argptr, error);
|
||||
|
||||
if (jit_frames == 0)
|
||||
{
|
||||
vsprintf (errortext, error, argptr);
|
||||
va_end (argptr);
|
||||
|
||||
throw CRecoverableError(errortext);
|
||||
}
|
||||
else
|
||||
{
|
||||
I_FatalError(error, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
}
|
||||
|
||||
void I_SetIWADInfo ()
|
||||
{
|
||||
|
|
|
@ -315,21 +315,12 @@ void JitCompiler::SetupSimpleFrame()
|
|||
}
|
||||
|
||||
static VMFrameStack *CreateFullVMFrame(VMScriptFunction *func, VMValue *args, int numargs)
|
||||
{
|
||||
try
|
||||
{
|
||||
VMFrameStack *stack = &GlobalVMStack;
|
||||
VMFrame *newf = stack->AllocFrame(func);
|
||||
CurrentJitExceptInfo->vmframes++;
|
||||
VMFillParams(args, newf, numargs);
|
||||
return stack;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VMThrowException(std::current_exception());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void JitCompiler::SetupFullVMFrame()
|
||||
{
|
||||
|
@ -361,16 +352,8 @@ void JitCompiler::SetupFullVMFrame()
|
|||
}
|
||||
|
||||
static void PopFullVMFrame(VMFrameStack *stack)
|
||||
{
|
||||
try
|
||||
{
|
||||
stack->PopFrame();
|
||||
CurrentJitExceptInfo->vmframes--;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VMThrowException(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
void JitCompiler::EmitPopFrame()
|
||||
|
@ -433,16 +416,9 @@ void JitCompiler::EmitNullPointerThrow(int index, EVMAbortException reason)
|
|||
}
|
||||
|
||||
void JitCompiler::ThrowException(VMScriptFunction *func, VMOP *line, int reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
ThrowAbortException(func, line, (EVMAbortException)reason, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VMThrowException(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
void JitCompiler::EmitThrowException(EVMAbortException reason)
|
||||
{
|
||||
|
|
|
@ -46,16 +46,9 @@ void JitCompiler::EmitIJMP()
|
|||
}
|
||||
|
||||
static void ValidateCall(DObject *o, VMFunction *f, int b)
|
||||
{
|
||||
try
|
||||
{
|
||||
FScopeBarrier::ValidateCall(o->GetClass(), f, b - 1);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VMThrowException(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
void JitCompiler::EmitSCOPE()
|
||||
{
|
||||
|
@ -240,8 +233,6 @@ void JitCompiler::EmitRETI()
|
|||
}
|
||||
|
||||
static DObject* CreateNew(PClass *cls, int c)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!cls->ConstructNative)
|
||||
{
|
||||
|
@ -260,12 +251,6 @@ static DObject* CreateNew(PClass *cls, int c)
|
|||
if (c) FScopeBarrier::ValidateNew(cls, c - 1);
|
||||
return cls->CreateNew();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VMThrowException(std::current_exception());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void JitCompiler::EmitNEW()
|
||||
{
|
||||
|
@ -279,8 +264,6 @@ void JitCompiler::EmitNEW()
|
|||
}
|
||||
|
||||
static void ThrowNewK(PClass *cls, int c)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!cls->ConstructNative)
|
||||
{
|
||||
|
@ -295,25 +278,12 @@ static void ThrowNewK(PClass *cls, int c)
|
|||
ThrowAbortException(X_OTHER, "Cannot create actors with 'new'");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VMThrowException(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
static DObject *CreateNewK(PClass *cls, int c)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (c) FScopeBarrier::ValidateNew(cls, c - 1);
|
||||
return cls->CreateNew();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VMThrowException(std::current_exception());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void JitCompiler::EmitNEW_K()
|
||||
{
|
||||
|
@ -392,8 +362,6 @@ void JitCompiler::EmitBOUND_R()
|
|||
}
|
||||
|
||||
void JitCompiler::ThrowArrayOutOfBounds(VMScriptFunction *func, VMOP *line, int index, int size)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (index >= size)
|
||||
{
|
||||
|
@ -404,8 +372,3 @@ void JitCompiler::ThrowArrayOutOfBounds(VMScriptFunction *func, VMOP *line, int
|
|||
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Negative current index = %i\n", index);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VMThrowException(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,17 @@ CUSTOM_CVAR(Bool, vm_jit, true, CVAR_NOINITCALL)
|
|||
CVAR(Bool, vm_jit, false, CVAR_NOINITCALL|CVAR_NOSET)
|
||||
#endif
|
||||
|
||||
// On Windows we can rely on the system's unwinder to deal with JITed code.
|
||||
// On Linux we can not and need to be able to tell the error management that we have some JITed code in the call chain.
|
||||
#ifdef _WIN32
|
||||
inline void beginVM() { }
|
||||
inline void endVM() { }
|
||||
#else
|
||||
thread_local int jit_frames;
|
||||
inline void beginVM() { if (vm_jit) jit_frames++; }
|
||||
inline void endVM() { if (vm_jit) jit_frames--; }
|
||||
#endif
|
||||
|
||||
cycle_t VMCycles[10];
|
||||
int VMCalls[10];
|
||||
|
||||
|
@ -314,13 +325,7 @@ int VMNativeFunction::NativeScriptCall(VMFunction *func, VMValue *params, int nu
|
|||
{
|
||||
err.MaybePrintMessage();
|
||||
err.stacktrace.AppendFormat("Called from %s\n", func->PrintableName.GetChars());
|
||||
VMThrowException(std::current_exception());
|
||||
return 0;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VMThrowException(std::current_exception());
|
||||
return 0;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,32 +539,6 @@ VMFrame *VMFrameStack::PopFrame()
|
|||
return parent;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// The jitted code does not implement C++ exception handling.
|
||||
// Catch them, longjmp out of the jitted functions, perform vmframe cleanup
|
||||
// then rethrow the C++ exception
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
thread_local JitExceptionInfo *CurrentJitExceptInfo;
|
||||
|
||||
void VMThrowException(std::exception_ptr cppException)
|
||||
{
|
||||
CurrentJitExceptInfo->cppException = cppException;
|
||||
longjmp(CurrentJitExceptInfo->sjljbuf, 1);
|
||||
}
|
||||
|
||||
static void VMRethrowException(JitExceptionInfo *exceptInfo)
|
||||
{
|
||||
int c = exceptInfo->vmframes;
|
||||
VMFrameStack *stack = &GlobalVMStack;
|
||||
for (int i = 0; i < c; i++)
|
||||
stack->PopFrame();
|
||||
|
||||
std::rethrow_exception(exceptInfo->cppException);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: Call
|
||||
|
@ -601,26 +580,13 @@ int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results,
|
|||
{
|
||||
VMCycles[0].Clock();
|
||||
|
||||
JitExceptionInfo *prevExceptInfo = CurrentJitExceptInfo;
|
||||
JitExceptionInfo newExceptInfo;
|
||||
CurrentJitExceptInfo = &newExceptInfo;
|
||||
if (setjmp(CurrentJitExceptInfo->sjljbuf) == 0)
|
||||
{
|
||||
auto sfunc = static_cast<VMScriptFunction *>(func);
|
||||
beginVM();
|
||||
int numret = sfunc->ScriptCall(sfunc, params, numparams, results, numresults);
|
||||
CurrentJitExceptInfo = prevExceptInfo;
|
||||
endVM();
|
||||
VMCycles[0].Unclock();
|
||||
return numret;
|
||||
}
|
||||
else
|
||||
{
|
||||
VMCycles[0].Unclock();
|
||||
auto exceptInfo = CurrentJitExceptInfo;
|
||||
CurrentJitExceptInfo = prevExceptInfo;
|
||||
VMRethrowException(exceptInfo);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
|
@ -733,7 +699,18 @@ void ThrowAbortException(VMScriptFunction *sfunc, VMOP *line, EVMAbortException
|
|||
{
|
||||
va_list ap;
|
||||
va_start(ap, moreinfo);
|
||||
|
||||
CVMAbortException err(reason, moreinfo, ap);
|
||||
|
||||
#ifndef _WIN32)
|
||||
|
||||
// Without a usable unwinder, aborting is the only real option here. :(
|
||||
if (jit_frames)
|
||||
{
|
||||
I_FatalError("VM error: %s", err.what());
|
||||
}
|
||||
#endif
|
||||
|
||||
err.stacktrace.AppendFormat("Called from %s at %s, line %d\n", sfunc->PrintableName.GetChars(), sfunc->SourceFileName.GetChars(), sfunc->PCToLine(line));
|
||||
throw err;
|
||||
va_end(ap);
|
||||
|
|
|
@ -439,17 +439,6 @@ extern thread_local VMFrameStack GlobalVMStack;
|
|||
|
||||
typedef std::pair<const class PType *, unsigned> FTypeAndOffset;
|
||||
|
||||
struct JitExceptionInfo
|
||||
{
|
||||
std::exception_ptr cppException;
|
||||
std::jmp_buf sjljbuf;
|
||||
int vmframes = 0;
|
||||
};
|
||||
|
||||
extern thread_local JitExceptionInfo *CurrentJitExceptInfo;
|
||||
|
||||
void VMThrowException(std::exception_ptr cppException);
|
||||
|
||||
typedef int(*JitFuncPtr)(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret);
|
||||
|
||||
class VMScriptFunction : public VMFunction
|
||||
|
|
Loading…
Reference in a new issue