1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
17 #include "dbginterface.h"
19 #include "comutilnative.h"
20 #include "sigformat.h"
21 #include "siginfo.hpp"
22 #include "gcheaputilities.h"
23 #include "eedbginterfaceimpl.h" //so we can clearexception in COMPlusThrow
24 #include "perfcounters.h"
25 #include "eventtrace.h"
26 #include "eetoprofinterfacewrapper.inl"
27 #include "eedbginterfaceimpl.inl"
28 #include "dllimportcallback.h"
30 #ifdef FEATURE_REMOTING
31 #include "appdomainhelper.h"
37 #include "asmconstants.h"
38 #include "virtualcallstub.h"
40 #ifndef WIN64EXCEPTIONS
41 MethodDesc * GetUserMethodForILStub(Thread * pThread, UINT_PTR uStubSP, MethodDesc * pILStubMD, Frame ** ppFrameOut);
43 #if !defined(DACCESS_COMPILE)
45 #define FORMAT_MESSAGE_BUFFER_LENGTH 1024
47 BOOL ComPlusFrameSEH(EXCEPTION_REGISTRATION_RECORD*);
48 PEXCEPTION_REGISTRATION_RECORD GetPrevSEHRecord(EXCEPTION_REGISTRATION_RECORD*);
52 VOID STDCALL ResumeAtJitEHHelper(EHContext *pContext);
53 int STDCALL CallJitEHFilterHelper(size_t *pShadowSP, EHContext *pContext);
54 VOID STDCALL CallJitEHFinallyHelper(size_t *pShadowSP, EHContext *pContext);
56 typedef void (*RtlUnwindCallbackType)(void);
58 BOOL CallRtlUnwind(EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame,
59 RtlUnwindCallbackType callback,
60 EXCEPTION_RECORD *pExceptionRecord,
63 BOOL CallRtlUnwindSafe(EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame,
64 RtlUnwindCallbackType callback,
65 EXCEPTION_RECORD *pExceptionRecord,
70 CPFH_ShouldUnwindStack(const EXCEPTION_RECORD * pCER) {
72 LIMITED_METHOD_CONTRACT;
74 _ASSERTE(pCER != NULL);
76 // We can only unwind those exceptions whose context/record we don't need for a
77 // rethrow. This is complus, and stack overflow. For all the others, we
78 // need to keep the context around for a rethrow, which means they can't
80 if (IsComPlusException(pCER) || pCER->ExceptionCode == STATUS_STACK_OVERFLOW)
86 static inline BOOL IsComPlusNestedExceptionRecord(EXCEPTION_REGISTRATION_RECORD* pEHR)
88 LIMITED_METHOD_CONTRACT;
89 if (pEHR->Handler == (PEXCEPTION_ROUTINE)COMPlusNestedExceptionHandler)
94 EXCEPTION_REGISTRATION_RECORD *TryFindNestedEstablisherFrame(EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame)
96 LIMITED_METHOD_CONTRACT;
97 while (pEstablisherFrame->Handler != (PEXCEPTION_ROUTINE)COMPlusNestedExceptionHandler) {
98 pEstablisherFrame = pEstablisherFrame->Next;
99 if (pEstablisherFrame == EXCEPTION_CHAIN_END) return 0;
101 return pEstablisherFrame;
105 // stores last handler we went to in case we didn't get an endcatch and stack is
106 // corrupted we can figure out who did it.
107 static MethodDesc *gLastResumedExceptionFunc = NULL;
108 static DWORD gLastResumedExceptionHandler = 0;
111 //---------------------------------------------------------------------
112 // void RtlUnwindCallback()
113 // call back function after global unwind, rtlunwind calls this function
114 //---------------------------------------------------------------------
115 static void RtlUnwindCallback()
117 LIMITED_METHOD_CONTRACT;
118 _ASSERTE(!"Should never get here");
121 BOOL NExportSEH(EXCEPTION_REGISTRATION_RECORD* pEHR)
123 LIMITED_METHOD_CONTRACT;
125 if ((LPVOID)pEHR->Handler == (LPVOID)UMThunkPrestubHandler)
132 BOOL FastNExportSEH(EXCEPTION_REGISTRATION_RECORD* pEHR)
134 LIMITED_METHOD_CONTRACT;
136 if ((LPVOID)pEHR->Handler == (LPVOID)FastNExportExceptHandler)
141 BOOL ReverseCOMSEH(EXCEPTION_REGISTRATION_RECORD* pEHR)
143 LIMITED_METHOD_CONTRACT;
145 #ifdef FEATURE_COMINTEROP
146 if ((LPVOID)pEHR->Handler == (LPVOID)COMPlusFrameHandlerRevCom)
148 #endif // FEATURE_COMINTEROP
154 // Returns true if the given SEH handler is one of our SEH handlers that is responsible for managing exceptions in
155 // regions of managed code.
157 BOOL IsUnmanagedToManagedSEHHandler(EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame)
162 // ComPlusFrameSEH() is for COMPlusFrameHandler & COMPlusNestedExceptionHandler.
163 // FastNExportSEH() is for FastNExportExceptHandler.
164 // NExportSEH() is for UMThunkPrestubHandler.
166 return (ComPlusFrameSEH(pEstablisherFrame) || FastNExportSEH(pEstablisherFrame) || NExportSEH(pEstablisherFrame) || ReverseCOMSEH(pEstablisherFrame));
169 Frame *GetCurrFrame(EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame)
173 _ASSERTE(IsUnmanagedToManagedSEHHandler(pEstablisherFrame));
174 if (NExportSEH(pEstablisherFrame))
175 pFrame = ((ComToManagedExRecord *)pEstablisherFrame)->GetCurrFrame();
177 pFrame = ((FrameHandlerExRecord *)pEstablisherFrame)->GetCurrFrame();
179 _ASSERTE(GetThread() == NULL || GetThread()->GetFrame() <= pFrame);
184 EXCEPTION_REGISTRATION_RECORD* GetNextCOMPlusSEHRecord(EXCEPTION_REGISTRATION_RECORD* pRec) {
186 if (pRec == EXCEPTION_CHAIN_END)
187 return EXCEPTION_CHAIN_END;
192 } while (pRec != EXCEPTION_CHAIN_END && !IsUnmanagedToManagedSEHHandler(pRec));
194 _ASSERTE(pRec == EXCEPTION_CHAIN_END || IsUnmanagedToManagedSEHHandler(pRec));
200 * GetClrSEHRecordServicingStackPointer
202 * This function searchs all the Frame SEH records, and finds the one that is
203 * currently signed up to do all exception handling for the given stack pointer
204 * on the given thread.
207 * pThread - The thread to search on.
208 * pStackPointer - The stack location that we are finding the Frame SEH Record for.
211 * A pointer to the SEH record, or EXCEPTION_CHAIN_END if none was found.
215 PEXCEPTION_REGISTRATION_RECORD
216 GetClrSEHRecordServicingStackPointer(Thread *pThread,
219 ThreadExceptionState* pExState = pThread->GetExceptionState();
222 // We can only do this if there is a context in the pExInfo. There are cases (most notably the
223 // EEPolicy::HandleFatalError case) where we don't have that. In these cases we will return
224 // no enclosing handler since we cannot accurately determine the FS:0 entry which services
225 // this stack address.
227 // The side effect of this is that for these cases, the debugger cannot intercept
230 CONTEXT* pContextRecord = pExState->GetContextRecord();
231 if (pContextRecord == NULL)
233 return EXCEPTION_CHAIN_END;
236 void *exceptionSP = dac_cast<PTR_VOID>(GetSP(pContextRecord));
240 // Now set the establishing frame. What this means in English is that we need to find
241 // the fs:0 entry that handles exceptions for the place on the stack given in stackPointer.
243 PEXCEPTION_REGISTRATION_RECORD pSEHRecord = GetFirstCOMPlusSEHRecord(pThread);
245 while (pSEHRecord != EXCEPTION_CHAIN_END)
249 // Skip any SEHRecord which is not a CLR record or was pushed after the exception
250 // on this thread occurred.
252 if (IsUnmanagedToManagedSEHHandler(pSEHRecord) && (exceptionSP <= (void *)pSEHRecord))
254 Frame *pFrame = GetCurrFrame(pSEHRecord);
256 // Arcane knowledge here. All Frame records are stored on the stack by the runtime
257 // in ever decreasing address space. So, we merely have to search back until
258 // we find the first frame record with a higher stack value to find the
259 // establishing frame for the given stack address.
261 if (((void *)pFrame) >= pStackPointer)
268 pSEHRecord = GetNextCOMPlusSEHRecord(pSEHRecord);
275 // We've deteremined during a stack walk that managed code is transitioning to unamanaged (EE) code. Check that the
276 // state of the EH chain is correct.
278 // For x86, check that we do INSTALL_COMPLUS_EXCEPTION_HANDLER before calling managed code. This check should be
279 // done for all managed code sites, not just transistions. But this will catch most problem cases.
280 void VerifyValidTransitionFromManagedCode(Thread *pThread, CrawlFrame *pCF)
284 _ASSERTE(ExecutionManager::IsManagedCode(GetControlPC(pCF->GetRegisterSet())));
286 // Cannot get to the TEB of other threads. So ignore them.
287 if (pThread != GetThread())
292 // Find the EH record guarding the current region of managed code, based on the CrawlFrame passed in.
293 PEXCEPTION_REGISTRATION_RECORD pEHR = GetCurrentSEHRecord();
295 while ((pEHR != EXCEPTION_CHAIN_END) && ((ULONG_PTR)pEHR < GetRegdisplaySP(pCF->GetRegisterSet())))
300 // VerifyValidTransitionFromManagedCode can be called before the CrawlFrame's MethodDesc is initialized.
301 // Fix that if necessary for the consistency check.
302 MethodDesc * pFunction = pCF->GetFunction();
303 if ((!IsUnmanagedToManagedSEHHandler(pEHR)) && // Will the assert fire? If not, don't waste our time.
306 _ASSERTE(pCF->GetRegisterSet());
307 PCODE ip = GetControlPC(pCF->GetRegisterSet());
308 pFunction = ExecutionManager::GetCodeMethodDesc(ip);
312 // Great, we've got the EH record that's next up the stack from the current SP (which is in managed code). That
313 // had better be a record for one of our handlers responsible for handling exceptions in managed code. If its
314 // not, then someone made it into managed code without setting up one of our EH handlers, and that's really
316 CONSISTENCY_CHECK_MSGF(IsUnmanagedToManagedSEHHandler(pEHR),
317 ("Invalid transition into managed code!\n\n"
318 "We're walking this thread's stack and we've reached a managed frame at Esp=0x%p. "
319 "(The method is %s::%s) "
320 "The very next FS:0 record (0x%p) up from this point on the stack should be one of "
321 "our 'unmanaged to managed SEH handlers', but its not... its something else, and "
322 "that's very bad. It indicates that someone managed to call into managed code without "
323 "setting up the proper exception handling.\n\n"
324 "Get a good unmanaged stack trace for this thread. All FS:0 records are on the stack, "
325 "so you can see who installed the last handler. Somewhere between that function and "
326 "where the thread is now is where the bad transition occurred.\n\n"
327 "A little extra info: FS:0 = 0x%p, pEHR->Handler = 0x%p\n",
328 GetRegdisplaySP(pCF->GetRegisterSet()),
329 pFunction ->m_pszDebugClassName,
330 pFunction ->m_pszDebugMethodName,
332 GetCurrentSEHRecord(),
338 //================================================================================
340 // There are some things that should never be true when handling an
341 // exception. This function checks for them. Will assert or trap
342 // if it finds an error.
344 CPFH_VerifyThreadIsInValidState(Thread* pThread, DWORD exceptionCode, EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame) {
347 if ( exceptionCode == STATUS_BREAKPOINT
348 || exceptionCode == STATUS_SINGLE_STEP) {
353 // check for overwriting of stack
354 CheckStackBarrier(pEstablisherFrame);
355 // trigger check for bad fs:0 chain
356 GetCurrentSEHRecord();
359 if (!g_fEEShutDown) {
360 // An exception on the GC thread, or while holding the thread store lock, will likely lock out the entire process.
361 if (::IsGCThread() || ThreadStore::HoldingThreadStore())
363 _ASSERTE(!"Exception during garbage collection or while holding thread store");
364 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
370 #ifdef FEATURE_HIJACK
372 CPFH_AdjustContextForThreadSuspensionRace(CONTEXT *pContext, Thread *pThread)
377 PCODE f_IP = GetIP(pContext);
378 if (Thread::IsAddrOfRedirectFunc((PVOID)f_IP)) {
380 // This is a very rare case where we tried to redirect a thread that was
381 // just about to dispatch an exception, and our update of EIP took, but
382 // the thread continued dispatching the exception.
384 // If this should happen (very rare) then we fix it up here.
386 _ASSERTE(pThread->GetSavedRedirectContext());
387 SetIP(pContext, GetIP(pThread->GetSavedRedirectContext()));
388 STRESS_LOG1(LF_EH, LL_INFO100, "CPFH_AdjustContextForThreadSuspensionRace: Case 1 setting IP = %x\n", pContext->Eip);
391 if (f_IP == GetEEFuncEntryPoint(THROW_CONTROL_FOR_THREAD_FUNCTION)) {
393 // This is a very rare case where we tried to redirect a thread that was
394 // just about to dispatch an exception, and our update of EIP took, but
395 // the thread continued dispatching the exception.
397 // If this should happen (very rare) then we fix it up here.
399 SetIP(pContext, GetIP(pThread->m_OSContext));
400 STRESS_LOG1(LF_EH, LL_INFO100, "CPFH_AdjustContextForThreadSuspensionRace: Case 2 setting IP = %x\n", pContext->Eip);
403 // We have another even rarer race condition:
404 // - A) On thread A, Debugger puts an int 3 in the code stream at address X
405 // - A) We hit it and the begin an exception. The eip will be X + 1 (int3 is special)
406 // - B) Meanwhile, thread B redirects A's eip to Y. (Although A is really somewhere
407 // in the kernel, it looks like it's still in user code, so it can fall under the
408 // HandledJitCase and can be redirected)
409 // - A) The OS, trying to be nice, expects we have a breakpoint exception at X+1,
410 // but does -1 on the address since it knows int3 will leave the eip +1.
411 // So the context structure it will pass to the Handler is ideally (X+1)-1 = X
413 // ** Here's the race: Since thread B redirected A, the eip is actually Y (not X+1),
414 // but the kernel still touches it up to Y-1. So there's a window between when we hit a
415 // bp and when the handler gets called that this can happen.
416 // This causes an unhandled BP (since the debugger doesn't recognize the bp at Y-1)
418 // So what to do: If we land at Y-1 (ie, if f_IP+1 is the addr of a Redirected Func),
419 // then restore the EIP back to X. This will skip the redirection.
420 // Fortunately, this only occurs in cases where it's ok
421 // to skip. The debugger will recognize the patch and handle it.
423 if (Thread::IsAddrOfRedirectFunc((PVOID)(f_IP + 1))) {
424 _ASSERTE(pThread->GetSavedRedirectContext());
425 SetIP(pContext, GetIP(pThread->GetSavedRedirectContext()) - 1);
426 STRESS_LOG1(LF_EH, LL_INFO100, "CPFH_AdjustContextForThreadSuspensionRace: Case 3 setting IP = %x\n", pContext->Eip);
429 if (f_IP + 1 == GetEEFuncEntryPoint(THROW_CONTROL_FOR_THREAD_FUNCTION)) {
430 SetIP(pContext, GetIP(pThread->m_OSContext) - 1);
431 STRESS_LOG1(LF_EH, LL_INFO100, "CPFH_AdjustContextForThreadSuspensionRace: Case 4 setting IP = %x\n", pContext->Eip);
434 PORTABILITY_ASSERT("CPFH_AdjustContextForThreadSuspensionRace");
437 #endif // FEATURE_HIJACK
442 CPFH_UpdatePerformanceCounters() {
444 COUNTER_ONLY(GetPerfCounters().m_Excep.cThrown++);
448 //******************************************************************************
449 EXCEPTION_DISPOSITION COMPlusAfterUnwind(
450 EXCEPTION_RECORD *pExceptionRecord,
451 EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame,
452 ThrowCallbackType& tct)
456 // Note: we've completed the unwind pass up to the establisher frame, and we're headed off to finish our
457 // cleanup and end up back in jitted code. Any more FS0 handlers pushed from this point on out will _not_ be
458 // unwound. We go ahead and assert right here that indeed there are no handlers below the establisher frame
459 // before we go any further.
460 _ASSERTE(pEstablisherFrame == GetCurrentSEHRecord());
462 Thread* pThread = GetThread();
464 _ASSERTE(tct.pCurrentExceptionRecord == pEstablisherFrame);
466 NestedHandlerExRecord nestedHandlerExRecord;
467 nestedHandlerExRecord.Init((PEXCEPTION_ROUTINE)COMPlusNestedExceptionHandler, GetCurrFrame(pEstablisherFrame));
469 // ... and now, put the nested record back on.
470 INSTALL_EXCEPTION_HANDLING_RECORD(&(nestedHandlerExRecord.m_ExReg));
472 // We entered COMPlusAfterUnwind in PREEMP, but we need to be in COOP from here on out
475 tct.bIsUnwind = TRUE;
476 tct.pProfilerNotify = NULL;
478 LOG((LF_EH, LL_INFO100, "COMPlusFrameHandler: unwinding\n"));
480 tct.bUnwindStack = CPFH_ShouldUnwindStack(pExceptionRecord);
482 LOG((LF_EH, LL_INFO1000, "COMPlusAfterUnwind: going to: pFunc:%#X, pStack:%#X\n",
483 tct.pFunc, tct.pStack));
485 // TODO: UnwindFrames ends up calling into StackWalkFrames which is SO_INTOLERANT
486 // as is UnwindFrames, etc... Should we make COMPlusAfterUnwind SO_INTOLERANT???
487 ANNOTATION_VIOLATION(SOToleranceViolation);
489 UnwindFrames(pThread, &tct);
491 #ifdef DEBUGGING_SUPPORTED
492 ExInfo* pExInfo = pThread->GetExceptionState()->GetCurrentExceptionTracker();
493 if (pExInfo->m_ValidInterceptionContext)
495 // By now we should have all unknown FS:[0] handlers unwinded along with the managed Frames until
496 // the interception point. We can now pop nested exception handlers and resume at interception context.
497 EHContext context = pExInfo->m_InterceptionContext;
498 pExInfo->m_InterceptionContext.Init();
499 pExInfo->m_ValidInterceptionContext = FALSE;
501 UnwindExceptionTrackerAndResumeInInterceptionFrame(pExInfo, &context);
503 #endif // DEBUGGING_SUPPORTED
505 _ASSERTE(!"Should not get here");
506 return ExceptionContinueSearch;
507 } // EXCEPTION_DISPOSITION COMPlusAfterUnwind()
509 #ifdef DEBUGGING_SUPPORTED
511 //---------------------------------------------------------------------------------------
513 // This function is called to intercept an exception and start an unwind.
516 // pCurrentEstablisherFrame - the exception registration record covering the stack range
517 // containing the interception point
518 // pExceptionRecord - EXCEPTION_RECORD of the exception being intercepted
521 // ExceptionContinueSearch if the exception cannot be intercepted
524 // If the exception is intercepted, this function never returns.
527 EXCEPTION_DISPOSITION ClrDebuggerDoUnwindAndIntercept(EXCEPTION_REGISTRATION_RECORD *pCurrentEstablisherFrame,
528 EXCEPTION_RECORD *pExceptionRecord)
532 if (!CheckThreadExceptionStateForInterception())
534 return ExceptionContinueSearch;
537 Thread* pThread = GetThread();
538 ThreadExceptionState* pExState = pThread->GetExceptionState();
540 EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame;
541 ThrowCallbackType tct;
544 pExState->GetDebuggerState()->GetDebuggerInterceptInfo(&pEstablisherFrame,
553 // If the handler that we've selected as the handler for the target frame of the unwind is in fact above the
554 // handler that we're currently executing in, then use the current handler instead. Why? Our handlers for
555 // nested exceptions actually process managed frames that live above them, up to the COMPlusFrameHanlder that
556 // pushed the nested handler. If the user selectes a frame above the nested handler, then we will have selected
557 // the COMPlusFrameHandler above the current nested handler. But we don't want to ask RtlUnwind to unwind past
558 // the nested handler that we're currently executing in.
560 if (pEstablisherFrame > pCurrentEstablisherFrame)
562 // This should only happen if we're in a COMPlusNestedExceptionHandler.
563 _ASSERTE(IsComPlusNestedExceptionRecord(pCurrentEstablisherFrame));
565 pEstablisherFrame = pCurrentEstablisherFrame;
569 tct.pCurrentExceptionRecord = pEstablisherFrame;
572 LOG((LF_EH|LF_CORDB, LL_INFO100, "ClrDebuggerDoUnwindAndIntercept: Intercepting at %s\n", tct.pFunc->m_pszDebugMethodName));
573 LOG((LF_EH|LF_CORDB, LL_INFO100, "\t\t: pFunc is 0x%X\n", tct.pFunc));
574 LOG((LF_EH|LF_CORDB, LL_INFO100, "\t\t: pStack is 0x%X\n", tct.pStack));
576 CallRtlUnwindSafe(pEstablisherFrame, RtlUnwindCallback, pExceptionRecord, 0);
578 ExInfo* pExInfo = pThread->GetExceptionState()->GetCurrentExceptionTracker();
579 if (pExInfo->m_ValidInterceptionContext)
581 // By now we should have all unknown FS:[0] handlers unwinded along with the managed Frames until
582 // the interception point. We can now pop nested exception handlers and resume at interception context.
584 EHContext context = pExInfo->m_InterceptionContext;
585 pExInfo->m_InterceptionContext.Init();
586 pExInfo->m_ValidInterceptionContext = FALSE;
588 UnwindExceptionTrackerAndResumeInInterceptionFrame(pExInfo, &context);
591 // on x86 at least, RtlUnwind always returns
593 // Note: we've completed the unwind pass up to the establisher frame, and we're headed off to finish our
594 // cleanup and end up back in jitted code. Any more FS0 handlers pushed from this point on out will _not_ be
596 return COMPlusAfterUnwind(pExState->GetExceptionRecord(), pEstablisherFrame, tct);
597 } // EXCEPTION_DISPOSITION ClrDebuggerDoUnwindAndIntercept()
599 #endif // DEBUGGING_SUPPORTED
601 // This is a wrapper around the assembly routine that invokes RtlUnwind in the OS.
602 // When we invoke RtlUnwind, the OS will modify the ExceptionFlags field in the
603 // exception record to reflect unwind. Since we call RtlUnwind in the first pass
604 // with a valid exception record when we find an exception handler AND because RtlUnwind
605 // returns on x86, the OS would have flagged the exception record for unwind.
607 // Incase the exception is rethrown from the catch/filter-handler AND it's a non-COMPLUS
608 // exception, the runtime will use the reference to the saved exception record to reraise
609 // the exception, as part of rethrow fixup. Since the OS would have modified the exception record
610 // to reflect unwind, this wrapper will "reset" the ExceptionFlags field when RtlUnwind returns.
611 // Otherwise, the rethrow will result in second pass, as opposed to first, since the ExceptionFlags
612 // would indicate an unwind.
614 // This rethrow issue does not affect COMPLUS exceptions since we always create a brand new exception
615 // record for them in RaiseTheExceptionInternalOnly.
616 BOOL CallRtlUnwindSafe(EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame,
617 RtlUnwindCallbackType callback,
618 EXCEPTION_RECORD *pExceptionRecord,
621 LIMITED_METHOD_CONTRACT;
623 // Save the ExceptionFlags value before invoking RtlUnwind.
624 DWORD dwExceptionFlags = pExceptionRecord->ExceptionFlags;
626 BOOL fRetVal = CallRtlUnwind(pEstablisherFrame, callback, pExceptionRecord, retval);
628 // Reset ExceptionFlags field, if applicable
629 if (pExceptionRecord->ExceptionFlags != dwExceptionFlags)
631 // We would expect the 32bit OS to have set the unwind flag at this point.
632 _ASSERTE(pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING);
633 LOG((LF_EH, LL_INFO100, "CallRtlUnwindSafe: Resetting ExceptionFlags from %lu to %lu\n", pExceptionRecord->ExceptionFlags, dwExceptionFlags));
634 pExceptionRecord->ExceptionFlags = dwExceptionFlags;
640 //******************************************************************************
641 // The essence of the first pass handler (after we've decided to actually do
642 // the first pass handling).
643 //******************************************************************************
644 inline EXCEPTION_DISPOSITION __cdecl
645 CPFH_RealFirstPassHandler( // ExceptionContinueSearch, etc.
646 EXCEPTION_RECORD *pExceptionRecord, // The exception record, with exception type.
647 EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame, // Exception frame on whose behalf this is called.
648 CONTEXT *pContext, // Context from the exception.
649 void *pDispatcherContext, // @todo
650 BOOL bAsynchronousThreadStop, // @todo
651 BOOL fPGCDisabledOnEntry) // @todo
653 // We don't want to use a runtime contract here since this codepath is used during
654 // the processing of a hard SO. Contracts use a significant amount of stack
655 // which we can't afford for those cases.
656 STATIC_CONTRACT_THROWS;
657 STATIC_CONTRACT_GC_TRIGGERS;
658 STATIC_CONTRACT_MODE_COOPERATIVE;
659 STATIC_CONTRACT_SO_TOLERANT;
662 static int breakOnFirstPass = -1;
664 if (breakOnFirstPass == -1)
665 breakOnFirstPass = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnFirstPass);
667 if (breakOnFirstPass != 0)
669 _ASSERTE(!"First pass exception handler");
673 EXCEPTION_DISPOSITION retval;
674 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
675 Thread *pThread = GetThread();
678 static int breakOnSO = -1;
681 breakOnSO = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BreakOnSO);
683 if (breakOnSO != 0 && exceptionCode == STATUS_STACK_OVERFLOW)
685 DebugBreak(); // ASSERTing will overwrite the guard region
689 // We always want to be in co-operative mode when we run this function and whenever we return
690 // from it, want to go to pre-emptive mode because are returning to OS.
691 _ASSERTE(pThread->PreemptiveGCDisabled());
693 BOOL bPopNestedHandlerExRecord = FALSE;
694 LFH found = LFH_NOT_FOUND; // Result of calling LookForHandler.
695 BOOL bRethrownException = FALSE;
696 BOOL bNestedException = FALSE;
699 BOOL bPopFaultingExceptionFrame = FALSE;
700 FrameWithCookie<FaultingExceptionFrame> faultingExceptionFrame;
702 ExInfo* pExInfo = &(pThread->GetExceptionState()->m_currentExInfo);
704 ThrowCallbackType tct;
707 tct.pTopFrame = GetCurrFrame(pEstablisherFrame); // highest frame to search to
710 tct.pCurrentExceptionRecord = pEstablisherFrame;
711 tct.pPrevExceptionRecord = GetPrevSEHRecord(pEstablisherFrame);
714 BOOL fIsManagedCode = pContext ? ExecutionManager::IsManagedCode(GetIP(pContext)) : FALSE;
717 // this establishes a marker so can determine if are processing a nested exception
718 // don't want to use the current frame to limit search as it could have been unwound by
719 // the time get to nested handler (ie if find an exception, unwind to the call point and
720 // then resume in the catch and then get another exception) so make the nested handler
721 // have the same boundary as this one. If nested handler can't find a handler, we won't
722 // end up searching this frame list twice because the nested handler will set the search
723 // boundary in the thread and so if get back to this handler it will have a range that starts
724 // and ends at the same place.
726 NestedHandlerExRecord nestedHandlerExRecord;
727 nestedHandlerExRecord.Init((PEXCEPTION_ROUTINE)COMPlusNestedExceptionHandler, GetCurrFrame(pEstablisherFrame));
729 INSTALL_EXCEPTION_HANDLING_RECORD(&(nestedHandlerExRecord.m_ExReg));
730 bPopNestedHandlerExRecord = TRUE;
733 // Note: don't attempt to push a FEF for an exception in managed code if we weren't in cooperative mode when
734 // the exception was received. If preemptive GC was enabled when we received the exception, then it means the
735 // exception was rethrown from unmangaed code (including EE impl), and we shouldn't push a FEF.
736 if (fIsManagedCode &&
737 fPGCDisabledOnEntry &&
738 (pThread->m_pFrame == FRAME_TOP ||
739 pThread->m_pFrame->GetVTablePtr() != FaultingExceptionFrame::GetMethodFrameVPtr() ||
740 (size_t)pThread->m_pFrame > (size_t)pEstablisherFrame))
742 // setup interrupted frame so that GC during calls to init won't collect the frames
743 // only need it for non COM+ exceptions in managed code when haven't already
744 // got one on the stack (will have one already if we have called rtlunwind because
745 // the instantiation that called unwind would have installed one)
746 faultingExceptionFrame.InitAndLink(pContext);
747 bPopFaultingExceptionFrame = TRUE;
752 e = pThread->LastThrownObject();
754 STRESS_LOG7(LF_EH, LL_INFO10, "CPFH_RealFirstPassHandler: code:%X, LastThrownObject:%p, MT:%pT"
755 ", IP:%p, SP:%p, pContext:%p, pEstablisherFrame:%p\n",
756 exceptionCode, OBJECTREFToObject(e), (e!=0)?e->GetMethodTable():0,
757 pContext ? GetIP(pContext) : 0, pContext ? GetSP(pContext) : 0,
758 pContext, pEstablisherFrame);
761 // If it is a complus exception, and there is a thrown object, get its name, for better logging.
762 if (IsComPlusException(pExceptionRecord))
764 const char * eClsName = "!EXCEPTION_COMPLUS";
767 eClsName = e->GetTrueMethodTable()->GetDebugClassName();
769 LOG((LF_EH, LL_INFO100, "CPFH_RealFirstPassHandler: exception: 0x%08X, class: '%s', IP: 0x%p\n",
770 exceptionCode, eClsName, pContext ? GetIP(pContext) : NULL));
774 EXCEPTION_POINTERS exceptionPointers = {pExceptionRecord, pContext};
776 STRESS_LOG4(LF_EH, LL_INFO10000, "CPFH_RealFirstPassHandler: setting boundaries: Exinfo: 0x%p, BottomMostHandler:0x%p, SearchBoundary:0x%p, TopFrame:0x%p\n",
777 pExInfo, pExInfo->m_pBottomMostHandler, pExInfo->m_pSearchBoundary, tct.pTopFrame);
779 // Here we are trying to decide if we are coming in as:
780 // 1) first handler in a brand new exception
781 // 2) a subsequent handler in an exception
782 // 3) a nested exception
783 // m_pBottomMostHandler is the registration structure (establisher frame) for the most recent (ie lowest in
784 // memory) non-nested handler that was installed and pEstablisher frame is what the current handler
785 // was registered with.
786 // The OS calls each registered handler in the chain, passing its establisher frame to it.
787 if (pExInfo->m_pBottomMostHandler != NULL && pEstablisherFrame > pExInfo->m_pBottomMostHandler)
789 STRESS_LOG3(LF_EH, LL_INFO10000, "CPFH_RealFirstPassHandler: detected subsequent handler. ExInfo:0x%p, BottomMost:0x%p SearchBoundary:0x%p\n",
790 pExInfo, pExInfo->m_pBottomMostHandler, pExInfo->m_pSearchBoundary);
792 // If the establisher frame of this handler is greater than the bottommost then it must have been
793 // installed earlier and therefore we are case 2
794 if (pThread->GetThrowable() == NULL)
796 // Bottommost didn't setup a throwable, so not exception not for us
797 retval = ExceptionContinueSearch;
801 // setup search start point
802 tct.pBottomFrame = pExInfo->m_pSearchBoundary;
804 if (tct.pTopFrame == tct.pBottomFrame)
806 // this will happen if our nested handler already searched for us so we don't want
808 retval = ExceptionContinueSearch;
813 { // we are either case 1 or case 3
814 #if defined(_DEBUG_IMPL)
815 //@todo: merge frames, context, handlers
816 if (pThread->GetFrame() != FRAME_TOP)
817 pThread->GetFrame()->LogFrameChain(LF_EH, LL_INFO1000);
818 #endif // _DEBUG_IMPL
820 // If the exception was rethrown, we'll create a new ExInfo, which will represent the rethrown exception.
821 // The original exception is not the rethrown one.
822 if (pExInfo->m_ExceptionFlags.IsRethrown() && pThread->LastThrownObject() != NULL)
824 pExInfo->m_ExceptionFlags.ResetIsRethrown();
825 bRethrownException = TRUE;
828 if (bPopFaultingExceptionFrame)
830 // if we added a FEF, it will refer to the frame at the point of the original exception which is
831 // already unwound so don't want it.
832 // If we rethrew the exception we have already added a helper frame for the rethrow, so don't
833 // need this one. If we didn't rethrow it, (ie rethrow from native) then there the topmost frame will
834 // be a transition to native frame in which case we don't need it either
835 faultingExceptionFrame.Pop();
836 bPopFaultingExceptionFrame = FALSE;
841 // If the establisher frame is less than the bottommost handler, then this is nested because the
842 // establisher frame was installed after the bottommost.
843 if (pEstablisherFrame < pExInfo->m_pBottomMostHandler
844 /* || IsComPlusNestedExceptionRecord(pEstablisherFrame) */ )
846 bNestedException = TRUE;
848 // case 3: this is a nested exception. Need to save and restore the thread info
849 STRESS_LOG3(LF_EH, LL_INFO10000, "CPFH_RealFirstPassHandler: ExInfo:0x%p detected nested exception 0x%p < 0x%p\n",
850 pExInfo, pEstablisherFrame, pExInfo->m_pBottomMostHandler);
852 EXCEPTION_REGISTRATION_RECORD* pNestedER = TryFindNestedEstablisherFrame(pEstablisherFrame);
853 ExInfo *pNestedExInfo;
855 if (!pNestedER || pNestedER >= pExInfo->m_pBottomMostHandler )
857 // RARE CASE. We've re-entered the EE from an unmanaged filter.
861 // We can be here if we dont find a nested exception handler. This is exemplified using
862 // call chain of scenario 2 explained further below.
864 // Assuming __try of NativeB throws an exception E1 and it gets caught in ManagedA2, then
865 // bottom-most handler (BMH) is going to be CPFH_A. The catch will trigger an unwind
866 // and invoke __finally in NativeB. Let the __finally throw a new exception E2.
868 // Assuming ManagedB2 has a catch block to catch E2, when we enter CPFH_B looking for a
869 // handler for E2, our establisher frame will be that of CPFH_B, which will be lower
870 // in stack than current BMH (which is CPFH_A). Thus, we will come here, determining
871 // E2 to be nested exception correctly but not find a nested exception handler.
872 void *limit = (void *) GetPrevSEHRecord(pExInfo->m_pBottomMostHandler);
874 pNestedExInfo = new (nothrow) ExInfo(); // Very rare failure here; need robust allocator.
875 if (pNestedExInfo == NULL)
876 { // if we can't allocate memory, we can't correctly continue.
878 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NestedEhOom))
879 _ASSERTE(!"OOM in callback from unmanaged filter.");
882 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_OUTOFMEMORY);
886 pNestedExInfo->m_StackAddress = limit; // Note: this is also the flag that tells us this
887 // ExInfo was stack allocated.
891 pNestedExInfo = &((NestedHandlerExRecord*)pNestedER)->m_handlerInfo;
894 LOG((LF_EH, LL_INFO100, "CPFH_RealFirstPassHandler: PushExInfo() current: 0x%p previous: 0x%p\n",
895 pExInfo->m_StackAddress, pNestedExInfo->m_StackAddress));
897 _ASSERTE(pNestedExInfo);
898 pNestedExInfo->m_hThrowable = NULL; // pNestedExInfo may be stack allocated, and as such full of
899 // garbage. m_hThrowable must be sane, so set it to NULL. (We could
900 // zero the entire record, but this is cheaper.)
902 pNestedExInfo->CopyAndClearSource(pExInfo);
904 pExInfo->m_pPrevNestedInfo = pNestedExInfo; // Save at head of nested info chain
907 /* the following code was introduced in Whidbey as part of the Faulting Exception Frame removal (12/03).
908 However it isn't correct. If any nested exceptions occur while processing a rethrow, we would
909 incorrectly consider the nested exception to be a rethrow. See VSWhidbey 349379 for an example.
911 Therefore I am disabling this code until we see a failure that explains why it was added in the first
914 // If we're here as a result of a rethrown exception, set the rethrown flag on the new ExInfo.
915 if (bRethrownException)
917 pExInfo->m_ExceptionFlags.SetIsRethrown();
923 // At this point, either:
925 // 1) the bottom-most handler is NULL, implying this is a new exception for which we are getting ready, OR
926 // 2) the bottom-most handler is not-NULL, implying that a there is already an existing exception in progress.
928 // Scenario 1 is that of a new throw and is easy to understand. Scenario 2 is the interesting one.
930 // ManagedA1 -> ManagedA2 -> ManagedA3 -> NativeCodeA -> ManagedB1 -> ManagedB2 -> ManagedB3 -> NativeCodeB
932 // On x86, each block of managed code is protected by one COMPlusFrameHandler [CPFH] (CLR's exception handler
933 // for managed code), unlike 64bit where each frame has a personality routine attached to it. Thus,
934 // for the example above, assume CPFH_A protects ManagedA* blocks and is setup just before the call to
935 // ManagedA1. Likewise, CPFH_B protects ManagedB* blocks and is setup just before the call to ManagedB1.
937 // When ManagedB3 throws an exception, CPFH_B is invoked to look for a handler in all of the ManagedB* blocks.
938 // At this point, it is setup as the "bottom-most-handler" (BMH). If no handler is found and exception reaches
939 // ManagedA* blocks, CPFH_A is invoked to look for a handler and thus, becomes BMH.
941 // Thus, in the first pass on x86 for a given exception, a particular CPFH will be invoked only once when looking
942 // for a handler and thus, registered as BMH only once. Either the exception goes unhandled and the process will
943 // terminate or a handler will be found and second pass will commence.
945 // However, assume NativeCodeB had a __try/__finally and raised an exception [E1] within the __try. Let's assume
946 // it gets caught in ManagedB1 and thus, unwind is triggered. At this point, the active exception tracker
947 // has context about the exception thrown out of __try and CPFH_B is registered as BMH.
949 // If the __finally throws a new exception [E2], CPFH_B will be invoked again for first pass while looking for
950 // a handler for the thrown exception. Since BMH is already non-NULL, we will come here since EstablisherFrame will be
951 // the same as BMH (because EstablisherFrame will be that of CPFH_B). We will proceed to overwrite the "required" parts
952 // of the existing exception tracker with the details of E2 (see setting of exception record and context below), erasing
953 // any artifact of E1.
955 // This is unlike Scenario 1 when exception tracker is completely initialized to default values. This is also
956 // unlike 64bit which will detect that E1 and E2 are different exceptions and hence, will setup a new tracker
957 // to track E2, effectively behaving like Scenario 1 above. X86 cannot do this since there is no nested exception
958 // tracker setup that gets to see the new exception.
960 // Thus, if E1 was a CSE and E2 isn't, we will come here and treat E2 as a CSE as well since corruption severity
961 // is initialized as part of exception tracker initialization. Thus, E2 will start to be treated as CSE, which is
962 // incorrect. Similar argument applies to delivery of First chance exception notification delivery.
964 // <QUIP> Another example why we should unify EH systems :) </QUIP>
966 // To address this issue, we will need to reset exception tracker here, just like the overwriting of "required"
967 // parts of exception tracker.
969 // If the current establisher frame is the same as the bottom-most-handler and we are here
970 // in the first pass, assert that current exception and the one tracked by active exception tracker
971 // are indeed different exceptions. In such a case, we must reset the exception tracker so that it can be
972 // setup correctly further down when CEHelper::SetupCorruptionSeverityForActiveException is invoked.
974 if ((pExInfo->m_pBottomMostHandler != NULL) &&
975 (pEstablisherFrame == pExInfo->m_pBottomMostHandler))
977 // Current exception should be different from the one exception tracker is already tracking.
978 _ASSERTE(pExceptionRecord != pExInfo->m_pExceptionRecord);
980 // This cannot be nested exceptions - they are handled earlier (see above).
981 _ASSERTE(!bNestedException);
983 LOG((LF_EH, LL_INFO100, "CPFH_RealFirstPassHandler: Bottom-most handler (0x%p) is the same as EstablisherFrame.\n",
984 pExInfo->m_pBottomMostHandler));
985 LOG((LF_EH, LL_INFO100, "CPFH_RealFirstPassHandler: Exception record in exception tracker is 0x%p, while that of new exception is 0x%p.\n",
986 pExInfo->m_pExceptionRecord, pExceptionRecord));
987 LOG((LF_EH, LL_INFO100, "CPFH_RealFirstPassHandler: Resetting exception tracker (0x%p).\n", pExInfo));
989 // This will reset the exception tracker state, including the corruption severity.
994 // If we are handling a fault from managed code, we need to set the Thread->ExInfo->pContext to
995 // the current fault context, which is used in the stack walk to get back into the managed
996 // stack with the correct registers. (Previously, this was done by linking in a FaultingExceptionFrame
998 // We are about to create the managed exception object, which may trigger a GC, so set this up now.
1000 pExInfo->m_pExceptionRecord = pExceptionRecord;
1001 pExInfo->m_pContext = pContext;
1002 if (pContext && ShouldHandleManagedFault(pExceptionRecord, pContext, pEstablisherFrame, pThread))
1003 { // If this was a fault in managed code, rather than create a Frame for stackwalking,
1004 // we can use this exinfo (after all, it has all the register info.)
1005 pExInfo->m_ExceptionFlags.SetUseExInfoForStackwalk();
1008 // It should now be safe for a GC to happen.
1010 // case 1 & 3: this is the first time through of a new, nested, or rethrown exception, so see if we can
1011 // find a handler. Only setup throwable if are bottommost handler
1012 if (IsComPlusException(pExceptionRecord) && (!bAsynchronousThreadStop))
1015 // Update the throwable from the last thrown object. Note: this may cause OOM, in which case we replace
1016 // both throwables with the preallocated OOM exception.
1017 pThread->SafeSetThrowables(pThread->LastThrownObject());
1019 // now we've got a COM+ exception, fall through to so see if we handle it
1021 STRESS_LOG3(LF_EH, LL_INFO10000, "CPFH_RealFirstPassHandler: fall through ExInfo:0x%p setting m_pBottomMostHandler to 0x%p from 0x%p\n",
1022 pExInfo, pEstablisherFrame, pExInfo->m_pBottomMostHandler);
1023 pExInfo->m_pBottomMostHandler = pEstablisherFrame;
1025 else if (bRethrownException)
1027 // If it was rethrown and not COM+, will still be the last one thrown. Either we threw it last and
1028 // stashed it here or someone else caught it and rethrew it, in which case it will still have been
1029 // originally stashed here.
1031 // Update the throwable from the last thrown object. Note: this may cause OOM, in which case we replace
1032 // both throwables with the preallocated OOM exception.
1033 pThread->SafeSetThrowables(pThread->LastThrownObject());
1034 STRESS_LOG3(LF_EH, LL_INFO10000, "CPFH_RealFirstPassHandler: rethrow non-COM+ ExInfo:0x%p setting m_pBottomMostHandler to 0x%p from 0x%p\n",
1035 pExInfo, pEstablisherFrame, pExInfo->m_pBottomMostHandler);
1036 pExInfo->m_pBottomMostHandler = pEstablisherFrame;
1040 if (!fIsManagedCode)
1042 tct.bDontCatch = false;
1045 if (exceptionCode == STATUS_BREAKPOINT)
1047 // don't catch int 3
1048 retval = ExceptionContinueSearch;
1052 // We need to set m_pBottomMostHandler here, Thread::IsExceptionInProgress returns 1.
1053 // This is a necessary part of suppressing thread abort exceptions in the constructor
1054 // of any exception object we might create.
1055 STRESS_LOG3(LF_EH, LL_INFO10000, "CPFH_RealFirstPassHandler: setting ExInfo:0x%p m_pBottomMostHandler for IsExceptionInProgress to 0x%p from 0x%p\n",
1056 pExInfo, pEstablisherFrame, pExInfo->m_pBottomMostHandler);
1057 pExInfo->m_pBottomMostHandler = pEstablisherFrame;
1059 // Create the managed exception object.
1060 OBJECTREF throwable = CreateCOMPlusExceptionObject(pThread, pExceptionRecord, bAsynchronousThreadStop);
1062 // Set the throwables on the thread to the newly created object. If this fails, it will return a
1063 // preallocated exception object instead. This also updates the last thrown exception, for rethrows.
1064 throwable = pThread->SafeSetThrowables(throwable);
1066 // Set the exception code and pointers. We set these after setting the throwables on the thread,
1067 // because if the proper exception is replaced by an OOM exception, we still want the exception code
1068 // and pointers set in the OOM exception.
1069 EXCEPTIONREF exceptionRef = (EXCEPTIONREF)throwable;
1070 exceptionRef->SetXCode(pExceptionRecord->ExceptionCode);
1071 exceptionRef->SetXPtrs(&exceptionPointers);
1074 tct.pBottomFrame = NULL;
1076 EEToProfilerExceptionInterfaceWrapper::ExceptionThrown(pThread);
1078 CPFH_UpdatePerformanceCounters();
1079 } // End of case-1-or-3
1082 // Allocate storage for the stack trace.
1083 OBJECTREF throwable = NULL;
1084 GCPROTECT_BEGIN(throwable);
1085 throwable = pThread->GetThrowable();
1087 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
1089 BEGIN_SO_INTOLERANT_CODE(GetThread());
1090 // Setup the state in current exception tracker indicating the corruption severity
1091 // of the active exception.
1092 CEHelper::SetupCorruptionSeverityForActiveException(bRethrownException, bNestedException,
1093 CEHelper::ShouldTreatActiveExceptionAsNonCorrupting());
1094 END_SO_INTOLERANT_CODE;
1096 #endif // FEATURE_CORRUPTING_EXCEPTIONS
1098 // Check if we are dealing with AV or not and if we are,
1099 // ensure that this is a real AV and not managed AV exception
1100 BOOL fIsThrownExceptionAV = FALSE;
1101 if ((pExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) &&
1102 (MscorlibBinder::GetException(kAccessViolationException) == throwable->GetMethodTable()))
1104 // Its an AV - set the flag
1105 fIsThrownExceptionAV = TRUE;
1108 // Did we get an AV?
1109 if (fIsThrownExceptionAV == TRUE)
1111 // Get the escalation policy action for handling AV
1112 EPolicyAction actionAV = GetEEPolicy()->GetActionOnFailure(FAIL_AccessViolation);
1114 // Valid actions are: eNoAction (default behviour) or eRudeExitProcess
1115 _ASSERTE(((actionAV == eNoAction) || (actionAV == eRudeExitProcess)));
1116 if (actionAV == eRudeExitProcess)
1118 LOG((LF_EH, LL_INFO100, "CPFH_RealFirstPassHandler: AccessViolation handler found and doing RudeExitProcess due to escalation policy (eRudeExitProcess)\n"));
1120 // EEPolicy::HandleFatalError will help us RudeExit the process.
1121 // RudeExitProcess due to AV is to prevent a security risk - we are ripping
1122 // at the boundary, without looking for the handlers.
1123 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_SECURITY);
1127 // If we're out of memory, then we figure there's probably not memory to maintain a stack trace, so we skip it.
1128 // If we've got a stack overflow, then we figure the stack will be so huge as to make tracking the stack trace
1129 // impracticle, so we skip it.
1130 if ((throwable == CLRException::GetPreallocatedOutOfMemoryException()) ||
1131 (throwable == CLRException::GetPreallocatedStackOverflowException()))
1133 tct.bAllowAllocMem = FALSE;
1137 pExInfo->m_StackTraceInfo.AllocateStackTrace();
1143 // Set up information for GetExceptionPointers()/GetExceptionCode() callback.
1144 pExInfo->SetExceptionCode(pExceptionRecord);
1146 pExInfo->m_pExceptionPointers = &exceptionPointers;
1149 if (bRethrownException || bNestedException)
1151 _ASSERTE(pExInfo->m_pPrevNestedInfo != NULL);
1153 BEGIN_SO_INTOLERANT_CODE(GetThread());
1154 SetStateForWatsonBucketing(bRethrownException, pExInfo->GetPreviousExceptionTracker()->GetThrowableAsHandle());
1155 END_SO_INTOLERANT_CODE;
1159 #ifdef DEBUGGING_SUPPORTED
1161 // At this point the exception is still fresh to us, so assert that
1162 // there should be nothing from the debugger on it.
1164 _ASSERTE(!pExInfo->m_ExceptionFlags.DebuggerInterceptInfo());
1167 if (pThread->IsRudeAbort())
1169 OBJECTREF rudeAbortThrowable = CLRException::GetPreallocatedRudeThreadAbortException();
1171 if (pThread->GetThrowable() != rudeAbortThrowable)
1173 // Neither of these sets will throw because the throwable that we're setting is a preallocated
1174 // exception. This also updates the last thrown exception, for rethrows.
1175 pThread->SafeSetThrowables(rudeAbortThrowable);
1178 if (!pThread->IsRudeAbortInitiated())
1180 pThread->PreWorkForThreadAbort();
1184 LOG((LF_EH, LL_INFO100, "CPFH_RealFirstPassHandler: looking for handler bottom %x, top %x\n",
1185 tct.pBottomFrame, tct.pTopFrame));
1186 tct.bReplaceStack = pExInfo->m_pBottomMostHandler == pEstablisherFrame && !bRethrownException;
1187 tct.bSkipLastElement = bRethrownException && bNestedException;
1188 found = LookForHandler(&exceptionPointers,
1192 // We have searched this far.
1193 pExInfo->m_pSearchBoundary = tct.pTopFrame;
1194 LOG((LF_EH, LL_INFO1000, "CPFH_RealFirstPassHandler: set pSearchBoundary to 0x%p\n", pExInfo->m_pSearchBoundary));
1196 if ((found == LFH_NOT_FOUND)
1197 #ifdef DEBUGGING_SUPPORTED
1198 && !pExInfo->m_ExceptionFlags.DebuggerInterceptInfo()
1202 LOG((LF_EH, LL_INFO100, "CPFH_RealFirstPassHandler: NOT_FOUND\n"));
1204 if (tct.pTopFrame == FRAME_TOP)
1206 LOG((LF_EH, LL_INFO100, "CPFH_RealFirstPassHandler: NOT_FOUND at FRAME_TOP\n"));
1209 retval = ExceptionContinueSearch;
1214 // so we are going to handle the exception
1216 // Remove the nested exception record -- before calling RtlUnwind.
1217 // The second-pass callback for a NestedExceptionRecord assumes that if it's
1218 // being unwound, it should pop one exception from the pExInfo chain. This is
1219 // true for any older NestedRecords that might be unwound -- but not for the
1220 // new one we're about to add. To avoid this, we remove the new record
1221 // before calling Unwind.
1223 // <TODO>@NICE: This can probably be a little cleaner -- the nested record currently
1224 // is also used to guard the running of the filter code. When we clean up the
1225 // behaviour of exceptions within filters, we should be able to get rid of this
1226 // PUSH/POP/PUSH behaviour.</TODO>
1227 _ASSERTE(bPopNestedHandlerExRecord);
1229 UNINSTALL_EXCEPTION_HANDLING_RECORD(&(nestedHandlerExRecord.m_ExReg));
1231 // Since we are going to handle the exception we switch into preemptive mode
1232 GCX_PREEMP_NO_DTOR();
1234 #ifdef DEBUGGING_SUPPORTED
1236 // Check if the debugger wants to intercept this frame at a different point than where we are.
1238 if (pExInfo->m_ExceptionFlags.DebuggerInterceptInfo())
1240 ClrDebuggerDoUnwindAndIntercept(pEstablisherFrame, pExceptionRecord);
1243 // If this returns, then the debugger couldn't do it's stuff and we default to the found handler.
1245 if (found == LFH_NOT_FOUND)
1247 retval = ExceptionContinueSearch;
1248 // we need to be sure to switch back into Cooperative mode since we are going to
1249 // jump to the exit: label and follow the normal return path (it is expected that
1250 // CPFH_RealFirstPassHandler returns in COOP.
1251 GCX_PREEMP_NO_DTOR_END();
1257 LOG((LF_EH, LL_INFO100, "CPFH_RealFirstPassHandler: handler found: %s\n", tct.pFunc->m_pszDebugMethodName));
1259 CallRtlUnwindSafe(pEstablisherFrame, RtlUnwindCallback, pExceptionRecord, 0);
1260 // on x86 at least, RtlUnwind always returns
1262 // Note: we've completed the unwind pass up to the establisher frame, and we're headed off to finish our
1263 // cleanup and end up back in jitted code. Any more FS0 handlers pushed from this point on out will _not_ be
1265 // Note: we are still in Preemptive mode here and that is correct, COMPlusAfterUnwind will switch us back
1266 // into Cooperative mode.
1267 return COMPlusAfterUnwind(pExceptionRecord, pEstablisherFrame, tct);
1272 // We need to be in COOP if we get here
1276 // If we got as far as saving pExInfo, save the context pointer so it's available for the unwind.
1279 pExInfo->m_pContext = pContext;
1280 // pExInfo->m_pExceptionPointers points to a local structure, which is now going out of scope.
1281 pExInfo->m_pExceptionPointers = NULL;
1284 #if defined(USE_FEF)
1285 if (bPopFaultingExceptionFrame)
1287 faultingExceptionFrame.Pop();
1291 if (bPopNestedHandlerExRecord)
1293 UNINSTALL_EXCEPTION_HANDLING_RECORD(&(nestedHandlerExRecord.m_ExReg));
1296 } // CPFH_RealFirstPassHandler()
1299 //******************************************************************************
1301 void InitializeExceptionHandling()
1303 WRAPPER_NO_CONTRACT;
1305 InitSavedExceptionInfo();
1307 CLRAddVectoredHandlers();
1309 // Initialize the lock used for synchronizing access to the stacktrace in the exception object
1310 g_StackTraceArrayLock.Init(LOCK_TYPE_DEFAULT, TRUE);
1313 //******************************************************************************
1314 static inline EXCEPTION_DISPOSITION __cdecl
1315 CPFH_FirstPassHandler(EXCEPTION_RECORD *pExceptionRecord,
1316 EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame,
1318 DISPATCHER_CONTEXT *pDispatcherContext)
1320 WRAPPER_NO_CONTRACT;
1321 EXCEPTION_DISPOSITION retval;
1323 _ASSERTE (!(pExceptionRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)));
1325 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
1327 Thread *pThread = GetThread();
1329 STRESS_LOG4(LF_EH, LL_INFO100,
1330 "CPFH_FirstPassHandler: pEstablisherFrame = %x EH code = %x EIP = %x with ESP = %x\n",
1331 pEstablisherFrame, exceptionCode, pContext ? GetIP(pContext) : 0, pContext ? GetSP(pContext) : 0);
1333 EXCEPTION_POINTERS ptrs = { pExceptionRecord, pContext };
1335 // Call to the vectored handler to give other parts of the Runtime a chance to jump in and take over an
1336 // exception before we do too much with it. The most important point in the vectored handler is not to toggle
1338 DWORD filter = CLRVectoredExceptionHandler(&ptrs);
1340 if (filter == (DWORD) EXCEPTION_CONTINUE_EXECUTION)
1342 return ExceptionContinueExecution;
1344 else if (filter == EXCEPTION_CONTINUE_SEARCH)
1346 return ExceptionContinueSearch;
1349 #if defined(STRESS_HEAP)
1351 // Check to see if this exception is due to GCStress. Since the GCStress mechanism only injects these faults
1352 // into managed code, we only need to check for them in CPFH_FirstPassHandler.
1354 if (IsGcMarker(exceptionCode, pContext))
1356 return ExceptionContinueExecution;
1358 #endif // STRESS_HEAP
1360 // We always want to be in co-operative mode when we run this function and whenever we return
1361 // from it, want to go to pre-emptive mode because are returning to OS.
1362 BOOL disabled = pThread->PreemptiveGCDisabled();
1365 BOOL bAsynchronousThreadStop = IsThreadHijackedForThreadStop(pThread, pExceptionRecord);
1367 if (bAsynchronousThreadStop)
1369 // If we ever get here in preemptive mode, we're in trouble. We've
1370 // changed the thread's IP to point at a little function that throws ... if
1371 // the thread were to be in preemptive mode and a GC occurred, the stack
1372 // crawl would have been all messed up (becuase we have no frame that points
1373 // us back to the right place in managed code).
1376 AdjustContextForThreadStop(pThread, pContext);
1377 LOG((LF_EH, LL_INFO100, "CPFH_FirstPassHandler is Asynchronous Thread Stop or Abort\n"));
1380 pThread->ResetThrowControlForThread();
1382 CPFH_VerifyThreadIsInValidState(pThread, exceptionCode, pEstablisherFrame);
1384 // If we were in cooperative mode when we came in here, then its okay to see if we should do HandleManagedFault
1385 // and push a FaultingExceptionFrame. If we weren't in coop mode coming in here, then it means that there's no
1386 // way the exception could really be from managed code. I might look like it was from managed code, but in
1387 // reality its a rethrow from unmanaged code, either unmanaged user code, or unmanaged EE implementation.
1388 if (disabled && ShouldHandleManagedFault(pExceptionRecord, pContext, pEstablisherFrame, pThread))
1390 #if defined(USE_FEF)
1391 HandleManagedFault(pExceptionRecord, pContext, pEstablisherFrame, pThread);
1392 retval = ExceptionContinueExecution;
1395 // Save the context pointer in the Thread's EXInfo, so that a stack crawl can recover the
1396 // register values from the fault.
1398 //@todo: I haven't yet found any case where we need to do anything here. If there are none, eliminate
1399 // this entire if () {} block.
1403 // OK. We're finally ready to start the real work. Nobody else grabbed the exception in front of us. Now we can
1405 retval = CPFH_RealFirstPassHandler(pExceptionRecord,
1409 bAsynchronousThreadStop,
1412 #if defined(USE_FEF) // This label is only used in the HandleManagedFault() case above.
1415 if (retval != ExceptionContinueExecution || !disabled)
1417 GCX_PREEMP_NO_DTOR();
1420 STRESS_LOG1(LF_EH, LL_INFO100, "CPFH_FirstPassHandler: exiting with retval %d\n", retval);
1422 } // CPFH_FirstPassHandler()
1424 //******************************************************************************
1426 CPFH_UnwindFrames1(Thread* pThread, EXCEPTION_REGISTRATION_RECORD* pEstablisherFrame, DWORD exceptionCode)
1428 WRAPPER_NO_CONTRACT;
1430 ExInfo* pExInfo = &(pThread->GetExceptionState()->m_currentExInfo);
1432 // Ready to unwind the stack...
1433 ThrowCallbackType tct;
1435 tct.bIsUnwind = TRUE;
1436 tct.pTopFrame = GetCurrFrame(pEstablisherFrame); // highest frame to search to
1437 tct.pBottomFrame = NULL;
1439 // Set the flag indicating if the current exception represents a longjmp.
1440 // See comment in COMPlusUnwindCallback for details.
1441 CORRUPTING_EXCEPTIONS_ONLY(tct.m_fIsLongJump = (exceptionCode == STATUS_LONGJUMP);)
1444 tct.pCurrentExceptionRecord = pEstablisherFrame;
1445 tct.pPrevExceptionRecord = GetPrevSEHRecord(pEstablisherFrame);
1448 #ifdef DEBUGGING_SUPPORTED
1449 EXCEPTION_REGISTRATION_RECORD *pInterceptEstablisherFrame = NULL;
1451 // If the exception is intercepted, use information stored in the DebuggerExState to unwind the stack.
1452 if (pExInfo->m_ExceptionFlags.DebuggerInterceptInfo())
1454 pExInfo->m_DebuggerExState.GetDebuggerInterceptInfo(&pInterceptEstablisherFrame,
1455 NULL, // MethodDesc **ppFunc,
1456 NULL, // int *pdHandler,
1457 NULL, // BYTE **ppStack
1458 NULL, // ULONG_PTR *pNativeOffset,
1459 NULL // Frame **ppFrame)
1461 LOG((LF_EH, LL_INFO1000, "CPFH_UnwindFrames1: frames are Est 0x%X, Intercept 0x%X\n",
1462 pEstablisherFrame, pInterceptEstablisherFrame));
1465 // When we set up for the interception we store off the CPFH or CPNEH that we
1466 // *know* will handle unwinding the destination of the intercept.
1468 // However, a CPNEH with the same limiting Capital-F-rame could do the work
1469 // and unwind us, so...
1471 // If this is the exact frame handler we are supposed to search for, or
1472 // if this frame handler services the same Capital-F-rame as the frame handler
1473 // we are looking for (i.e. this frame handler may do the work that we would
1474 // expect our frame handler to do),
1476 // we need to pass the interception destination during this unwind.
1478 _ASSERTE(IsUnmanagedToManagedSEHHandler(pEstablisherFrame));
1480 if ((pEstablisherFrame == pInterceptEstablisherFrame) ||
1481 (GetCurrFrame(pEstablisherFrame) == GetCurrFrame(pInterceptEstablisherFrame)))
1483 pExInfo->m_DebuggerExState.GetDebuggerInterceptInfo(NULL,
1491 LOG((LF_EH, LL_INFO1000, "CPFH_UnwindFrames1: going to: pFunc:%#X, pStack:%#X\n",
1492 tct.pFunc, tct.pStack));
1499 UnwindFrames(pThread, &tct);
1501 LOG((LF_EH, LL_INFO1000, "CPFH_UnwindFrames1: after unwind ec:%#x, tct.pTopFrame:0x%p, pSearchBndry:0x%p\n"
1502 " pEstFrame:0x%p, IsC+NestExRec:%d, !Nest||Active:%d\n",
1503 exceptionCode, tct.pTopFrame, pExInfo->m_pSearchBoundary, pEstablisherFrame,
1504 IsComPlusNestedExceptionRecord(pEstablisherFrame),
1505 (!IsComPlusNestedExceptionRecord(pEstablisherFrame) || reinterpret_cast<NestedHandlerExRecord*>(pEstablisherFrame)->m_ActiveForUnwind)));
1507 if (tct.pTopFrame >= pExInfo->m_pSearchBoundary &&
1508 (!IsComPlusNestedExceptionRecord(pEstablisherFrame) ||
1509 reinterpret_cast<NestedHandlerExRecord*>(pEstablisherFrame)->m_ActiveForUnwind) )
1511 // If this is the search boundary, and we're not a nested handler, then
1512 // this is the last time we'll see this exception. Time to unwind our
1514 STRESS_LOG0(LF_EH, LL_INFO100, "CPFH_UnwindFrames1: Exception unwind -- unmanaged catcher detected\n");
1515 pExInfo->UnwindExInfo((VOID*)pEstablisherFrame);
1517 } // CPFH_UnwindFrames1()
1519 //******************************************************************************
1520 inline EXCEPTION_DISPOSITION __cdecl
1521 CPFH_UnwindHandler(EXCEPTION_RECORD *pExceptionRecord,
1522 EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame,
1524 void *pDispatcherContext)
1526 WRAPPER_NO_CONTRACT;
1527 _ASSERTE (pExceptionRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND));
1530 // Note: you might be inclined to write "static int breakOnSecondPass = CLRConfig::GetConfigValue(...);", but
1531 // you can't do that here. That causes C++ EH to be generated under the covers for this function, and this
1532 // function isn't allowed to have any C++ EH in it because its never going to return.
1533 static int breakOnSecondPass; // = 0
1534 static BOOL breakOnSecondPassSetup; // = FALSE
1535 if (!breakOnSecondPassSetup)
1537 breakOnSecondPass = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnSecondPass);
1538 breakOnSecondPassSetup = TRUE;
1540 if (breakOnSecondPass != 0)
1542 _ASSERTE(!"Unwind handler");
1546 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
1547 Thread *pThread = GetThread();
1549 ExInfo* pExInfo = &(pThread->GetExceptionState()->m_currentExInfo);
1551 STRESS_LOG4(LF_EH, LL_INFO100, "In CPFH_UnwindHandler EHCode = %x EIP = %x with ESP = %x, pEstablisherFrame = 0x%p\n", exceptionCode,
1552 pContext ? GetIP(pContext) : 0, pContext ? GetSP(pContext) : 0, pEstablisherFrame);
1554 // We always want to be in co-operative mode when we run this function. Whenever we return
1555 // from it, want to go to pre-emptive mode because are returning to OS.
1558 // needs to be in its own scope to avoid polluting the namespace, since
1559 // we don't do a _END then we don't revert the state
1563 CPFH_VerifyThreadIsInValidState(pThread, exceptionCode, pEstablisherFrame);
1565 if (IsComPlusNestedExceptionRecord(pEstablisherFrame))
1567 NestedHandlerExRecord *pHandler = reinterpret_cast<NestedHandlerExRecord*>(pEstablisherFrame);
1568 if (pHandler->m_pCurrentExInfo != NULL)
1570 // See the comment at the end of COMPlusNestedExceptionHandler about nested exception.
1571 // OS is going to skip the EstablisherFrame before our NestedHandler.
1572 if (pHandler->m_pCurrentExInfo->m_pBottomMostHandler <= pHandler->m_pCurrentHandler)
1574 // We're unwinding -- the bottom most handler is potentially off top-of-stack now. If
1575 // it is, change it to the next COM+ frame. (This one is not good, as it's about to
1577 EXCEPTION_REGISTRATION_RECORD *pNextBottomMost = GetNextCOMPlusSEHRecord(pHandler->m_pCurrentHandler);
1579 STRESS_LOG3(LF_EH, LL_INFO10000, "COMPlusNestedExceptionHandler: setting ExInfo:0x%p m_pBottomMostHandler from 0x%p to 0x%p\n",
1580 pHandler->m_pCurrentExInfo, pHandler->m_pCurrentExInfo->m_pBottomMostHandler, pNextBottomMost);
1582 pHandler->m_pCurrentExInfo->m_pBottomMostHandler = pNextBottomMost;
1587 // this establishes a marker so can determine if are processing a nested exception
1588 // don't want to use the current frame to limit search as it could have been unwound by
1589 // the time get to nested handler (ie if find an exception, unwind to the call point and
1590 // then resume in the catch and then get another exception) so make the nested handler
1591 // have the same boundary as this one. If nested handler can't find a handler, we won't
1592 // end up searching this frame list twice because the nested handler will set the search
1593 // boundary in the thread and so if get back to this handler it will have a range that starts
1594 // and ends at the same place.
1595 NestedHandlerExRecord nestedHandlerExRecord;
1596 nestedHandlerExRecord.Init((PEXCEPTION_ROUTINE)COMPlusNestedExceptionHandler, GetCurrFrame(pEstablisherFrame));
1598 nestedHandlerExRecord.m_ActiveForUnwind = TRUE;
1599 nestedHandlerExRecord.m_pCurrentExInfo = pExInfo;
1600 nestedHandlerExRecord.m_pCurrentHandler = pEstablisherFrame;
1602 INSTALL_EXCEPTION_HANDLING_RECORD(&(nestedHandlerExRecord.m_ExReg));
1604 // Unwind the stack. The establisher frame sets the boundary.
1605 CPFH_UnwindFrames1(pThread, pEstablisherFrame, exceptionCode);
1607 // We're unwinding -- the bottom most handler is potentially off top-of-stack now. If
1608 // it is, change it to the next COM+ frame. (This one is not good, as it's about to
1610 if (pExInfo->m_pBottomMostHandler &&
1611 pExInfo->m_pBottomMostHandler <= pEstablisherFrame)
1613 EXCEPTION_REGISTRATION_RECORD *pNextBottomMost = GetNextCOMPlusSEHRecord(pEstablisherFrame);
1615 // If there is no previous COM+ SEH handler, GetNextCOMPlusSEHRecord() will return -1. Much later, we will dereference that and AV.
1616 _ASSERTE (pNextBottomMost != EXCEPTION_CHAIN_END);
1618 STRESS_LOG3(LF_EH, LL_INFO10000, "CPFH_UnwindHandler: setting ExInfo:0x%p m_pBottomMostHandler from 0x%p to 0x%p\n",
1619 pExInfo, pExInfo->m_pBottomMostHandler, pNextBottomMost);
1621 pExInfo->m_pBottomMostHandler = pNextBottomMost;
1625 // needs to be in its own scope to avoid polluting the namespace, since
1626 // we don't do a _END then we don't revert the state
1627 GCX_PREEMP_NO_DTOR();
1629 UNINSTALL_EXCEPTION_HANDLING_RECORD(&(nestedHandlerExRecord.m_ExReg));
1631 // If we are here, then exception was not caught in managed code protected by this
1632 // ComplusFrameHandler. Hence, reset thread abort state if this is the last personality routine,
1633 // for managed code, on the stack.
1634 ResetThreadAbortState(pThread, pEstablisherFrame);
1636 STRESS_LOG0(LF_EH, LL_INFO100, "CPFH_UnwindHandler: Leaving with ExceptionContinueSearch\n");
1637 return ExceptionContinueSearch;
1638 } // CPFH_UnwindHandler()
1640 //******************************************************************************
1641 // This is the first handler that is called in the context of managed code
1642 // It is the first level of defense and tries to find a handler in the user
1643 // code to handle the exception
1644 //-------------------------------------------------------------------------
1645 // EXCEPTION_DISPOSITION __cdecl COMPlusFrameHandler(
1646 // EXCEPTION_RECORD *pExceptionRecord,
1647 // _EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame,
1648 // CONTEXT *pContext,
1649 // DISPATCHER_CONTEXT *pDispatcherContext)
1651 // See http://www.microsoft.com/msj/0197/exception/exception.aspx for a background piece on Windows
1652 // unmanaged structured exception handling.
1653 EXCEPTION_HANDLER_IMPL(COMPlusFrameHandler)
1655 WRAPPER_NO_CONTRACT;
1656 _ASSERTE(!DebugIsEECxxException(pExceptionRecord) && "EE C++ Exception leaked into managed code!");
1658 STRESS_LOG5(LF_EH, LL_INFO100, "In COMPlusFrameHander EH code = %x flag = %x EIP = %x with ESP = %x, pEstablisherFrame = 0x%p\n",
1659 pExceptionRecord->ExceptionCode, pExceptionRecord->ExceptionFlags,
1660 pContext ? GetIP(pContext) : 0, pContext ? GetSP(pContext) : 0, pEstablisherFrame);
1662 _ASSERTE((pContext == NULL) || ((pContext->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL));
1664 if (g_fNoExceptions)
1665 return ExceptionContinueSearch; // No EH during EE shutdown.
1667 // Check if the exception represents a GCStress Marker. If it does,
1668 // we shouldnt record its entry in the TLS as such exceptions are
1669 // continuable and can confuse the VM to treat them as CSE,
1670 // as they are implemented using illegal instruction exception.
1672 bool fIsGCMarker = false;
1674 #ifdef HAVE_GCCOVER // This is a debug only macro
1675 if (GCStress<cfg_instr_jit>::IsEnabled())
1677 // UnsafeTlsGetValue trashes last error. When Complus_GCStress=4, GC is invoked
1678 // on every allowable JITed instruction by means of our exception handling machanism
1679 // it is very easy to trash the last error. For example, a p/invoke called a native method
1680 // which sets last error. Before we getting the last error in the IL stub, it is trashed here
1681 DWORD dwLastError = GetLastError();
1682 fIsGCMarker = IsGcMarker(pExceptionRecord->ExceptionCode, pContext);
1685 SaveCurrentExceptionInfo(pExceptionRecord, pContext);
1687 SetLastError(dwLastError);
1692 // GCStress does not exist on retail builds (see IsGcMarker implementation for details).
1693 SaveCurrentExceptionInfo(pExceptionRecord, pContext);
1698 // If this was a GCStress marker exception, then return
1699 // ExceptionContinueExecution to the OS.
1700 return ExceptionContinueExecution;
1703 EXCEPTION_DISPOSITION retVal = ExceptionContinueSearch;
1705 Thread *pThread = GetThread();
1706 if ((pExceptionRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)) == 0)
1708 if (IsSOExceptionCode(pExceptionRecord->ExceptionCode))
1710 EEPolicy::HandleStackOverflow(SOD_ManagedFrameHandler, (void*)pEstablisherFrame);
1712 // VC's unhandled exception filter plays with stack. It VirtualAlloc's a new stack, and
1713 // then launch Watson from the new stack. When Watson asks CLR to save required data, we
1714 // are not able to walk the stack.
1715 // Setting Context in ExInfo so that our Watson dump routine knows how to walk this stack.
1716 ExInfo* pExInfo = &(pThread->GetExceptionState()->m_currentExInfo);
1717 pExInfo->m_pContext = pContext;
1719 // Save the reference to the topmost handler we see during first pass when an SO goes past us.
1720 // When an unwind gets triggered for the exception, we will reset the frame chain when we reach
1721 // the topmost handler we saw during the first pass.
1723 // This unifies, behaviour-wise, 32bit with 64bit.
1724 if ((pExInfo->m_pTopMostHandlerDuringSO == NULL) ||
1725 (pEstablisherFrame > pExInfo->m_pTopMostHandlerDuringSO))
1727 pExInfo->m_pTopMostHandlerDuringSO = pEstablisherFrame;
1730 // Switch to preemp mode since we are returning back to the OS.
1731 // We will do the quick switch since we are short of stack
1732 FastInterlockAnd (&pThread->m_fPreemptiveGCDisabled, 0);
1734 return ExceptionContinueSearch;
1738 #ifdef FEATURE_STACK_PROBE
1739 if (GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) == eRudeUnloadAppDomain)
1741 RetailStackProbe(static_cast<unsigned int>(ADJUST_PROBE(BACKOUT_CODE_STACK_LIMIT)), pThread);
1748 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
1750 if (exceptionCode == STATUS_UNWIND)
1752 // If exceptionCode is STATUS_UNWIND, RtlUnwind is called with a NULL ExceptionRecord,
1753 // therefore OS uses a faked ExceptionRecord with STATUS_UNWIND code. Then we need to
1754 // look at our saved exception code.
1755 exceptionCode = GetCurrentExceptionCode();
1758 if (IsSOExceptionCode(exceptionCode))
1760 // We saved the context during the first pass in case the stack overflow exception is
1761 // unhandled and Watson dump code needs it. Now we are in the second pass, therefore
1762 // either the exception is handled by user code, or we have finished unhandled exception
1763 // filter process, and the OS is unwinding the stack. Either way, we don't need the
1764 // context any more. It is very important to reset the context so that our code does not
1765 // accidentally walk the frame using the dangling context in ExInfoWalker::WalkToPosition.
1766 ExInfo* pExInfo = &(pThread->GetExceptionState()->m_currentExInfo);
1767 pExInfo->m_pContext = NULL;
1769 // We should have the reference to the topmost handler seen during the first pass of SO
1770 _ASSERTE(pExInfo->m_pTopMostHandlerDuringSO != NULL);
1772 // Reset frame chain till we reach the topmost establisher frame we saw in the first pass.
1773 // This will ensure that if any intermediary frame calls back into managed (e.g. native frame
1774 // containing a __finally that reverse pinvokes into managed), then we have the correct
1775 // explicit frame on the stack. Resetting the frame chain only when we reach the topmost
1776 // personality routine seen in the first pass may not result in expected behaviour,
1777 // specially during stack walks when crawl frame needs to be initialized from
1779 if (pEstablisherFrame <= pExInfo->m_pTopMostHandlerDuringSO)
1783 if (pThread->GetFrame() < GetCurrFrame(pEstablisherFrame))
1785 // We are very short of stack. We avoid calling UnwindFrame which may
1786 // run unknown code here.
1787 pThread->SetFrame(GetCurrFrame(pEstablisherFrame));
1791 // Switch to preemp mode since we are returning back to the OS.
1792 // We will do the quick switch since we are short of stack
1793 FastInterlockAnd(&pThread->m_fPreemptiveGCDisabled, 0);
1795 return ExceptionContinueSearch;
1799 // <TODO> . We need to probe here, but can't introduce destructors etc. </TODO>
1800 BEGIN_CONTRACT_VIOLATION(SOToleranceViolation);
1802 if (pExceptionRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))
1804 retVal = CPFH_UnwindHandler(pExceptionRecord,
1807 pDispatcherContext);
1812 /* Make no assumptions about the current machine state.
1813 <TODO>@PERF: Only needs to be called by the very first handler invoked by SEH </TODO>*/
1814 ResetCurrentContext();
1816 retVal = CPFH_FirstPassHandler(pExceptionRecord,
1819 pDispatcherContext);
1823 END_CONTRACT_VIOLATION;
1826 } // COMPlusFrameHandler()
1829 //-------------------------------------------------------------------------
1830 // This is called by the EE to restore the stack pointer if necessary.
1831 //-------------------------------------------------------------------------
1833 // This can't be inlined into the caller to avoid introducing EH frame
1834 NOINLINE LPVOID COMPlusEndCatchWorker(Thread * pThread)
1836 STATIC_CONTRACT_THROWS;
1837 STATIC_CONTRACT_GC_TRIGGERS;
1838 STATIC_CONTRACT_MODE_COOPERATIVE;
1839 STATIC_CONTRACT_SO_INTOLERANT;
1841 LOG((LF_EH, LL_INFO1000, "COMPlusPEndCatch:called with "
1842 "pThread:0x%x\n",pThread));
1844 // indicate that we are out of the managed clause as early as possible
1845 ExInfo* pExInfo = &(pThread->GetExceptionState()->m_currentExInfo);
1846 pExInfo->m_EHClauseInfo.SetManagedCodeEntered(FALSE);
1850 // @todo . We need to probe in the EH code, but can't introduce destructors etc.
1851 BEGIN_CONTRACT_VIOLATION(SOToleranceViolation);
1853 // Notify the profiler that the catcher has finished running
1854 // IL stubs don't contain catch blocks so inability to perform this check does not matter.
1855 // if (!pFunc->IsILStub())
1856 EEToProfilerExceptionInterfaceWrapper::ExceptionCatcherLeave();
1858 // no need to set pExInfo->m_ClauseType = (DWORD)COR_PRF_CLAUSE_NONE now that the
1859 // notification is done because because the ExInfo record is about to be popped off anyway
1861 LOG((LF_EH, LL_INFO1000, "COMPlusPEndCatch:pThread:0x%x\n",pThread));
1864 gLastResumedExceptionFunc = NULL;
1865 gLastResumedExceptionHandler = 0;
1867 // Set the thrown object to NULL as no longer needed. This also sets the last thrown object to NULL.
1868 pThread->SafeSetThrowables(NULL);
1870 // reset the stashed exception info
1871 pExInfo->m_pExceptionRecord = NULL;
1872 pExInfo->m_pContext = NULL;
1873 pExInfo->m_pExceptionPointers = NULL;
1875 if (pExInfo->m_pShadowSP)
1877 *pExInfo->m_pShadowSP = 0; // Reset the shadow SP
1880 // pExInfo->m_dEsp was set in ResumeAtJITEH(). It is the Esp of the
1881 // handler nesting level which catches the exception.
1882 esp = (void*)(size_t)pExInfo->m_dEsp;
1884 pExInfo->UnwindExInfo(esp);
1886 // Prepare to sync managed exception state
1888 // In a case when we're nested inside another catch block, the domain in which we're executing may not be the
1889 // same as the one the domain of the throwable that was just made the current throwable above. Therefore, we
1890 // make a special effort to preserve the domain of the throwable as we update the the last thrown object.
1892 // This function (COMPlusEndCatch) can also be called by the in-proc debugger helper thread on x86 when
1893 // an attempt to SetIP takes place to set IP outside the catch clause. In such a case, managed thread object
1894 // will not be available. Thus, we should reset the severity only if its not such a thread.
1896 // This behaviour (of debugger doing SetIP) is not allowed on 64bit since the catch clauses are implemented
1897 // as a seperate funclet and it's just not allowed to set the IP across EH scopes, such as from inside a catch
1898 // clause to outside of the catch clause.
1899 bool fIsDebuggerHelperThread = (g_pDebugInterface == NULL) ? false : g_pDebugInterface->ThisIsHelperThread();
1901 // Sync managed exception state, for the managed thread, based upon any active exception tracker
1902 pThread->SyncManagedExceptionState(fIsDebuggerHelperThread);
1904 LOG((LF_EH, LL_INFO1000, "COMPlusPEndCatch: esp=%p\n", esp));
1906 END_CONTRACT_VIOLATION;
1912 // This function works in conjunction with JIT_EndCatch. On input, the parameters are set as follows:
1913 // ebp, ebx, edi, esi: the values of these registers at the end of the catch block
1914 // *pRetAddress: the next instruction after the call to JIT_EndCatch
1916 // On output, *pRetAddress is the instruction at which to resume execution. This may be user code,
1917 // or it may be ThrowControlForThread (which will re-raise a pending ThreadAbortException).
1919 // Returns the esp to set before resuming at *pRetAddress.
1921 LPVOID STDCALL COMPlusEndCatch(LPVOID ebp, DWORD ebx, DWORD edi, DWORD esi, LPVOID* pRetAddress)
1924 // PopNestedExceptionRecords directly manipulates fs:[0] chain. This method can't have any EH!
1926 STATIC_CONTRACT_THROWS;
1927 STATIC_CONTRACT_GC_TRIGGERS;
1928 STATIC_CONTRACT_MODE_COOPERATIVE;
1929 STATIC_CONTRACT_SO_INTOLERANT;
1931 ETW::ExceptionLog::ExceptionCatchEnd();
1932 ETW::ExceptionLog::ExceptionThrownEnd();
1934 void* esp = COMPlusEndCatchWorker(GetThread());
1936 // We are going to resume at a handler nesting level whose esp is dEsp. Pop off any SEH records below it. This
1937 // would be the COMPlusNestedExceptionHandler we had inserted.
1938 PopNestedExceptionRecords(esp);
1941 // Set up m_OSContext for the call to COMPlusCheckForAbort
1943 Thread* pThread = GetThread();
1944 _ASSERTE(pThread != NULL);
1946 SetIP(pThread->m_OSContext, (PCODE)*pRetAddress);
1947 SetSP(pThread->m_OSContext, (TADDR)esp);
1948 SetFP(pThread->m_OSContext, (TADDR)ebp);
1949 pThread->m_OSContext->Ebx = ebx;
1950 pThread->m_OSContext->Edi = edi;
1951 pThread->m_OSContext->Esi = esi;
1953 LPVOID throwControl = COMPlusCheckForAbort((UINT_PTR)*pRetAddress);
1955 *pRetAddress = throwControl;
1960 #endif // !DACCESS_COMPILE
1962 PTR_CONTEXT GetCONTEXTFromRedirectedStubStackFrame(CONTEXT * pContext)
1964 LIMITED_METHOD_DAC_CONTRACT;
1966 UINT_PTR stackSlot = pContext->Ebp + REDIRECTSTUB_EBP_OFFSET_CONTEXT;
1967 PTR_PTR_CONTEXT ppContext = dac_cast<PTR_PTR_CONTEXT>((TADDR)stackSlot);
1971 #if !defined(DACCESS_COMPILE)
1973 static PEXCEPTION_REGISTRATION_RECORD CurrentSEHRecord = EXCEPTION_CHAIN_END;
1976 PEXCEPTION_REGISTRATION_RECORD GetCurrentSEHRecord()
1978 WRAPPER_NO_CONTRACT;
1981 LPVOID fs0 = CurrentSEHRecord;
1982 #else // FEATURE_PAL
1983 LPVOID fs0 = (LPVOID)__readfsdword(0);
1985 #if 0 // This walk is too expensive considering we hit it every time we a CONTRACT(NOTHROW)
1987 EXCEPTION_REGISTRATION_RECORD *pEHR = (EXCEPTION_REGISTRATION_RECORD *)fs0;
1993 // check that all the eh frames are all greater than the current stack value. If not, the
1994 // stack has been updated somehow w/o unwinding the SEH chain.
1996 // LOG((LF_EH, LL_INFO1000000, "ER Chain:\n"));
1997 while (pEHR != NULL && pEHR != EXCEPTION_CHAIN_END) {
1998 // LOG((LF_EH, LL_INFO1000000, "\tp: prev:p handler:%x\n", pEHR, pEHR->Next, pEHR->Handler));
2000 if (gLastResumedExceptionFunc != 0)
2001 _ASSERTE(!"Stack is greater than start of SEH chain - possible missing leave in handler. See gLastResumedExceptionHandler & gLastResumedExceptionFunc for info");
2003 _ASSERTE(!"Stack is greater than start of SEH chain (FS:0)");
2005 if (pEHR->Handler == (void *)-1)
2006 _ASSERTE(!"Handler value has been corrupted");
2008 _ASSERTE(pEHR < pEHR->Next);
2014 #endif // FEATURE_PAL
2016 return (EXCEPTION_REGISTRATION_RECORD*) fs0;
2019 PEXCEPTION_REGISTRATION_RECORD GetFirstCOMPlusSEHRecord(Thread *pThread) {
2020 WRAPPER_NO_CONTRACT;
2022 EXCEPTION_REGISTRATION_RECORD *pEHR = *(pThread->GetExceptionListPtr());
2023 if (pEHR == EXCEPTION_CHAIN_END || IsUnmanagedToManagedSEHHandler(pEHR)) {
2026 return GetNextCOMPlusSEHRecord(pEHR);
2028 #else // FEATURE_PAL
2029 PORTABILITY_ASSERT("GetFirstCOMPlusSEHRecord");
2031 #endif // FEATURE_PAL
2035 PEXCEPTION_REGISTRATION_RECORD GetPrevSEHRecord(EXCEPTION_REGISTRATION_RECORD *next)
2037 WRAPPER_NO_CONTRACT;
2038 _ASSERTE(IsUnmanagedToManagedSEHHandler(next));
2040 EXCEPTION_REGISTRATION_RECORD *pEHR = GetCurrentSEHRecord();
2041 _ASSERTE(pEHR != 0 && pEHR != EXCEPTION_CHAIN_END);
2043 EXCEPTION_REGISTRATION_RECORD *pBest = 0;
2044 while (pEHR != next) {
2045 if (IsUnmanagedToManagedSEHHandler(pEHR))
2048 _ASSERTE(pEHR != 0 && pEHR != EXCEPTION_CHAIN_END);
2054 VOID SetCurrentSEHRecord(EXCEPTION_REGISTRATION_RECORD *pSEH)
2056 WRAPPER_NO_CONTRACT;
2058 *GetThread()->GetExceptionListPtr() = pSEH;
2059 #else // FEATURE_PAL
2061 #endif // FEATURE_PAL
2066 // Unwind pExinfo, pops FS:[0] handlers until the interception context SP, and
2067 // resumes at interception context.
2069 VOID UnwindExceptionTrackerAndResumeInInterceptionFrame(ExInfo* pExInfo, EHContext* context)
2071 STATIC_CONTRACT_NOTHROW;
2072 STATIC_CONTRACT_GC_NOTRIGGER;
2073 STATIC_CONTRACT_MODE_COOPERATIVE;
2074 STATIC_CONTRACT_SO_TOLERANT;
2076 _ASSERTE(pExInfo && context);
2078 pExInfo->UnwindExInfo((LPVOID)(size_t)context->Esp);
2079 PopNestedExceptionRecords((LPVOID)(size_t)context->Esp);
2081 STRESS_LOG3(LF_EH|LF_CORDB, LL_INFO100, "UnwindExceptionTrackerAndResumeInInterceptionFrame: completing intercept at EIP = %p ESP = %p EBP = %p\n", context->Eip, context->Esp, context->Ebp);
2083 ResumeAtJitEHHelper(context);
2084 UNREACHABLE_MSG("Should never return from ResumeAtJitEHHelper!");
2088 // Pop SEH records below the given target ESP. This is only used to pop nested exception records.
2089 // If bCheckForUnknownHandlers is set, it only checks for unknown FS:[0] handlers.
2091 BOOL PopNestedExceptionRecords(LPVOID pTargetSP, BOOL bCheckForUnknownHandlers)
2093 // No CONTRACT here, because we can't run the risk of it pushing any SEH into the current method.
2094 STATIC_CONTRACT_NOTHROW;
2095 STATIC_CONTRACT_GC_NOTRIGGER;
2096 STATIC_CONTRACT_SO_TOLERANT;
2099 PEXCEPTION_REGISTRATION_RECORD pEHR = GetCurrentSEHRecord();
2101 while ((LPVOID)pEHR < pTargetSP)
2104 // The only handler type we're allowed to have below the limit on the FS:0 chain in these cases is a nested
2105 // exception record, so we verify that here.
2107 // There is a special case, of course: for an unhandled exception, when the default handler does the exit
2108 // unwind, we may have an exception that escapes a finally clause, thus replacing the original unhandled
2109 // exception. If we find a catcher for that new exception, then we'll go ahead and do our own unwind, then
2110 // jump to the catch. When we are called here, just before jumpping to the catch, we'll pop off our nested
2111 // handlers, then we'll pop off one more handler: the handler that ntdll!ExecuteHandler2 pushed before
2112 // calling our nested handler. We go ahead and pop off that handler, too. Its okay, its only there to catch
2113 // exceptions from handlers and turn them into collided unwind status codes... there's no cleanup in the
2114 // handler that we're removing, and that's the important point. The handler that ExecuteHandler2 pushes
2115 // isn't a public export from ntdll, but its named "UnwindHandler" and is physically shortly after
2116 // ExecuteHandler2 in ntdll.
2118 static HINSTANCE ExecuteHandler2Module = 0;
2119 static BOOL ExecuteHandler2ModuleInited = FALSE;
2121 // Cache the handle to the dll with the handler pushed by ExecuteHandler2.
2122 if (!ExecuteHandler2ModuleInited)
2124 ExecuteHandler2Module = WszGetModuleHandle(W("ntdll.dll"));
2125 ExecuteHandler2ModuleInited = TRUE;
2128 if (bCheckForUnknownHandlers)
2130 if (!IsComPlusNestedExceptionRecord(pEHR) ||
2131 !((ExecuteHandler2Module != NULL) && IsIPInModule(ExecuteHandler2Module, (PCODE)pEHR->Handler)))
2139 // Note: if we can't find the module containing ExecuteHandler2, we'll just be really strict and require
2140 // that we're only popping nested handlers.
2141 _ASSERTE(IsComPlusNestedExceptionRecord(pEHR) ||
2142 ((ExecuteHandler2Module != NULL) && IsIPInModule(ExecuteHandler2Module, (PCODE)pEHR->Handler)));
2149 if (!bCheckForUnknownHandlers)
2151 SetCurrentSEHRecord(pEHR);
2154 #else // FEATURE_PAL
2155 PORTABILITY_ASSERT("PopNestedExceptionRecords");
2157 #endif // FEATURE_PAL
2161 // This is implemented differently from the PopNestedExceptionRecords above because it's called in the context of
2162 // the DebuggerRCThread to operate on the stack of another thread.
2164 VOID PopNestedExceptionRecords(LPVOID pTargetSP, CONTEXT *pCtx, void *pSEH)
2166 // No CONTRACT here, because we can't run the risk of it pushing any SEH into the current method.
2167 STATIC_CONTRACT_NOTHROW;
2168 STATIC_CONTRACT_GC_NOTRIGGER;
2171 LOG((LF_CORDB,LL_INFO1000, "\nPrintSEHRecords:\n"));
2173 EXCEPTION_REGISTRATION_RECORD *pEHR = (EXCEPTION_REGISTRATION_RECORD *)(size_t)*(DWORD *)pSEH;
2175 // check that all the eh frames are all greater than the current stack value. If not, the
2176 // stack has been updated somehow w/o unwinding the SEH chain.
2177 while (pEHR != NULL && pEHR != EXCEPTION_CHAIN_END)
2179 LOG((LF_EH, LL_INFO1000000, "\t%08x: next:%08x handler:%x\n", pEHR, pEHR->Next, pEHR->Handler));
2184 DWORD dwCur = *(DWORD*)pSEH; // 'EAX' in the original routine
2185 DWORD dwPrev = (DWORD)(size_t)pSEH;
2187 while (dwCur < (DWORD)(size_t)pTargetSP)
2189 // Watch for the OS handler
2190 // for nested exceptions, or any C++ handlers for destructors in our call
2191 // stack, or anything else.
2192 if (dwCur < (DWORD)GetSP(pCtx))
2195 dwCur = *(DWORD *)(size_t)dwCur;
2197 LOG((LF_CORDB,LL_INFO10000, "dwCur: 0x%x dwPrev:0x%x pTargetSP:0x%x\n",
2198 dwCur, dwPrev, pTargetSP));
2201 *(DWORD *)(size_t)dwPrev = dwCur;
2204 pEHR = (EXCEPTION_REGISTRATION_RECORD *)(size_t)*(DWORD *)pSEH;
2205 // check that all the eh frames are all greater than the current stack value. If not, the
2206 // stack has been updated somehow w/o unwinding the SEH chain.
2208 LOG((LF_CORDB,LL_INFO1000, "\nPopSEHRecords:\n"));
2209 while (pEHR != NULL && pEHR != (void *)-1)
2211 LOG((LF_EH, LL_INFO1000000, "\t%08x: next:%08x handler:%x\n", pEHR, pEHR->Next, pEHR->Handler));
2217 //==========================================================================
2218 // COMPlusThrowCallback
2220 //==========================================================================
2224 * COMPlusThrowCallbackHelper
2226 * This function is a simple helper function for COMPlusThrowCallback. It is needed
2227 * because of the EX_TRY macro. This macro does an alloca(), which allocates space
2228 * off the stack, not free'ing it. Thus, doing a EX_TRY in a loop can easily result
2229 * in a stack overflow error. By factoring out the EX_TRY into a separate function,
2230 * we recover that stack space.
2233 * pJitManager - The JIT manager that will filter the EH.
2234 * pCf - The frame to crawl.
2237 * pThread - Used to determine if the thread is throwable or not.
2243 int COMPlusThrowCallbackHelper(IJitManager *pJitManager,
2245 ThrowCallbackType* pData,
2246 EE_ILEXCEPTION_CLAUSE *EHClausePtr,
2248 OBJECTREF throwable,
2265 GCPROTECT_BEGIN (throwable);
2267 // We want to call filters even if the thread is aborting, so suppress abort
2268 // checks while the filter runs.
2269 ThreadPreventAsyncHolder preventAbort;
2271 BYTE* startAddress = (BYTE*)pCf->GetCodeInfo()->GetStartAddress();
2272 iFilt = ::CallJitEHFilter(pCf, startAddress, EHClausePtr, nestingLevel, throwable);
2278 // We had an exception in filter invocation that remained unhandled.
2279 // Sync managed exception state, for the managed thread, based upon the active exception tracker.
2280 pThread->SyncManagedExceptionState(false);
2283 // Swallow exception. Treat as exception continue search.
2285 iFilt = EXCEPTION_CONTINUE_SEARCH;
2288 EX_END_CATCH(SwallowAllExceptions)
2291 #else // FEATURE_PAL
2292 PORTABILITY_ASSERT("COMPlusThrowCallbackHelper");
2293 return EXCEPTION_CONTINUE_SEARCH;
2294 #endif // FEATURE_PAL
2297 //******************************************************************************
2298 // The stack walk callback for exception handling on x86.
2300 // SWA_CONTINUE = 0, // continue walking
2301 // SWA_ABORT = 1, // stop walking, early out in "failure case"
2302 // SWA_FAILED = 2 // couldn't walk stack
2303 StackWalkAction COMPlusThrowCallback( // SWA value
2304 CrawlFrame *pCf, // Data from StackWalkFramesEx
2305 ThrowCallbackType *pData) // Context data passed through from CPFH
2307 // We don't want to use a runtime contract here since this codepath is used during
2308 // the processing of a hard SO. Contracts use a significant amount of stack
2309 // which we can't afford for those cases.
2310 STATIC_CONTRACT_THROWS;
2311 STATIC_CONTRACT_GC_TRIGGERS;
2312 STATIC_CONTRACT_MODE_COOPERATIVE;
2314 Frame *pFrame = pCf->GetFrame();
2315 MethodDesc *pFunc = pCf->GetFunction();
2318 #define METHODNAME(pFunc) (pFunc?pFunc->m_pszDebugMethodName:"<n/a>")
2320 #define METHODNAME(pFunc) "<n/a>"
2322 STRESS_LOG4(LF_EH, LL_INFO100, "COMPlusThrowCallback: STACKCRAWL method:%pM ('%s'), Frame:%p, FrameVtable = %pV\n",
2323 pFunc, METHODNAME(pFunc), pFrame, pCf->IsFrameless()?0:(*(void**)pFrame));
2326 Thread *pThread = GetThread();
2328 if (pFrame && pData->pTopFrame == pFrame)
2329 /* Don't look past limiting frame if there is one */
2333 return SWA_CONTINUE;
2335 if (pThread->IsRudeAbortInitiated() && !pThread->IsWithinCer(pCf))
2337 return SWA_CONTINUE;
2340 ExInfo* pExInfo = &(pThread->GetExceptionState()->m_currentExInfo);
2342 _ASSERTE(!pData->bIsUnwind);
2344 // It SHOULD be the case that any frames we consider live between this exception
2345 // record and the previous one.
2346 if (!pExInfo->m_pPrevNestedInfo) {
2347 if (pData->pCurrentExceptionRecord) {
2348 if (pFrame) _ASSERTE(pData->pCurrentExceptionRecord > pFrame);
2349 if (pCf->IsFrameless()) _ASSERTE((ULONG_PTR)pData->pCurrentExceptionRecord >= GetRegdisplaySP(pCf->GetRegisterSet()));
2351 if (pData->pPrevExceptionRecord) {
2352 // FCALLS have an extra SEH record in debug because of the desctructor
2353 // associated with ForbidGC checking. This is benign, so just ignore it.
2354 if (pFrame) _ASSERTE(pData->pPrevExceptionRecord < pFrame || pFrame->GetVTablePtr() == HelperMethodFrame::GetMethodFrameVPtr());
2355 if (pCf->IsFrameless()) _ASSERTE((ULONG_PTR)pData->pPrevExceptionRecord <= GetRegdisplaySP(pCf->GetRegisterSet()));
2360 UINT_PTR currentIP = 0;
2361 UINT_PTR currentSP = 0;
2363 if (pCf->IsFrameless())
2365 currentIP = (UINT_PTR)GetControlPC(pCf->GetRegisterSet());
2366 currentSP = (UINT_PTR)GetRegdisplaySP(pCf->GetRegisterSet());
2368 else if (InlinedCallFrame::FrameHasActiveCall(pFrame))
2370 // don't have the IP, SP for native code
2376 currentIP = (UINT_PTR)(pCf->GetFrame()->GetIP());
2377 currentSP = 0; //Don't have an SP to get.
2380 if (!pFunc->IsILStub())
2382 // Append the current frame to the stack trace and save the save trace to the managed Exception object.
2383 pExInfo->m_StackTraceInfo.AppendElement(pData->bAllowAllocMem, currentIP, currentSP, pFunc, pCf);
2385 pExInfo->m_StackTraceInfo.SaveStackTrace(pData->bAllowAllocMem,
2386 pThread->GetThrowableAsHandle(),
2387 pData->bReplaceStack,
2388 pData->bSkipLastElement);
2392 LOG((LF_EH, LL_INFO1000, "COMPlusThrowCallback: Skipping AppendElement/SaveStackTrace for IL stub MD %p\n", pFunc));
2395 // Fire an exception thrown ETW event when an exception occurs
2396 ETW::ExceptionLog::ExceptionThrown(pCf, pData->bSkipLastElement, pData->bReplaceStack);
2398 // Reset the flags. These flags are set only once before each stack walk done by LookForHandler(), and
2399 // they apply only to the first frame we append to the stack trace. Subsequent frames are always appended.
2400 if (pData->bReplaceStack)
2402 pData->bReplaceStack = FALSE;
2404 if (pData->bSkipLastElement)
2406 pData->bSkipLastElement = FALSE;
2409 // now we've got the stack trace, if we aren't allowed to catch this and we're first pass, return
2410 if (pData->bDontCatch)
2411 return SWA_CONTINUE;
2413 if (!pCf->IsFrameless())
2415 // @todo - remove this once SIS is fully enabled.
2416 extern bool g_EnableSIS;
2419 // For debugger, we may want to notify 1st chance exceptions if they're coming out of a stub.
2420 // We recognize stubs as Frames with a M2U transition type. The debugger's stackwalker also
2421 // recognizes these frames and publishes ICorDebugInternalFrames in the stackwalk. It's
2422 // important to use pFrame as the stack address so that the Exception callback matches up
2423 // w/ the ICorDebugInternlFrame stack range.
2424 if (CORDebuggerAttached())
2426 Frame * pFrameStub = pCf->GetFrame();
2427 Frame::ETransitionType t = pFrameStub->GetTransitionType();
2428 if (t == Frame::TT_M2U)
2430 // Use address of the frame as the stack address.
2431 currentSP = (SIZE_T) ((void*) pFrameStub);
2432 currentIP = 0; // no IP.
2433 EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedException(pThread, (SIZE_T)currentIP, (SIZE_T)currentSP);
2434 #ifdef FEATURE_EXCEPTION_NOTIFICATIONS
2435 // Deliver the FirstChanceNotification after the debugger, if not already delivered.
2436 if (!pExInfo->DeliveredFirstChanceNotification())
2438 ExceptionNotifications::DeliverFirstChanceNotification();
2440 #endif // FEATURE_EXCEPTION_NOTIFICATIONS
2444 return SWA_CONTINUE;
2447 bool fIsILStub = pFunc->IsILStub();
2448 bool fGiveDebuggerAndProfilerNotification = !fIsILStub;
2449 BOOL fMethodCanHandleException = TRUE;
2451 MethodDesc * pUserMDForILStub = NULL;
2452 Frame * pILStubFrame = NULL;
2454 pUserMDForILStub = GetUserMethodForILStub(pThread, currentSP, pFunc, &pILStubFrame);
2456 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
2457 CorruptionSeverity currentSeverity = pThread->GetExceptionState()->GetCurrentExceptionTracker()->GetCorruptionSeverity();
2459 // We must defer to the MethodDesc of the user method instead of the IL stub
2460 // itself because the user can specify the policy on a per-method basis and
2461 // that won't be reflected via the IL stub's MethodDesc.
2462 MethodDesc * pMDWithCEAttribute = fIsILStub ? pUserMDForILStub : pFunc;
2464 // Check if the exception can be delivered to the method? It will check if the exception
2465 // is a CE or not. If it is, it will check if the method can process it or not.
2466 fMethodCanHandleException = CEHelper::CanMethodHandleException(currentSeverity, pMDWithCEAttribute);
2468 #endif // FEATURE_CORRUPTING_EXCEPTIONS
2470 // Let the profiler know that we are searching for a handler within this function instance
2471 if (fGiveDebuggerAndProfilerNotification)
2472 EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionEnter(pFunc);
2474 // The following debugger notification and AppDomain::FirstChanceNotification should be scoped together
2475 // since the AD notification *must* follow immediately after the debugger's notification.
2477 #ifdef DEBUGGING_SUPPORTED
2479 // Go ahead and notify any debugger of this exception.
2481 EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedException(pThread, (SIZE_T)currentIP, (SIZE_T)currentSP);
2483 if (CORDebuggerAttached() && pExInfo->m_ExceptionFlags.DebuggerInterceptInfo())
2487 #endif // DEBUGGING_SUPPORTED
2489 #ifdef FEATURE_EXCEPTION_NOTIFICATIONS
2490 // Attempt to deliver the first chance notification to the AD only *AFTER* the debugger
2491 // has done that, provided we have not already done that.
2492 if (!pExInfo->DeliveredFirstChanceNotification())
2494 ExceptionNotifications::DeliverFirstChanceNotification();
2496 #endif // FEATURE_EXCEPTION_NOTIFICATIONS
2498 IJitManager* pJitManager = pCf->GetJitManager();
2499 _ASSERTE(pJitManager);
2500 EH_CLAUSE_ENUMERATOR pEnumState;
2501 unsigned EHCount = 0;
2503 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
2504 // If exception cannot be handled, then just bail out. We shouldnt examine the EH clauses
2505 // in such a method.
2506 if (!fMethodCanHandleException)
2508 LOG((LF_EH, LL_INFO100, "COMPlusThrowCallback - CEHelper decided not to look for exception handlers in the method(MD:%p).\n", pFunc));
2510 // Set the flag to skip this frame since the CE cannot be delivered
2511 _ASSERTE(currentSeverity == ProcessCorrupting);
2513 // Ensure EHClause count is zero
2517 #endif // FEATURE_CORRUPTING_EXCEPTIONS
2519 EHCount = pJitManager->InitializeEHEnumeration(pCf->GetMethodToken(), &pEnumState);
2524 // Inform the profiler that we're leaving, and what pass we're on
2525 if (fGiveDebuggerAndProfilerNotification)
2526 EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionLeave(pFunc);
2527 return SWA_CONTINUE;
2530 TypeHandle thrownType = TypeHandle();
2531 // if we are being called on an unwind for an exception that we did not try to catch, eg.
2532 // an internal EE exception, then pThread->GetThrowable will be null
2534 OBJECTREF throwable = pThread->GetThrowable();
2535 if (throwable != NULL)
2537 throwable = PossiblyUnwrapThrowable(throwable, pCf->GetAssembly());
2538 thrownType = TypeHandle(throwable->GetTrueMethodTable());
2542 PREGDISPLAY regs = pCf->GetRegisterSet();
2543 BYTE *pStack = (BYTE *) GetRegdisplaySP(regs);
2544 #ifdef DEBUGGING_SUPPORTED
2545 BYTE *pHandlerEBP = (BYTE *) GetRegdisplayFP(regs);
2548 DWORD offs = (DWORD)pCf->GetRelOffset(); //= (BYTE*) (*regs->pPC) - (BYTE*) pCf->GetStartAddress();
2549 STRESS_LOG1(LF_EH, LL_INFO10000, "COMPlusThrowCallback: offset is %d\n", offs);
2551 EE_ILEXCEPTION_CLAUSE EHClause;
2552 unsigned start_adjust, end_adjust;
2554 start_adjust = !(pCf->HasFaulted() || pCf->IsIPadjusted());
2555 end_adjust = pCf->IsActiveFunc();
2557 for(ULONG i=0; i < EHCount; i++)
2559 pJitManager->GetNextEHClause(&pEnumState, &EHClause);
2560 _ASSERTE(IsValidClause(&EHClause));
2562 STRESS_LOG4(LF_EH, LL_INFO100, "COMPlusThrowCallback: considering '%s' clause [%d,%d], ofs:%d\n",
2563 (IsFault(&EHClause) ? "fault" : (
2564 IsFinally(&EHClause) ? "finally" : (
2565 IsFilterHandler(&EHClause) ? "filter" : (
2566 IsTypedHandler(&EHClause) ? "typed" : "unknown")))),
2567 EHClause.TryStartPC,
2572 // Checking the exception range is a bit tricky because
2573 // on CPU faults (null pointer access, div 0, ..., the IP points
2574 // to the faulting instruction, but on calls, the IP points
2575 // to the next instruction.
2576 // This means that we should not include the start point on calls
2577 // as this would be a call just preceding the try block.
2578 // Also, we should include the end point on calls, but not faults.
2580 // If we're in the FILTER part of a filter clause, then we
2581 // want to stop crawling. It's going to be caught in a
2582 // EX_CATCH just above us. If not, the exception
2583 if ( IsFilterHandler(&EHClause)
2584 && ( offs > EHClause.FilterOffset
2585 || (offs == EHClause.FilterOffset && !start_adjust) )
2586 && ( offs < EHClause.HandlerStartPC
2587 || (offs == EHClause.HandlerStartPC && !end_adjust) )) {
2589 STRESS_LOG4(LF_EH, LL_INFO100, "COMPlusThrowCallback: Fault inside filter [%d,%d] startAdj %d endAdj %d\n",
2590 EHClause.FilterOffset, EHClause.HandlerStartPC, start_adjust, end_adjust);
2592 if (fGiveDebuggerAndProfilerNotification)
2593 EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionLeave(pFunc);
2597 if ( (offs < EHClause.TryStartPC) ||
2598 (offs > EHClause.TryEndPC) ||
2599 (offs == EHClause.TryStartPC && start_adjust) ||
2600 (offs == EHClause.TryEndPC && end_adjust))
2603 BOOL typeMatch = FALSE;
2604 BOOL isTypedHandler = IsTypedHandler(&EHClause);
2606 if (isTypedHandler && !thrownType.IsNull())
2608 if (EHClause.TypeHandle == (void*)(size_t)mdTypeRefNil)
2610 // this is a catch(...)
2615 TypeHandle exnType = pJitManager->ResolveEHClause(&EHClause,pCf);
2617 // if doesn't have cached class then class wasn't loaded so couldn't have been thrown
2618 typeMatch = !exnType.IsNull() && ExceptionIsOfRightType(exnType, thrownType);
2622 // <TODO>@PERF: Is this too expensive? Consider storing the nesting level
2623 // instead of the HandlerEndPC.</TODO>
2625 // Determine the nesting level of EHClause. Just walk the table
2626 // again, and find out how many handlers enclose it
2627 DWORD nestingLevel = 0;
2629 if (IsFaultOrFinally(&EHClause))
2633 LOG((LF_EH, LL_INFO100, "COMPlusThrowCallback: %s match for typed handler.\n", typeMatch?"Found":"Did not find"));
2641 // Must be an exception filter (__except() part of __try{}__except(){}).
2642 nestingLevel = ComputeEnclosingHandlerNestingLevel(pJitManager,
2643 pCf->GetMethodToken(),
2644 EHClause.HandlerStartPC);
2646 // We just need *any* address within the method. This will let the debugger
2647 // resolve the EnC version of the method.
2648 PCODE pMethodAddr = GetControlPC(regs);
2649 if (fGiveDebuggerAndProfilerNotification)
2650 EEToDebuggerExceptionInterfaceWrapper::ExceptionFilter(pFunc, pMethodAddr, EHClause.FilterOffset, pHandlerEBP);
2652 UINT_PTR uStartAddress = (UINT_PTR)pCf->GetCodeInfo()->GetStartAddress();
2654 // save clause information in the exinfo
2655 pExInfo->m_EHClauseInfo.SetInfo(COR_PRF_CLAUSE_FILTER,
2656 uStartAddress + EHClause.FilterOffset,
2657 StackFrame((UINT_PTR)pHandlerEBP));
2659 // Let the profiler know we are entering a filter
2660 if (fGiveDebuggerAndProfilerNotification)
2661 EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFilterEnter(pFunc);
2663 COUNTER_ONLY(GetPerfCounters().m_Excep.cFiltersExecuted++);
2665 STRESS_LOG3(LF_EH, LL_INFO10, "COMPlusThrowCallback: calling filter code, EHClausePtr:%08x, Start:%08x, End:%08x\n",
2666 &EHClause, EHClause.HandlerStartPC, EHClause.HandlerEndPC);
2668 OBJECTREF throwable = PossiblyUnwrapThrowable(pThread->GetThrowable(), pCf->GetAssembly());
2670 pExInfo->m_EHClauseInfo.SetManagedCodeEntered(TRUE);
2672 int iFilt = COMPlusThrowCallbackHelper(pJitManager,
2680 pExInfo->m_EHClauseInfo.SetManagedCodeEntered(FALSE);
2682 // Let the profiler know we are leaving a filter
2683 if (fGiveDebuggerAndProfilerNotification)
2684 EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFilterLeave();
2686 pExInfo->m_EHClauseInfo.ResetInfo();
2688 if (pThread->IsRudeAbortInitiated() && !pThread->IsWithinCer(pCf))
2690 if (fGiveDebuggerAndProfilerNotification)
2691 EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionLeave(pFunc);
2692 return SWA_CONTINUE;
2695 // If this filter didn't want the exception, keep looking.
2696 if (EXCEPTION_EXECUTE_HANDLER != iFilt)
2700 // Record this location, to stop the unwind phase, later.
2701 pData->pFunc = pFunc;
2702 pData->dHandler = i;
2703 pData->pStack = pStack;
2705 // Notify the profiler that a catcher has been found
2706 if (fGiveDebuggerAndProfilerNotification)
2708 EEToProfilerExceptionInterfaceWrapper::ExceptionSearchCatcherFound(pFunc);
2709 EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionLeave(pFunc);
2712 #ifdef DEBUGGING_SUPPORTED
2714 // Notify debugger that a catcher has been found.
2718 EEToDebuggerExceptionInterfaceWrapper::NotifyOfCHFFilter(pExInfo->m_pExceptionPointers, pILStubFrame);
2721 if (fGiveDebuggerAndProfilerNotification &&
2722 CORDebuggerAttached() && !pExInfo->m_ExceptionFlags.DebuggerInterceptInfo())
2725 // We just need *any* address within the method. This will let the debugger
2726 // resolve the EnC version of the method.
2727 PCODE pMethodAddr = GetControlPC(regs);
2729 EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedExceptionCatcherFound(pThread,
2730 pData->pFunc, pMethodAddr,
2731 (SIZE_T)pData->pStack,
2734 #endif // DEBUGGING_SUPPORTED
2738 if (fGiveDebuggerAndProfilerNotification)
2739 EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionLeave(pFunc);
2740 return SWA_CONTINUE;
2741 } // StackWalkAction COMPlusThrowCallback()
2744 //==========================================================================
2745 // COMPlusUnwindCallback
2746 //==========================================================================
2748 #if defined(_MSC_VER)
2749 #pragma warning(push)
2750 #pragma warning (disable : 4740) // There is inline asm code in this function, which disables
2751 // global optimizations.
2752 #pragma warning (disable : 4731)
2754 StackWalkAction COMPlusUnwindCallback (CrawlFrame *pCf, ThrowCallbackType *pData)
2756 STATIC_CONTRACT_THROWS;
2757 STATIC_CONTRACT_GC_NOTRIGGER;
2758 STATIC_CONTRACT_MODE_COOPERATIVE;
2760 _ASSERTE(pData->bIsUnwind);
2762 Frame *pFrame = pCf->GetFrame();
2763 MethodDesc *pFunc = pCf->GetFunction();
2766 #define METHODNAME(pFunc) (pFunc?pFunc->m_pszDebugMethodName:"<n/a>")
2768 #define METHODNAME(pFunc) "<n/a>"
2770 STRESS_LOG4(LF_EH, LL_INFO100, "COMPlusUnwindCallback: STACKCRAWL method:%pM ('%s'), Frame:%p, FrameVtable = %pV\n",
2771 pFunc, METHODNAME(pFunc), pFrame, pCf->IsFrameless()?0:(*(void**)pFrame));
2774 if (pFrame && pData->pTopFrame == pFrame)
2775 /* Don't look past limiting frame if there is one */
2779 return SWA_CONTINUE;
2781 if (!pCf->IsFrameless())
2782 return SWA_CONTINUE;
2784 Thread *pThread = GetThread();
2786 // If the thread is being RudeAbort, we will not run any finally
2787 if (pThread->IsRudeAbortInitiated() && !pThread->IsWithinCer(pCf))
2789 return SWA_CONTINUE;
2792 IJitManager* pJitManager = pCf->GetJitManager();
2793 _ASSERTE(pJitManager);
2795 ExInfo *pExInfo = &(pThread->GetExceptionState()->m_currentExInfo);
2797 PREGDISPLAY regs = pCf->GetRegisterSet();
2798 BYTE *pStack = (BYTE *) GetRegdisplaySP(regs);
2800 TypeHandle thrownType = TypeHandle();
2802 BOOL fCanMethodHandleException = TRUE;
2803 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
2804 // MethodDesc's security information (i.e. whether it is critical or transparent) is calculated lazily.
2805 // If this method's security information was not precalculated, then it would have been in the first pass
2806 // already using Security::IsMethodCritical which could take have taken us down a path which is GC_TRIGGERS.
2809 // However, this unwind callback (for X86) is GC_NOTRIGGER and at this point the security information would have been
2810 // calculated already. Hence, we wouldnt endup in the GC_TRIGGERS path. Thus, to keep SCAN.EXE (static contract analyzer) happy,
2811 // we will pass a FALSE to the CanMethodHandleException call, indicating we dont need to calculate security information (and thus,
2812 // not go down the GC_TRIGGERS path.
2814 // Check if the exception can be delivered to the method? It will check if the exception
2815 // is a CE or not. If it is, it will check if the method can process it or not.
2816 CorruptionSeverity currentSeverity = pThread->GetExceptionState()->GetCurrentExceptionTracker()->GetCorruptionSeverity();
2818 // We have to do this check for x86 since, unlike 64bit which will setup a new exception tracker for longjmp,
2819 // x86 only sets up new trackers in the first pass (and longjmp is 2nd pass only exception). Hence, we pass
2820 // this information in the callback structure without affecting any existing exception tracker (incase longjmp was
2821 // a nested exception).
2822 if (pData->m_fIsLongJump)
2824 // Longjump is not a CSE. With a CSE in progress, this can be invoked by either:
2826 // 1) Managed code (e.g. finally/fault/catch), OR
2827 // 2) By native code
2829 // In scenario (1), managed code can invoke it only if it was attributed with HPCSE attribute. Thus,
2830 // longjmp is no different than managed code doing a "throw new Exception();".
2832 // In scenario (2), longjmp is no different than any other non-CSE native exception raised.
2834 // In both these case, longjmp should be treated as non-CSE. Since x86 does not setup a tracker for
2835 // it (see comment above), we pass this information (of whether the current exception is a longjmp or not)
2836 // to this callback (from UnwindFrames) to setup the correct corruption severity.
2838 // http://www.nynaeve.net/?p=105 has a brief description of how exception-safe setjmp/longjmp works.
2839 currentSeverity = NotCorrupting;
2842 MethodDesc * pFuncWithCEAttribute = pFunc;
2843 Frame * pILStubFrame = NULL;
2844 if (pFunc->IsILStub())
2846 // We must defer to the MethodDesc of the user method instead of the IL stub
2847 // itself because the user can specify the policy on a per-method basis and
2848 // that won't be reflected via the IL stub's MethodDesc.
2849 pFuncWithCEAttribute = GetUserMethodForILStub(pThread, (UINT_PTR)pStack, pFunc, &pILStubFrame);
2851 fCanMethodHandleException = CEHelper::CanMethodHandleException(currentSeverity, pFuncWithCEAttribute, FALSE);
2853 #endif // FEATURE_CORRUPTING_EXCEPTIONS
2855 #ifdef DEBUGGING_SUPPORTED
2856 LOG((LF_EH, LL_INFO1000, "COMPlusUnwindCallback: Intercept %d, pData->pFunc 0x%X, pFunc 0x%X, pData->pStack 0x%X, pStack 0x%X\n",
2857 pExInfo->m_ExceptionFlags.DebuggerInterceptInfo(),
2864 // If the debugger wants to intercept this exception here, go do that.
2866 if (pExInfo->m_ExceptionFlags.DebuggerInterceptInfo() && (pData->pFunc == pFunc) && (pData->pStack == pStack))
2868 goto LDoDebuggerIntercept;
2872 bool fGiveDebuggerAndProfilerNotification;
2873 fGiveDebuggerAndProfilerNotification = !pFunc->IsILStub();
2875 // Notify the profiler of the function we're dealing with in the unwind phase
2876 if (fGiveDebuggerAndProfilerNotification)
2877 EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFunctionEnter(pFunc);
2879 EH_CLAUSE_ENUMERATOR pEnumState;
2882 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
2883 if (!fCanMethodHandleException)
2885 LOG((LF_EH, LL_INFO100, "COMPlusUnwindCallback - CEHelper decided not to look for exception handlers in the method(MD:%p).\n", pFunc));
2887 // Set the flag to skip this frame since the CE cannot be delivered
2888 _ASSERTE(currentSeverity == ProcessCorrupting);
2890 // Force EHClause count to be zero
2894 #endif // FEATURE_CORRUPTING_EXCEPTIONS
2896 EHCount = pJitManager->InitializeEHEnumeration(pCf->GetMethodToken(), &pEnumState);
2901 // Inform the profiler that we're leaving, and what pass we're on
2902 if (fGiveDebuggerAndProfilerNotification)
2903 EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFunctionLeave(pFunc);
2905 return SWA_CONTINUE;
2908 // if we are being called on an unwind for an exception that we did not try to catch, eg.
2909 // an internal EE exception, then pThread->GetThrowable will be null
2911 OBJECTREF throwable = pThread->GetThrowable();
2912 if (throwable != NULL)
2914 throwable = PossiblyUnwrapThrowable(throwable, pCf->GetAssembly());
2915 thrownType = TypeHandle(throwable->GetTrueMethodTable());
2918 #ifdef DEBUGGING_SUPPORTED
2920 pHandlerEBP = (BYTE *) GetRegdisplayFP(regs);
2924 offs = (DWORD)pCf->GetRelOffset(); //= (BYTE*) (*regs->pPC) - (BYTE*) pCf->GetStartAddress();
2926 LOG((LF_EH, LL_INFO100, "COMPlusUnwindCallback: current EIP offset in method 0x%x, \n", offs));
2928 EE_ILEXCEPTION_CLAUSE EHClause;
2929 unsigned start_adjust, end_adjust;
2931 start_adjust = !(pCf->HasFaulted() || pCf->IsIPadjusted());
2932 end_adjust = pCf->IsActiveFunc();
2934 for(ULONG i=0; i < EHCount; i++)
2936 pJitManager->GetNextEHClause(&pEnumState, &EHClause);
2937 _ASSERTE(IsValidClause(&EHClause));
2939 STRESS_LOG4(LF_EH, LL_INFO100, "COMPlusUnwindCallback: considering '%s' clause [%d,%d], offs:%d\n",
2940 (IsFault(&EHClause) ? "fault" : (
2941 IsFinally(&EHClause) ? "finally" : (
2942 IsFilterHandler(&EHClause) ? "filter" : (
2943 IsTypedHandler(&EHClause) ? "typed" : "unknown")))),
2944 EHClause.TryStartPC,
2949 // Checking the exception range is a bit tricky because
2950 // on CPU faults (null pointer access, div 0, ..., the IP points
2951 // to the faulting instruction, but on calls, the IP points
2952 // to the next instruction.
2953 // This means that we should not include the start point on calls
2954 // as this would be a call just preceding the try block.
2955 // Also, we should include the end point on calls, but not faults.
2957 if ( IsFilterHandler(&EHClause)
2958 && ( offs > EHClause.FilterOffset
2959 || (offs == EHClause.FilterOffset && !start_adjust) )
2960 && ( offs < EHClause.HandlerStartPC
2961 || (offs == EHClause.HandlerStartPC && !end_adjust) )
2963 STRESS_LOG4(LF_EH, LL_INFO100, "COMPlusUnwindCallback: Fault inside filter [%d,%d] startAdj %d endAdj %d\n",
2964 EHClause.FilterOffset, EHClause.HandlerStartPC, start_adjust, end_adjust);
2966 // Make the filter as done. See comment in CallJitEHFilter
2967 // on why we have to do it here.
2968 Frame* pFilterFrame = pThread->GetFrame();
2969 _ASSERTE(pFilterFrame->GetVTablePtr() == ExceptionFilterFrame::GetMethodFrameVPtr());
2970 ((ExceptionFilterFrame*)pFilterFrame)->SetFilterDone();
2972 // Inform the profiler that we're leaving, and what pass we're on
2973 if (fGiveDebuggerAndProfilerNotification)
2974 EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFunctionLeave(pFunc);
2979 if ( (offs < EHClause.TryStartPC) ||
2980 (offs > EHClause.TryEndPC) ||
2981 (offs == EHClause.TryStartPC && start_adjust) ||
2982 (offs == EHClause.TryEndPC && end_adjust))
2985 // <TODO>@PERF : Is this too expensive? Consider storing the nesting level
2986 // instead of the HandlerEndPC.</TODO>
2988 // Determine the nesting level of EHClause. Just walk the table
2989 // again, and find out how many handlers enclose it
2991 DWORD nestingLevel = ComputeEnclosingHandlerNestingLevel(pJitManager,
2992 pCf->GetMethodToken(),
2993 EHClause.HandlerStartPC);
2995 // We just need *any* address within the method. This will let the debugger
2996 // resolve the EnC version of the method.
2997 PCODE pMethodAddr = GetControlPC(regs);
2999 UINT_PTR uStartAddress = (UINT_PTR)pCf->GetCodeInfo()->GetStartAddress();
3001 if (IsFaultOrFinally(&EHClause))
3003 COUNTER_ONLY(GetPerfCounters().m_Excep.cFinallysExecuted++);
3005 if (fGiveDebuggerAndProfilerNotification)
3006 EEToDebuggerExceptionInterfaceWrapper::ExceptionHandle(pFunc, pMethodAddr, EHClause.HandlerStartPC, pHandlerEBP);
3008 pExInfo->m_EHClauseInfo.SetInfo(COR_PRF_CLAUSE_FINALLY,
3009 uStartAddress + EHClause.HandlerStartPC,
3010 StackFrame((UINT_PTR)pHandlerEBP));
3012 // Notify the profiler that we are about to execute the finally code
3013 if (fGiveDebuggerAndProfilerNotification)
3014 EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFinallyEnter(pFunc);
3016 LOG((LF_EH, LL_INFO100, "COMPlusUnwindCallback: finally clause [%d,%d] - call\n", EHClause.TryStartPC, EHClause.TryEndPC));
3018 pExInfo->m_EHClauseInfo.SetManagedCodeEntered(TRUE);
3020 ::CallJitEHFinally(pCf, (BYTE *)uStartAddress, &EHClause, nestingLevel);
3022 pExInfo->m_EHClauseInfo.SetManagedCodeEntered(FALSE);
3024 LOG((LF_EH, LL_INFO100, "COMPlusUnwindCallback: finally - returned\n"));
3026 // Notify the profiler that we are done with the finally code
3027 if (fGiveDebuggerAndProfilerNotification)
3028 EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFinallyLeave();
3030 pExInfo->m_EHClauseInfo.ResetInfo();
3035 // Current is not a finally, check if it's the catching handler (or filter).
3036 if (pData->pFunc != pFunc || (ULONG)(pData->dHandler) != i || pData->pStack != pStack)
3042 gLastResumedExceptionFunc = pCf->GetFunction();
3043 gLastResumedExceptionHandler = i;
3046 // save clause information in the exinfo
3047 pExInfo->m_EHClauseInfo.SetInfo(COR_PRF_CLAUSE_CATCH,
3048 uStartAddress + EHClause.HandlerStartPC,
3049 StackFrame((UINT_PTR)pHandlerEBP));
3051 // Notify the profiler that we are about to resume at the catcher.
3052 if (fGiveDebuggerAndProfilerNotification)
3054 DACNotify::DoExceptionCatcherEnterNotification(pFunc, EHClause.HandlerStartPC);
3056 EEToProfilerExceptionInterfaceWrapper::ExceptionCatcherEnter(pThread, pFunc);
3058 EEToDebuggerExceptionInterfaceWrapper::ExceptionHandle(pFunc, pMethodAddr, EHClause.HandlerStartPC, pHandlerEBP);
3061 STRESS_LOG4(LF_EH, LL_INFO100, "COMPlusUnwindCallback: offset 0x%x matches clause [0x%x, 0x%x) matches in method %pM\n",
3062 offs, EHClause.TryStartPC, EHClause.TryEndPC, pFunc);
3064 // ResumeAtJitEH will set pExInfo->m_EHClauseInfo.m_fManagedCodeEntered = TRUE; at the appropriate time
3065 ::ResumeAtJitEH(pCf, (BYTE *)uStartAddress, &EHClause, nestingLevel, pThread, pData->bUnwindStack);
3066 //UNREACHABLE_MSG("ResumeAtJitEH shouldn't have returned!");
3068 // we do not set pExInfo->m_EHClauseInfo.m_fManagedCodeEntered = FALSE here,
3069 // that happens when the catch clause calls back to COMPlusEndCatch
3073 STRESS_LOG1(LF_EH, LL_INFO100, "COMPlusUnwindCallback: no handler found in method %pM\n", pFunc);
3074 if (fGiveDebuggerAndProfilerNotification)
3075 EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFunctionLeave(pFunc);
3077 return SWA_CONTINUE;
3080 #ifdef DEBUGGING_SUPPORTED
3081 LDoDebuggerIntercept:
3083 STRESS_LOG1(LF_EH|LF_CORDB, LL_INFO100, "COMPlusUnwindCallback: Intercepting in method %pM\n", pFunc);
3086 // Setup up the easy parts of the context to restart at.
3091 // Note: EAX ECX EDX are scratch
3093 context.Esp = (DWORD)(size_t)(GetRegdisplaySP(regs));
3094 context.Ebx = *regs->pEbx;
3095 context.Esi = *regs->pEsi;
3096 context.Edi = *regs->pEdi;
3097 context.Ebp = *regs->pEbp;
3100 // Set scratch registers to 0 to avoid reporting incorrect values to GC in case of debugger changing the IP
3101 // in the middle of a scratch register lifetime (see Dev10 754922)
3108 // Ok, now set the target Eip to the address the debugger requested.
3110 ULONG_PTR nativeOffset;
3111 pExInfo->m_DebuggerExState.GetDebuggerInterceptInfo(NULL, NULL, NULL, NULL, &nativeOffset, NULL);
3112 context.Eip = GetControlPC(regs) - (pCf->GetRelOffset() - nativeOffset);
3115 // Finally we need to get the correct Esp for this nested level
3118 context.Esp = pCf->GetCodeManager()->GetAmbientSP(regs,
3122 pCf->GetCodeManState()
3125 // In case we see unknown FS:[0] handlers we delay the interception point until we reach the handler that protects the interception point.
3126 // This way we have both FS:[0] handlers being poped up by RtlUnwind and managed capital F Frames being unwinded by managed stackwalker.
3128 BOOL fCheckForUnknownHandler = TRUE;
3129 if (PopNestedExceptionRecords((LPVOID)(size_t)context.Esp, fCheckForUnknownHandler))
3131 // Let ClrDebuggerDoUnwindAndIntercept RtlUnwind continue to unwind frames until we reach the handler protected by COMPlusNestedExceptionHandler.
3132 pExInfo->m_InterceptionContext = context;
3133 pExInfo->m_ValidInterceptionContext = TRUE;
3134 STRESS_LOG0(LF_EH|LF_CORDB, LL_INFO100, "COMPlusUnwindCallback: Skip interception until unwinding reaches the actual handler protected by COMPlusNestedExceptionHandler\n");
3139 // Pop off all the Exception information up to this point in the stack
3141 UnwindExceptionTrackerAndResumeInInterceptionFrame(pExInfo, &context);
3144 #endif // DEBUGGING_SUPPORTED
3145 } // StackWalkAction COMPlusUnwindCallback ()
3146 #if defined(_MSC_VER)
3147 #pragma warning(pop)
3150 #if defined(_MSC_VER)
3151 #pragma warning(push)
3152 #pragma warning (disable : 4740) // There is inline asm code in this function, which disables
3153 // global optimizations.
3154 #pragma warning (disable : 4731)
3156 void ResumeAtJitEH(CrawlFrame* pCf,
3158 EE_ILEXCEPTION_CLAUSE *EHClausePtr,
3163 // No dynamic contract here because this function doesn't return and destructors wouldn't be executed
3164 WRAPPER_NO_CONTRACT;
3168 context.Setup(PCODE(startPC + EHClausePtr->HandlerStartPC), pCf->GetRegisterSet());
3170 size_t * pShadowSP = NULL; // Write Esp to *pShadowSP before jumping to handler
3171 size_t * pHandlerEnd = NULL;
3173 OBJECTREF throwable = PossiblyUnwrapThrowable(pThread->GetThrowable(), pCf->GetAssembly());
3175 pCf->GetCodeManager()->FixContext(ICodeManager::CATCH_CONTEXT,
3178 EHClausePtr->HandlerStartPC,
3181 pCf->GetCodeManState(),
3187 *pHandlerEnd = EHClausePtr->HandlerEndPC;
3190 // save esp so that endcatch can restore it (it always restores, so want correct value)
3191 ExInfo* pExInfo = &(pThread->GetExceptionState()->m_currentExInfo);
3192 pExInfo->m_dEsp = (LPVOID)context.GetSP();
3193 LOG((LF_EH, LL_INFO1000, "ResumeAtJitEH: current m_dEsp set to %p\n", context.GetSP()));
3195 PVOID dEsp = GetCurrentSP();
3199 // If we don't want to unwind the stack, then the guard page had better not be gone!
3200 _ASSERTE(pThread->DetermineIfGuardPagePresent());
3202 // so down below won't really update esp
3203 context.SetSP(dEsp);
3204 pExInfo->m_pShadowSP = pShadowSP; // so that endcatch can zero it back
3208 *pShadowSP = (size_t)dEsp;
3213 // so shadow SP has the real SP as we are going to unwind the stack
3214 dEsp = (LPVOID)context.GetSP();
3216 // BEGIN: pExInfo->UnwindExInfo(dEsp);
3217 ExInfo *pPrevNestedInfo = pExInfo->m_pPrevNestedInfo;
3219 while (pPrevNestedInfo && pPrevNestedInfo->m_StackAddress < dEsp)
3221 LOG((LF_EH, LL_INFO1000, "ResumeAtJitEH: popping nested ExInfo at 0x%p\n", pPrevNestedInfo->m_StackAddress));
3223 pPrevNestedInfo->DestroyExceptionHandle();
3224 pPrevNestedInfo->m_StackTraceInfo.FreeStackTrace();
3226 #ifdef DEBUGGING_SUPPORTED
3227 if (g_pDebugInterface != NULL)
3229 g_pDebugInterface->DeleteInterceptContext(pPrevNestedInfo->m_DebuggerExState.GetDebuggerInterceptContext());
3231 #endif // DEBUGGING_SUPPORTED
3233 pPrevNestedInfo = pPrevNestedInfo->m_pPrevNestedInfo;
3236 pExInfo->m_pPrevNestedInfo = pPrevNestedInfo;
3238 _ASSERTE(pExInfo->m_pPrevNestedInfo == 0 || pExInfo->m_pPrevNestedInfo->m_StackAddress >= dEsp);
3240 // Before we unwind the SEH records, get the Frame from the top-most nested exception record.
3241 Frame* pNestedFrame = GetCurrFrame(FindNestedEstablisherFrame(GetCurrentSEHRecord()));
3243 PopNestedExceptionRecords((LPVOID)(size_t)dEsp);
3245 EXCEPTION_REGISTRATION_RECORD* pNewBottomMostHandler = GetCurrentSEHRecord();
3247 pExInfo->m_pShadowSP = pShadowSP;
3249 // The context and exception record are no longer any good.
3250 _ASSERTE(pExInfo->m_pContext < dEsp); // It must be off the top of the stack.
3251 pExInfo->m_pContext = 0; // Whack it.
3252 pExInfo->m_pExceptionRecord = 0;
3253 pExInfo->m_pExceptionPointers = 0;
3255 // We're going to put one nested record back on the stack before we resume. This is
3257 NestedHandlerExRecord *pNestedHandlerExRecord = (NestedHandlerExRecord*)((BYTE*)dEsp - ALIGN_UP(sizeof(NestedHandlerExRecord), STACK_ALIGN_SIZE));
3259 // The point of no return. The next statement starts scribbling on the stack. It's
3260 // deep enough that we won't hit our own locals. (That's important, 'cuz we're still
3263 _ASSERTE(dEsp > &pCf);
3264 pNestedHandlerExRecord->m_handlerInfo.m_hThrowable=NULL; // This is random memory. Handle
3265 // must be initialized to null before
3266 // calling Init(), as Init() will try
3267 // to free any old handle.
3268 pNestedHandlerExRecord->Init((PEXCEPTION_ROUTINE)COMPlusNestedExceptionHandler, pNestedFrame);
3270 INSTALL_EXCEPTION_HANDLING_RECORD(&(pNestedHandlerExRecord->m_ExReg));
3272 context.SetSP(pNestedHandlerExRecord);
3274 // We might have moved the bottommost handler. The nested record itself is never
3275 // the bottom most handler -- it's pushed afte the fact. So we have to make the
3276 // bottom-most handler the one BEFORE the nested record.
3277 if (pExInfo->m_pBottomMostHandler < pNewBottomMostHandler)
3279 STRESS_LOG3(LF_EH, LL_INFO10000, "ResumeAtJitEH: setting ExInfo:0x%p m_pBottomMostHandler from 0x%p to 0x%p\n",
3280 pExInfo, pExInfo->m_pBottomMostHandler, pNewBottomMostHandler);
3281 pExInfo->m_pBottomMostHandler = pNewBottomMostHandler;
3286 *pShadowSP = context.GetSP();
3290 STRESS_LOG3(LF_EH, LL_INFO100, "ResumeAtJitEH: resuming at EIP = %p ESP = %p EBP = %p\n",
3291 context.Eip, context.GetSP(), context.GetFP());
3293 #ifdef STACK_GUARDS_DEBUG
3294 // We are transitioning back to managed code, so ensure that we are in
3295 // SO-tolerant mode before we do so.
3296 RestoreSOToleranceState();
3299 // we want this to happen as late as possible but certainly after the notification
3300 // that the handle for the current ExInfo has been freed has been delivered
3301 pExInfo->m_EHClauseInfo.SetManagedCodeEntered(TRUE);
3303 ETW::ExceptionLog::ExceptionCatchBegin(pCf->GetCodeInfo()->GetMethodDesc(), (PVOID)pCf->GetCodeInfo()->GetStartAddress());
3305 ResumeAtJitEHHelper(&context);
3306 UNREACHABLE_MSG("Should never return from ResumeAtJitEHHelper!");
3308 // we do not set pExInfo->m_EHClauseInfo.m_fManagedCodeEntered = FALSE here,
3309 // that happens when the catch clause calls back to COMPlusEndCatch
3310 // we don't return to this point so it would be moot (see unreachable_msg above)
3313 #if defined(_MSC_VER)
3314 #pragma warning(pop)
3317 // Must be in a separate function because INSTALL_COMPLUS_EXCEPTION_HANDLER has a filter
3318 int CallJitEHFilterWorker(size_t *pShadowSP, EHContext *pContext)
3320 STATIC_CONTRACT_THROWS;
3321 STATIC_CONTRACT_GC_TRIGGERS;
3322 STATIC_CONTRACT_MODE_COOPERATIVE;
3323 STATIC_CONTRACT_SO_INTOLERANT;
3325 int retVal = EXCEPTION_CONTINUE_SEARCH;
3327 BEGIN_CALL_TO_MANAGED();
3329 retVal = CallJitEHFilterHelper(pShadowSP, pContext);
3331 END_CALL_TO_MANAGED();
3336 int CallJitEHFilter(CrawlFrame* pCf, BYTE* startPC, EE_ILEXCEPTION_CLAUSE *EHClausePtr, DWORD nestingLevel, OBJECTREF thrownObj)
3338 STATIC_CONTRACT_THROWS;
3339 STATIC_CONTRACT_GC_TRIGGERS;
3340 STATIC_CONTRACT_MODE_COOPERATIVE;
3342 int retVal = EXCEPTION_CONTINUE_SEARCH;
3343 size_t * pShadowSP = NULL;
3346 context.Setup(PCODE(startPC + EHClausePtr->FilterOffset), pCf->GetRegisterSet());
3348 size_t * pEndFilter = NULL; // Write
3349 pCf->GetCodeManager()->FixContext(ICodeManager::FILTER_CONTEXT, &context, pCf->GetCodeInfo(),
3350 EHClausePtr->FilterOffset, nestingLevel, thrownObj, pCf->GetCodeManState(),
3351 &pShadowSP, &pEndFilter);
3353 // End of the filter is the same as start of handler
3356 *pEndFilter = EHClausePtr->HandlerStartPC;
3359 // ExceptionFilterFrame serves two purposes:
3361 // 1. It serves as a frame that stops the managed search for handler
3362 // if we fault in the filter. ThrowCallbackType.pTopFrame is going point
3363 // to this frame during search for exception handler inside filter.
3364 // The search for handler needs a frame to stop. If we had no frame here,
3365 // the exceptions in filters would not be swallowed correctly since we would
3366 // walk past the EX_TRY/EX_CATCH block in COMPlusThrowCallbackHelper.
3368 // 2. It allows setting of SHADOW_SP_FILTER_DONE flag in UnwindFrames()
3369 // if we fault in the filter. We have to set this flag together with unwinding
3370 // of the filter frame. Using a regular C++ holder to clear this flag here would cause
3371 // GC holes. The stack would be in inconsistent state when we trigger gc just before
3372 // returning from UnwindFrames.
3374 FrameWithCookie<ExceptionFilterFrame> exceptionFilterFrame(pShadowSP);
3376 ETW::ExceptionLog::ExceptionFilterBegin(pCf->GetCodeInfo()->GetMethodDesc(), (PVOID)pCf->GetCodeInfo()->GetStartAddress());
3378 retVal = CallJitEHFilterWorker(pShadowSP, &context);
3380 ETW::ExceptionLog::ExceptionFilterEnd();
3382 exceptionFilterFrame.Pop();
3387 void CallJitEHFinally(CrawlFrame* pCf, BYTE* startPC, EE_ILEXCEPTION_CLAUSE *EHClausePtr, DWORD nestingLevel)
3389 WRAPPER_NO_CONTRACT;
3392 context.Setup(PCODE(startPC + EHClausePtr->HandlerStartPC), pCf->GetRegisterSet());
3394 size_t * pShadowSP = NULL; // Write Esp to *pShadowSP before jumping to handler
3396 size_t * pFinallyEnd = NULL;
3397 pCf->GetCodeManager()->FixContext(
3398 ICodeManager::FINALLY_CONTEXT, &context, pCf->GetCodeInfo(),
3399 EHClausePtr->HandlerStartPC, nestingLevel, ObjectToOBJECTREF((Object *) NULL), pCf->GetCodeManState(),
3400 &pShadowSP, &pFinallyEnd);
3404 *pFinallyEnd = EHClausePtr->HandlerEndPC;
3407 ETW::ExceptionLog::ExceptionFinallyBegin(pCf->GetCodeInfo()->GetMethodDesc(), (PVOID)pCf->GetCodeInfo()->GetStartAddress());
3409 CallJitEHFinallyHelper(pShadowSP, &context);
3411 ETW::ExceptionLog::ExceptionFinallyEnd();
3414 // Update the registers using new context
3416 // This is necessary to reflect GC pointer changes during the middle of a unwind inside a
3417 // finally clause, because:
3418 // 1. GC won't see the part of stack inside try (which has thrown an exception) that is already
3419 // unwinded and thus GC won't update GC pointers for this portion of the stack, but rather the
3420 // call stack in finally.
3421 // 2. upon return of finally, the unwind process continues and unwinds stack based on the part
3422 // of stack inside try and won't see the updated values in finally.
3423 // As a result, we need to manually update the context using register values upon return of finally
3425 // Note that we only update the registers for finally clause because
3426 // 1. For filter handlers, stack walker is able to see the whole stack (including the try part)
3427 // with the help of ExceptionFilterFrame as filter handlers are called in first pass
3428 // 2. For catch handlers, the current unwinding is already finished
3430 context.UpdateFrame(pCf->GetRegisterSet());
3432 // This does not need to be guarded by a holder because the frame is dead if an exception gets thrown. Filters are different
3433 // since they are run in the first pass, so we must update the shadowSP reset in CallJitEHFilter.
3435 *pShadowSP = 0; // reset the shadowSP to 0
3438 #if defined(_MSC_VER)
3439 #pragma warning (default : 4731)
3442 //=====================================================================
3443 // *********************************************************************
3444 BOOL ComPlusFrameSEH(EXCEPTION_REGISTRATION_RECORD* pEHR)
3446 LIMITED_METHOD_CONTRACT;
3448 return ((LPVOID)pEHR->Handler == (LPVOID)COMPlusFrameHandler || (LPVOID)pEHR->Handler == (LPVOID)COMPlusNestedExceptionHandler);
3453 //-------------------------------------------------------------------------
3454 // This is installed when we call COMPlusFrameHandler to provide a bound to
3455 // determine when are within a nested exception
3456 //-------------------------------------------------------------------------
3457 EXCEPTION_HANDLER_IMPL(COMPlusNestedExceptionHandler)
3459 WRAPPER_NO_CONTRACT;
3461 if (pExceptionRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))
3463 LOG((LF_EH, LL_INFO100, " COMPlusNestedHandler(unwind) with %x at %x\n", pExceptionRecord->ExceptionCode,
3464 pContext ? GetIP(pContext) : 0));
3467 // We're unwinding past a nested exception record, which means that we've thrown
3468 // a new exception out of a region in which we're handling a previous one. The
3469 // previous exception is overridden -- and needs to be unwound.
3471 // The preceding is ALMOST true. There is one more case, where we use setjmp/longjmp
3472 // from withing a nested handler. We won't have a nested exception in that case -- just
3475 Thread* pThread = GetThread();
3477 ExInfo* pExInfo = &(pThread->GetExceptionState()->m_currentExInfo);
3478 ExInfo* pPrevNestedInfo = pExInfo->m_pPrevNestedInfo;
3480 if (pPrevNestedInfo == &((NestedHandlerExRecord*)pEstablisherFrame)->m_handlerInfo)
3482 _ASSERTE(pPrevNestedInfo);
3484 LOG((LF_EH, LL_INFO100, "COMPlusNestedExceptionHandler: PopExInfo(): popping nested ExInfo at 0x%p\n", pPrevNestedInfo));
3486 pPrevNestedInfo->DestroyExceptionHandle();
3487 pPrevNestedInfo->m_StackTraceInfo.FreeStackTrace();
3489 #ifdef DEBUGGING_SUPPORTED
3490 if (g_pDebugInterface != NULL)
3492 g_pDebugInterface->DeleteInterceptContext(pPrevNestedInfo->m_DebuggerExState.GetDebuggerInterceptContext());
3494 #endif // DEBUGGING_SUPPORTED
3496 pExInfo->m_pPrevNestedInfo = pPrevNestedInfo->m_pPrevNestedInfo;
3499 // The whacky setjmp/longjmp case. Nothing to do.
3503 LOG((LF_EH, LL_INFO100, " InCOMPlusNestedHandler with %x at %x\n", pExceptionRecord->ExceptionCode,
3504 pContext ? GetIP(pContext) : 0));
3508 // There is a nasty "gotcha" in the way exception unwinding, finally's, and nested exceptions
3509 // interact. Here's the scenario ... it involves two exceptions, one normal one, and one
3510 // raised in a finally.
3512 // The first exception occurs, and is caught by some handler way up the stack. That handler
3513 // calls RtlUnwind -- and handlers that didn't catch this first exception are called again, with
3514 // the UNWIND flag set. If, one of the handlers throws an exception during
3515 // unwind (like, a throw from a finally) -- then that same handler is not called during
3516 // the unwind pass of the second exception. [ASIDE: It is called on first-pass.]
3518 // What that means is -- the COMPlusExceptionHandler, can't count on unwinding itself correctly
3519 // if an exception is thrown from a finally. Instead, it relies on the NestedExceptionHandler
3520 // that it pushes for this.
3523 EXCEPTION_DISPOSITION retval = EXCEPTION_HANDLER_FWD(COMPlusFrameHandler);
3524 LOG((LF_EH, LL_INFO100, "Leaving COMPlusNestedExceptionHandler with %d\n", retval));
3528 EXCEPTION_REGISTRATION_RECORD *FindNestedEstablisherFrame(EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame)
3530 LIMITED_METHOD_CONTRACT;
3532 while (pEstablisherFrame->Handler != (PEXCEPTION_ROUTINE)COMPlusNestedExceptionHandler) {
3533 pEstablisherFrame = pEstablisherFrame->Next;
3534 _ASSERTE(pEstablisherFrame != EXCEPTION_CHAIN_END); // should always find one
3536 return pEstablisherFrame;
3539 EXCEPTION_HANDLER_IMPL(FastNExportExceptHandler)
3541 WRAPPER_NO_CONTRACT;
3543 // Most of our logic is in commin with COMPlusFrameHandler.
3544 EXCEPTION_DISPOSITION retval = EXCEPTION_HANDLER_FWD(COMPlusFrameHandler);
3547 // If the exception is escaping the last CLR personality routine on the stack,
3548 // then state a flag on the thread to indicate so.
3549 if (retval == ExceptionContinueSearch)
3551 SetReversePInvokeEscapingUnhandledExceptionStatus(IS_UNWINDING(pExceptionRecord->ExceptionFlags), pEstablisherFrame);
3559 // Just like a regular NExport handler -- except it pops an extra frame on unwind. A handler
3560 // like this is needed by the COMMethodStubProlog code. It first pushes a frame -- and then
3561 // pushes a handler. When we unwind, we need to pop the extra frame to avoid corrupting the
3562 // frame chain in the event of an unmanaged catcher.
3564 EXCEPTION_HANDLER_IMPL(UMThunkPrestubHandler)
3566 // @todo: we'd like to have a dynamic contract here, but there's a problem. (Bug 129180) Enter on the CRST used
3567 // in HandleManagedFault leaves the no-trigger count incremented. The destructor of this contract will restore
3568 // it to zero, then when we leave the CRST in LinkFrameAndThrow, we assert because we're trying to decrement the
3569 // gc-trigger count down past zero. The solution is to fix what we're doing with this CRST. </TODO>
3570 STATIC_CONTRACT_THROWS; // COMPlusFrameHandler throws
3571 STATIC_CONTRACT_GC_TRIGGERS;
3572 STATIC_CONTRACT_MODE_ANY;
3574 EXCEPTION_DISPOSITION retval = ExceptionContinueSearch;
3576 BEGIN_CONTRACT_VIOLATION(SOToleranceViolation);
3578 // We must forward to the COMPlusFrameHandler. This will unwind the Frame Chain up to here, and also leave the
3579 // preemptive GC mode set correctly.
3580 retval = EXCEPTION_HANDLER_FWD(COMPlusFrameHandler);
3583 // If the exception is escaping the last CLR personality routine on the stack,
3584 // then state a flag on the thread to indicate so.
3585 if (retval == ExceptionContinueSearch)
3587 SetReversePInvokeEscapingUnhandledExceptionStatus(IS_UNWINDING(pExceptionRecord->ExceptionFlags), pEstablisherFrame);
3591 if (IS_UNWINDING(pExceptionRecord->ExceptionFlags))
3593 // Pops an extra frame on unwind.
3595 GCX_COOP(); // Must be cooperative to modify frame chain.
3597 Thread *pThread = GetThread();
3599 Frame *pFrame = pThread->GetFrame();
3600 pFrame->ExceptionUnwind();
3601 pFrame->Pop(pThread);
3604 END_CONTRACT_VIOLATION;
3609 #ifdef FEATURE_COMINTEROP
3610 // The reverse COM interop path needs to be sure to pop the ComMethodFrame that is pushed, but we do not want
3611 // to have an additional FS:0 handler between the COM callsite and the call into managed. So we push this
3612 // FS:0 handler, which will defer to the usual COMPlusFrameHandler and then perform the cleanup of the
3613 // ComMethodFrame, if needed.
3614 EXCEPTION_HANDLER_IMPL(COMPlusFrameHandlerRevCom)
3616 STATIC_CONTRACT_THROWS;
3617 STATIC_CONTRACT_GC_TRIGGERS;
3618 STATIC_CONTRACT_MODE_ANY;
3620 // Defer to COMPlusFrameHandler
3621 EXCEPTION_DISPOSITION result = EXCEPTION_HANDLER_FWD(COMPlusFrameHandler);
3623 if (pExceptionRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))
3625 // Do cleanup as needed
3626 ComMethodFrame::DoSecondPassHandlerCleanup(GetCurrFrame(pEstablisherFrame));
3631 #endif // FEATURE_COMINTEROP
3632 #endif // !DACCESS_COMPILE
3633 #endif // !WIN64EXCEPTIONS
3635 #ifndef DACCESS_COMPILE
3636 LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv)
3638 #ifndef WIN64EXCEPTIONS
3639 WRAPPER_NO_CONTRACT;
3640 STATIC_CONTRACT_ENTRY_POINT;
3642 LONG result = EXCEPTION_CONTINUE_SEARCH;
3644 // This function can be called during the handling of a SO
3645 //BEGIN_ENTRYPOINT_VOIDRET;
3647 result = CLRVectoredExceptionHandler(pExceptionInfo);
3649 if (EXCEPTION_EXECUTE_HANDLER == result)
3651 result = EXCEPTION_CONTINUE_SEARCH;
3654 //END_ENTRYPOINT_VOIDRET;
3657 #else // !WIN64EXCEPTIONS
3658 return EXCEPTION_CONTINUE_SEARCH;
3659 #endif // !WIN64EXCEPTIONS
3661 #endif // !DACCESS_COMPILE
3663 // Returns TRUE if caller should resume execution.
3665 AdjustContextForVirtualStub(
3666 EXCEPTION_RECORD *pExceptionRecord,
3669 LIMITED_METHOD_CONTRACT;
3671 Thread * pThread = GetThread();
3673 // We may not have a managed thread object. Example is an AV on the helper thread.
3674 // (perhaps during StubManager::IsStub)
3675 if (pThread == NULL)
3680 PCODE f_IP = GetIP(pContext);
3682 VirtualCallStubManager::StubKind sk;
3683 /* VirtualCallStubManager *pMgr = */ VirtualCallStubManager::FindStubManager(f_IP, &sk);
3685 if (sk == VirtualCallStubManager::SK_DISPATCH)
3687 if (*PTR_WORD(f_IP) != X86_INSTR_CMP_IND_ECX_IMM32)
3689 _ASSERTE(!"AV in DispatchStub at unknown instruction");
3694 if (sk == VirtualCallStubManager::SK_RESOLVE)
3696 if (*PTR_WORD(f_IP) != X86_INSTR_MOV_EAX_ECX_IND)
3698 _ASSERTE(!"AV in ResolveStub at unknown instruction");
3702 SetSP(pContext, dac_cast<PCODE>(dac_cast<PTR_BYTE>(GetSP(pContext)) + sizeof(void*))); // rollback push eax
3709 PCODE callsite = GetAdjustedCallAddress(*dac_cast<PTR_PCODE>(GetSP(pContext)));
3710 pExceptionRecord->ExceptionAddress = (PVOID)callsite;
3711 SetIP(pContext, callsite);
3713 // put ESP back to what it was before the call.
3714 SetSP(pContext, dac_cast<PCODE>(dac_cast<PTR_BYTE>(GetSP(pContext)) + sizeof(void*)));