/*** *trnsctrl.cpp - * * Copyright (c) 1990-2001, Microsoft Corporation. All rights reserved. * *Purpose: * *Revision History: * 06-01-97 Created by TiborL. * 07-12-99 RDL Image relative fixes under CC_P7_SOFT25. * 10-07-99 SAH utc_p7#1126: fix ipsr.ri reset. * 10-19-99 TGL Miscellaneous unwind fixes. * 03-15-00 PML Remove CC_P7_SOFT25, which is now on permanently. * 03-30-00 SAH New version of GetLanguageSpecificData from ntia64.h. * 06-08-00 RDL VS#111429: IA64 workaround for AV while handling throw. * ****/ #if defined(_NTSUBSET_) extern "C" { #include #include #include #include // STATUS_UNHANDLED_EXCEPTION #include #include // ExRaiseException } #endif extern "C" { #include }; #include #include #include #include #include #include #include #include #include #include #pragma hdrstop #ifdef _MT #define pFrameInfoChain (*((FRAMEINFO **) &(_getptd()->_pFrameInfoChain))) #define pUnwindContext (*((CONTEXT **) &(_getptd()->_pUnwindContext))) #define _ImageBase (_getptd()->_ImageBase) #define _TargetGp (_getptd()->_TargetGp) #define _ThrowImageBase (_getptd()->_ThrowImageBase) #define _pCurrentException (*((EHExceptionRecord **)&(_getptd()->_curexception))) #else static FRAMEINFO *pFrameInfoChain = NULL; // used to remember nested frames static CONTEXT *pUnwindContext = NULL; // context to assist the return to 'UnwindNestedFrames' static unsigned __int64 _ImageBase = 0; static unsigned __int64 _ThrowImageBase = 0; static unsigned __int64 _TargetGp = 0; extern EHExceptionRecord *_pCurrentException; // defined in frame.cpp #endif // Should be used out of ntia64.h, but can't figure out how to allow that with // existing dependencies. // If GetLanguageSpecificData changes cause a redefinition error rather than // using wrong version. // Version 2 = soft2.3 conventions // Version 3 = soft2.6 conventions #define GetLanguageSpecificData(f, base) \ ((((PUNWIND_INFO)(base + f->UnwindInfoAddress))->Version <= 2) ? \ (((PVOID)(base + f->UnwindInfoAddress + sizeof(UNWIND_INFO) + \ ((PUNWIND_INFO)(base+f->UnwindInfoAddress))->DataLength*sizeof(ULONGLONG) + sizeof(ULONGLONG)))) : \ (((PVOID)(base + f->UnwindInfoAddress + sizeof(UNWIND_INFO) + \ ((PUNWIND_INFO)(base+f->UnwindInfoAddress))->DataLength*sizeof(ULONGLONG) + sizeof(ULONG))))) extern "C" VOID RtlRestoreContext (PCONTEXT ContextRecord,PEXCEPTION_RECORD ExceptionRecord OPTIONAL); extern "C" void RtlCaptureContext(CONTEXT*); extern "C" void _GetNextInstrOffset(PVOID*); extern "C" void __FrameUnwindToState(EHRegistrationNode *, DispatcherContext *, FuncInfo *, __ehstate_t); // // Returns the establisher frame pointers. For catch handlers it is the parent's frame pointer. // EHRegistrationNode *_GetEstablisherFrame( EHRegistrationNode *pRN, DispatcherContext *pDC, FuncInfo *pFuncInfo, EHRegistrationNode *pEstablisher ) { TryBlockMapEntry *pEntry; unsigned num_of_try_blocks = FUNC_NTRYBLOCKS(*pFuncInfo); unsigned index; __ehstate_t curState; curState = _StateFromControlPc(pFuncInfo, pDC); pEstablisher->MemoryStackFp = pRN->MemoryStackFp; pEstablisher->BackingStoreFp = pRN->BackingStoreFp; for (index = 0; index < num_of_try_blocks; index++) { pEntry = FUNC_PTRYBLOCK(*pFuncInfo, index, pDC->ImageBase); if (curState > TBME_HIGH(*pEntry) && curState <= TBME_CATCHHIGH(*pEntry)) { pEstablisher->MemoryStackFp = *(__int64 *)OffsetToAddress(-8,pRN->MemoryStackFp); pEstablisher->BackingStoreFp = *(__int64 *)OffsetToAddress(-16,pRN->BackingStoreFp); break; } } return pEstablisher; } #if 0 // v-vadimp new version with NLG support for the debugger is in handlers.s // // Temporary version until the asm version is written that supports NLG // extern "C" void* _CallSettingFrame( void* handler, EHRegistrationNode *pEstablisher, ULONG NLG_CODE) { void* retValue = __Cxx_ExecuteHandler( pEstablisher->MemoryStackFp, pEstablisher->BackingStoreFp, (__int64)handler, _TargetGp ); return retValue? (void*)(_ImageBase + (__int32)retValue) : NULL; } #endif extern "C" CONTEXT* _GetUnwindContext() { return pUnwindContext; } extern "C" unsigned __int64 _GetImageBase() { return _ImageBase; } extern "C" VOID _SetImageBase(unsigned __int64 ImageBaseToRestore) { _ImageBase = ImageBaseToRestore; } extern "C" unsigned __int64 _GetThrowImageBase() { return _ThrowImageBase; } extern "C" VOID _SetThrowImageBase(unsigned __int64 NewThrowImageBase) { _ThrowImageBase = NewThrowImageBase; } extern "C" unsigned __int64 _GetTargetGP(unsigned __int64 TargetAddress) { unsigned __int64 ImageBase; unsigned __int64 TargetGp; PRUNTIME_FUNCTION pContFunctionEntry = RtlLookupFunctionEntry(TargetAddress, &ImageBase, &TargetGp); // return _TargetGp; return TargetGp; } extern "C" VOID _MoveContext(CONTEXT* pTarget, CONTEXT* pSource) { RtlMoveMemory(pTarget, pSource, sizeof(CONTEXT)); } // This function returns the try block for the given state if the state is in a // catch; otherwise, NULL is returned. static __inline TryBlockMapEntry *_CatchTryBlock( FuncInfo *pFuncInfo, __ehstate_t curState ) { TryBlockMapEntry *pEntry; unsigned num_of_try_blocks = FUNC_NTRYBLOCKS(*pFuncInfo); unsigned index; for (index = 0; index < num_of_try_blocks; index++) { pEntry = FUNC_PTRYBLOCK(*pFuncInfo, index, _ImageBase); if (curState > TBME_HIGH(*pEntry) && curState <= TBME_CATCHHIGH(*pEntry)) { return pEntry; } } return NULL; } // // This routine returns TRUE if we are executing from within a catch. Otherwise, FALSE is returned. // BOOL _ExecutionInCatch( DispatcherContext *pDC, FuncInfo *pFuncInfo ) { __ehstate_t curState = _StateFromControlPc(pFuncInfo, pDC); return _CatchTryBlock(pFuncInfo, curState)? TRUE : FALSE; } // This function unwinds to the empty state. VOID __FrameUnwindToEmptyState( EHRegistrationNode *pRN, DispatcherContext *pDC, FuncInfo *pFuncInfo ) { __ehstate_t stateFromControlPC; TryBlockMapEntry *pEntry; EHRegistrationNode EstablisherFramePointers, *pEstablisher; pEstablisher = _GetEstablisherFrame(pRN, pDC, pFuncInfo, &EstablisherFramePointers); stateFromControlPC = _StateFromControlPc(pFuncInfo, pDC); pEntry = _CatchTryBlock(pFuncInfo, stateFromControlPC); // Reuse pFrameInfoChain->State to propagate the previous catch block's final state. // This is used in case there is a nested catch inside this catch // who already dtored object's in our frame. This can happen if in our catch // there is a nested catch that throws and the catch executed is an outer catch. // //try{ // try{ // throw(1); // }catch(int){ // B b4; // try{ // throw 2; // }catch(int){ // //this throw would dtor b4 by calling the outermost catch, // //and also during unwinding the first catch would try to dtor as well. // throw 3; // } // } //}catch(int){ // throw 4; //} // if( pEntry && pFrameInfoChain != NULL ) { __ehstate_t curState = GetCurrentState(pEstablisher, pDC, pFuncInfo); if( pFrameInfoChain->State != -2 && pFrameInfoChain->State < curState ) SetState(pEstablisher, pDC, pFuncInfo, pFrameInfoChain->State); pFrameInfoChain->State = curState; } else if( pFrameInfoChain != NULL ) { __ehstate_t curState = GetCurrentState(pEstablisher, pDC, pFuncInfo); if( pEntry == NULL && stateFromControlPC < curState ) { SetState(pEstablisher, pDC, pFuncInfo, stateFromControlPC - 1); } pFrameInfoChain->State = -2; } __FrameUnwindToState(pEstablisher, pDC, pFuncInfo, pEntry == NULL ? EH_EMPTY_STATE : TBME_HIGH(*pEntry) /*+ 1*/); } BOOL _IsExceptionObjectDestroyed(PVOID pExceptionObject,FRAMEINFO *pFrameInfo) { for (; pFrameInfo != NULL; pFrameInfo = pFrameInfo->pNext ) { if( pFrameInfo->pExceptionObjectDestroyed == pExceptionObject ) { return TRUE; } } return FALSE; } void _MarkExceptionObjectDestroyed(EHExceptionRecord *pExcept) { for (FRAMEINFO *pFrameInfo = pFrameInfoChain; pFrameInfo != NULL; pFrameInfo = pFrameInfo->pNext ) { if( pFrameInfo->pExcept == pExcept ) { pFrameInfo->pExceptionObjectDestroyed = PER_PEXCEPTOBJ(pExcept); } } } void _UnlinkFrame(FRAMEINFO *pFrameInfo) { FRAMEINFO *pPrevFrameInfo = pFrameInfoChain; if( pFrameInfoChain == pFrameInfo ) { pFrameInfoChain = pFrameInfoChain->pNext; return; } for (FRAMEINFO *pCurFrameInfo = pFrameInfoChain; pCurFrameInfo != NULL; pCurFrameInfo = pCurFrameInfo->pNext ) { if( pCurFrameInfo == pFrameInfo ) { pPrevFrameInfo->pNext = pCurFrameInfo->pNext; return; } pPrevFrameInfo = pCurFrameInfo; } } // Find the frame info structure corresponding to the given address. Return // NULL if the frame info structure does not exist. FRAMEINFO *_FindFrameInfo( PVOID pContinuation, FRAMEINFO *pFrameInfo ) { unsigned __int64 ImageBase; unsigned __int64 TargetGp; PRUNTIME_FUNCTION pContFunctionEntry = RtlLookupFunctionEntry((unsigned __int64)pContinuation, &ImageBase, &TargetGp); PRUNTIME_FUNCTION pFrameFunctionEntry = pFrameInfo->pFunctionEntry; DASSERT(pFrameInfo != NULL); for (; pFrameInfo != NULL; pFrameInfo = pFrameInfo->pNext ) { if (pContFunctionEntry == pFrameInfo->pFunctionEntry && (pContinuation > OffsetToAddress(pFrameInfo->pFunctionEntry->BeginAddress,ImageBase)) && (pContinuation <= OffsetToAddress(pFrameInfo->pFunctionEntry->EndAddress,ImageBase)) ){ return pFrameInfo; } } return NULL; } BOOL __IsFramePdataMatch( PVOID pContinuation, RUNTIME_FUNCTION* pFrameRfe) { BOOL fRetVal = FALSE; unsigned __int64 ImageBase, TargetGp; PRUNTIME_FUNCTION pContRfe = RtlLookupFunctionEntry((unsigned __int64) pContinuation, &ImageBase, &TargetGp); FuncInfo* pContFuncInfo = (FuncInfo*)(ImageBase + *(PULONG)GetLanguageSpecificData(pContRfe, ImageBase)); FuncInfo* pFrameFuncInfo = (FuncInfo*)(ImageBase + *(PULONG)GetLanguageSpecificData(pFrameRfe, ImageBase)); // // first see if there is a regular match, i.e. if the RFE registered with the frame // matches the RFE for the continuation address // fRetVal = (pContRfe == pFrameRfe) && (pContinuation > OffsetToAddress(pFrameRfe->BeginAddress,ImageBase)) && (pContinuation <= OffsetToAddress(pFrameRfe->EndAddress,ImageBase)); if (!fRetVal && (pContFuncInfo->bbtFlags == BBT_UNIQUE_FUNCINFO)) { fRetVal = (pContFuncInfo == pFrameFuncInfo); } return fRetVal; } // // Given the address of a continuation point, return the corresponding context. // Each frame info was saved just before a catch handler was called. // The most recently encountered frame is at the head of the chain. // The routine starts out with the frame given as the second argument, and scans the // linked list for the frame that corresponds to the continuation point. // CONTEXT* _FindAndUnlinkFrame(PVOID pContinuation, FRAMEINFO *pFrameInfo) { DASSERT(pFrameInfo != NULL); for( ; pFrameInfo != NULL; pFrameInfo = pFrameInfo->pNext ) { if(__IsFramePdataMatch(pContinuation, pFrameInfo->pFunctionEntry)) { // // We found the frame. // All frames preceeding and including this one are gone. so unlink them. // CONTEXT* pExitContext = pFrameInfo->pExitContext; pFrameInfoChain = pFrameInfo->pNext; // // If there are no more exceptions are pending get rid of unneeded frame info records. // if( _pCurrentException == NULL ) { while( pFrameInfoChain != NULL && pFrameInfoChain->pExceptionObjectDestroyed ) { pFrameInfoChain = pFrameInfoChain->pNext; } } return pExitContext; } } DASSERT(pFrameInfo != NULL); return NULL; } // // Save the frame information for this scope. Put it at the head of the linked-list. // FRAMEINFO* _CreateFrameInfo( FRAMEINFO *pFrameInfo, DispatcherContext *pDC, CONTEXT *pExitContext, __ehstate_t State, PVOID pExceptionObjectDestroyed, EHExceptionRecord *pExcept ) { pFrameInfo->pFunctionEntry = pDC->FunctionEntry; pFrameInfo->pExitContext = pExitContext; pFrameInfo->pExceptionObjectDestroyed = pExceptionObjectDestroyed; pFrameInfo->pExceptionObjectToBeDestroyed = NULL; pFrameInfo->pExcept = pExcept; pFrameInfo->State = State; pFrameInfo->dtorThrowFlag = FALSE; pFrameInfo->isRethrow = FALSE; if( pFrameInfoChain != NULL && pFrameInfoChain->dtorThrowFlag ) { pFrameInfoChain->dtorThrowFlag = FALSE; pFrameInfoChain = pFrameInfoChain->pNext; } pFrameInfo->pNext = (pFrameInfo < pFrameInfoChain)? pFrameInfoChain : NULL; pFrameInfoChain = pFrameInfo; return pFrameInfo; } // // THIS ROUTINE IS USED ONLY TO JUMP TO THE CONTINUATION POINT // // Sets SP and jumps to specified code address. // Does not return. // void _JumpToContinuation( unsigned __int64 TargetAddress, // The target address to call CONTEXT *pContext, // Context of target function EHExceptionRecord *pExcept ) { unsigned __int64 ImageBase; unsigned __int64 TargetGp; PRUNTIME_FUNCTION pFunctionEntry = RtlLookupFunctionEntry(TargetAddress, &ImageBase, &TargetGp); pContext->StIIP = TargetAddress; pContext->IntGp = TargetGp; pContext->StIPSR &= ~IPSR_RI_MASK; RtlRestoreContext(pContext,(PEXCEPTION_RECORD)pExcept); } // // Prototype for the internal handler // extern "C" EXCEPTION_DISPOSITION __InternalCxxFrameHandler( EHExceptionRecord *pExcept, // Information for this exception EHRegistrationNode *pRN, // Dynamic information for this frame CONTEXT *pContext, // Context info DispatcherContext *pDC, // More dynamic info for this frame FuncInfo *pFuncInfo, // Static information for this frame int CatchDepth, // How deeply nested are we? EHRegistrationNode *pMarkerRN, // Marker node for when checking inside catch block BOOL recursive); // True if this is a translation exception // // __CxxFrameHandler - Real entry point to the runtime // extern "C" _CRTIMP EXCEPTION_DISPOSITION __CxxFrameHandler( EHExceptionRecord *pExcept, // Information for this exception __int64 MemoryStackFp, // SP of user program __int64 BackingStoreFp, // BSP of user program CONTEXT *pContext, // Context info DispatcherContext *pDC, // More dynamic info for this frame __int64 TargetGp // GP of user program ) { FuncInfo *pFuncInfo; EXCEPTION_DISPOSITION result; EHRegistrationNode EstablisherFrame = { MemoryStackFp, BackingStoreFp }; _ImageBase = pDC->ImageBase; _TargetGp = TargetGp; _ThrowImageBase = (unsigned __int64)pExcept->params.pThrowImageBase; pFuncInfo = (FuncInfo*)(_ImageBase + *(PULONG)GetLanguageSpecificData(pDC->FunctionEntry, _ImageBase)); result = __InternalCxxFrameHandler( pExcept, &EstablisherFrame, pContext, pDC, pFuncInfo, 0, NULL, FALSE ); return result; } // Call the SEH to EH translator. BOOL _CallSETranslator( EHExceptionRecord *pExcept, // The exception to be translated EHRegistrationNode *pRN, // Dynamic info of function with catch CONTEXT *pContext, // Context info DispatcherContext *pDC, // More dynamic info of function with catch (ignored) FuncInfo *pFuncInfo, // Static info of function with catch ULONG CatchDepth, // How deeply nested in catch blocks are we? EHRegistrationNode *pMarkerRN // Marker for parent context ) { pRN; pDC; pFuncInfo; CatchDepth; // Call the translator. _EXCEPTION_POINTERS excptr = { (PEXCEPTION_RECORD)pExcept, pContext }; __pSETranslator(PER_CODE(pExcept), &excptr); // If we got back, then we were unable to translate it. return FALSE; } // // This structure is the FuncInfo (HandlerData) for handler __TranslatorGuardHandler // struct TransGuardRec { FuncInfo *pFuncInfo; // Static info for subject function EHRegistrationNode *pFrame; // Dynamic info for subject function ULONG CatchDepth; // How deeply nested are we? EHRegistrationNode *pMarkerFrame; // Marker for parent context PVOID pContinue; // Continuation address within CallSEHTranslator PVOID pSP; // SP within CallSEHTranslator BOOL DidUnwind; // True if this frame was unwound }; #if 0 // // This routine is the handler for CallSETranslator which is defined in handlers.s // extern "C" _CRTIMP EXCEPTION_DISPOSITION __TranslatorGuardHandler( EHExceptionRecord *pExcept, // Information for this exception EHRegistrationNode *pFrame, // The translator guard frame CONTEXT *pContext, // Context info DispatcherContext *pDC // Dynamic info for this frame ) { // // The handler data is a pointer to an integer that is an offset to the TGRN structure // relative to the frame pointer. // TransGuardRec *pTransGuardData = (TransGuardRec*)((char*)pFrame->MemoryStackFp - *(int*)(pDC->FunctionEntry->HandlerData)); if (IS_UNWINDING(PER_FLAGS(pExcept))) { pTransGuardData->DidUnwind = TRUE; return ExceptionContinueSearch; } else { // // Check for a handler: // __InternalCxxFrameHandler( pExcept, pTransGuardData->pFrame, pContext, pDC, pTransGuardData->pFuncInfo, pTransGuardData->CatchDepth, pTransGuardData->pMarkerFrame, TRUE ); // Unreached. return ExceptionContinueSearch; } } #endif ///////////////////////////////////////////////////////////////////////////// // // _GetRangeOfTrysToCheck - determine which try blocks are of interest, given // the current catch block nesting depth. We only check the trys at a single // depth. // // Returns: // Address of first try block of interest is returned // pStart and pEnd get the indices of the range in question // TryBlockMapEntry* _GetRangeOfTrysToCheck( EHRegistrationNode *pRN, FuncInfo *pFuncInfo, int CatchDepth, __ehstate_t curState, unsigned *pStart, unsigned *pEnd, DispatcherContext *pDC ) { TryBlockMapEntry *pEntry; unsigned num_of_try_blocks = FUNC_NTRYBLOCKS(*pFuncInfo); DASSERT( num_of_try_blocks > 0 ); for( unsigned int index = 0; index < num_of_try_blocks; index++ ) { pEntry = FUNC_PTRYBLOCK(*pFuncInfo, index, pDC->ImageBase); if( curState >= TBME_LOW(*pEntry) && curState <= TBME_HIGH(*pEntry) ) { *pStart = index; *pEnd = FUNC_NTRYBLOCKS(*pFuncInfo); DASSERT( *pEnd <= num_of_try_blocks && *pStart < *pEnd ); #if 1 int SavedState = GetUnwindTryBlock(pRN, pDC, pFuncInfo); if( SavedState != -1 && SavedState >= curState ) continue; #else int SavedTryNdx = GetUnwindTryBlock(pRN, pDC, pFuncInfo); if( SavedTryNdx != -1 ) { *pStart = SavedTryNdx + 1; if( *pStart < *pEnd && pEntry != NULL ) { pEntry = FUNC_PTRYBLOCK(*pFuncInfo, SavedTryNdx + 1, pDC->ImageBase); } } #endif return pEntry; } } *pStart = *pEnd = 0; return NULL; } #pragma optimize("",off) extern "C" unsigned __int64 __getReg(int); extern "C" void __setReg(int, unsigned __int64); #pragma intrinsic(__getReg) #pragma intrinsic(__setReg) extern "C" void _UnwindNestedFrames( EHRegistrationNode *pFrame, // Unwind up to (but not including) this frame EHExceptionRecord *pExcept, // The exception that initiated this unwind CONTEXT *pContext // Context info for current exception ) { CONTEXT LocalContext; // Create context for this routine to return from RtlUnwind CONTEXT ScratchContext; // Context record to pass to RtlUnwind2 to be used as scratch volatile int Unwound = FALSE; // Flag that assist to return from RtlUnwind2 PVOID pReturnPoint = NULL; // The address we want to return from RtlUnwind2 RtlCaptureContext(&LocalContext); // // set up the return label // _GetNextInstrOffset(&pReturnPoint); if(Unwound) goto LAB_UNWOUND; LocalContext.StIIP = (ULONGLONG)pReturnPoint; LocalContext.IntGp = __getReg(CV_IA64_IntR1); LocalContext.StIPSR &= ~IPSR_RI_MASK; pUnwindContext = &LocalContext; // save address of LocalContext in global/TLS pUnwindContext Unwound = TRUE; #ifdef _NT RtlUnwind2(*pFrame, (ULONG_PTR)pReturnPoint, (PEXCEPTION_RECORD)pExcept, NULL, &ScratchContext); #else RtlUnwind2(*pFrame, pReturnPoint, (PEXCEPTION_RECORD)pExcept, NULL, &ScratchContext); #endif LAB_UNWOUND: __setReg(CV_IA64_IntR1,LocalContext.IntGp); pContext->StIPSR &= ~IPSR_RI_MASK; PER_FLAGS(pExcept) &= ~EXCEPTION_UNWINDING; // // reset global/TLS pUnwindContext for future exceptions // pUnwindContext = NULL; }