/** Personality function that is called to request a stack unwinding operation. * The purpose of this function is essentially to jump to the nearest then * (or else) branch of a Pony try/then/else expression, relative to the 'error' * expression that triggered this unwind request. * * The EstablisherFrame given to this function describes the stack pointer * (RSP) value of the target of this unwind operation. * * Hence, the EstablisherFrame refers to the stack pointer of the caller * of the function that caused the exception that we are inspecting here. * Depending on the current exception handling state, this frame pointer * is the target to a call to RtlUnwindEx, which implements the logic * to unwind the stack to some frame further down the call stack. * * Most importantly, RtlUnwindEx is capable of unwinding past multiple * procedure frames, such that the cost of a Pony exception (neglecting * the kernel transition of RaiseException) ends up in being a single * longjump to the correct stack frame, where the handling else (and * possibly then block) resides. We statically know that there will * be one. * * Signature of RtlUnwindEx: * * VOID * NTAPI * RtlUnwindEx( * __in_opt ULONG64 TargetFrame, * __in_opt ULONG64 TargetIp, * __in_opt PEXCEPTION_RECORD ExceptionRecord, * __in PVOID ReturnValue, * __out PCONTEXT OriginalContext, * __in_opt PUNWIND_HISTORY_TABLE HistoryTable * ); * * TargetFrame is the stack pointer value to which we want to unwind to. * Consequently, TargetIp is the instruction pointer (which must be valid * within the provided stack frame) to an instruction where execution * should be continued. For our purposes, the ExceptionRecord argument * is optional and describes the reason of an exception. In Pony, there * is only one reason for stack unwinding, so we will basically ignore * the ExceptionRecord and simply pass it through from this personality * function. The motivation for this is performance, as otherwise * RtlUnwindEx would allocate a default record type STATUS_UNWIND). * * The ReturnValue argument can be used to place a value into the return * register once stack unwinding is completed. Since we are unwinding * function calls, we ignore this parameter. The OriginalContext can be * used to supply user defined scratch space to store information between * unwind operations. The runtimes exception dispatcher 'pony_throw' does * not supply any arguments, so this parameter is ignored as well. * * The HistoryTable can be used as a cache to improve performance of * repeated function entry lookups. If unwinding non-trivial call frames * becomes critical, using it might improve performance. * * RtlUnwindEx does not ever return to its caller (i.e. this personality * function). If it does, the process will (most likely) be terminated. */ EXCEPTION_DISPOSITION pony_personality_v0(EXCEPTION_RECORD *ExcRecord, void* EstablisherFrame, _CONTEXT *ContextRecord, DISPATCHER_CONTEXT* DispatcherContext) { if(ExcRecord->ExceptionCode != PONY_EXCEPTION_CLASS || IS_UNWINDING(ExcRecord->ExceptionFlags)) return ExceptionContinueSearch; if(!(ExcRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))) { if(!ponyint_lsda_scan(DispatcherContext, &landing_pad)) return ExceptionContinueSearch; RtlUnwindEx(EstablisherFrame, (PVOID)landing_pad, ExcRecord, NULL, ContextRecord, DispatcherContext->HistoryTable); } if(ExcRecord->ExceptionFlags & EXCEPTION_TARGET_UNWIND) { DispatcherContext->ContextRecord->Rdx = landing_pad; return ExceptionContinueSearch; } //never reaches here, if it does, impending doom incoming abort(); }
// // __CxxFrameHandler - Real entry point to the runtime // extern "C" _CRTIMP EXCEPTION_DISPOSITION __CxxFrameHandler( EHExceptionRecord *pExcept, // Information for this exception EHRegistrationNode *pFrame, // Dynamic information for this frame void *pContext, // Context info DispatcherContext *pDC // More dynamic info for this frame ) { FuncInfo *pFuncInfo; EXCEPTION_DISPOSITION result; PFRAMEINFO pframeinfo; if (!(IS_UNWINDING(PER_FLAGS(pExcept))) && ((unsigned long)pContext != (unsigned long)GetRTOC()) ) { // we are calling catch from different CFM, re-initialize our globals _pFrameInfoChain =(PFRAMEINFO) pDC->pftinfo->pFrameInfo; _pftinfo->pFrameInfo = pDC->pftinfo->pFrameInfo; cNested = 0; pframeinfo = (PFRAMEINFO)_pftinfo->pFrameInfo; while (pframeinfo) { cNested++; pframeinfo = pframeinfo->pNext; } } pFuncInfo = (FuncInfo*)pDC->FunctionEntry->HandlerData; result = __InternalCxxFrameHandler( pExcept, pFrame, pContext, pDC, pFuncInfo, 0, NULL, FALSE ); return result; }