🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

[Win64] Custom exception handling implementation - resuming from specific address

Started by
1 comment, last by Juliean 2 years, 1 month ago

Hello,

I'm at the last hurdle for my JIT-implementation, for now. I've implemented exception-handling based on this article: x64 exception handling

I'm registering the unwind-information using RtlAddFunctionTable, specifying a “language-specific handler" that invokes the necessary cleanup-code:

EXCEPTION_DISPOSITION handleException(PEXCEPTION_RECORD exceptionRecord, ULONG64 establisherFrame, PCONTEXT contextRecord, PDISPATCHER_CONTEXT dispatcherContext)
{
	auto& state = *reinterpret_cast<ExecutionStateJIT*>(contextRecord->Rbx);
	
	// TODO: compile into handler directly
	const auto* pAddress = (const char*)dispatcherContext->ControlPc;
	const auto* pHandler = g_interpreter.GetExceptionHandlerAddress(pAddress);

	if (const auto* pResumeAddress = (JitExceptionFunction(pHandler))(state))
	// TODO: implement => doesn't work
	//	return (const char*)EXCEPTION_DISPOSITION::ExceptionContinueExecution;
	else
		return (const char*)EXCEPTION_DISPOSITION::ExceptionContinueSearch;
	

Now the basic process is working, when I throw an exception:

throw AERuntimeError(sys::toA(strMessage));

Then the unwind is performed properly, and the exception is passed from my jit-code into the c++ that is calling it. However, I can't seem to find a way how to prevent the exception from being passed on. Conceptually, I have the ability to specify a “catch” block in my visual-scripting language, akin to:

try
{
	throwingMethod();
}
catch (...) {} // do nothing

// continue

But I'm failing to understand how I would be able to implement this with the exception-handling available to me. EXCEPTION_DISPOSITION is extremely limited in its return-values. The only option that comes close is “ExceptionContinueExecution”, but this would not be the correct address (the address I actually want to resume at is returned by my own handler), and it also fails since for some reason the exception that the handler receives is marked as EXCEPTION_NONCONTINUABLE - which I don't quite understand the difference, there's only a short remark at the end of https://docs.microsoft.com/en-us/windows/win32/debug/exception-handling .

I've tried to look at how the normal c++ runtimes handles this kind of situation, but it seems to invoke an entirely different routine. The stack for the exception-code above in c++ is:

vcruntime140_1d.dll!_CallSettingFrame_LookupContinuationIndex() Line 98 Unknown
vcruntime140_1d.dll!__FrameHandler4::CxxCallCatchBlock(_EXCEPTION_RECORD * pExcept) Line 1393 C++
ntdll.dll!RcConsolidateFrames?() Unknown

while the callstack for my handler ends up being:

ntdll.dll!RtlpExecuteHandlerForException?()	Unknown
ntdll.dll!RtlDispatchException()	Unknown
ntdll.dll!KiUserExceptionDispatch?()	Unknown
KernelBase.dll!RaiseException?()	Unknown
vcruntime140d.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 75	C++

Which makes sense that its different, but not why the c++-stack does not have the _CxxThrowException at all anymore (which means that I cannot really implement a custom “jump to my address”-solution without seriously messing with the registered callstack).

So, does somebody have any experience using this exception-handling process for my specific use-case?

Advertisement

Ok, after browsing many more articles I found the solution here: https://blog.talosintelligence.com/2014/06/exceptional-behavior-windows-81-x64-seh.html

Its actually rather simple, you have to call RtlUnwindEx in the exception-handler, with the target-address as second argument:

if (const auto* pResumeAddress = (JitExceptionFunction(pHandler))(state))
{
	RtlUnwindEx((void*)establisherFrame, (void*)pResumeAddress, exceptionRecord, 0, contextRecord, nullptr);
	
	UNREACHABLE;
}
else
	return EXCEPTION_DISPOSITION::ExceptionContinueSearch;

I have no idea how anyone is supposed to figure that out on their own since the official documentation for the whole process is missing such important details, but oh well.

This topic is closed to new replies.

Advertisement