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.
4//
5
6//
7
8/* EXCEP.CPP:
9 *
10 */
11
12#include "common.h"
13
14#include "frames.h"
15#include "threads.h"
16#include "excep.h"
17#include "object.h"
18#include "field.h"
19#include "dbginterface.h"
20#include "cgensys.h"
21#include "comutilnative.h"
22#include "siginfo.hpp"
23#include "gcheaputilities.h"
24#include "eedbginterfaceimpl.h" //so we can clearexception in RealCOMPlusThrow
25#include "perfcounters.h"
26#include "dllimportcallback.h"
27#include "stackwalk.h" //for CrawlFrame, in SetIPFromSrcToDst
28#include "shimload.h"
29#include "eeconfig.h"
30#include "virtualcallstub.h"
31#include "typestring.h"
32
33#ifndef FEATURE_PAL
34#include "dwreport.h"
35#endif // !FEATURE_PAL
36
37#include "eventreporter.h"
38
39#ifdef FEATURE_COMINTEROP
40#include<roerrorapi.h>
41#endif
42#ifdef WIN64EXCEPTIONS
43#include "exceptionhandling.h"
44#endif
45
46#include <errorrep.h>
47#ifndef FEATURE_PAL
48// Include definition of GenericModeBlock
49#include <msodw.h>
50#endif // FEATURE_PAL
51
52
53// Support for extracting MethodDesc of a delegate.
54#include "comdelegate.h"
55
56
57#ifndef FEATURE_PAL
58// Windows uses 64kB as the null-reference area
59#define NULL_AREA_SIZE (64 * 1024)
60#else // !FEATURE_PAL
61#define NULL_AREA_SIZE GetOsPageSize()
62#endif // !FEATURE_PAL
63
64#ifndef CROSSGEN_COMPILE
65
66BOOL IsIPInEE(void *ip);
67
68//----------------------------------------------------------------------------
69//
70// IsExceptionFromManagedCode - determine if pExceptionRecord points to a managed exception
71//
72// Arguments:
73// pExceptionRecord - pointer to exception record
74//
75// Return Value:
76// TRUE or FALSE
77//
78//----------------------------------------------------------------------------
79BOOL IsExceptionFromManagedCode(const EXCEPTION_RECORD * pExceptionRecord)
80{
81 CONTRACTL {
82 NOTHROW;
83 GC_NOTRIGGER;
84 SO_TOLERANT;
85 SUPPORTS_DAC;
86 PRECONDITION(CheckPointer(pExceptionRecord));
87 } CONTRACTL_END;
88
89 if (pExceptionRecord == NULL)
90 {
91 return FALSE;
92 }
93
94 DACCOP_IGNORE(FieldAccess, "EXCEPTION_RECORD is a OS structure, and ExceptionAddress is actually a target address here.");
95 UINT_PTR address = reinterpret_cast<UINT_PTR>(pExceptionRecord->ExceptionAddress);
96
97 // An exception code of EXCEPTION_COMPLUS indicates a managed exception
98 // has occurred (most likely due to executing a "throw" instruction).
99 //
100 // Also, a hardware level exception may not have an exception code of
101 // EXCEPTION_COMPLUS. In this case, an exception address that resides in
102 // managed code indicates a managed exception has occurred.
103 return (IsComPlusException(pExceptionRecord) ||
104 (ExecutionManager::IsManagedCode((PCODE)address)));
105}
106
107
108#ifndef DACCESS_COMPILE
109
110#define SZ_UNHANDLED_EXCEPTION W("Unhandled Exception:")
111#define SZ_UNHANDLED_EXCEPTION_CHARLEN ((sizeof(SZ_UNHANDLED_EXCEPTION) / sizeof(WCHAR)))
112
113
114typedef struct {
115 OBJECTREF pThrowable;
116 STRINGREF s1;
117 OBJECTREF pTmpThrowable;
118} ProtectArgsStruct;
119
120PEXCEPTION_REGISTRATION_RECORD GetCurrentSEHRecord();
121BOOL IsUnmanagedToManagedSEHHandler(EXCEPTION_REGISTRATION_RECORD*);
122
123VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable, BOOL rethrow
124#ifdef FEATURE_CORRUPTING_EXCEPTIONS
125 , CorruptionSeverity severity = NotCorrupting
126#endif // FEATURE_CORRUPTING_EXCEPTIONS
127 );
128
129//-------------------------------------------------------------------------------
130// Basically, this asks whether the exception is a managed exception thrown by
131// this instance of the CLR.
132//
133// The way the result is used, however, is to decide whether this instance is the
134// one to throw up the Watson box.
135//-------------------------------------------------------------------------------
136BOOL ShouldOurUEFDisplayUI(PEXCEPTION_POINTERS pExceptionInfo)
137{
138 STATIC_CONTRACT_NOTHROW;
139 STATIC_CONTRACT_GC_NOTRIGGER;
140 STATIC_CONTRACT_FORBID_FAULT;
141
142 // Test first for the canned SO EXCEPTION_POINTERS structure as it has a NULL context record and will break the code below.
143 extern EXCEPTION_POINTERS g_SOExceptionPointers;
144 if (pExceptionInfo == &g_SOExceptionPointers)
145 {
146 return TRUE;
147 }
148 return IsComPlusException(pExceptionInfo->ExceptionRecord) || ExecutionManager::IsManagedCode(GetIP(pExceptionInfo->ContextRecord));
149}
150
151BOOL NotifyAppDomainsOfUnhandledException(
152 PEXCEPTION_POINTERS pExceptionPointers,
153 OBJECTREF *pThrowableIn,
154 BOOL useLastThrownObject,
155 BOOL isTerminating);
156
157VOID SetManagedUnhandledExceptionBit(
158 BOOL useLastThrownObject);
159
160
161void COMPlusThrowBoot(HRESULT hr)
162{
163 STATIC_CONTRACT_THROWS;
164
165 _ASSERTE(g_fEEShutDown >= ShutDown_Finalize2 || !"This should not be called unless we are in the last phase of shutdown!");
166 ULONG_PTR arg = hr;
167 RaiseException(BOOTUP_EXCEPTION_COMPLUS, EXCEPTION_NONCONTINUABLE, 1, &arg);
168}
169
170
171//-------------------------------------------------------------------------------
172// This simply tests to see if the exception object is a subclass of
173// the descriminating class specified in the exception clause.
174//-------------------------------------------------------------------------------
175BOOL ExceptionIsOfRightType(TypeHandle clauseType, TypeHandle thrownType)
176{
177 CONTRACTL
178 {
179 NOTHROW;
180 GC_NOTRIGGER;
181 MODE_ANY;
182 FORBID_FAULT;
183 }
184 CONTRACTL_END;
185
186 // if not resolved to, then it wasn't loaded and couldn't have been thrown
187 if (clauseType.IsNull())
188 return FALSE;
189
190 if (clauseType == thrownType)
191 return TRUE;
192
193 // now look for parent match
194 TypeHandle superType = thrownType;
195 while (!superType.IsNull()) {
196 if (superType == clauseType) {
197 break;
198 }
199 superType = superType.GetParent();
200 }
201
202 return !superType.IsNull();
203}
204
205//===========================================================================
206// Gets the message text from an exception
207//===========================================================================
208ULONG GetExceptionMessage(OBJECTREF throwable,
209 __inout_ecount(bufferLength) LPWSTR buffer,
210 ULONG bufferLength)
211{
212 CONTRACTL
213 {
214 THROWS;
215 GC_TRIGGERS;
216 MODE_COOPERATIVE;
217 INJECT_FAULT(ThrowOutOfMemory());
218 }
219 CONTRACTL_END;
220
221 // Prefast buffer sanity check. Don't call the API with a zero length buffer.
222 if (bufferLength == 0)
223 {
224 _ASSERTE(bufferLength > 0);
225 return 0;
226 }
227
228 StackSString result;
229 GetExceptionMessage(throwable, result);
230
231 ULONG length = result.GetCount();
232 LPCWSTR chars = result.GetUnicode();
233
234 if (length < bufferLength)
235 {
236 wcsncpy_s(buffer, bufferLength, chars, length);
237 }
238 else
239 {
240 wcsncpy_s(buffer, bufferLength, chars, bufferLength-1);
241 }
242
243 return length;
244}
245
246//-----------------------------------------------------------------------------
247// Given an object, get the "message" from it. If the object is an Exception
248// call Exception.InternalToString, otherwise, call Object.ToString
249//-----------------------------------------------------------------------------
250void GetExceptionMessage(OBJECTREF throwable, SString &result)
251{
252 CONTRACTL
253 {
254 THROWS;
255 GC_TRIGGERS;
256 MODE_COOPERATIVE;
257 INJECT_FAULT(ThrowOutOfMemory());
258 }
259 CONTRACTL_END;
260
261 STRINGREF pString = GetExceptionMessage(throwable);
262
263 // If call returned NULL (not empty), oh well, no message.
264 if (pString != NULL)
265 pString->GetSString(result);
266} // void GetExceptionMessage()
267
268#if FEATURE_COMINTEROP
269// This method returns IRestrictedErrorInfo associated with the ErrorObject.
270// It checks whether the given managed exception object has __HasRestrictedLanguageErrorObject set
271// in which case it returns the IRestrictedErrorInfo associated with the __RestrictedErrorObject.
272IRestrictedErrorInfo* GetRestrictedErrorInfoFromErrorObject(OBJECTREF throwable)
273{
274 CONTRACTL
275 {
276 THROWS;
277 GC_TRIGGERS;
278 MODE_COOPERATIVE;
279 INJECT_FAULT(ThrowOutOfMemory());
280 }
281 CONTRACTL_END;
282
283 IRestrictedErrorInfo* pRestrictedErrorInfo = NULL;
284
285 // If there is no object, there is no restricted error.
286 if (throwable == NULL)
287 return NULL;
288
289 _ASSERTE(IsException(throwable->GetMethodTable())); // what is the pathway here?
290 if (!IsException(throwable->GetMethodTable()))
291 {
292 return NULL;
293 }
294
295 struct _gc {
296 OBJECTREF Throwable;
297 OBJECTREF RestrictedErrorInfoObjRef;
298 } gc;
299
300 ZeroMemory(&gc, sizeof(gc));
301 GCPROTECT_BEGIN(gc);
302
303 gc.Throwable = throwable;
304
305 // Get the MethodDesc on which we'll call.
306 MethodDescCallSite getRestrictedLanguageErrorObject(METHOD__EXCEPTION__TRY_GET_RESTRICTED_LANGUAGE_ERROR_OBJECT, &gc.Throwable);
307
308 // Make the call.
309 ARG_SLOT Args[] =
310 {
311 ObjToArgSlot(gc.Throwable),
312 PtrToArgSlot(&gc.RestrictedErrorInfoObjRef)
313 };
314
315 BOOL bHasLanguageRestrictedErrorObject = (BOOL)getRestrictedLanguageErrorObject.Call_RetBool(Args);
316
317 if(bHasLanguageRestrictedErrorObject)
318 {
319 // The __RestrictedErrorObject represents IRestrictedErrorInfo RCW of a non-CLR platform. Lets get the corresponding IRestrictedErrorInfo for it.
320 pRestrictedErrorInfo = (IRestrictedErrorInfo *)GetComIPFromObjectRef(&gc.RestrictedErrorInfoObjRef, IID_IRestrictedErrorInfo);
321 }
322
323 GCPROTECT_END();
324
325 return pRestrictedErrorInfo;
326}
327#endif
328
329STRINGREF GetExceptionMessage(OBJECTREF throwable)
330{
331 CONTRACTL
332 {
333 THROWS;
334 GC_TRIGGERS;
335 MODE_COOPERATIVE;
336 INJECT_FAULT(ThrowOutOfMemory());
337 }
338 CONTRACTL_END;
339
340 // If there is no object, there is no message.
341 if (throwable == NULL)
342 return NULL;
343
344 // Assume we're calling Exception.InternalToString() ...
345 BinderMethodID sigID = METHOD__EXCEPTION__INTERNAL_TO_STRING;
346
347 // ... but if it isn't an exception, call Object.ToString().
348 _ASSERTE(IsException(throwable->GetMethodTable())); // what is the pathway here?
349 if (!IsException(throwable->GetMethodTable()))
350 {
351 sigID = METHOD__OBJECT__TO_STRING;
352 }
353
354 // Return value.
355 STRINGREF pString = NULL;
356
357 GCPROTECT_BEGIN(throwable);
358
359 // Get the MethodDesc on which we'll call.
360 MethodDescCallSite toString(sigID, &throwable);
361
362 // Make the call.
363 ARG_SLOT arg[1] = {ObjToArgSlot(throwable)};
364 pString = toString.Call_RetSTRINGREF(arg);
365
366 GCPROTECT_END();
367
368 return pString;
369}
370
371HRESULT GetExceptionHResult(OBJECTREF throwable)
372{
373 CONTRACTL
374 {
375 NOTHROW;
376 GC_NOTRIGGER;
377 MODE_COOPERATIVE;
378 SO_TOLERANT;
379 }
380 CONTRACTL_END;
381
382 HRESULT hr = E_FAIL;
383 if (throwable == NULL)
384 return hr;
385
386 // Since any object can be thrown in managed code, not only instances of System.Exception subclasses
387 // we need to check to see if we are dealing with an exception before attempting to retrieve
388 // the HRESULT field. If we are not dealing with an exception, then we will simply return E_FAIL.
389 _ASSERTE(IsException(throwable->GetMethodTable())); // what is the pathway here?
390 if (IsException(throwable->GetMethodTable()))
391 {
392 hr = ((EXCEPTIONREF)throwable)->GetHResult();
393 }
394
395 return hr;
396} // HRESULT GetExceptionHResult()
397
398DWORD GetExceptionXCode(OBJECTREF throwable)
399{
400 CONTRACTL
401 {
402 NOTHROW;
403 GC_NOTRIGGER;
404 MODE_COOPERATIVE;
405 SO_TOLERANT;
406 }
407 CONTRACTL_END;
408
409 HRESULT hr = E_FAIL;
410 if (throwable == NULL)
411 return hr;
412
413 // Since any object can be thrown in managed code, not only instances of System.Exception subclasses
414 // we need to check to see if we are dealing with an exception before attempting to retrieve
415 // the HRESULT field. If we are not dealing with an exception, then we will simply return E_FAIL.
416 _ASSERTE(IsException(throwable->GetMethodTable())); // what is the pathway here?
417 if (IsException(throwable->GetMethodTable()))
418 {
419 hr = ((EXCEPTIONREF)throwable)->GetXCode();
420 }
421
422 return hr;
423} // DWORD GetExceptionXCode()
424
425//------------------------------------------------------------------------------
426// This function will extract some information from an Access Violation SEH
427// exception, and store it in the System.AccessViolationException object.
428// - the faulting instruction's IP.
429// - the target address of the faulting instruction.
430// - a code indicating attempted read vs write
431//------------------------------------------------------------------------------
432void SetExceptionAVParameters( // No return.
433 OBJECTREF throwable, // The object into which to set the values.
434 EXCEPTION_RECORD *pExceptionRecord) // The SEH exception information.
435{
436 CONTRACTL
437 {
438 THROWS;
439 GC_TRIGGERS;
440 MODE_COOPERATIVE;
441 PRECONDITION(throwable != NULL);
442 }
443 CONTRACTL_END;
444
445 GCPROTECT_BEGIN(throwable)
446 {
447 // This should only be called for AccessViolationException
448 _ASSERTE(MscorlibBinder::GetException(kAccessViolationException) == throwable->GetMethodTable());
449
450 FieldDesc *pFD_ip = MscorlibBinder::GetField(FIELD__ACCESS_VIOLATION_EXCEPTION__IP);
451 FieldDesc *pFD_target = MscorlibBinder::GetField(FIELD__ACCESS_VIOLATION_EXCEPTION__TARGET);
452 FieldDesc *pFD_access = MscorlibBinder::GetField(FIELD__ACCESS_VIOLATION_EXCEPTION__ACCESSTYPE);
453
454 _ASSERTE(pFD_ip->GetFieldType() == ELEMENT_TYPE_I);
455 _ASSERTE(pFD_target->GetFieldType() == ELEMENT_TYPE_I);
456 _ASSERTE(pFD_access->GetFieldType() == ELEMENT_TYPE_I4);
457
458 void *ip = pExceptionRecord->ExceptionAddress;
459 void *target = (void*)(pExceptionRecord->ExceptionInformation[1]);
460 DWORD access = (DWORD)(pExceptionRecord->ExceptionInformation[0]);
461
462 pFD_ip->SetValuePtr(throwable, ip);
463 pFD_target->SetValuePtr(throwable, target);
464 pFD_access->SetValue32(throwable, access);
465
466 }
467 GCPROTECT_END();
468
469} // void SetExceptionAVParameters()
470
471//------------------------------------------------------------------------------
472// This will call InternalPreserveStackTrace (if the throwable derives from
473// System.Exception), to copy the stack trace to the _remoteStackTraceString.
474// Doing so allows the stack trace of an exception caught by the runtime, and
475// rethrown with COMPlusThrow(OBJECTREF thowable), to be preserved. Otherwise
476// the exception handling code may clear the stack trace. (Generally, we see
477// the stack trace preserved on win32 and cleared on win64.)
478//------------------------------------------------------------------------------
479void ExceptionPreserveStackTrace( // No return.
480 OBJECTREF throwable) // Object about to be thrown.
481{
482 CONTRACTL
483 {
484 THROWS;
485 GC_TRIGGERS;
486 MODE_COOPERATIVE;
487 INJECT_FAULT(ThrowOutOfMemory());
488 }
489 CONTRACTL_END;
490
491 // If there is no object, there is no stack trace to save.
492 if (throwable == NULL)
493 return;
494
495 GCPROTECT_BEGIN(throwable);
496
497 // Make sure it is derived from System.Exception, that it is not one of the
498 // preallocated exception objects, and that it has a stack trace to save.
499 if (IsException(throwable->GetMethodTable()) &&
500 !CLRException::IsPreallocatedExceptionObject(throwable))
501 {
502 LOG((LF_EH, LL_INFO1000, "ExceptionPreserveStackTrace called\n"));
503
504 // We're calling Exception.InternalPreserveStackTrace() ...
505 BinderMethodID sigID = METHOD__EXCEPTION__INTERNAL_PRESERVE_STACK_TRACE;
506
507
508 // Get the MethodDesc on which we'll call.
509 MethodDescCallSite preserveStackTrace(sigID, &throwable);
510
511 // Make the call.
512 ARG_SLOT arg[1] = {ObjToArgSlot(throwable)};
513 preserveStackTrace.Call(arg);
514 }
515
516 GCPROTECT_END();
517
518} // void ExceptionPreserveStackTrace()
519
520
521// We have to cache the MethodTable and FieldDesc for wrapped non-compliant exceptions the first
522// time we wrap, because we cannot tolerate a GC when it comes time to detect and unwrap one.
523
524static MethodTable *pMT_RuntimeWrappedException;
525static FieldDesc *pFD_WrappedException;
526
527// Non-compliant exceptions are immediately wrapped in a RuntimeWrappedException instance. The entire
528// exception system can now ignore the possibility of these cases except:
529//
530// 1) IL_Throw, which must wrap via this API
531// 2) Calls to Filters & Catch handlers, which must unwrap based on whether the assembly is on the legacy
532// plan.
533//
534void WrapNonCompliantException(OBJECTREF *ppThrowable)
535{
536 CONTRACTL
537 {
538 THROWS;
539 GC_TRIGGERS;
540 MODE_COOPERATIVE;
541 PRECONDITION(IsProtectedByGCFrame(ppThrowable));
542 }
543 CONTRACTL_END;
544
545 _ASSERTE(!IsException((*ppThrowable)->GetMethodTable()));
546
547 EX_TRY
548 {
549 // idempotent operations, so the race condition is okay.
550 if (pMT_RuntimeWrappedException == NULL)
551 pMT_RuntimeWrappedException = MscorlibBinder::GetException(kRuntimeWrappedException);
552
553 if (pFD_WrappedException == NULL)
554 pFD_WrappedException = MscorlibBinder::GetField(FIELD__RUNTIME_WRAPPED_EXCEPTION__WRAPPED_EXCEPTION);
555
556 OBJECTREF orWrapper = AllocateObject(MscorlibBinder::GetException(kRuntimeWrappedException));
557
558 GCPROTECT_BEGIN(orWrapper);
559
560 MethodDescCallSite ctor(METHOD__RUNTIME_WRAPPED_EXCEPTION__OBJ_CTOR, &orWrapper);
561
562 ARG_SLOT args[] =
563 {
564 ObjToArgSlot(orWrapper),
565 ObjToArgSlot(*ppThrowable)
566 };
567
568 ctor.Call(args);
569
570 *ppThrowable = orWrapper;
571
572 GCPROTECT_END();
573 }
574 EX_CATCH
575 {
576 // If we took an exception while binding, or running the constructor of the RuntimeWrappedException
577 // instance, we know that this new exception is CLS compliant. In fact, it's likely to be
578 // OutOfMemoryException, StackOverflowException or ThreadAbortException.
579 OBJECTREF orReplacement = GET_THROWABLE();
580
581 _ASSERTE(IsException(orReplacement->GetMethodTable()));
582
583 *ppThrowable = orReplacement;
584
585 } EX_END_CATCH(SwallowAllExceptions);
586}
587
588// Before presenting an exception object to a handler (filter or catch, not finally or fault), it
589// may be necessary to turn it back into a non-compliant exception. This is conditioned on an
590// assembly level setting.
591OBJECTREF PossiblyUnwrapThrowable(OBJECTREF throwable, Assembly *pAssembly)
592{
593 // Check if we are required to compute the RuntimeWrapExceptions status.
594 BOOL fIsRuntimeWrappedException = ((throwable != NULL) && (throwable->GetMethodTable() == pMT_RuntimeWrappedException));
595 BOOL fRequiresComputingRuntimeWrapExceptionsStatus = (fIsRuntimeWrappedException &&
596 (!(pAssembly->GetManifestModule()->IsRuntimeWrapExceptionsStatusComputed())));
597
598 CONTRACTL
599 {
600 THROWS;
601 // If we are required to compute the status of RuntimeWrapExceptions, then the operation could trigger a GC.
602 // Thus, conditionally setup the contract.
603 if (fRequiresComputingRuntimeWrapExceptionsStatus) GC_TRIGGERS; else GC_NOTRIGGER;
604 MODE_COOPERATIVE;
605 PRECONDITION(CheckPointer(pAssembly));
606 }
607 CONTRACTL_END;
608
609 if (fIsRuntimeWrappedException && (!pAssembly->GetManifestModule()->IsRuntimeWrapExceptions()))
610 {
611 // We already created the instance, fetched the field. We know it is
612 // not marshal by ref, or any of the other cases that might trigger GC.
613 ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
614
615 throwable = pFD_WrappedException->GetRefValue(throwable);
616 }
617
618 return throwable;
619}
620
621
622// This is used by a holder in CreateTypeInitializationExceptionObject to
623// reset the state as appropriate.
624void ResetTypeInitializationExceptionState(BOOL isAlreadyCreating)
625{
626 LIMITED_METHOD_CONTRACT;
627 if (!isAlreadyCreating)
628 GetThread()->ResetIsCreatingTypeInitException();
629}
630
631void CreateTypeInitializationExceptionObject(LPCWSTR pTypeThatFailed,
632 OBJECTREF *pInnerException,
633 OBJECTREF *pInitException,
634 OBJECTREF *pThrowable)
635{
636 CONTRACTL {
637 NOTHROW;
638 GC_TRIGGERS;
639 MODE_COOPERATIVE;
640 PRECONDITION(CheckPointer(pInnerException, NULL_OK));
641 PRECONDITION(CheckPointer(pInitException));
642 PRECONDITION(CheckPointer(pThrowable));
643 PRECONDITION(IsProtectedByGCFrame(pInnerException));
644 PRECONDITION(IsProtectedByGCFrame(pInitException));
645 PRECONDITION(IsProtectedByGCFrame(pThrowable));
646 PRECONDITION(CheckPointer(GetThread()));
647 } CONTRACTL_END;
648
649 Thread *pThread = GetThread();
650 *pThrowable = NULL;
651
652 // This will make sure to put the thread back to its original state if something
653 // throws out of this function (like an OOM exception or something)
654 Holder< BOOL, DoNothing< BOOL >, ResetTypeInitializationExceptionState, FALSE, NoNull< BOOL > >
655 isAlreadyCreating(pThread->IsCreatingTypeInitException());
656
657 EX_TRY {
658 // This will contain the type of exception we want to create. Read comment below
659 // on why we'd want to create an exception other than TypeInitException
660 MethodTable *pMT;
661 BinderMethodID methodID;
662
663 // If we are already in the midst of creating a TypeInitializationException object,
664 // and we get here, it means there was an exception thrown while initializing the
665 // TypeInitializationException type itself, or one of the types used by its class
666 // constructor. In this case, we're going to back down and use a SystemException
667 // object in its place. It is *KNOWN* that both these exception types have identical
668 // .ctor sigs "void instance (string, exception)" so both can be used interchangeably
669 // in the code that follows.
670 if (!isAlreadyCreating.GetValue()) {
671 pThread->SetIsCreatingTypeInitException();
672 pMT = MscorlibBinder::GetException(kTypeInitializationException);
673 methodID = METHOD__TYPE_INIT_EXCEPTION__STR_EX_CTOR;
674 }
675 else {
676 // If we ever hit one of these asserts, then it is bad
677 // because we do not know what exception to return then.
678 _ASSERTE(pInnerException != NULL);
679 _ASSERTE(*pInnerException != NULL);
680 *pThrowable = *pInnerException;
681 *pInitException = *pInnerException;
682 goto ErrExit;
683 }
684
685 // Allocate the exception object
686 *pThrowable = AllocateObject(pMT);
687
688 MethodDescCallSite ctor(methodID, pThrowable);
689
690 // Since the inner exception object in the .ctor is of type Exception, make sure
691 // that the object we're passed in derives from Exception. If not, pass NULL.
692 BOOL isException = FALSE;
693 if (pInnerException != NULL)
694 isException = IsException((*pInnerException)->GetMethodTable());
695
696 _ASSERTE(isException); // What pathway can give us non-compliant exceptions?
697
698 STRINGREF sType = StringObject::NewString(pTypeThatFailed);
699
700 // If the inner object derives from exception, set it as the third argument.
701 ARG_SLOT args[] = { ObjToArgSlot(*pThrowable),
702 ObjToArgSlot(sType),
703 ObjToArgSlot(isException ? *pInnerException : NULL) };
704
705 // Call the .ctor
706 ctor.Call(args);
707
708 // On success, set the init exception.
709 *pInitException = *pThrowable;
710 }
711 EX_CATCH {
712 // If calling the constructor fails, then we'll call ourselves again, and this time
713 // through we will try and create an EEException object. If that fails, then the
714 // else block of this will be executed.
715 if (!isAlreadyCreating.GetValue()) {
716 CreateTypeInitializationExceptionObject(pTypeThatFailed, pInnerException, pInitException, pThrowable);
717 }
718
719 // If we were already in the middle of creating a type init
720 // exception when we were called, we would have tried to create an EEException instead
721 // of a TypeInitException.
722 else {
723 // If we're recursing, then we should be calling ourselves from DoRunClassInitThrowing,
724 // in which case we're guaranteed that we're passing in all three arguments.
725 *pInitException = pInnerException ? *pInnerException : NULL;
726 *pThrowable = GET_THROWABLE();
727 }
728 } EX_END_CATCH(SwallowAllExceptions);
729
730 CONSISTENCY_CHECK(*pInitException != NULL || !pInnerException);
731
732 ErrExit:
733 ;
734}
735
736// ==========================================================================
737// ComputeEnclosingHandlerNestingLevel
738//
739// This is code factored out of COMPlusThrowCallback to figure out
740// what the number of nested exception handlers is.
741// ==========================================================================
742DWORD ComputeEnclosingHandlerNestingLevel(IJitManager *pIJM,
743 const METHODTOKEN& mdTok,
744 SIZE_T offsNat)
745{
746 CONTRACTL
747 {
748 NOTHROW;
749 GC_NOTRIGGER;
750 MODE_ANY;
751 FORBID_FAULT;
752 }
753 CONTRACTL_END;
754
755 // Determine the nesting level of EHClause. Just walk the table
756 // again, and find out how many handlers enclose it
757 DWORD nestingLevel = 0;
758 EH_CLAUSE_ENUMERATOR pEnumState;
759 unsigned EHCount = pIJM->InitializeEHEnumeration(mdTok, &pEnumState);
760
761 for (unsigned j=0; j<EHCount; j++)
762 {
763 EE_ILEXCEPTION_CLAUSE EHClause;
764
765 pIJM->GetNextEHClause(&pEnumState,&EHClause);
766 _ASSERTE(EHClause.HandlerEndPC != (DWORD) -1); // <TODO> remove, only protects against a deprecated convention</TODO>
767
768 if ((offsNat > EHClause.HandlerStartPC) &&
769 (offsNat < EHClause.HandlerEndPC))
770 {
771 nestingLevel++;
772 }
773 }
774
775 return nestingLevel;
776}
777
778// ******************************* EHRangeTreeNode ************************** //
779EHRangeTreeNode::EHRangeTreeNode(void)
780{
781 WRAPPER_NO_CONTRACT;
782 CommonCtor(0, false);
783}
784
785EHRangeTreeNode::EHRangeTreeNode(DWORD offset, bool fIsRange /* = false */)
786{
787 WRAPPER_NO_CONTRACT;
788 CommonCtor(offset, fIsRange);
789}
790
791void EHRangeTreeNode::CommonCtor(DWORD offset, bool fIsRange)
792{
793 LIMITED_METHOD_CONTRACT;
794
795 m_pTree = NULL;
796 m_clause = NULL;
797
798 m_pContainedBy = NULL;
799
800 m_offset = offset;
801 m_fIsRange = fIsRange;
802 m_fIsRoot = false; // must set this flag explicitly
803}
804
805inline bool EHRangeTreeNode::IsRange()
806{
807 // Please see the header file for an explanation of this assertion.
808 _ASSERTE(m_fIsRoot || m_clause != NULL || !m_fIsRange);
809 return m_fIsRange;
810}
811
812void EHRangeTreeNode::MarkAsRange()
813{
814 m_offset = 0;
815 m_fIsRange = true;
816 m_fIsRoot = false;
817}
818
819inline bool EHRangeTreeNode::IsRoot()
820{
821 // Please see the header file for an explanation of this assertion.
822 _ASSERTE(m_fIsRoot || m_clause != NULL || !m_fIsRange);
823 return m_fIsRoot;
824}
825
826void EHRangeTreeNode::MarkAsRoot(DWORD offset)
827{
828 m_offset = offset;
829 m_fIsRange = true;
830 m_fIsRoot = true;
831}
832
833inline DWORD EHRangeTreeNode::GetOffset()
834{
835 _ASSERTE(m_clause == NULL);
836 _ASSERTE(IsRoot() || !IsRange());
837 return m_offset;
838}
839
840inline DWORD EHRangeTreeNode::GetTryStart()
841{
842 _ASSERTE(IsRange());
843 _ASSERTE(!IsRoot());
844 if (IsRoot())
845 {
846 return 0;
847 }
848 else
849 {
850 return m_clause->TryStartPC;
851 }
852}
853
854inline DWORD EHRangeTreeNode::GetTryEnd()
855{
856 _ASSERTE(IsRange());
857 _ASSERTE(!IsRoot());
858 if (IsRoot())
859 {
860 return GetOffset();
861 }
862 else
863 {
864 return m_clause->TryEndPC;
865 }
866}
867
868inline DWORD EHRangeTreeNode::GetHandlerStart()
869{
870 _ASSERTE(IsRange());
871 _ASSERTE(!IsRoot());
872 if (IsRoot())
873 {
874 return 0;
875 }
876 else
877 {
878 return m_clause->HandlerStartPC;
879 }
880}
881
882inline DWORD EHRangeTreeNode::GetHandlerEnd()
883{
884 _ASSERTE(IsRange());
885 _ASSERTE(!IsRoot());
886 if (IsRoot())
887 {
888 return GetOffset();
889 }
890 else
891 {
892 return m_clause->HandlerEndPC;
893 }
894}
895
896inline DWORD EHRangeTreeNode::GetFilterStart()
897{
898 _ASSERTE(IsRange());
899 _ASSERTE(!IsRoot());
900 if (IsRoot())
901 {
902 return 0;
903 }
904 else
905 {
906 return m_clause->FilterOffset;
907 }
908}
909
910// Get the end offset of the filter clause. This offset is exclusive.
911inline DWORD EHRangeTreeNode::GetFilterEnd()
912{
913 _ASSERTE(IsRange());
914 _ASSERTE(!IsRoot());
915 if (IsRoot())
916 {
917 // We should never get here if the "this" node is the root.
918 // By definition, the root contains everything. No checking is necessary.
919 return 0;
920 }
921 else
922 {
923 return m_FilterEndPC;
924 }
925}
926
927bool EHRangeTreeNode::Contains(DWORD offset)
928{
929 WRAPPER_NO_CONTRACT;
930
931 EHRangeTreeNode node(offset);
932 return Contains(&node);
933}
934
935bool EHRangeTreeNode::TryContains(DWORD offset)
936{
937 WRAPPER_NO_CONTRACT;
938
939 EHRangeTreeNode node(offset);
940 return TryContains(&node);
941}
942
943bool EHRangeTreeNode::HandlerContains(DWORD offset)
944{
945 WRAPPER_NO_CONTRACT;
946
947 EHRangeTreeNode node(offset);
948 return HandlerContains(&node);
949}
950
951bool EHRangeTreeNode::FilterContains(DWORD offset)
952{
953 WRAPPER_NO_CONTRACT;
954
955 EHRangeTreeNode node(offset);
956 return FilterContains(&node);
957}
958
959bool EHRangeTreeNode::Contains(EHRangeTreeNode* pNode)
960{
961 LIMITED_METHOD_CONTRACT;
962
963 // If we are checking a range of address, then we should check the end address inclusively.
964 if (pNode->IsRoot())
965 {
966 // No node contains the root node.
967 return false;
968 }
969 else if (this->IsRoot())
970 {
971 return (pNode->IsRange() ?
972 (pNode->GetTryEnd() <= this->GetOffset()) && (pNode->GetHandlerEnd() <= this->GetOffset())
973 : (pNode->GetOffset() < this->GetOffset()) );
974 }
975 else
976 {
977 return (this->TryContains(pNode) || this->HandlerContains(pNode) || this->FilterContains(pNode));
978 }
979}
980
981bool EHRangeTreeNode::TryContains(EHRangeTreeNode* pNode)
982{
983 LIMITED_METHOD_CONTRACT;
984
985 _ASSERTE(this->IsRange());
986
987 if (pNode->IsRoot())
988 {
989 // No node contains the root node.
990 return false;
991 }
992 else if (this->IsRoot())
993 {
994 // We will only get here from GetTcf() to determine if an address is in a try clause.
995 // In this case we want to return false.
996 return false;
997 }
998 else
999 {
1000 DWORD tryStart = this->GetTryStart();
1001 DWORD tryEnd = this->GetTryEnd();
1002
1003 // If we are checking a range of address, then we should check the end address inclusively.
1004 if (pNode->IsRange())
1005 {
1006 DWORD start = pNode->GetTryStart();
1007 DWORD end = pNode->GetTryEnd();
1008
1009 if (start == tryStart && end == tryEnd)
1010 {
1011 return false;
1012 }
1013 else if (start == end)
1014 {
1015 // This is effectively a single offset.
1016 if ((tryStart <= start) && (end < tryEnd))
1017 {
1018 return true;
1019 }
1020 }
1021 else if ((tryStart <= start) && (end <= tryEnd))
1022 {
1023 return true;
1024 }
1025 }
1026 else
1027 {
1028 DWORD offset = pNode->GetOffset();
1029 if ((tryStart <= offset) && (offset < tryEnd))
1030 {
1031 return true;
1032 }
1033 }
1034 }
1035
1036#ifdef WIN64EXCEPTIONS
1037 // If we are boot-strapping the tree, don't recurse down because the result could be unreliable. Note that
1038 // even if we don't recurse, given a particular node, we can still always find its most specific container with
1039 // the logic above, i.e. it's always safe to do one depth level of checking.
1040 //
1041 // To build the tree, all we need to know is the most specific container of a particular node. This can be
1042 // done by just comparing the offsets of the try regions. However, funclets create a problem because even if
1043 // a funclet is conceptually contained in a try region, we cannot determine this fact just by comparing the offsets.
1044 // This is when we need to recurse the tree. Here is a classic example:
1045 // try
1046 // {
1047 // try
1048 // {
1049 // }
1050 // catch
1051 // {
1052 // // If the offset is here, then we need to recurse.
1053 // }
1054 // }
1055 // catch
1056 // {
1057 // }
1058 if (!m_pTree->m_fInitializing)
1059 {
1060 // Iterate all the contained clauses, and for the ones which are contained in the try region,
1061 // ask if the requested range is contained by it.
1062 USHORT i = 0;
1063 USHORT numNodes = m_containees.Count();
1064 EHRangeTreeNode** ppNodes = NULL;
1065 for (i = 0, ppNodes = m_containees.Table(); i < numNodes; i++, ppNodes++)
1066 {
1067 // This variable is purely used for readability.
1068 EHRangeTreeNode* pNodeCur = *ppNodes;
1069
1070 // it's possible for nested try blocks to have the same beginning and end offsets
1071 if ( ( this->GetTryStart() <= pNodeCur->GetTryStart() ) &&
1072 ( pNodeCur->GetTryEnd() <= this->GetTryEnd() ) )
1073 {
1074 if (pNodeCur->Contains(pNode))
1075 {
1076 return true;
1077 }
1078 }
1079 }
1080 }
1081#endif // WIN64EXCEPTIONS
1082
1083 return false;
1084}
1085
1086bool EHRangeTreeNode::HandlerContains(EHRangeTreeNode* pNode)
1087{
1088 LIMITED_METHOD_CONTRACT;
1089
1090 _ASSERTE(this->IsRange());
1091
1092 if (pNode->IsRoot())
1093 {
1094 // No node contains the root node.
1095 return false;
1096 }
1097 else if (this->IsRoot())
1098 {
1099 // We will only get here from GetTcf() to determine if an address is in a try clause.
1100 // In this case we want to return false.
1101 return false;
1102 }
1103 else
1104 {
1105 DWORD handlerStart = this->GetHandlerStart();
1106 DWORD handlerEnd = this->GetHandlerEnd();
1107
1108 // If we are checking a range of address, then we should check the end address inclusively.
1109 if (pNode->IsRange())
1110 {
1111 DWORD start = pNode->GetTryStart();
1112 DWORD end = pNode->GetTryEnd();
1113
1114 if (start == handlerStart && end == handlerEnd)
1115 {
1116 return false;
1117 }
1118 else if ((handlerStart <= start) && (end <= handlerEnd))
1119 {
1120 return true;
1121 }
1122 }
1123 else
1124 {
1125 DWORD offset = pNode->GetOffset();
1126 if ((handlerStart <= offset) && (offset < handlerEnd))
1127 {
1128 return true;
1129 }
1130 }
1131 }
1132
1133#ifdef WIN64EXCEPTIONS
1134 // Refer to the comment in TryContains().
1135 if (!m_pTree->m_fInitializing)
1136 {
1137 // Iterate all the contained clauses, and for the ones which are contained in the try region,
1138 // ask if the requested range is contained by it.
1139 USHORT i = 0;
1140 USHORT numNodes = m_containees.Count();
1141 EHRangeTreeNode** ppNodes = NULL;
1142 for (i = 0, ppNodes = m_containees.Table(); i < numNodes; i++, ppNodes++)
1143 {
1144 // This variable is purely used for readability.
1145 EHRangeTreeNode* pNodeCur = *ppNodes;
1146
1147 if ( ( this->GetHandlerStart() <= pNodeCur->GetTryStart() ) &&
1148 ( pNodeCur->GetTryEnd() < this->GetHandlerEnd() ) )
1149 {
1150 if (pNodeCur->Contains(pNode))
1151 {
1152 return true;
1153 }
1154 }
1155 }
1156 }
1157#endif // WIN64EXCEPTIONS
1158
1159 return false;
1160}
1161
1162bool EHRangeTreeNode::FilterContains(EHRangeTreeNode* pNode)
1163{
1164 LIMITED_METHOD_CONTRACT;
1165
1166 _ASSERTE(this->IsRange());
1167
1168 if (pNode->IsRoot())
1169 {
1170 // No node contains the root node.
1171 return false;
1172 }
1173 else if (this->IsRoot() || !IsFilterHandler(this->m_clause))
1174 {
1175 // We will only get here from GetTcf() to determine if an address is in a try clause.
1176 // In this case we want to return false.
1177 return false;
1178 }
1179 else
1180 {
1181 DWORD filterStart = this->GetFilterStart();
1182 DWORD filterEnd = this->GetFilterEnd();
1183
1184 // If we are checking a range of address, then we should check the end address inclusively.
1185 if (pNode->IsRange())
1186 {
1187 DWORD start = pNode->GetTryStart();
1188 DWORD end = pNode->GetTryEnd();
1189
1190 if (start == filterStart && end == filterEnd)
1191 {
1192 return false;
1193 }
1194 else if ((filterStart <= start) && (end <= filterEnd))
1195 {
1196 return true;
1197 }
1198 }
1199 else
1200 {
1201 DWORD offset = pNode->GetOffset();
1202 if ((filterStart <= offset) && (offset < filterEnd))
1203 {
1204 return true;
1205 }
1206 }
1207 }
1208
1209#ifdef WIN64EXCEPTIONS
1210 // Refer to the comment in TryContains().
1211 if (!m_pTree->m_fInitializing)
1212 {
1213 // Iterate all the contained clauses, and for the ones which are contained in the try region,
1214 // ask if the requested range is contained by it.
1215 USHORT i = 0;
1216 USHORT numNodes = m_containees.Count();
1217 EHRangeTreeNode** ppNodes = NULL;
1218 for (i = 0, ppNodes = m_containees.Table(); i < numNodes; i++, ppNodes++)
1219 {
1220 // This variable is purely used for readability.
1221 EHRangeTreeNode* pNodeCur = *ppNodes;
1222
1223 if ( ( this->GetFilterStart() <= pNodeCur->GetTryStart() ) &&
1224 ( pNodeCur->GetTryEnd() < this->GetFilterEnd() ) )
1225 {
1226 if (pNodeCur->Contains(pNode))
1227 {
1228 return true;
1229 }
1230 }
1231 }
1232 }
1233#endif // WIN64EXCEPTIONS
1234
1235 return false;
1236}
1237
1238EHRangeTreeNode* EHRangeTreeNode::GetContainer()
1239{
1240 return m_pContainedBy;
1241}
1242
1243HRESULT EHRangeTreeNode::AddNode(EHRangeTreeNode *pNode)
1244{
1245 CONTRACTL
1246 {
1247 NOTHROW;
1248 GC_NOTRIGGER;
1249 MODE_ANY;
1250 INJECT_FAULT(return E_OUTOFMEMORY;);
1251 PRECONDITION(pNode != NULL);
1252 }
1253 CONTRACTL_END;
1254
1255 EHRangeTreeNode **ppEH = m_containees.Append();
1256
1257 if (ppEH == NULL)
1258 return E_OUTOFMEMORY;
1259
1260 (*ppEH) = pNode;
1261 return S_OK;
1262}
1263
1264// ******************************* EHRangeTree ************************** //
1265
1266EHRangeTree::EHRangeTree(IJitManager* pIJM,
1267 const METHODTOKEN& methodToken,
1268 DWORD methodSize,
1269 int cFunclet,
1270 const DWORD * rgFunclet)
1271{
1272 LIMITED_METHOD_CONTRACT;
1273
1274 LOG((LF_CORDB, LL_INFO10000, "EHRT::ERHT: already loaded!\n"));
1275
1276 EH_CLAUSE_ENUMERATOR pEnumState;
1277 m_EHCount = pIJM->InitializeEHEnumeration(methodToken, &pEnumState);
1278
1279 _ASSERTE(m_EHCount != 0xFFFFFFFF);
1280
1281 ULONG i = 0;
1282
1283 m_rgClauses = NULL;
1284 m_rgNodes = NULL;
1285 m_root = NULL;
1286 m_hrInit = S_OK;
1287 m_fInitializing = true;
1288
1289 if (m_EHCount > 0)
1290 {
1291 m_rgClauses = new (nothrow) EE_ILEXCEPTION_CLAUSE[m_EHCount];
1292 if (m_rgClauses == NULL)
1293 {
1294 m_hrInit = E_OUTOFMEMORY;
1295 goto LError;
1296 }
1297 }
1298
1299 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: m_ehcount:0x%x, m_rgClauses:0%x\n",
1300 m_EHCount, m_rgClauses));
1301
1302 m_rgNodes = new (nothrow) EHRangeTreeNode[m_EHCount+1];
1303 if (m_rgNodes == NULL)
1304 {
1305 m_hrInit = E_OUTOFMEMORY;
1306 goto LError;
1307 }
1308
1309 //this contains everything, even stuff on the last IP
1310 m_root = &(m_rgNodes[m_EHCount]);
1311 m_root->MarkAsRoot(methodSize + 1);
1312
1313 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: rgNodes:0x%x\n", m_rgNodes));
1314
1315 if (m_EHCount ==0)
1316 {
1317 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: About to leave!\n"));
1318 goto LSuccess;
1319 }
1320
1321 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: Sticking around!\n"));
1322
1323 // First, load all the EH clauses into the object.
1324 for (i = 0; i < m_EHCount; i++)
1325 {
1326 EE_ILEXCEPTION_CLAUSE * pEHClause = &(m_rgClauses[i]);
1327
1328 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: i:0x%x!\n", i));
1329
1330 pIJM->GetNextEHClause(&pEnumState, pEHClause);
1331
1332 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: EHRTT_JIT_MANAGER got clause\n", i));
1333
1334 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: clause 0x%x,"
1335 "addrof:0x%x\n", i, pEHClause ));
1336
1337 _ASSERTE(pEHClause->HandlerEndPC != (DWORD) -1); // <TODO> remove, only protects against a deprecated convention</TODO>
1338
1339 EHRangeTreeNode * pNodeCur = &(m_rgNodes[i]);
1340
1341 pNodeCur->m_pTree = this;
1342 pNodeCur->m_clause = pEHClause;
1343
1344 if (pEHClause->Flags == COR_ILEXCEPTION_CLAUSE_FILTER)
1345 {
1346#ifdef WIN64EXCEPTIONS
1347 // Because of funclets, there is no way to guarantee the placement of a filter.
1348 // Thus, we need to loop through the funclets to find the end offset.
1349 for (int f = 0; f < cFunclet; f++)
1350 {
1351 // Check the start offset of the filter funclet.
1352 if (pEHClause->FilterOffset == rgFunclet[f])
1353 {
1354 if (f < (cFunclet - 1))
1355 {
1356 // If it's NOT the last funclet, use the start offset of the next funclet.
1357 pNodeCur->m_FilterEndPC = rgFunclet[f + 1];
1358 }
1359 else
1360 {
1361 // If it's the last funclet, use the size of the method.
1362 pNodeCur->m_FilterEndPC = methodSize;
1363 }
1364 break;
1365 }
1366 }
1367#else // WIN64EXCEPTIONS
1368 // On x86, since the filter doesn't have an end FilterPC, the only way we can know the size
1369 // of the filter is if it's located immediately prior to it's handler and immediately after
1370 // its try region. We assume that this is, and if it isn't, we're so amazingly hosed that
1371 // we can't continue.
1372 if ((pEHClause->FilterOffset >= pEHClause->HandlerStartPC) ||
1373 (pEHClause->FilterOffset < pEHClause->TryEndPC))
1374 {
1375 m_hrInit = CORDBG_E_SET_IP_IMPOSSIBLE;
1376 goto LError;
1377 }
1378 pNodeCur->m_FilterEndPC = pEHClause->HandlerStartPC;
1379#endif // WIN64EXCEPTIONS
1380 }
1381
1382 pNodeCur->MarkAsRange();
1383 }
1384
1385 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: about to do the second pass\n"));
1386
1387
1388 // Second, for each EH, find it's most limited, containing clause
1389 // On WIN64, we have duplicate clauses. There are two types of duplicate clauses.
1390 //
1391 // The first type is described in ExceptionHandling.cpp. This type doesn't add additional information to the
1392 // EH tree structure. For example, if an offset is in the try region of a duplicate clause of this type,
1393 // then some clause which comes before the duplicate clause should contain the offset in its handler region.
1394 // Therefore, even though this type of duplicate clauses are added to the EH tree, they should never be used.
1395 //
1396 // The second type is what's called the protected clause. These clauses are used to mark the cloned finally
1397 // region. They have an empty try region. Here's an example:
1398 //
1399 // // C# code
1400 // try
1401 // {
1402 // A
1403 // }
1404 // finally
1405 // {
1406 // B
1407 // }
1408 //
1409 // // jitted code
1410 // parent
1411 // -------
1412 // A
1413 // B'
1414 // -------
1415 //
1416 // funclet
1417 // -------
1418 // B
1419 // -------
1420 //
1421 // A protected clause covers the B' region in the parent method. In essence you can think of the method as
1422 // having two try/finally regions, and that's exactly how protected clauses are handled in the EH tree.
1423 // They are added to the EH tree just like any other EH clauses.
1424 for (i = 0; i < m_EHCount; i++)
1425 {
1426 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: SP:0x%x\n", i));
1427
1428 EHRangeTreeNode * pNodeCur = &(m_rgNodes[i]);
1429
1430 EHRangeTreeNode *pNodeCandidate = NULL;
1431 pNodeCandidate = FindContainer(pNodeCur);
1432 _ASSERTE(pNodeCandidate != NULL);
1433
1434 pNodeCur->m_pContainedBy = pNodeCandidate;
1435
1436 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: SP: about to add to tree\n"));
1437
1438 HRESULT hr = pNodeCandidate->AddNode(pNodeCur);
1439 if (FAILED(hr))
1440 {
1441 m_hrInit = hr;
1442 goto LError;
1443 }
1444 }
1445
1446LSuccess:
1447 m_fInitializing = false;
1448 return;
1449
1450LError:
1451 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: LError - something went wrong!\n"));
1452
1453 if (m_rgClauses != NULL)
1454 {
1455 delete [] m_rgClauses;
1456 m_rgClauses = NULL;
1457 }
1458
1459 if (m_rgNodes != NULL)
1460 {
1461 delete [] m_rgNodes;
1462 m_rgNodes = NULL;
1463 }
1464
1465 m_fInitializing = false;
1466
1467 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: Falling off of LError!\n"));
1468} // Ctor Core
1469
1470EHRangeTree::~EHRangeTree()
1471{
1472 LIMITED_METHOD_CONTRACT;
1473
1474 if (m_rgNodes != NULL)
1475 delete [] m_rgNodes;
1476
1477 if (m_rgClauses != NULL)
1478 delete [] m_rgClauses;
1479} //Dtor
1480
1481EHRangeTreeNode *EHRangeTree::FindContainer(EHRangeTreeNode *pNodeSearch)
1482{
1483 LIMITED_METHOD_CONTRACT;
1484
1485 EHRangeTreeNode *pNodeCandidate = NULL;
1486
1487 // Examine the root, too.
1488 for (ULONG iInner = 0; iInner < m_EHCount+1; iInner++)
1489 {
1490 EHRangeTreeNode *pNodeCur = &(m_rgNodes[iInner]);
1491
1492 // Check if the current node contains the node we are searching for.
1493 if ((pNodeSearch != pNodeCur) &&
1494 pNodeCur->Contains(pNodeSearch))
1495 {
1496 // Update the candidate node if it is NULL or if it contains the current node
1497 // (i.e. the current node is more specific than the candidate node).
1498 if ((pNodeCandidate == NULL) ||
1499 pNodeCandidate->Contains(pNodeCur))
1500 {
1501 pNodeCandidate = pNodeCur;
1502 }
1503 }
1504 }
1505
1506 return pNodeCandidate;
1507}
1508
1509EHRangeTreeNode *EHRangeTree::FindMostSpecificContainer(DWORD addr)
1510{
1511 WRAPPER_NO_CONTRACT;
1512
1513 EHRangeTreeNode node(addr);
1514 return FindContainer(&node);
1515}
1516
1517EHRangeTreeNode *EHRangeTree::FindNextMostSpecificContainer(EHRangeTreeNode *pNodeSearch, DWORD addr)
1518{
1519 WRAPPER_NO_CONTRACT;
1520
1521 _ASSERTE(!m_fInitializing);
1522
1523 EHRangeTreeNode **rgpNodes = pNodeSearch->m_containees.Table();
1524
1525 if (NULL == rgpNodes)
1526 return pNodeSearch;
1527
1528 // It's possible that no subrange contains the desired address, so
1529 // keep a reasonable default around.
1530 EHRangeTreeNode *pNodeCandidate = pNodeSearch;
1531
1532 USHORT cSubRanges = pNodeSearch->m_containees.Count();
1533 EHRangeTreeNode **ppNodeCur = pNodeSearch->m_containees.Table();
1534
1535 for (int i = 0; i < cSubRanges; i++, ppNodeCur++)
1536 {
1537 if ((*ppNodeCur)->Contains(addr) &&
1538 pNodeCandidate->Contains((*ppNodeCur)))
1539 {
1540 pNodeCandidate = (*ppNodeCur);
1541 }
1542 }
1543
1544 return pNodeCandidate;
1545}
1546
1547BOOL EHRangeTree::isAtStartOfCatch(DWORD offset)
1548{
1549 LIMITED_METHOD_CONTRACT;
1550
1551 if (NULL != m_rgNodes && m_EHCount != 0)
1552 {
1553 for(unsigned i = 0; i < m_EHCount;i++)
1554 {
1555 if (m_rgNodes[i].m_clause->HandlerStartPC == offset &&
1556 (!IsFilterHandler(m_rgNodes[i].m_clause) && !IsFaultOrFinally(m_rgNodes[i].m_clause)))
1557 return TRUE;
1558 }
1559 }
1560
1561 return FALSE;
1562}
1563
1564enum TRY_CATCH_FINALLY
1565{
1566 TCF_NONE= 0,
1567 TCF_TRY,
1568 TCF_FILTER,
1569 TCF_CATCH,
1570 TCF_FINALLY,
1571 TCF_COUNT, //count of all elements, not an element itself
1572};
1573
1574#ifdef LOGGING
1575const char *TCFStringFromConst(TRY_CATCH_FINALLY tcf)
1576{
1577 LIMITED_METHOD_CONTRACT;
1578
1579 switch( tcf )
1580 {
1581 case TCF_NONE:
1582 return "TCFS_NONE";
1583 break;
1584 case TCF_TRY:
1585 return "TCFS_TRY";
1586 break;
1587 case TCF_FILTER:
1588 return "TCF_FILTER";
1589 break;
1590 case TCF_CATCH:
1591 return "TCFS_CATCH";
1592 break;
1593 case TCF_FINALLY:
1594 return "TCFS_FINALLY";
1595 break;
1596 case TCF_COUNT:
1597 return "TCFS_COUNT";
1598 break;
1599 default:
1600 return "INVALID TCFS VALUE";
1601 break;
1602 }
1603}
1604#endif //LOGGING
1605
1606#ifndef WIN64EXCEPTIONS
1607// We're unwinding if we'll return to the EE's code. Otherwise
1608// we'll return to someplace in the current code. Anywhere outside
1609// this function is "EE code".
1610bool FinallyIsUnwinding(EHRangeTreeNode *pNode,
1611 ICodeManager* pEECM,
1612 PREGDISPLAY pReg,
1613 SLOT addrStart)
1614{
1615 CONTRACTL
1616 {
1617 NOTHROW;
1618 GC_NOTRIGGER;
1619 MODE_ANY;
1620 FORBID_FAULT;
1621 }
1622 CONTRACTL_END;
1623
1624 const BYTE *pbRetAddr = pEECM->GetFinallyReturnAddr(pReg);
1625
1626 if (pbRetAddr < (const BYTE *)addrStart)
1627 return true;
1628
1629 DWORD offset = (DWORD)(size_t)(pbRetAddr - addrStart);
1630 EHRangeTreeNode *pRoot = pNode->m_pTree->m_root;
1631
1632 if (!pRoot->Contains(offset))
1633 return true;
1634 else
1635 return false;
1636}
1637
1638BOOL LeaveCatch(ICodeManager* pEECM,
1639 Thread *pThread,
1640 CONTEXT *pCtx,
1641 GCInfoToken gcInfoToken,
1642 unsigned offset)
1643{
1644 CONTRACTL
1645 {
1646 THROWS;
1647 GC_TRIGGERS;
1648 MODE_ANY;
1649 }
1650 CONTRACTL_END;
1651
1652 // We can assert these things here, and skip a call
1653 // to COMPlusCheckForAbort later.
1654
1655 // If no abort has been requested,
1656 _ASSERTE((pThread->GetThrowable() != NULL) ||
1657 // or if there is a pending exception.
1658 (!pThread->IsAbortRequested()) );
1659
1660 LPVOID esp = COMPlusEndCatchWorker(pThread);
1661
1662 PopNestedExceptionRecords(esp, pCtx, pThread->GetExceptionListPtr());
1663
1664 // Do JIT-specific work
1665 pEECM->LeaveCatch(gcInfoToken, offset, pCtx);
1666
1667 SetSP(pCtx, (UINT_PTR)esp);
1668 return TRUE;
1669}
1670#endif // WIN64EXCEPTIONS
1671
1672TRY_CATCH_FINALLY GetTcf(EHRangeTreeNode *pNode,
1673 unsigned offset)
1674{
1675 CONTRACTL
1676 {
1677 NOTHROW;
1678 GC_NOTRIGGER;
1679 MODE_ANY;
1680 FORBID_FAULT;
1681 }
1682 CONTRACTL_END;
1683
1684 _ASSERTE(pNode->IsRange() && !pNode->IsRoot());
1685
1686 TRY_CATCH_FINALLY tcf;
1687
1688 if (!pNode->Contains(offset))
1689 {
1690 tcf = TCF_NONE;
1691 }
1692 else if (pNode->TryContains(offset))
1693 {
1694 tcf = TCF_TRY;
1695 }
1696 else if (pNode->FilterContains(offset))
1697 {
1698 tcf = TCF_FILTER;
1699 }
1700 else
1701 {
1702 _ASSERTE(pNode->HandlerContains(offset));
1703 if (IsFaultOrFinally(pNode->m_clause))
1704 tcf = TCF_FINALLY;
1705 else
1706 tcf = TCF_CATCH;
1707 }
1708
1709 return tcf;
1710}
1711
1712const DWORD bEnter = 0x01;
1713const DWORD bLeave = 0x02;
1714
1715HRESULT IsLegalTransition(Thread *pThread,
1716 bool fCanSetIPOnly,
1717 DWORD fEnter,
1718 EHRangeTreeNode *pNode,
1719 DWORD offFrom,
1720 DWORD offTo,
1721 ICodeManager* pEECM,
1722 PREGDISPLAY pReg,
1723 SLOT addrStart,
1724 GCInfoToken gcInfoToken,
1725 PCONTEXT pCtx)
1726{
1727 CONTRACTL
1728 {
1729 THROWS;
1730 GC_TRIGGERS;
1731 MODE_ANY;
1732 }
1733 CONTRACTL_END;
1734
1735#ifdef _DEBUG
1736 if (fEnter & bEnter)
1737 {
1738 _ASSERTE(pNode->Contains(offTo));
1739 }
1740 if (fEnter & bLeave)
1741 {
1742 _ASSERTE(pNode->Contains(offFrom));
1743 }
1744#endif //_DEBUG
1745
1746 // First, figure out where we're coming from/going to
1747 TRY_CATCH_FINALLY tcfFrom = GetTcf(pNode,
1748 offFrom);
1749
1750 TRY_CATCH_FINALLY tcfTo = GetTcf(pNode,
1751 offTo);
1752
1753 LOG((LF_CORDB, LL_INFO10000, "ILT: from %s to %s\n",
1754 TCFStringFromConst(tcfFrom),
1755 TCFStringFromConst(tcfTo)));
1756
1757 // Now we'll consider, case-by-case, the various permutations that
1758 // can arise
1759 switch(tcfFrom)
1760 {
1761 case TCF_NONE:
1762 case TCF_TRY:
1763 {
1764 switch(tcfTo)
1765 {
1766 case TCF_NONE:
1767 case TCF_TRY:
1768 {
1769 return S_OK;
1770 break;
1771 }
1772
1773 case TCF_FILTER:
1774 {
1775 return CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER;
1776 break;
1777 }
1778
1779 case TCF_CATCH:
1780 {
1781 return CORDBG_E_CANT_SET_IP_INTO_CATCH;
1782 break;
1783 }
1784
1785 case TCF_FINALLY:
1786 {
1787 return CORDBG_E_CANT_SET_IP_INTO_FINALLY;
1788 break;
1789 }
1790 default:
1791 break;
1792 }
1793 break;
1794 }
1795
1796 case TCF_FILTER:
1797 {
1798 switch(tcfTo)
1799 {
1800 case TCF_NONE:
1801 case TCF_TRY:
1802 case TCF_CATCH:
1803 case TCF_FINALLY:
1804 {
1805 return CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER;
1806 break;
1807 }
1808 case TCF_FILTER:
1809 {
1810 return S_OK;
1811 break;
1812 }
1813 default:
1814 break;
1815
1816 }
1817 break;
1818 }
1819
1820 case TCF_CATCH:
1821 {
1822 switch(tcfTo)
1823 {
1824 case TCF_NONE:
1825 case TCF_TRY:
1826 {
1827#if !defined(WIN64EXCEPTIONS)
1828 CONTEXT *pFilterCtx = pThread->GetFilterContext();
1829 if (pFilterCtx == NULL)
1830 return CORDBG_E_SET_IP_IMPOSSIBLE;
1831
1832 if (!fCanSetIPOnly)
1833 {
1834 if (!LeaveCatch(pEECM,
1835 pThread,
1836 pFilterCtx,
1837 gcInfoToken,
1838 offFrom))
1839 return E_FAIL;
1840 }
1841 return S_OK;
1842#else // WIN64EXCEPTIONS
1843 // <NOTE>
1844 // Setting IP out of a catch clause is not supported for WIN64EXCEPTIONS because of funclets.
1845 // This scenario is disabled with approval from VS because it's not considered to
1846 // be a common user scenario.
1847 // </NOTE>
1848 return CORDBG_E_CANT_SET_IP_OUT_OF_CATCH_ON_WIN64;
1849#endif // !WIN64EXCEPTIONS
1850 break;
1851 }
1852
1853 case TCF_FILTER:
1854 {
1855 return CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER;
1856 break;
1857 }
1858
1859 case TCF_CATCH:
1860 {
1861 return S_OK;
1862 break;
1863 }
1864
1865 case TCF_FINALLY:
1866 {
1867 return CORDBG_E_CANT_SET_IP_INTO_FINALLY;
1868 break;
1869 }
1870 default:
1871 break;
1872 }
1873 break;
1874 }
1875
1876 case TCF_FINALLY:
1877 {
1878 switch(tcfTo)
1879 {
1880 case TCF_NONE:
1881 case TCF_TRY:
1882 {
1883#ifndef WIN64EXCEPTIONS
1884 if (!FinallyIsUnwinding(pNode, pEECM, pReg, addrStart))
1885 {
1886 CONTEXT *pFilterCtx = pThread->GetFilterContext();
1887 if (pFilterCtx == NULL)
1888 return CORDBG_E_SET_IP_IMPOSSIBLE;
1889
1890 if (!fCanSetIPOnly)
1891 {
1892 if (!pEECM->LeaveFinally(gcInfoToken,
1893 offFrom,
1894 pFilterCtx))
1895 return E_FAIL;
1896 }
1897 return S_OK;
1898 }
1899 else
1900 {
1901 return CORDBG_E_CANT_SET_IP_OUT_OF_FINALLY;
1902 }
1903#else // !WIN64EXCEPTIONS
1904 // <NOTE>
1905 // Setting IP out of a non-unwinding finally clause is not supported on WIN64EXCEPTIONS because of funclets.
1906 // This scenario is disabled with approval from VS because it's not considered to be a common user
1907 // scenario.
1908 // </NOTE>
1909 return CORDBG_E_CANT_SET_IP_OUT_OF_FINALLY_ON_WIN64;
1910#endif // WIN64EXCEPTIONS
1911
1912 break;
1913 }
1914
1915 case TCF_FILTER:
1916 {
1917 return CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER;
1918 break;
1919 }
1920
1921 case TCF_CATCH:
1922 {
1923 return CORDBG_E_CANT_SET_IP_INTO_CATCH;
1924 break;
1925 }
1926
1927 case TCF_FINALLY:
1928 {
1929 return S_OK;
1930 break;
1931 }
1932 default:
1933 break;
1934 }
1935 break;
1936 }
1937 break;
1938 default:
1939 break;
1940 }
1941
1942 _ASSERTE( !"IsLegalTransition: We should never reach this point!" );
1943
1944 return CORDBG_E_SET_IP_IMPOSSIBLE;
1945}
1946
1947// We need this to determine what
1948// to do based on whether the stack in general is empty
1949HRESULT DestinationIsValid(void *pDjiToken,
1950 DWORD offTo,
1951 EHRangeTree *pEHRT)
1952{
1953 CONTRACTL
1954 {
1955 NOTHROW;
1956 GC_NOTRIGGER;
1957 MODE_ANY;
1958 FORBID_FAULT;
1959 }
1960 CONTRACTL_END;
1961
1962 // We'll add a call to the DebugInterface that takes this
1963 // & tells us if the destination is a stack empty point.
1964// DebuggerJitInfo *pDji = (DebuggerJitInfo *)pDjiToken;
1965
1966 if (pEHRT->isAtStartOfCatch(offTo))
1967 return CORDBG_S_BAD_START_SEQUENCE_POINT;
1968 else
1969 return S_OK;
1970} // HRESULT DestinationIsValid()
1971
1972// We want to keep the 'worst' HRESULT - if one has failed (..._E_...) & the
1973// other hasn't, take the failing one. If they've both/neither failed, then
1974// it doesn't matter which we take.
1975// Note that this macro favors retaining the first argument
1976#define WORST_HR(hr1,hr2) (FAILED(hr1)?hr1:hr2)
1977HRESULT SetIPFromSrcToDst(Thread *pThread,
1978 SLOT addrStart, // base address of method
1979 DWORD offFrom, // native offset
1980 DWORD offTo, // native offset
1981 bool fCanSetIPOnly, // if true, don't do any real work
1982 PREGDISPLAY pReg,
1983 PCONTEXT pCtx,
1984 void *pDji,
1985 EHRangeTree *pEHRT)
1986{
1987 CONTRACTL
1988 {
1989 THROWS;
1990 GC_TRIGGERS;
1991 MODE_ANY;
1992 INJECT_FAULT(return E_OUTOFMEMORY;);
1993 }
1994 CONTRACTL_END;
1995
1996 HRESULT hr = S_OK;
1997 HRESULT hrReturn = S_OK;
1998 bool fCheckOnly = true;
1999
2000 EECodeInfo codeInfo((TADDR)(addrStart));
2001
2002 ICodeManager * pEECM = codeInfo.GetCodeManager();
2003 GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken();
2004
2005 // Do both checks here so compiler doesn't complain about skipping
2006 // initialization b/c of goto.
2007 if (fCanSetIPOnly && !pEECM->IsGcSafe(&codeInfo, offFrom))
2008 {
2009 hrReturn = WORST_HR(hrReturn, CORDBG_E_SET_IP_IMPOSSIBLE);
2010 }
2011
2012 if (fCanSetIPOnly && !pEECM->IsGcSafe(&codeInfo, offTo))
2013 {
2014 hrReturn = WORST_HR(hrReturn, CORDBG_E_SET_IP_IMPOSSIBLE);
2015 }
2016
2017 if ((hr = DestinationIsValid(pDji, offTo, pEHRT)) != S_OK
2018 && fCanSetIPOnly)
2019 {
2020 hrReturn = WORST_HR(hrReturn,hr);
2021 }
2022
2023 // The basic approach is this: We'll start with the most specific (smallest)
2024 // EHClause that contains the starting address. We'll 'back out', to larger
2025 // and larger ranges, until we either find an EHClause that contains both
2026 // the from and to addresses, or until we reach the root EHRangeTreeNode,
2027 // which contains all addresses within it. At each step, we check/do work
2028 // that the various transitions (from inside to outside a catch, etc).
2029 // At that point, we do the reverse process - we go from the EHClause that
2030 // encompasses both from and to, and narrow down to the smallest EHClause that
2031 // encompasses the to point. We use our nifty data structure to manage
2032 // the tree structure inherent in this process.
2033 //
2034 // NOTE: We do this process twice, once to check that we're not doing an
2035 // overall illegal transition, such as ultimately set the IP into
2036 // a catch, which is never allowed. We're doing this because VS
2037 // calls SetIP without calling CanSetIP first, and so we should be able
2038 // to return an error code and have the stack in the same condition
2039 // as the start of the call, and so we shouldn't back out of clauses
2040 // or move into them until we're sure that can be done.
2041
2042retryForCommit:
2043
2044 EHRangeTreeNode *node;
2045 EHRangeTreeNode *nodeNext;
2046 node = pEHRT->FindMostSpecificContainer(offFrom);
2047
2048 while (!node->Contains(offTo))
2049 {
2050 hr = IsLegalTransition(pThread,
2051 fCheckOnly,
2052 bLeave,
2053 node,
2054 offFrom,
2055 offTo,
2056 pEECM,
2057 pReg,
2058 addrStart,
2059 gcInfoToken,
2060 pCtx);
2061
2062 if (FAILED(hr))
2063 {
2064 hrReturn = WORST_HR(hrReturn,hr);
2065 }
2066
2067 node = node->GetContainer();
2068 // m_root prevents node from ever being NULL.
2069 }
2070
2071 if (node != pEHRT->m_root)
2072 {
2073 hr = IsLegalTransition(pThread,
2074 fCheckOnly,
2075 bEnter|bLeave,
2076 node,
2077 offFrom,
2078 offTo,
2079 pEECM,
2080 pReg,
2081 addrStart,
2082 gcInfoToken,
2083 pCtx);
2084
2085 if (FAILED(hr))
2086 {
2087 hrReturn = WORST_HR(hrReturn,hr);
2088 }
2089 }
2090
2091 nodeNext = pEHRT->FindNextMostSpecificContainer(node,
2092 offTo);
2093
2094 while(nodeNext != node)
2095 {
2096 hr = IsLegalTransition(pThread,
2097 fCheckOnly,
2098 bEnter,
2099 nodeNext,
2100 offFrom,
2101 offTo,
2102 pEECM,
2103 pReg,
2104 addrStart,
2105 gcInfoToken,
2106 pCtx);
2107
2108 if (FAILED(hr))
2109 {
2110 hrReturn = WORST_HR(hrReturn, hr);
2111 }
2112
2113 node = nodeNext;
2114 nodeNext = pEHRT->FindNextMostSpecificContainer(node,
2115 offTo);
2116 }
2117
2118 // If it was the intention to actually set the IP and the above transition checks succeeded,
2119 // then go back and do it all again but this time widen and narrow the thread's actual scope
2120 if (!fCanSetIPOnly && fCheckOnly && SUCCEEDED(hrReturn))
2121 {
2122 fCheckOnly = false;
2123 goto retryForCommit;
2124 }
2125
2126 return hrReturn;
2127} // HRESULT SetIPFromSrcToDst()
2128
2129// This function should only be called if the thread is suspended and sitting in jitted code
2130BOOL IsInFirstFrameOfHandler(Thread *pThread, IJitManager *pJitManager, const METHODTOKEN& MethodToken, DWORD offset)
2131{
2132 CONTRACTL
2133 {
2134 NOTHROW;
2135 GC_NOTRIGGER;
2136 MODE_ANY;
2137 FORBID_FAULT;
2138 }
2139 CONTRACTL_END;
2140
2141 // if don't have a throwable the aren't processing an exception
2142 if (IsHandleNullUnchecked(pThread->GetThrowableAsHandle()))
2143 return FALSE;
2144
2145 EH_CLAUSE_ENUMERATOR pEnumState;
2146 unsigned EHCount = pJitManager->InitializeEHEnumeration(MethodToken, &pEnumState);
2147
2148 for(ULONG i=0; i < EHCount; i++)
2149 {
2150 EE_ILEXCEPTION_CLAUSE EHClause;
2151 pJitManager->GetNextEHClause(&pEnumState, &EHClause);
2152 _ASSERTE(IsValidClause(&EHClause));
2153
2154 if ( offset >= EHClause.HandlerStartPC && offset < EHClause.HandlerEndPC)
2155 return TRUE;
2156
2157 // check if it's in the filter itself if we're not in the handler
2158 if (IsFilterHandler(&EHClause) && offset >= EHClause.FilterOffset && offset < EHClause.HandlerStartPC)
2159 return TRUE;
2160 }
2161 return FALSE;
2162} // BOOL IsInFirstFrameOfHandler()
2163
2164
2165#if !defined(WIN64EXCEPTIONS)
2166
2167//******************************************************************************
2168// LookForHandler -- search for a function that will handle the exception.
2169//******************************************************************************
2170LFH LookForHandler( // LFH return types
2171 const EXCEPTION_POINTERS *pExceptionPointers, // The ExceptionRecord and ExceptionContext
2172 Thread *pThread, // Thread on which to look (always current?)
2173 ThrowCallbackType *tct) // Structure to pass back to callback functions.
2174{
2175 // We don't want to use a runtime contract here since this codepath is used during
2176 // the processing of a hard SO. Contracts use a significant amount of stack
2177 // which we can't afford for those cases.
2178 STATIC_CONTRACT_THROWS;
2179 STATIC_CONTRACT_GC_TRIGGERS;
2180 STATIC_CONTRACT_MODE_COOPERATIVE;
2181
2182 // go through to find if anyone handles the exception
2183 StackWalkAction action = pThread->StackWalkFrames((PSTACKWALKFRAMESCALLBACK)COMPlusThrowCallback,
2184 tct,
2185 0, //can't use FUNCTIONSONLY because the callback uses non-function frames to stop the walk
2186 tct->pBottomFrame);
2187
2188 // If someone handles it, the action will be SWA_ABORT with pFunc and dHandler indicating the
2189 // function and handler that is handling the exception. Debugger can put a hook in here.
2190 if (action == SWA_ABORT && tct->pFunc != NULL)
2191 return LFH_FOUND;
2192
2193 // nobody is handling it
2194 return LFH_NOT_FOUND;
2195} // LFH LookForHandler()
2196
2197StackWalkAction COMPlusUnwindCallback (CrawlFrame *pCf, ThrowCallbackType *pData);
2198
2199//******************************************************************************
2200// UnwindFrames
2201//******************************************************************************
2202void UnwindFrames( // No return value.
2203 Thread *pThread, // Thread to unwind.
2204 ThrowCallbackType *tct) // Structure to pass back to callback function.
2205{
2206 STATIC_CONTRACT_THROWS;
2207 STATIC_CONTRACT_GC_NOTRIGGER;
2208 STATIC_CONTRACT_MODE_COOPERATIVE;
2209
2210 if (pThread->IsExceptionInProgress())
2211 {
2212 pThread->GetExceptionState()->GetFlags()->SetUnwindHasStarted();
2213 }
2214
2215 #ifdef DEBUGGING_SUPPORTED
2216 //
2217 // If a debugger is attached, notify it that unwinding is going on.
2218 //
2219 if (CORDebuggerAttached())
2220 {
2221 g_pDebugInterface->ManagedExceptionUnwindBegin(pThread);
2222 }
2223 #endif // DEBUGGING_SUPPORTED
2224
2225 LOG((LF_EH, LL_INFO1000, "UnwindFrames: going to: pFunc:%#X, pStack:%#X\n",
2226 tct->pFunc, tct->pStack));
2227
2228 pThread->StackWalkFrames((PSTACKWALKFRAMESCALLBACK)COMPlusUnwindCallback,
2229 tct,
2230 POPFRAMES,
2231 tct->pBottomFrame);
2232} // void UnwindFrames()
2233
2234#endif // !defined(WIN64EXCEPTIONS)
2235
2236void StackTraceInfo::SaveStackTrace(BOOL bAllowAllocMem, OBJECTHANDLE hThrowable, BOOL bReplaceStack, BOOL bSkipLastElement)
2237{
2238 CONTRACTL
2239 {
2240 NOTHROW;
2241 GC_TRIGGERS;
2242 MODE_COOPERATIVE;
2243 }
2244 CONTRACTL_END;
2245
2246 // Do not save stacktrace to preallocated exception. These are shared.
2247 if (CLRException::IsPreallocatedExceptionHandle(hThrowable))
2248 {
2249 // Preallocated exceptions will never have this flag set. However, its possible
2250 // that after this flag is set for a regular exception but before we throw, we have an async
2251 // exception like a RudeThreadAbort, which will replace the exception
2252 // containing the restored stack trace.
2253 //
2254 // In such a case, we should clear the flag as the throwable representing the
2255 // preallocated exception will not have the restored (or any) stack trace.
2256 PTR_ThreadExceptionState pCurTES = GetThread()->GetExceptionState();
2257 pCurTES->ResetRaisingForeignException();
2258
2259 return;
2260 }
2261
2262 LOG((LF_EH, LL_INFO1000, "StackTraceInfo::SaveStackTrace (%p), alloc = %d, replace = %d, skiplast = %d\n", this, bAllowAllocMem, bReplaceStack, bSkipLastElement));
2263
2264 // if have bSkipLastElement, must also keep the stack
2265 _ASSERTE(! bSkipLastElement || ! bReplaceStack);
2266
2267 bool fSuccess = false;
2268 MethodTable* pMT = ObjectFromHandle(hThrowable)->GetMethodTable();
2269
2270 // Check if the flag indicating foreign exception raise has been setup or not,
2271 // and then reset it so that subsequent processing of managed frames proceeds
2272 // normally.
2273 PTR_ThreadExceptionState pCurTES = GetThread()->GetExceptionState();
2274 BOOL fRaisingForeignException = pCurTES->IsRaisingForeignException();
2275 pCurTES->ResetRaisingForeignException();
2276
2277 if (bAllowAllocMem && m_dFrameCount != 0)
2278 {
2279 EX_TRY
2280 {
2281 // Only save stack trace info on exceptions
2282 _ASSERTE(IsException(pMT)); // what is the pathway here?
2283 if (!IsException(pMT))
2284 {
2285 fSuccess = true;
2286 }
2287 else
2288 {
2289 // If the stack trace contains DynamicMethodDescs, we need to save the corrosponding
2290 // System.Resolver objects in the Exception._dynamicMethods field. Failing to do that
2291 // will cause an AV in the runtime when we try to visit those MethodDescs in the
2292 // Exception._stackTrace field, because they have been recycled or destroyed.
2293 unsigned iNumDynamics = 0;
2294
2295 // How many DynamicMethodDescs do we need to keep alive?
2296 for (unsigned iElement=0; iElement < m_dFrameCount; iElement++)
2297 {
2298 MethodDesc *pMethod = m_pStackTrace[iElement].pFunc;
2299 _ASSERTE(pMethod);
2300
2301 if (pMethod->IsLCGMethod())
2302 {
2303 // Increment the number of new dynamic methods we have found
2304 iNumDynamics++;
2305 }
2306 else
2307 if (pMethod->GetMethodTable()->Collectible())
2308 {
2309 iNumDynamics++;
2310 }
2311 }
2312
2313 struct _gc
2314 {
2315 StackTraceArray stackTrace;
2316 StackTraceArray stackTraceTemp;
2317 PTRARRAYREF dynamicMethodsArrayTemp;
2318 PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
2319 PTRARRAYREF pOrigDynamicArray;
2320
2321 _gc()
2322 : stackTrace()
2323 , stackTraceTemp()
2324 , dynamicMethodsArrayTemp(static_cast<PTRArray *>(NULL))
2325 , dynamicMethodsArray(static_cast<PTRArray *>(NULL))
2326 , pOrigDynamicArray(static_cast<PTRArray *>(NULL))
2327 {}
2328 };
2329
2330 _gc gc;
2331 GCPROTECT_BEGIN(gc);
2332
2333 // If the flag indicating foreign exception raise has been setup, then check
2334 // if the exception object has stacktrace or not. If we have an async non-preallocated
2335 // exception after setting this flag but before we throw, then the new
2336 // exception will not have any stack trace set and thus, we should behave as if
2337 // the flag was not setup.
2338 if (fRaisingForeignException)
2339 {
2340 // Get the reference to stack trace and reset our flag if applicable.
2341 ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->GetStackTrace(gc.stackTraceTemp);
2342 if (gc.stackTraceTemp.Size() == 0)
2343 {
2344 fRaisingForeignException = FALSE;
2345 }
2346 }
2347
2348 // Replace stack (i.e. build a new stack trace) only if we are not raising a foreign exception.
2349 // If we are, then we will continue to extend the existing stack trace.
2350 if (bReplaceStack
2351 && (!fRaisingForeignException)
2352 )
2353 {
2354 // Cleanup previous info
2355 gc.stackTrace.Append(m_pStackTrace, m_pStackTrace + m_dFrameCount);
2356
2357 if (iNumDynamics)
2358 {
2359 // Adjust the allocation size of the array, if required
2360 if (iNumDynamics > m_cDynamicMethodItems)
2361 {
2362 S_UINT32 cNewSize = S_UINT32(2) * S_UINT32(iNumDynamics);
2363 if (cNewSize.IsOverflow())
2364 {
2365 // Overflow here implies we cannot allocate memory anymore
2366 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - Cannot calculate initial resolver array size due to overflow!\n"));
2367 COMPlusThrowOM();
2368 }
2369
2370 m_cDynamicMethodItems = cNewSize.Value();
2371 }
2372
2373 gc.dynamicMethodsArray = (PTRARRAYREF)AllocateObjectArray(m_cDynamicMethodItems, g_pObjectClass);
2374 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - allocated dynamic array for first frame of size %lu\n",
2375 m_cDynamicMethodItems));
2376 }
2377
2378 m_dCurrentDynamicIndex = 0;
2379 }
2380 else
2381 {
2382 // Fetch the stacktrace and the dynamic method array
2383 ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->GetStackTrace(gc.stackTrace, &gc.pOrigDynamicArray);
2384
2385 if (fRaisingForeignException)
2386 {
2387 // Just before we append to the stack trace, mark the last recorded frame to be from
2388 // the foreign thread so that we can insert an annotation indicating so when building
2389 // the stack trace string.
2390 size_t numCurrentFrames = gc.stackTrace.Size();
2391 if (numCurrentFrames > 0)
2392 {
2393 // "numCurrentFrames" can be zero if the user created an EDI using
2394 // an unthrown exception.
2395 StackTraceElement & refLastElementFromForeignStackTrace = gc.stackTrace[numCurrentFrames - 1];
2396 refLastElementFromForeignStackTrace.fIsLastFrameFromForeignStackTrace = TRUE;
2397 }
2398 }
2399
2400 if (!bSkipLastElement)
2401 gc.stackTrace.Append(m_pStackTrace, m_pStackTrace + m_dFrameCount);
2402
2403 //////////////////////////////
2404
2405 unsigned cOrigDynamic = 0; // number of objects in the old array
2406 if (gc.pOrigDynamicArray != NULL)
2407 {
2408 cOrigDynamic = gc.pOrigDynamicArray->GetNumComponents();
2409 }
2410 else
2411 {
2412 // Since there is no dynamic method array, reset the corresponding state variables
2413 m_dCurrentDynamicIndex = 0;
2414 m_cDynamicMethodItems = 0;
2415 }
2416
2417 if ((gc.pOrigDynamicArray != NULL)
2418 || (fRaisingForeignException)
2419 )
2420 {
2421 // Since we have just restored the dynamic method array as well,
2422 // calculate the dynamic array index which would be the total
2423 // number of dynamic methods present in the stack trace.
2424 //
2425 // In addition to the ForeignException scenario, we need to reset these
2426 // values incase the exception object in question is being thrown by
2427 // multiple threads in parallel and thus, could have potentially different
2428 // dynamic method array contents/size as opposed to the current state of
2429 // StackTraceInfo.
2430
2431 unsigned iStackTraceElements = (unsigned)gc.stackTrace.Size();
2432 m_dCurrentDynamicIndex = 0;
2433 for (unsigned iIndex = 0; iIndex < iStackTraceElements; iIndex++)
2434 {
2435 MethodDesc *pMethod = gc.stackTrace[iIndex].pFunc;
2436 if (pMethod)
2437 {
2438 if ((pMethod->IsLCGMethod()) || (pMethod->GetMethodTable()->Collectible()))
2439 {
2440 // Increment the number of new dynamic methods we have found
2441 m_dCurrentDynamicIndex++;
2442 }
2443 }
2444 }
2445
2446 // Total number of elements in the dynamic method array should also be
2447 // reset based upon the restored array size.
2448 m_cDynamicMethodItems = cOrigDynamic;
2449 }
2450
2451 // Make the dynamic Array field reference the original array we got from the
2452 // Exception object. If, below, we have to add new entries, we will add it to the
2453 // array if it is allocated, or else, we will allocate it before doing so.
2454 gc.dynamicMethodsArray = gc.pOrigDynamicArray;
2455
2456 // Create an object array if we have new dynamic method entries AND
2457 // if we are at the (or went past) the current size limit
2458 if (iNumDynamics > 0)
2459 {
2460 // Reallocate the array if we are at the (or went past) the current size limit
2461 unsigned cTotalDynamicMethodCount = m_dCurrentDynamicIndex;
2462
2463 S_UINT32 cNewSum = S_UINT32(cTotalDynamicMethodCount) + S_UINT32(iNumDynamics);
2464 if (cNewSum.IsOverflow())
2465 {
2466 // If the current size is already the UINT32 max size, then we
2467 // cannot go further. Overflow here implies we cannot allocate memory anymore.
2468 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - Cannot calculate resolver array size due to overflow!\n"));
2469 COMPlusThrowOM();
2470 }
2471
2472 cTotalDynamicMethodCount = cNewSum.Value();
2473
2474 if (cTotalDynamicMethodCount > m_cDynamicMethodItems)
2475 {
2476 // Double the current limit of the array.
2477 S_UINT32 cNewSize = S_UINT32(2) * S_UINT32(cTotalDynamicMethodCount);
2478 if (cNewSize.IsOverflow())
2479 {
2480 // Overflow here implies that we cannot allocate any more memory
2481 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - Cannot resize resolver array beyond max size due to overflow!\n"));
2482 COMPlusThrowOM();
2483 }
2484
2485 m_cDynamicMethodItems = cNewSize.Value();
2486 gc.dynamicMethodsArray = (PTRARRAYREF)AllocateObjectArray(m_cDynamicMethodItems,
2487 g_pObjectClass);
2488
2489 _ASSERTE(!(cOrigDynamic && !gc.pOrigDynamicArray));
2490
2491 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - resized dynamic array to size %lu\n",
2492 m_cDynamicMethodItems));
2493
2494 // Copy previous entries if there are any, and update iCurDynamic to point
2495 // to the following index.
2496 if (cOrigDynamic && (gc.pOrigDynamicArray != NULL))
2497 {
2498 memmoveGCRefs(gc.dynamicMethodsArray->GetDataPtr(),
2499 gc.pOrigDynamicArray->GetDataPtr(),
2500 cOrigDynamic * sizeof(Object *));
2501
2502 // m_dCurrentDynamicIndex is already referring to the correct index
2503 // at which the next resolver object will be saved
2504 }
2505 }
2506 else
2507 {
2508 // We are adding objects to the existing array.
2509 //
2510 // We have new dynamic method entries for which
2511 // resolver objects need to be saved. Ensure
2512 // that we have the array to store them
2513 if (gc.dynamicMethodsArray == NULL)
2514 {
2515 _ASSERTE(m_cDynamicMethodItems > 0);
2516
2517 gc.dynamicMethodsArray = (PTRARRAYREF)AllocateObjectArray(m_cDynamicMethodItems,
2518 g_pObjectClass);
2519 m_dCurrentDynamicIndex = 0;
2520 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - allocated dynamic array of size %lu\n",
2521 m_cDynamicMethodItems));
2522 }
2523 else
2524 {
2525 // The array exists for storing resolver objects.
2526 // Simply set the index at which the next resolver
2527 // will be stored in it.
2528 }
2529 }
2530 }
2531 }
2532
2533 // Update _dynamicMethods field
2534 if (iNumDynamics)
2535 {
2536 // At this point, we should be having a valid array for storage
2537 _ASSERTE(gc.dynamicMethodsArray != NULL);
2538
2539 // Assert that we are in valid range of the array in which resolver objects will be saved.
2540 // We subtract 1 below since storage will start from m_dCurrentDynamicIndex onwards and not
2541 // from (m_dCurrentDynamicIndex + 1).
2542 _ASSERTE((m_dCurrentDynamicIndex + iNumDynamics - 1) < gc.dynamicMethodsArray->GetNumComponents());
2543
2544 for (unsigned i=0; i < m_dFrameCount; i++)
2545 {
2546 MethodDesc *pMethod = m_pStackTrace[i].pFunc;
2547 _ASSERTE(pMethod);
2548
2549 if (pMethod->IsLCGMethod())
2550 {
2551 // We need to append the corresponding System.Resolver for
2552 // this DynamicMethodDesc to keep it alive.
2553 DynamicMethodDesc *pDMD = (DynamicMethodDesc *) pMethod;
2554 OBJECTREF pResolver = pDMD->GetLCGMethodResolver()->GetManagedResolver();
2555
2556 _ASSERTE(pResolver != NULL);
2557
2558 // Store Resolver information in the array
2559 gc.dynamicMethodsArray->SetAt(m_dCurrentDynamicIndex++, pResolver);
2560 }
2561 else
2562 if (pMethod->GetMethodTable()->Collectible())
2563 {
2564 OBJECTREF pLoaderAllocator = pMethod->GetMethodTable()->GetLoaderAllocator()->GetExposedObject();
2565 _ASSERTE(pLoaderAllocator != NULL);
2566 gc.dynamicMethodsArray->SetAt (m_dCurrentDynamicIndex++, pLoaderAllocator);
2567 }
2568 }
2569 }
2570
2571 ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->SetStackTrace(gc.stackTrace, gc.dynamicMethodsArray);
2572
2573 // Update _stackTraceString field.
2574 ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->SetStackTraceString(NULL);
2575 fSuccess = true;
2576
2577 GCPROTECT_END(); // gc
2578 }
2579 }
2580 EX_CATCH
2581 {
2582 }
2583 EX_END_CATCH(SwallowAllExceptions)
2584 }
2585
2586 ClearStackTrace();
2587
2588 if (!fSuccess)
2589 {
2590 EX_TRY
2591 {
2592 _ASSERTE(IsException(pMT)); // what is the pathway here?
2593 if (bReplaceStack && IsException(pMT))
2594 ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->ClearStackTraceForThrow();
2595 }
2596 EX_CATCH
2597 {
2598 // Do nothing
2599 }
2600 EX_END_CATCH(SwallowAllExceptions);
2601 }
2602}
2603
2604// Copy a context record, being careful about whether or not the target
2605// is large enough to support CONTEXT_EXTENDED_REGISTERS.
2606//
2607// NOTE: this function can ONLY be used when a filter function will return
2608// EXCEPTION_CONTINUE_EXECUTION. On AMD64, replacing the CONTEXT in any other
2609// situation may break exception unwinding.
2610//
2611// NOTE: this function MUST be used on AMD64. During exception handling,
2612// parts of the CONTEXT struct must not be modified.
2613
2614
2615// High 2 bytes are machine type. Low 2 bytes are register subset.
2616#define CONTEXT_EXTENDED_BIT (CONTEXT_EXTENDED_REGISTERS & 0xffff)
2617
2618VOID
2619ReplaceExceptionContextRecord(CONTEXT *pTarget, CONTEXT *pSource)
2620{
2621 LIMITED_METHOD_CONTRACT;
2622
2623 _ASSERTE(pTarget);
2624 _ASSERTE(pSource);
2625
2626#if defined(_TARGET_X86_)
2627 //<TODO>
2628 // @TODO IA64: CONTEXT_DEBUG_REGISTERS not defined on IA64, may need updated SDK
2629 //</TODO>
2630
2631 // Want CONTROL, INTEGER, SEGMENTS. If we have Floating Point, fine.
2632 _ASSERTE((pSource->ContextFlags & CONTEXT_FULL) == CONTEXT_FULL);
2633#endif // _TARGET_X86_
2634
2635#ifdef CONTEXT_EXTENDED_REGISTERS
2636
2637 if (pSource->ContextFlags & CONTEXT_EXTENDED_BIT)
2638 {
2639 if (pTarget->ContextFlags & CONTEXT_EXTENDED_BIT)
2640 { // Source and Target have EXTENDED bit set.
2641 *pTarget = *pSource;
2642 }
2643 else
2644 { // Source has but Target doesn't have EXTENDED bit set. (Target is shorter than Source.)
2645 // Copy non-extended part of the struct, and reset the bit on the Target, as it was.
2646 memcpy(pTarget, pSource, offsetof(CONTEXT, ExtendedRegisters));
2647 pTarget->ContextFlags &= ~CONTEXT_EXTENDED_BIT; // Target was short. Reset the extended bit.
2648 }
2649 }
2650 else
2651 { // Source does not have EXTENDED bit. Copy only non-extended part of the struct.
2652 memcpy(pTarget, pSource, offsetof(CONTEXT, ExtendedRegisters));
2653 }
2654 STRESS_LOG3(LF_SYNC, LL_INFO1000, "ReSet thread context EIP = %p ESP = %p EBP = %p\n",
2655 GetIP((CONTEXT*)pTarget), GetSP((CONTEXT*)pTarget), GetFP((CONTEXT*)pTarget));
2656
2657#else // !CONTEXT_EXTENDED_REGISTERS
2658
2659 // Everything that's left
2660 *pTarget = *pSource;
2661
2662#endif // !CONTEXT_EXTENDED_REGISTERS
2663}
2664
2665VOID FixupOnRethrow(Thread* pCurThread, EXCEPTION_POINTERS* pExceptionPointers)
2666{
2667 WRAPPER_NO_CONTRACT;
2668
2669 ThreadExceptionState* pExState = pCurThread->GetExceptionState();
2670
2671#ifdef FEATURE_INTERPRETER
2672 // Abort if we don't have any state from the original exception.
2673 if (!pExState->IsExceptionInProgress())
2674 {
2675 return;
2676 }
2677#endif // FEATURE_INTERPRETER
2678
2679 // Don't allow rethrow of a STATUS_STACK_OVERFLOW -- it's a new throw of the COM+ exception.
2680 if (pExState->GetExceptionCode() == STATUS_STACK_OVERFLOW)
2681 {
2682 return;
2683 }
2684
2685 // For COMPLUS exceptions, we don't need the original context for our rethrow.
2686 if (!(pExState->IsComPlusException()))
2687 {
2688 _ASSERTE(pExState->GetExceptionRecord());
2689
2690 // don't copy parm args as have already supplied them on the throw
2691 memcpy((void*)pExceptionPointers->ExceptionRecord,
2692 (void*)pExState->GetExceptionRecord(),
2693 offsetof(EXCEPTION_RECORD, ExceptionInformation));
2694
2695// Replacing the exception context breaks unwinding on AMD64. It also breaks exception dispatch on IA64.
2696// The info saved by pExState will be given to exception filters.
2697#ifndef WIN64EXCEPTIONS
2698 // Restore original context if available.
2699 if (pExState->GetContextRecord())
2700 {
2701 ReplaceExceptionContextRecord(pExceptionPointers->ContextRecord,
2702 pExState->GetContextRecord());
2703 }
2704#endif // !WIN64EXCEPTIONS
2705 }
2706
2707 pExState->GetFlags()->SetIsRethrown();
2708}
2709
2710struct RaiseExceptionFilterParam
2711{
2712 BOOL isRethrown;
2713};
2714
2715LONG RaiseExceptionFilter(EXCEPTION_POINTERS* ep, LPVOID pv)
2716{
2717 STATIC_CONTRACT_NOTHROW;
2718 STATIC_CONTRACT_GC_NOTRIGGER;
2719 STATIC_CONTRACT_MODE_ANY;
2720
2721 RaiseExceptionFilterParam *pParam = (RaiseExceptionFilterParam *) pv;
2722
2723 if (1 == pParam->isRethrown)
2724 {
2725 // need to reset the EH info back to the original thrown exception
2726 FixupOnRethrow(GetThread(), ep);
2727#ifdef WIN64EXCEPTIONS
2728 // only do this once
2729 pParam->isRethrown++;
2730#endif // WIN64EXCEPTIONS
2731 }
2732 else
2733 {
2734 CONSISTENCY_CHECK((2 == pParam->isRethrown) || (0 == pParam->isRethrown));
2735 }
2736
2737 return EXCEPTION_CONTINUE_SEARCH;
2738}
2739
2740//==========================================================================
2741// Throw an object.
2742//==========================================================================
2743VOID DECLSPEC_NORETURN RaiseTheException(OBJECTREF throwable, BOOL rethrow
2744#ifdef FEATURE_CORRUPTING_EXCEPTIONS
2745 , CorruptionSeverity severity
2746#endif // FEATURE_CORRUPTING_EXCEPTIONS
2747 )
2748{
2749 STATIC_CONTRACT_THROWS;
2750 STATIC_CONTRACT_GC_TRIGGERS;
2751 STATIC_CONTRACT_MODE_COOPERATIVE;
2752
2753 LOG((LF_EH, LL_INFO100, "RealCOMPlusThrow throwing %s\n",
2754 throwable->GetMethodTable()->GetDebugClassName()));
2755
2756 if (throwable == NULL)
2757 {
2758 _ASSERTE(!"RealCOMPlusThrow(OBJECTREF) called with NULL argument. Somebody forgot to post an exception!");
2759 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
2760 }
2761
2762 if (g_CLRPolicyRequested &&
2763 throwable->GetMethodTable() == g_pOutOfMemoryExceptionClass)
2764 {
2765 // We depends on UNINSTALL_UNWIND_AND_CONTINUE_HANDLER to handle out of memory escalation.
2766 // We should throw c++ exception instead.
2767 ThrowOutOfMemory();
2768 }
2769#ifdef FEATURE_STACK_PROBE
2770 else if (throwable == CLRException::GetPreallocatedStackOverflowException())
2771 {
2772 ThrowStackOverflow();
2773 }
2774#else
2775 _ASSERTE(throwable != CLRException::GetPreallocatedStackOverflowException());
2776#endif
2777
2778#ifdef FEATURE_CORRUPTING_EXCEPTIONS
2779 if (!g_pConfig->LegacyCorruptedStateExceptionsPolicy())
2780 {
2781 // This is Scenario 3 described in clrex.h around the definition of SET_CE_RETHROW_FLAG_FOR_EX_CATCH macro.
2782 //
2783 // We are here because the VM is attempting to throw a managed exception. It is posssible this exception
2784 // may not be seen by CLR's exception handler for managed code (e.g. there maybe an EX_CATCH up the stack
2785 // that will swallow or rethrow this exception). In the following scenario:
2786 //
2787 // [VM1 - RethrowCSE] -> [VM2 - RethrowCSE] -> [VM3 - RethrowCSE] -> <managed code>
2788 //
2789 // When managed code throws a CSE (e.g. TargetInvocationException flagged as CSE), [VM3] will rethrow it and we will
2790 // enter EX_CATCH in VM2 which is supposed to rethrow it as well. Two things can happen:
2791 //
2792 // 1) The implementation of EX_CATCH in VM2 throws a new managed exception *before* rethrow policy is applied and control
2793 // will reach EX_CATCH in VM1, OR
2794 //
2795 // 2) EX_CATCH in VM2 swallows the exception, comes out of the catch block and later throws a new managed exception that
2796 // will be caught by EX_CATCH in VM1.
2797 //
2798 // In either of the cases, rethrow in VM1 should be on the basis of the new managed exception's corruption severity.
2799 //
2800 // To support this scenario, we set corruption severity of the managed exception VM is throwing. If its a rethrow,
2801 // it implies we are rethrowing the last exception that was seen by CLR's managed code exception handler. In such a case,
2802 // we will copy over the corruption severity of that exception.
2803
2804 // If throwable indicates corrupted state, forcibly set the severity.
2805 if (CEHelper::IsProcessCorruptedStateException(throwable))
2806 {
2807 severity = ProcessCorrupting;
2808 }
2809
2810 // No one should have passed us an invalid severity.
2811 _ASSERTE(severity > NotSet);
2812
2813 if (severity == NotSet)
2814 {
2815 severity = NotCorrupting;
2816 }
2817
2818 // Update the corruption severity of the exception being thrown by the VM.
2819 GetThread()->GetExceptionState()->SetLastActiveExceptionCorruptionSeverity(severity);
2820
2821 // Exception's corruption severity should be reused in reraise if this exception leaks out from the VM
2822 // into managed code
2823 CEHelper::MarkLastActiveExceptionCorruptionSeverityForReraiseReuse();
2824
2825 LOG((LF_EH, LL_INFO100, "RaiseTheException - Set VM thrown managed exception severity to %d.\n", severity));
2826 }
2827
2828#endif // FEATURE_CORRUPTING_EXCEPTIONS
2829
2830 RaiseTheExceptionInternalOnly(throwable,rethrow);
2831}
2832
2833HRESULT GetHRFromThrowable(OBJECTREF throwable)
2834{
2835 STATIC_CONTRACT_THROWS;
2836 STATIC_CONTRACT_GC_TRIGGERS;
2837 STATIC_CONTRACT_MODE_ANY;
2838
2839 HRESULT hr = E_FAIL;
2840 MethodTable *pMT = throwable->GetMethodTable();
2841
2842 // Only Exception objects have a HResult field
2843 // So don't fetch the field unless we have an exception
2844
2845 _ASSERTE(IsException(pMT)); // what is the pathway here?
2846 if (IsException(pMT))
2847 {
2848 hr = ((EXCEPTIONREF)throwable)->GetHResult();
2849 }
2850
2851 return hr;
2852}
2853
2854
2855VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL rethrow, BOOL fForStackOverflow)
2856{
2857 STATIC_CONTRACT_THROWS;
2858 STATIC_CONTRACT_GC_TRIGGERS;
2859 STATIC_CONTRACT_MODE_COOPERATIVE;
2860
2861 STRESS_LOG3(LF_EH, LL_INFO100, "******* MANAGED EXCEPTION THROWN: Object thrown: %p MT %pT rethrow %d\n",
2862 OBJECTREFToObject(throwable), (throwable!=0)?throwable->GetMethodTable():0, rethrow);
2863
2864#ifdef STRESS_LOG
2865 // Any object could have been thrown, but System.Exception objects have useful information for the stress log
2866 if (!NingenEnabled() && throwable == CLRException::GetPreallocatedStackOverflowException())
2867 {
2868 // if are handling an SO, don't try to get all that other goop. It isn't there anyway,
2869 // and it could cause us to take another SO.
2870 STRESS_LOG1(LF_EH, LL_INFO100, "Exception HRESULT = 0x%x \n", COR_E_STACKOVERFLOW);
2871 }
2872 else if (throwable != 0)
2873 {
2874 _ASSERTE(IsException(throwable->GetMethodTable()));
2875
2876 int hr = ((EXCEPTIONREF)throwable)->GetHResult();
2877 STRINGREF message = ((EXCEPTIONREF)throwable)->GetMessage();
2878 OBJECTREF innerEH = ((EXCEPTIONREF)throwable)->GetInnerException();
2879
2880 STRESS_LOG4(LF_EH, LL_INFO100, "Exception HRESULT = 0x%x Message String 0x%p (db will display) InnerException %p MT %pT\n",
2881 hr, OBJECTREFToObject(message), OBJECTREFToObject(innerEH), (innerEH!=0)?innerEH->GetMethodTable():0);
2882 }
2883#endif
2884
2885 struct Param : RaiseExceptionFilterParam
2886 {
2887 OBJECTREF throwable;
2888 BOOL fForStackOverflow;
2889 ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE];
2890 Thread *pThread;
2891 ThreadExceptionState* pExState;
2892 } param;
2893 param.isRethrown = rethrow ? 1 : 0; // normalize because we use it as a count in RaiseExceptionFilter
2894 param.throwable = throwable;
2895 param.fForStackOverflow = fForStackOverflow;
2896 param.pThread = GetThread();
2897
2898 _ASSERTE(param.pThread);
2899 param.pExState = param.pThread->GetExceptionState();
2900
2901 if (param.pThread->IsRudeAbortInitiated())
2902 {
2903 // Nobody should be able to swallow rude thread abort.
2904 param.throwable = CLRException::GetPreallocatedRudeThreadAbortException();
2905 }
2906
2907#if 0
2908 // TODO: enable this after we change RealCOMPlusThrow
2909#ifdef _DEBUG
2910 // If ThreadAbort exception is thrown, the thread should be marked with AbortRequest.
2911 // If not, we may see unhandled exception.
2912 if (param.throwable->GetMethodTable() == g_pThreadAbortExceptionClass)
2913 {
2914 _ASSERTE(GetThread()->IsAbortRequested()
2915#ifdef _TARGET_X86_
2916 ||
2917 GetFirstCOMPlusSEHRecord(this) == EXCEPTION_CHAIN_END
2918#endif
2919 );
2920 }
2921#endif
2922#endif
2923
2924 // raise
2925 PAL_TRY(Param *, pParam, &param)
2926 {
2927 //_ASSERTE(! pParam->isRethrown || pParam->pExState->m_pExceptionRecord);
2928 ULONG_PTR *args = NULL;
2929 ULONG argCount = 0;
2930 ULONG flags = 0;
2931 ULONG code = 0;
2932
2933 // Always save the current object in the handle so on rethrow we can reuse it. This is important as it
2934 // contains stack trace info.
2935 //
2936 // Note: we use SafeSetLastThrownObject, which will try to set the throwable and if there are any problems,
2937 // it will set the throwable to something appropiate (like OOM exception) and return the new
2938 // exception. Thus, the user's exception object can be replaced here.
2939 pParam->throwable = NingenEnabled() ? NULL : pParam->pThread->SafeSetLastThrownObject(pParam->throwable);
2940
2941 if (!pParam->isRethrown ||
2942#ifdef FEATURE_INTERPRETER
2943 !pParam->pExState->IsExceptionInProgress() ||
2944#endif // FEATURE_INTERPRETER
2945 pParam->pExState->IsComPlusException() ||
2946 (pParam->pExState->GetExceptionCode() == STATUS_STACK_OVERFLOW))
2947 {
2948 ULONG_PTR hr = NingenEnabled() ? E_FAIL : GetHRFromThrowable(pParam->throwable);
2949
2950 args = pParam->exceptionArgs;
2951 argCount = MarkAsThrownByUs(args, hr);
2952 flags = EXCEPTION_NONCONTINUABLE;
2953 code = EXCEPTION_COMPLUS;
2954 }
2955 else
2956 {
2957 // Exception code should be consistent.
2958 _ASSERTE((DWORD)(pParam->pExState->GetExceptionRecord()->ExceptionCode) == pParam->pExState->GetExceptionCode());
2959
2960 args = pParam->pExState->GetExceptionRecord()->ExceptionInformation;
2961 argCount = pParam->pExState->GetExceptionRecord()->NumberParameters;
2962 flags = pParam->pExState->GetExceptionRecord()->ExceptionFlags;
2963 code = pParam->pExState->GetExceptionRecord()->ExceptionCode;
2964 }
2965
2966 if (pParam->pThread->IsAbortInitiated () && IsExceptionOfType(kThreadAbortException,&pParam->throwable))
2967 {
2968 pParam->pThread->ResetPreparingAbort();
2969
2970 if (pParam->pThread->GetFrame() == FRAME_TOP)
2971 {
2972 // There is no more managed code on stack.
2973 pParam->pThread->EEResetAbort(Thread::TAR_ALL);
2974 }
2975 }
2976
2977 // Can't access the exception object when are in pre-emptive, so find out before
2978 // if its an SO.
2979 BOOL fIsStackOverflow = IsExceptionOfType(kStackOverflowException, &pParam->throwable);
2980
2981 if (fIsStackOverflow || pParam->fForStackOverflow)
2982 {
2983 // Don't probe if we're already handling an SO. Just throw the exception.
2984 RaiseException(code, flags, argCount, args);
2985 }
2986
2987 // Probe for sufficient stack.
2988 PUSH_STACK_PROBE_FOR_THROW(pParam->pThread);
2989
2990#ifndef STACK_GUARDS_DEBUG
2991 // This needs to be both here and inside the handler below
2992 // enable preemptive mode before call into OS
2993 GCX_PREEMP_NO_DTOR();
2994
2995 // In non-debug, we can just raise the exception once we've probed.
2996 RaiseException(code, flags, argCount, args);
2997
2998#else
2999 // In a debug build, we need to unwind our probe structure off the stack.
3000 BaseStackGuard *pThrowGuard = NULL;
3001 // Stach away the address of the guard we just pushed above in PUSH_STACK_PROBE_FOR_THROW
3002 SAVE_ADDRESS_OF_STACK_PROBE_FOR_THROW(pThrowGuard);
3003
3004 // Add the stack guard reference to the structure below so that it can be accessed within
3005 // PAL_TRY as well
3006 struct ParamInner
3007 {
3008 ULONG code;
3009 ULONG flags;
3010 ULONG argCount;
3011 ULONG_PTR *args;
3012 BaseStackGuard *pGuard;
3013 } param;
3014 param.code = code;
3015 param.flags = flags;
3016 param.argCount = argCount;
3017 param.args = args;
3018 param.pGuard = pThrowGuard;
3019
3020 PAL_TRY(ParamInner *, pParam, &param)
3021 {
3022 // enable preemptive mode before call into OS
3023 GCX_PREEMP_NO_DTOR();
3024
3025 RaiseException(pParam->code, pParam->flags, pParam->argCount, pParam->args);
3026
3027 // We never return from RaiseException, so shouldn't have to call SetNoException.
3028 // However, in the debugger we can, and if we don't call SetNoException we get
3029 // a short-circuit return assert.
3030 RESET_EXCEPTION_FROM_STACK_PROBE_FOR_THROW(pParam->pGuard);
3031 }
3032 PAL_FINALLY
3033 {
3034 // pop the guard that we pushed above in PUSH_STACK_PROBE_FOR_THROW
3035 POP_STACK_PROBE_FOR_THROW(pThrowGuard);
3036 }
3037 PAL_ENDTRY
3038#endif
3039 }
3040 PAL_EXCEPT_FILTER (RaiseExceptionFilter)
3041 {
3042 }
3043 PAL_ENDTRY
3044 _ASSERTE(!"Cannot continue after COM+ exception"); // Debugger can bring you here.
3045 // For example,
3046 // Debugger breaks in due to second chance exception (unhandled)
3047 // User hits 'g'
3048 // Then debugger can bring us here.
3049 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
3050}
3051
3052
3053// INSTALL_COMPLUS_EXCEPTION_HANDLER has a filter, so must put the call in a separate fcn
3054static VOID DECLSPEC_NORETURN RealCOMPlusThrowWorker(OBJECTREF throwable, BOOL rethrow
3055#ifdef FEATURE_CORRUPTING_EXCEPTIONS
3056 , CorruptionSeverity severity
3057#endif // FEATURE_CORRUPTING_EXCEPTIONS
3058) {
3059 STATIC_CONTRACT_THROWS;
3060 STATIC_CONTRACT_GC_TRIGGERS;
3061 STATIC_CONTRACT_MODE_ANY;
3062
3063 // RaiseTheException will throw C++ OOM and SO, so that our escalation policy can kick in.
3064 // Unfortunately, COMPlusFrameHandler installed here, will try to create managed exception object.
3065 // We may hit a recursion.
3066
3067 if (g_CLRPolicyRequested &&
3068 throwable->GetMethodTable() == g_pOutOfMemoryExceptionClass)
3069 {
3070 // We depends on UNINSTALL_UNWIND_AND_CONTINUE_HANDLER to handle out of memory escalation.
3071 // We should throw c++ exception instead.
3072 ThrowOutOfMemory();
3073 }
3074#ifdef FEATURE_STACK_PROBE
3075 else if (throwable == CLRException::GetPreallocatedStackOverflowException())
3076 {
3077 ThrowStackOverflow();
3078 }
3079#else
3080 _ASSERTE(throwable != CLRException::GetPreallocatedStackOverflowException());
3081#endif
3082
3083 // TODO: Do we need to install COMPlusFrameHandler here?
3084 INSTALL_COMPLUS_EXCEPTION_HANDLER();
3085 RaiseTheException(throwable, rethrow
3086#ifdef FEATURE_CORRUPTING_EXCEPTIONS
3087 , severity
3088#endif // FEATURE_CORRUPTING_EXCEPTIONS
3089 );
3090 UNINSTALL_COMPLUS_EXCEPTION_HANDLER();
3091}
3092
3093
3094VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable, BOOL rethrow
3095#ifdef FEATURE_CORRUPTING_EXCEPTIONS
3096 , CorruptionSeverity severity
3097#endif // FEATURE_CORRUPTING_EXCEPTIONS
3098) {
3099 STATIC_CONTRACT_THROWS;
3100 STATIC_CONTRACT_GC_TRIGGERS;
3101 STATIC_CONTRACT_MODE_ANY;
3102 GCPROTECT_BEGIN(throwable);
3103
3104 _ASSERTE(IsException(throwable->GetMethodTable()));
3105
3106 // This may look a bit odd, but there is an explaination. The rethrow boolean
3107 // means that an actual RaiseException(EXCEPTION_COMPLUS,...) is being re-thrown,
3108 // and that the exception context saved on the Thread object should replace
3109 // the exception context from the upcoming RaiseException(). There is logic
3110 // in the stack trace code to preserve MOST of the stack trace, but to drop the
3111 // last element of the stack trace (has to do with having the address of the rethrow
3112 // instead of the address of the original call in the stack trace. That is
3113 // controversial itself, but we won't get into that here.)
3114 // However, if this is not re-raising that original exception, but rather a new
3115 // os exception for what may be an existing exception object, it is generally
3116 // a good thing to preserve the stack trace.
3117 if (!rethrow)
3118 {
3119 ExceptionPreserveStackTrace(throwable);
3120 }
3121
3122 RealCOMPlusThrowWorker(throwable, rethrow
3123#ifdef FEATURE_CORRUPTING_EXCEPTIONS
3124 , severity
3125#endif // FEATURE_CORRUPTING_EXCEPTIONS
3126 );
3127
3128 GCPROTECT_END();
3129}
3130
3131VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable
3132#ifdef FEATURE_CORRUPTING_EXCEPTIONS
3133 , CorruptionSeverity severity
3134#endif // FEATURE_CORRUPTING_EXCEPTIONS
3135 )
3136{
3137 CONTRACTL
3138 {
3139 THROWS;
3140 GC_TRIGGERS;
3141 MODE_COOPERATIVE;
3142 }
3143 CONTRACTL_END;
3144
3145 RealCOMPlusThrow(throwable, FALSE
3146#ifdef FEATURE_CORRUPTING_EXCEPTIONS
3147 , severity
3148#endif // FEATURE_CORRUPTING_EXCEPTIONS
3149 );
3150}
3151
3152// this function finds the managed callback to get a resource
3153// string from the then current local domain and calls it
3154// this could be a lot of work
3155STRINGREF GetResourceStringFromManaged(STRINGREF key)
3156{
3157 CONTRACTL
3158 {
3159 THROWS;
3160 GC_TRIGGERS;
3161 MODE_COOPERATIVE;
3162 PRECONDITION(key != NULL);
3163 }
3164 CONTRACTL_END;
3165
3166 struct xx {
3167 STRINGREF key;
3168 STRINGREF ret;
3169 } gc;
3170
3171 gc.key = key;
3172 gc.ret = NULL;
3173
3174 // The standard probe isn't good enough here. It's possible that we only have ~14 pages of stack
3175 // left. By the time we transition to the default domain and start fetching this resource string,
3176 // another 12 page probe could fail.
3177 // This failing probe would cause us to unload the default appdomain, which would cause us
3178 // to take down the process.
3179
3180 // Instead, let's probe for a lots more stack to make sure that doesn' happen.
3181
3182 // We need to have enough stack to survive 2 more probes... the original entrypoint back
3183 // into mscorwks after we go into managed code, and a "large" probe that protects the GC
3184
3185 INTERIOR_STACK_PROBE_FOR(GetThread(), DEFAULT_ENTRY_PROBE_AMOUNT * 2);
3186 GCPROTECT_BEGIN(gc);
3187
3188 MethodDescCallSite getResourceStringLocal(METHOD__ENVIRONMENT__GET_RESOURCE_STRING_LOCAL);
3189
3190 // Call Environment::GetResourceStringLocal(String name). Returns String value (or maybe null)
3191
3192 ENTER_DOMAIN_PTR(SystemDomain::System()->DefaultDomain(),ADV_DEFAULTAD);
3193
3194 // Don't need to GCPROTECT pArgs, since it's not used after the function call.
3195
3196 ARG_SLOT pArgs[1] = { ObjToArgSlot(gc.key) };
3197 gc.ret = getResourceStringLocal.Call_RetSTRINGREF(pArgs);
3198
3199 END_DOMAIN_TRANSITION;
3200
3201 GCPROTECT_END();
3202
3203 END_INTERIOR_STACK_PROBE;
3204
3205
3206 return gc.ret;
3207}
3208
3209// This function does poentially a LOT of work (loading possibly 50 classes).
3210// The return value is an un-GC-protected string ref, or possibly NULL.
3211void ResMgrGetString(LPCWSTR wszResourceName, STRINGREF * ppMessage)
3212{
3213 CONTRACTL
3214 {
3215 THROWS;
3216 GC_TRIGGERS;
3217 MODE_COOPERATIVE;
3218 }
3219 CONTRACTL_END;
3220
3221 _ASSERTE(ppMessage != NULL);
3222
3223 if (wszResourceName == NULL || *wszResourceName == W('\0'))
3224 {
3225 ppMessage = NULL;
3226 return;
3227 }
3228
3229 // this function never looks at name again after
3230 // calling the helper so no need to GCPROTECT it
3231 STRINGREF name = StringObject::NewString(wszResourceName);
3232
3233 if (wszResourceName != NULL)
3234 {
3235 STRINGREF value = GetResourceStringFromManaged(name);
3236
3237 _ASSERTE(value!=NULL || !"Resource string lookup failed - possible misspelling or .resources missing or out of date?");
3238 *ppMessage = value;
3239 }
3240}
3241
3242// GetResourceFromDefault
3243// transition to the default domain and get a resource there
3244FCIMPL1(Object*, GetResourceFromDefault, StringObject* keyUnsafe)
3245{
3246 FCALL_CONTRACT;
3247
3248 STRINGREF ret = NULL;
3249 STRINGREF key = (STRINGREF)keyUnsafe;
3250
3251 HELPER_METHOD_FRAME_BEGIN_RET_2(ret, key);
3252
3253 ret = GetResourceStringFromManaged(key);
3254
3255 HELPER_METHOD_FRAME_END();
3256
3257 return OBJECTREFToObject(ret);
3258}
3259FCIMPLEND
3260
3261void FreeExceptionData(ExceptionData *pedata)
3262{
3263 CONTRACTL
3264 {
3265 NOTHROW;
3266 GC_TRIGGERS;
3267 SO_TOLERANT;
3268 }
3269 CONTRACTL_END;
3270
3271 _ASSERTE(pedata != NULL);
3272
3273 // <TODO>@NICE: At one point, we had the comment:
3274 // (DM) Remove this when shutdown works better.</TODO>
3275 // This test may no longer be necessary. Remove at own peril.
3276 Thread *pThread = GetThread();
3277 if (!pThread)
3278 return;
3279
3280 if (pedata->bstrSource)
3281 SysFreeString(pedata->bstrSource);
3282 if (pedata->bstrDescription)
3283 SysFreeString(pedata->bstrDescription);
3284 if (pedata->bstrHelpFile)
3285 SysFreeString(pedata->bstrHelpFile);
3286#ifdef FEATURE_COMINTEROP
3287 if (pedata->bstrRestrictedError)
3288 SysFreeString(pedata->bstrRestrictedError);
3289 if (pedata->bstrReference)
3290 SysFreeString(pedata->bstrReference);
3291 if (pedata->bstrCapabilitySid)
3292 SysFreeString(pedata->bstrCapabilitySid);
3293 if (pedata->pRestrictedErrorInfo)
3294 {
3295 ULONG cbRef = SafeRelease(pedata->pRestrictedErrorInfo);
3296 LogInteropRelease(pedata->pRestrictedErrorInfo, cbRef, "IRestrictedErrorInfo");
3297 }
3298#endif // FEATURE_COMINTEROP
3299}
3300
3301void GetExceptionForHR(HRESULT hr, IErrorInfo* pErrInfo, bool fUseCOMException, OBJECTREF* pProtectedThrowable, IRestrictedErrorInfo *pResErrorInfo, BOOL bHasLangRestrictedErrInfo)
3302{
3303 CONTRACTL
3304 {
3305 THROWS;
3306 GC_TRIGGERS;
3307 MODE_COOPERATIVE;
3308 PRECONDITION(IsProtectedByGCFrame(pProtectedThrowable));
3309 }
3310 CONTRACTL_END;
3311
3312 // Initialize
3313 *pProtectedThrowable = NULL;
3314
3315#if defined(FEATURE_COMINTEROP) && !defined(CROSSGEN_COMPILE)
3316 if (pErrInfo != NULL)
3317 {
3318 // If this represents a managed object...
3319 // ...then get the managed exception object and also check if it is a __ComObject...
3320 if (IsManagedObject(pErrInfo))
3321 {
3322 GetObjectRefFromComIP(pProtectedThrowable, pErrInfo);
3323 if ((*pProtectedThrowable) != NULL)
3324 {
3325 // ...if it is, then we'll just default to an exception based on the IErrorInfo.
3326 if ((*pProtectedThrowable)->GetMethodTable()->IsComObjectType())
3327 {
3328 (*pProtectedThrowable) = NULL;
3329 }
3330 else
3331 {
3332 // We have created an exception. Release the IErrorInfo
3333 ULONG cbRef = SafeRelease(pErrInfo);
3334 LogInteropRelease(pErrInfo, cbRef, "IErrorInfo release");
3335 return;
3336 }
3337 }
3338 }
3339
3340 // If we got here and we don't have an exception object, we have a native IErrorInfo or
3341 // a managed __ComObject based IErrorInfo, so we'll just create an exception based on
3342 // the native IErrorInfo.
3343 if ((*pProtectedThrowable) == NULL)
3344 {
3345 EECOMException ex(hr, pErrInfo, fUseCOMException, pResErrorInfo, bHasLangRestrictedErrInfo COMMA_INDEBUG(FALSE));
3346 (*pProtectedThrowable) = ex.GetThrowable();
3347 }
3348 }
3349#endif // defined(FEATURE_COMINTEROP) && !defined(CROSSGEN_COMPILE)
3350
3351 // If we made it here and we don't have an exception object, we didn't have a valid IErrorInfo
3352 // so we'll create an exception based solely on the hresult.
3353 if ((*pProtectedThrowable) == NULL)
3354 {
3355 EEMessageException ex(hr, fUseCOMException);
3356 (*pProtectedThrowable) = ex.GetThrowable();
3357 }
3358}
3359
3360void GetExceptionForHR(HRESULT hr, IErrorInfo* pErrInfo, OBJECTREF* pProtectedThrowable)
3361{
3362 WRAPPER_NO_CONTRACT;
3363
3364 GetExceptionForHR(hr, pErrInfo, true, pProtectedThrowable);
3365}
3366
3367void GetExceptionForHR(HRESULT hr, OBJECTREF* pProtectedThrowable)
3368{
3369 CONTRACTL
3370 {
3371 THROWS;
3372 GC_TRIGGERS; // because of IErrorInfo
3373 MODE_ANY;
3374 }
3375 CONTRACTL_END;
3376
3377 // Get an IErrorInfo if one is available.
3378 IErrorInfo *pErrInfo = NULL;
3379#ifndef CROSSGEN_COMPILE
3380 if (SafeGetErrorInfo(&pErrInfo) != S_OK)
3381 pErrInfo = NULL;
3382#endif
3383
3384 GetExceptionForHR(hr, pErrInfo, true, pProtectedThrowable);
3385}
3386
3387
3388//
3389// Maps a Win32 fault to a COM+ Exception enumeration code
3390//
3391DWORD MapWin32FaultToCOMPlusException(EXCEPTION_RECORD *pExceptionRecord)
3392{
3393 WRAPPER_NO_CONTRACT;
3394
3395 switch (pExceptionRecord->ExceptionCode)
3396 {
3397 case STATUS_FLOAT_INEXACT_RESULT:
3398 case STATUS_FLOAT_INVALID_OPERATION:
3399 case STATUS_FLOAT_STACK_CHECK:
3400 case STATUS_FLOAT_UNDERFLOW:
3401 return (DWORD) kArithmeticException;
3402 case STATUS_FLOAT_OVERFLOW:
3403 case STATUS_INTEGER_OVERFLOW:
3404 return (DWORD) kOverflowException;
3405
3406 case STATUS_FLOAT_DIVIDE_BY_ZERO:
3407 case STATUS_INTEGER_DIVIDE_BY_ZERO:
3408 return (DWORD) kDivideByZeroException;
3409
3410 case STATUS_FLOAT_DENORMAL_OPERAND:
3411 return (DWORD) kFormatException;
3412
3413 case STATUS_ACCESS_VIOLATION:
3414 {
3415 // We have a config key, InsecurelyTreatAVsAsNullReference, that ensures we always translate to
3416 // NullReferenceException instead of doing the new AV translation logic.
3417 if ((g_pConfig != NULL) && !g_pConfig->LegacyNullReferenceExceptionPolicy())
3418 {
3419#if defined(FEATURE_HIJACK) && !defined(PLATFORM_UNIX)
3420 // If we got the exception on a redirect function it means the original exception happened in managed code:
3421 if (Thread::IsAddrOfRedirectFunc(pExceptionRecord->ExceptionAddress))
3422 return (DWORD) kNullReferenceException;
3423
3424 if (pExceptionRecord->ExceptionAddress == (LPVOID)GetEEFuncEntryPoint(THROW_CONTROL_FOR_THREAD_FUNCTION))
3425 {
3426 return (DWORD) kNullReferenceException;
3427 }
3428#endif // FEATURE_HIJACK && !PLATFORM_UNIX
3429
3430 // If the IP of the AV is not in managed code, then its an AccessViolationException.
3431 if (!ExecutionManager::IsManagedCode((PCODE)pExceptionRecord->ExceptionAddress))
3432 {
3433 return (DWORD) kAccessViolationException;
3434 }
3435
3436 // If the address accessed is above 64k (Windows) or page size (PAL), then its an AccessViolationException.
3437 // Note: Win9x is a little different... it never gives you the proper address of the read or write that caused
3438 // the fault. It always gives -1, so we can't use it as part of the decision... just give
3439 // NullReferenceException instead.
3440 if (pExceptionRecord->ExceptionInformation[1] >= NULL_AREA_SIZE)
3441 {
3442 return (DWORD) kAccessViolationException;
3443 }
3444 }
3445
3446 return (DWORD) kNullReferenceException;
3447 }
3448
3449 case STATUS_ARRAY_BOUNDS_EXCEEDED:
3450 return (DWORD) kIndexOutOfRangeException;
3451
3452 case STATUS_NO_MEMORY:
3453 return (DWORD) kOutOfMemoryException;
3454
3455 case STATUS_STACK_OVERFLOW:
3456 return (DWORD) kStackOverflowException;
3457
3458#ifdef ALIGN_ACCESS
3459 case STATUS_DATATYPE_MISALIGNMENT:
3460 return (DWORD) kDataMisalignedException;
3461#endif // ALIGN_ACCESS
3462
3463 default:
3464 return kSEHException;
3465 }
3466}
3467
3468#ifdef _DEBUG
3469#ifndef WIN64EXCEPTIONS
3470// check if anyone has written to the stack above the handler which would wipe out the EH registration
3471void CheckStackBarrier(EXCEPTION_REGISTRATION_RECORD *exRecord)
3472{
3473 LIMITED_METHOD_CONTRACT;
3474
3475 if (exRecord->Handler != (PEXCEPTION_ROUTINE)COMPlusFrameHandler)
3476 return;
3477
3478 DWORD *stackOverwriteBarrier = (DWORD *)((BYTE*)exRecord - offsetof(FrameHandlerExRecordWithBarrier, m_ExRecord));
3479 for (int i =0; i < STACK_OVERWRITE_BARRIER_SIZE; i++) {
3480 if (*(stackOverwriteBarrier+i) != STACK_OVERWRITE_BARRIER_VALUE) {
3481 // to debug this error, you must determine who erroneously overwrote the stack
3482 _ASSERTE(!"Fatal error: the stack has been overwritten");
3483 }
3484 }
3485}
3486#endif // WIN64EXCEPTIONS
3487#endif // _DEBUG
3488
3489//-------------------------------------------------------------------------
3490// A marker for JIT -> EE transition when we know we're in preemptive
3491// gc mode. As we leave the EE, we fix a few things:
3492//
3493// - the gc state must be set back to preemptive-operative
3494// - the COM+ frame chain must be rewound to what it was on entry
3495// - ExInfo()->m_pSearchBoundary must be adjusted
3496// if we popped the frame that is identified as begnning the next
3497// crawl.
3498//-------------------------------------------------------------------------
3499
3500void COMPlusCooperativeTransitionHandler(Frame* pFrame)
3501{
3502 CONTRACTL
3503 {
3504 NOTHROW;
3505 GC_TRIGGERS;
3506 MODE_ANY;
3507 }
3508 CONTRACTL_END;
3509
3510 LOG((LF_EH, LL_INFO1000, "COMPlusCooprativeTransitionHandler unwinding\n"));
3511
3512 {
3513 Thread* pThread = GetThread();
3514
3515 // Restore us to cooperative gc mode.
3516 GCX_COOP();
3517
3518 // Pop the frame chain.
3519 UnwindFrameChain(pThread, pFrame);
3520 CONSISTENCY_CHECK(pFrame == pThread->GetFrame());
3521
3522#ifndef WIN64EXCEPTIONS
3523 // An exception is being thrown through here. The COM+ exception
3524 // info keeps a pointer to a frame that is used by the next
3525 // COM+ Exception Handler as the starting point of its crawl.
3526 // We may have popped this marker -- in which case, we need to
3527 // update it to the current frame.
3528 //
3529 ThreadExceptionState* pExState = pThread->GetExceptionState();
3530 Frame* pSearchBoundary = NULL;
3531
3532 if (pThread->IsExceptionInProgress())
3533 {
3534 pSearchBoundary = pExState->m_currentExInfo.m_pSearchBoundary;
3535 }
3536
3537 if (pSearchBoundary && pSearchBoundary < pFrame)
3538 {
3539 LOG((LF_EH, LL_INFO1000, "\tpExInfo->m_pSearchBoundary = %08x\n", (void*)pFrame));
3540 pExState->m_currentExInfo.m_pSearchBoundary = pFrame;
3541 }
3542#endif // WIN64EXCEPTIONS
3543}
3544
3545 // Restore us to preemptive gc mode.
3546 GCX_PREEMP_NO_DTOR();
3547}
3548
3549
3550
3551void StackTraceInfo::Init()
3552{
3553 CONTRACTL
3554 {
3555 NOTHROW;
3556 GC_NOTRIGGER;
3557 MODE_ANY;
3558 FORBID_FAULT;
3559 SO_TOLERANT;
3560 }
3561 CONTRACTL_END;
3562
3563 LOG((LF_EH, LL_INFO10000, "StackTraceInfo::Init (%p)\n", this));
3564
3565 m_pStackTrace = NULL;
3566 m_cStackTrace = 0;
3567 m_dFrameCount = 0;
3568 m_cDynamicMethodItems = 0;
3569 m_dCurrentDynamicIndex = 0;
3570}
3571
3572void StackTraceInfo::FreeStackTrace()
3573{
3574 CONTRACTL
3575 {
3576 NOTHROW;
3577 GC_NOTRIGGER;
3578 MODE_ANY;
3579 FORBID_FAULT;
3580 SO_TOLERANT;
3581 }
3582 CONTRACTL_END;
3583
3584 if (m_pStackTrace)
3585 {
3586 delete [] m_pStackTrace;
3587 m_pStackTrace = NULL;
3588 m_cStackTrace = 0;
3589 m_dFrameCount = 0;
3590 m_cDynamicMethodItems = 0;
3591 m_dCurrentDynamicIndex = 0;
3592 }
3593}
3594
3595BOOL StackTraceInfo::IsEmpty()
3596{
3597 LIMITED_METHOD_CONTRACT;
3598
3599 return 0 == m_dFrameCount;
3600}
3601
3602void StackTraceInfo::ClearStackTrace()
3603{
3604 LIMITED_METHOD_CONTRACT;
3605
3606 LOG((LF_EH, LL_INFO1000, "StackTraceInfo::ClearStackTrace (%p)\n", this));
3607 m_dFrameCount = 0;
3608}
3609
3610// allocate stack trace info. As each function is found in the stack crawl, it will be added
3611// to this list. If the list is too small, it is reallocated.
3612void StackTraceInfo::AllocateStackTrace()
3613{
3614 STATIC_CONTRACT_NOTHROW;
3615 STATIC_CONTRACT_GC_NOTRIGGER;
3616 STATIC_CONTRACT_MODE_ANY;
3617 STATIC_CONTRACT_FORBID_FAULT;
3618
3619 LOG((LF_EH, LL_INFO1000, "StackTraceInfo::AllocateStackTrace (%p)\n", this));
3620
3621 if (!m_pStackTrace)
3622 {
3623#ifdef _DEBUG
3624 unsigned int allocSize = 2; // make small to exercise realloc
3625#else
3626 unsigned int allocSize = 30;
3627#endif
3628
3629 SCAN_IGNORE_FAULT; // A fault of new is okay here. The rest of the system is cool if we don't have enough
3630 // memory to remember the stack as we run our first pass.
3631 m_pStackTrace = new (nothrow) StackTraceElement[allocSize];
3632
3633 if (m_pStackTrace != NULL)
3634 {
3635 // Remember how much we allocated.
3636 m_cStackTrace = allocSize;
3637 m_cDynamicMethodItems = allocSize;
3638 }
3639 else
3640 {
3641 m_cStackTrace = 0;
3642 m_cDynamicMethodItems = 0;
3643 }
3644 }
3645}
3646
3647//
3648// Returns true if it appended the element, false otherwise.
3649//
3650BOOL StackTraceInfo::AppendElement(BOOL bAllowAllocMem, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf)
3651{
3652 CONTRACTL
3653 {
3654 GC_TRIGGERS;
3655 NOTHROW;
3656 }
3657 CONTRACTL_END
3658
3659 LOG((LF_EH, LL_INFO10000, "StackTraceInfo::AppendElement (%p), IP = %p, SP = %p, %s::%s\n", this, currentIP, currentSP, pFunc ? pFunc->m_pszDebugClassName : "", pFunc ? pFunc->m_pszDebugMethodName : "" ));
3660 BOOL bRetVal = FALSE;
3661
3662 if (pFunc != NULL && pFunc->IsILStub())
3663 return FALSE;
3664
3665 // Save this function in the stack trace array, which we only build on the first pass. We'll try to expand the
3666 // stack trace array if we don't have enough room. Note that we only try to expand if we're allowed to allocate
3667 // memory (bAllowAllocMem).
3668 if (bAllowAllocMem && (m_dFrameCount >= m_cStackTrace))
3669 {
3670 StackTraceElement* pTempElement = new (nothrow) StackTraceElement[m_cStackTrace*2];
3671
3672 if (pTempElement != NULL)
3673 {
3674 memcpy(pTempElement, m_pStackTrace, m_cStackTrace * sizeof(StackTraceElement));
3675 delete [] m_pStackTrace;
3676 m_pStackTrace = pTempElement;
3677 m_cStackTrace *= 2;
3678 }
3679 }
3680
3681 // Add the function to the stack trace array if there's room.
3682 if (m_dFrameCount < m_cStackTrace)
3683 {
3684 StackTraceElement* pStackTraceElem;
3685
3686 // If we get in here, we'd better have a stack trace array.
3687 CONSISTENCY_CHECK(m_pStackTrace != NULL);
3688
3689 pStackTraceElem = &(m_pStackTrace[m_dFrameCount]);
3690
3691 pStackTraceElem->pFunc = pFunc;
3692
3693 pStackTraceElem->ip = currentIP;
3694 pStackTraceElem->sp = currentSP;
3695
3696 // When we are building stack trace as we encounter managed frames during exception dispatch,
3697 // then none of those frames represent a stack trace from a foreign exception (as they represent
3698 // the current exception). Hence, set the corresponding flag to FALSE.
3699 pStackTraceElem->fIsLastFrameFromForeignStackTrace = FALSE;
3700
3701 // This is a workaround to fix the generation of stack traces from exception objects so that
3702 // they point to the line that actually generated the exception instead of the line
3703 // following.
3704 if (!(pCf->HasFaulted() || pCf->IsIPadjusted()) && pStackTraceElem->ip != 0)
3705 {
3706 pStackTraceElem->ip -= 1;
3707 }
3708
3709 ++m_dFrameCount;
3710 bRetVal = TRUE;
3711 COUNTER_ONLY(GetPerfCounters().m_Excep.cThrowToCatchStackDepth++);
3712 }
3713
3714#ifndef FEATURE_PAL // Watson is supported on Windows only
3715 Thread *pThread = GetThread();
3716 _ASSERTE(pThread);
3717
3718 if (pThread && (currentIP != 0))
3719 {
3720 // Setup the watson bucketing details for the initial throw
3721 // callback only if we dont already have them.
3722 ThreadExceptionState *pExState = pThread->GetExceptionState();
3723 if (!pExState->GetFlags()->GotWatsonBucketDetails())
3724 {
3725 // Adjust the IP if necessary.
3726 UINT_PTR adjustedIp = currentIP;
3727 // This is a workaround copied from above.
3728 if (!(pCf->HasFaulted() || pCf->IsIPadjusted()) && adjustedIp != 0)
3729 {
3730 adjustedIp -= 1;
3731 }
3732
3733 // Setup the bucketing details for the initial throw
3734 SetupInitialThrowBucketDetails(adjustedIp);
3735 }
3736 }
3737#endif // !FEATURE_PAL
3738
3739 return bRetVal;
3740}
3741
3742void StackTraceInfo::GetLeafFrameInfo(StackTraceElement* pStackTraceElement)
3743{
3744 LIMITED_METHOD_CONTRACT;
3745
3746 if (NULL == m_pStackTrace)
3747 {
3748 return;
3749 }
3750 _ASSERTE(NULL != pStackTraceElement);
3751
3752 *pStackTraceElement = m_pStackTrace[0];
3753}
3754
3755
3756void UnwindFrameChain(Thread* pThread, LPVOID pvLimitSP)
3757{
3758 CONTRACTL
3759 {
3760 NOTHROW;
3761 DISABLED(GC_TRIGGERS); // some Frames' ExceptionUnwind methods trigger :(
3762 MODE_ANY;
3763 SO_TOLERANT;
3764 }
3765 CONTRACTL_END;
3766
3767 // @todo - Remove this and add a hard SO probe as can't throw from here.
3768 CONTRACT_VIOLATION(SOToleranceViolation);
3769
3770 Frame* pFrame = pThread->m_pFrame;
3771 if (pFrame < pvLimitSP)
3772 {
3773 GCX_COOP_THREAD_EXISTS(pThread);
3774
3775 //
3776 // call ExceptionUnwind with the Frame chain intact
3777 //
3778 pFrame = pThread->NotifyFrameChainOfExceptionUnwind(pFrame, pvLimitSP);
3779
3780 //
3781 // now pop the frames off by trimming the Frame chain
3782 //
3783 pThread->SetFrame(pFrame);
3784 }
3785}
3786
3787BOOL IsExceptionOfType(RuntimeExceptionKind reKind, Exception *pException)
3788{
3789 STATIC_CONTRACT_NOTHROW;
3790 STATIC_CONTRACT_GC_TRIGGERS;
3791 STATIC_CONTRACT_MODE_ANY;
3792 STATIC_CONTRACT_FORBID_FAULT;
3793
3794 if (pException->IsType(reKind))
3795 return TRUE;
3796
3797 if (pException->IsType(CLRException::GetType()))
3798 {
3799 // Since we're going to be holding onto the Throwable object we
3800 // need to be in COOPERATIVE.
3801 GCX_COOP();
3802
3803 OBJECTREF Throwable=((CLRException*)pException)->GetThrowable();
3804
3805 GCX_FORBID();
3806 if (IsExceptionOfType(reKind, &Throwable))
3807 return TRUE;
3808 }
3809 return FALSE;
3810}
3811
3812BOOL IsExceptionOfType(RuntimeExceptionKind reKind, OBJECTREF *pThrowable)
3813{
3814 STATIC_CONTRACT_NOTHROW;
3815 STATIC_CONTRACT_GC_NOTRIGGER;
3816 STATIC_CONTRACT_MODE_COOPERATIVE;
3817 STATIC_CONTRACT_FORBID_FAULT;
3818
3819 _ASSERTE(pThrowable != NULL);
3820
3821 if (*pThrowable == NULL)
3822 return FALSE;
3823
3824 MethodTable *pThrowableMT = (*pThrowable)->GetMethodTable();
3825
3826 // IsExceptionOfType is supported for mscorlib exception types only
3827 _ASSERTE(reKind <= kLastExceptionInMscorlib);
3828 return MscorlibBinder::IsException(pThrowableMT, reKind);
3829}
3830
3831BOOL IsAsyncThreadException(OBJECTREF *pThrowable) {
3832 STATIC_CONTRACT_NOTHROW;
3833 STATIC_CONTRACT_GC_NOTRIGGER;
3834 STATIC_CONTRACT_MODE_COOPERATIVE;
3835 STATIC_CONTRACT_FORBID_FAULT;
3836
3837 if ( (GetThread() && GetThread()->IsRudeAbort() && GetThread()->IsRudeAbortInitiated())
3838 ||IsExceptionOfType(kThreadAbortException, pThrowable)
3839 ||IsExceptionOfType(kThreadInterruptedException, pThrowable)) {
3840 return TRUE;
3841 } else {
3842 return FALSE;
3843 }
3844}
3845
3846BOOL IsUncatchable(OBJECTREF *pThrowable)
3847{
3848 CONTRACTL {
3849 SO_TOLERANT;
3850 NOTHROW;
3851 GC_NOTRIGGER;
3852 MODE_COOPERATIVE;
3853 FORBID_FAULT;
3854 } CONTRACTL_END;
3855
3856 _ASSERTE(pThrowable != NULL);
3857
3858 Thread *pThread = GetThread();
3859
3860 if (pThread)
3861 {
3862 if (pThread->IsAbortInitiated())
3863 return TRUE;
3864
3865 if (OBJECTREFToObject(*pThrowable)->GetMethodTable() == g_pExecutionEngineExceptionClass)
3866 return TRUE;
3867
3868#ifdef FEATURE_CORRUPTING_EXCEPTIONS
3869 // Corrupting exceptions are also uncatchable
3870 if (CEHelper::IsProcessCorruptedStateException(*pThrowable))
3871 {
3872 return TRUE;
3873 }
3874#endif //FEATURE_CORRUPTING_EXCEPTIONS
3875 }
3876
3877 return FALSE;
3878}
3879
3880BOOL IsStackOverflowException(Thread* pThread, EXCEPTION_RECORD* pExceptionRecord)
3881{
3882 if (IsSOExceptionCode(pExceptionRecord->ExceptionCode))
3883 {
3884 return true;
3885 }
3886
3887 if (IsComPlusException(pExceptionRecord) &&
3888 pThread->IsLastThrownObjectStackOverflowException())
3889 {
3890 return true;
3891 }
3892
3893 return false;
3894}
3895
3896
3897#ifdef _DEBUG
3898BOOL IsValidClause(EE_ILEXCEPTION_CLAUSE *EHClause)
3899{
3900 LIMITED_METHOD_CONTRACT;
3901
3902#if 0
3903 DWORD valid = COR_ILEXCEPTION_CLAUSE_FILTER | COR_ILEXCEPTION_CLAUSE_FINALLY |
3904 COR_ILEXCEPTION_CLAUSE_FAULT | COR_ILEXCEPTION_CLAUSE_CACHED_CLASS;
3905
3906 // <TODO>@NICE: enable this when VC stops generatng a bogus 0x8000.</TODO>
3907 if (EHClause->Flags & ~valid)
3908 return FALSE;
3909#endif
3910 if (EHClause->TryStartPC > EHClause->TryEndPC)
3911 return FALSE;
3912 return TRUE;
3913}
3914#endif
3915
3916
3917#ifdef DEBUGGING_SUPPORTED
3918LONG NotifyDebuggerLastChance(Thread *pThread,
3919 EXCEPTION_POINTERS *pExceptionInfo,
3920 BOOL jitAttachRequested)
3921{
3922 STATIC_CONTRACT_NOTHROW;
3923 STATIC_CONTRACT_GC_TRIGGERS;
3924 STATIC_CONTRACT_MODE_ANY;
3925
3926 LONG retval = EXCEPTION_CONTINUE_SEARCH;
3927
3928 // Debugger does func-evals inside this call, which may take nested exceptions. We need a nested exception
3929 // handler to allow this.
3930 INSTALL_NESTED_EXCEPTION_HANDLER(pThread->GetFrame());
3931
3932 EXCEPTION_POINTERS dummy;
3933 dummy.ExceptionRecord = NULL;
3934 dummy.ContextRecord = NULL;
3935
3936 if (NULL == pExceptionInfo)
3937 {
3938 pExceptionInfo = &dummy;
3939 }
3940 else if (NULL != pExceptionInfo->ExceptionRecord && NULL == pExceptionInfo->ContextRecord)
3941 {
3942 // In a soft stack overflow, we have an exception record but not a context record.
3943 // Debugger::LastChanceManagedException requires that both ExceptionRecord and
3944 // ContextRecord be valid or both be NULL.
3945 pExceptionInfo = &dummy;
3946 }
3947
3948 if (g_pDebugInterface && g_pDebugInterface->LastChanceManagedException(pExceptionInfo,
3949 pThread,
3950 jitAttachRequested) == ExceptionContinueExecution)
3951 {
3952 retval = EXCEPTION_CONTINUE_EXECUTION;
3953 }
3954
3955 UNINSTALL_NESTED_EXCEPTION_HANDLER();
3956
3957#ifdef DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED
3958 EX_TRY
3959 {
3960 // if the debugger wants to intercept the unhandled exception then we immediately unwind without returning
3961 // If there is a problem with this function unwinding here it could be separated out however
3962 // we need to be very careful. Previously we had the opposite problem in that we notified the debugger
3963 // of an unhandled exception and then either:
3964 // a) never gave the debugger a chance to intercept later, or
3965 // b) code changed more process state unaware that the debugger would be handling the exception
3966 if ((pThread->IsExceptionInProgress()) && pThread->GetExceptionState()->GetFlags()->DebuggerInterceptInfo())
3967 {
3968 // The debugger wants to intercept this exception. It may return in a failure case, in which case we want
3969 // to continue thru this path.
3970 ClrDebuggerDoUnwindAndIntercept(X86_FIRST_ARG(EXCEPTION_CHAIN_END) pExceptionInfo->ExceptionRecord);
3971 }
3972 }
3973 EX_CATCH // if we fail to intercept just continue as is
3974 {
3975 }
3976 EX_END_CATCH(SwallowAllExceptions);
3977#endif // DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED
3978
3979 return retval;
3980}
3981
3982#ifndef FEATURE_PAL
3983//----------------------------------------------------------------------------
3984//
3985// DoReportFault - wrapper for ReportFault in FaultRep.dll, which also handles
3986// debugger launch synchronization if the user chooses to launch
3987// a debugger
3988//
3989// Arguments:
3990// pExceptionInfo - pointer to exception info
3991//
3992// Return Value:
3993// The returned EFaultRepRetVal value from ReportFault
3994//
3995// Note:
3996//
3997//----------------------------------------------------------------------------
3998EFaultRepRetVal DoReportFault(EXCEPTION_POINTERS * pExceptionInfo)
3999{
4000 LIMITED_METHOD_CONTRACT;
4001
4002 HINSTANCE hmod = WszLoadLibrary(W("FaultRep.dll"));
4003 EFaultRepRetVal r = frrvErr;
4004 if (hmod)
4005 {
4006 pfn_REPORTFAULT pfnReportFault = (pfn_REPORTFAULT)GetProcAddress(hmod, "ReportFault");
4007 if (pfnReportFault)
4008 {
4009 r = pfnReportFault(pExceptionInfo, 0);
4010 }
4011 FreeLibrary(hmod);
4012 }
4013
4014 if (r == frrvLaunchDebugger)
4015 {
4016 // Wait until the pending managed debugger attach is completed
4017 if (g_pDebugInterface != NULL)
4018 {
4019 g_pDebugInterface->WaitForDebuggerAttach();
4020 }
4021 }
4022 return r;
4023}
4024
4025//----------------------------------------------------------------------------
4026//
4027// DisableOSWatson - Set error mode to disable OS Watson
4028//
4029// Arguments:
4030// None
4031//
4032// Return Value:
4033// None
4034//
4035// Note: SetErrorMode changes the process wide error mode, which can be overridden by other threads
4036// in a race. The solution is to use new Win7 per thread error mode APIs, which take precedence
4037// over process wide error mode. However, we shall not use per thread error mode if the runtime
4038// is being hosted because with per thread error mode being used the OS will ignore the process
4039// wide error mode set by the host.
4040//
4041//----------------------------------------------------------------------------
4042void DisableOSWatson(void)
4043{
4044 LIMITED_METHOD_CONTRACT;
4045
4046 // When a debugger is attached (or will be attaching), we need to disable the OS GPF dialog.
4047 // If we don't, an unhandled managed exception will launch the OS watson dialog even when
4048 // the debugger is attached.
4049 const UINT lastErrorMode = SetErrorMode(0);
4050 SetErrorMode(lastErrorMode | SEM_NOGPFAULTERRORBOX);
4051 LOG((LF_EH, LL_INFO100, "DisableOSWatson: SetErrorMode = 0x%x\n", lastErrorMode | SEM_NOGPFAULTERRORBOX));
4052
4053}
4054#endif // !FEATURE_PAL
4055
4056//------------------------------------------------------------------------------
4057// This function is called on an unhandled exception, via the runtime's
4058// Unhandled Exception Filter (Hence the name, "last chance", because this
4059// is the last chance to see the exception. When running under a native
4060// debugger, that won't generally happen, because the OS notifies the debugger
4061// instead of calling the application's registered UEF; the debugger will
4062// show the exception as second chance.)
4063// The function is also called sometimes for the side effects, which are
4064// to possibly invoke Watson and to possibly notify the managed debugger.
4065// If running in a debugger already, either native or managed, we shouldn't
4066// invoke Watson.
4067// If not running under a managed debugger, we shouldn't try to send a debugger
4068// notification.
4069//------------------------------------------------------------------------------
4070LONG WatsonLastChance( // EXCEPTION_CONTINUE_SEARCH, _CONTINUE_EXECUTION
4071 Thread *pThread, // Thread object.
4072 EXCEPTION_POINTERS *pExceptionInfo,// Information about reported exception.
4073 TypeOfReportedError tore) // Just what kind of error is reported?
4074{
4075 STATIC_CONTRACT_NOTHROW;
4076 STATIC_CONTRACT_GC_TRIGGERS;
4077 STATIC_CONTRACT_MODE_ANY;
4078
4079 // If allocation fails, we may not produce watson dump. But this is not fatal.
4080 CONTRACT_VIOLATION(AllViolation);
4081 LOG((LF_EH, LL_INFO10, "D::WLC: Enter WatsonLastChance\n"));
4082
4083#ifndef FEATURE_PAL
4084 static DWORD fDisableWatson = -1;
4085 if (fDisableWatson == -1)
4086 {
4087 fDisableWatson = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DisableWatsonForManagedExceptions);
4088 }
4089
4090 if (fDisableWatson && (tore.GetType() == TypeOfReportedError::UnhandledException))
4091 {
4092 DisableOSWatson();
4093 LOG((LF_EH, LL_INFO10, "D::WLC: OS Watson is disabled for an managed unhandled exception\n"));
4094 return EXCEPTION_CONTINUE_SEARCH;
4095 }
4096#endif // !FEATURE_PAL
4097
4098 // We don't want to launch Watson if a debugger is already attached to
4099 // the process.
4100 BOOL shouldNotifyDebugger = FALSE; // Assume we won't debug.
4101
4102 // VS debugger team requested the Whidbey experience, which is no Watson when the debugger thread detects
4103 // that the debugger process is abruptly terminated, and triggers a failfast error. In this particular
4104 // scenario CORDebuggerAttached() will be TRUE, but IsDebuggerPresent() will be FALSE because from OS
4105 // perspective the native debugger has been detached from the debuggee, but CLR has not yet marked the
4106 // managed debugger as detached. Therefore, CORDebuggerAttached() is checked, so Watson will not pop up
4107 // when a debugger is abruptly terminated. It also prevents a debugger from being launched on a helper
4108 // thread.
4109 BOOL alreadyDebugging = CORDebuggerAttached() || IsDebuggerPresent();
4110
4111 BOOL jitAttachRequested = !alreadyDebugging; // Launch debugger if not already running.
4112
4113#ifdef _DEBUG
4114 // If BreakOnUnCaughtException is set, we may be using a native debugger to debug this stuff
4115 BOOL BreakOnUnCaughtException = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException);
4116 if(!alreadyDebugging || (!CORDebuggerAttached() && BreakOnUnCaughtException) )
4117#else
4118 if (!alreadyDebugging)
4119#endif
4120 {
4121 LOG((LF_EH, LL_INFO10, "WatsonLastChance: Debugger not attached at sp %p ...\n", GetCurrentSP()));
4122
4123#ifndef FEATURE_PAL
4124 FaultReportResult result = FaultReportResultQuit;
4125
4126 BOOL fSOException = FALSE;
4127
4128 if ((pExceptionInfo != NULL) &&
4129 (pExceptionInfo->ExceptionRecord != NULL) &&
4130 (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW))
4131 {
4132 fSOException = TRUE;
4133 }
4134
4135 if (g_pDebugInterface)
4136 {
4137 // we are about to let the OS trigger jit attach, however we need to synchronize with our
4138 // own jit attach that we might be doing on another thread
4139 // PreJitAttach races this thread against any others which might be attaching and if some other
4140 // thread is doing it then we wait for its attach to complete first
4141 g_pDebugInterface->PreJitAttach(TRUE, FALSE, FALSE);
4142 }
4143
4144 // Let unhandled excpetions except stack overflow go to the OS
4145 if (tore.IsUnhandledException() && !fSOException)
4146 {
4147 return EXCEPTION_CONTINUE_SEARCH;
4148 }
4149 else if (tore.IsUserBreakpoint())
4150 {
4151 DoReportFault(pExceptionInfo);
4152 }
4153 else
4154 {
4155 BOOL fWatsonAlreadyLaunched = FALSE;
4156 if (FastInterlockCompareExchange(&g_watsonAlreadyLaunched, 1, 0) != 0)
4157 {
4158 fWatsonAlreadyLaunched = TRUE;
4159 }
4160
4161 // Logic to avoid double prompt if more than one threads calling into WatsonLastChance
4162 if (!fWatsonAlreadyLaunched)
4163 {
4164 // EEPolicy::HandleFatalStackOverflow pushes a FaultingExceptionFrame on the stack after SO
4165 // exception. Our hijack code runs in the exception context, and overwrites the stack space
4166 // after SO excpetion, so we need to pop up this frame before invoking RaiseFailFast.
4167 // This cumbersome code should be removed once SO synchronization is moved to be completely
4168 // out-of-process.
4169 if (fSOException && pThread && pThread->GetFrame() != FRAME_TOP)
4170 {
4171 GCX_COOP(); // Must be cooperative to modify frame chain.
4172 pThread->GetFrame()->Pop(pThread);
4173 }
4174
4175 LOG((LF_EH, LL_INFO10, "D::WLC: Call RaiseFailFastException\n"));
4176
4177 // enable preemptive mode before call into OS to allow runtime suspend to finish
4178 GCX_PREEMP();
4179
4180 STRESS_LOG0(LF_CORDB, LL_INFO10, "D::RFFE: About to call RaiseFailFastException\n");
4181 RaiseFailFastException(pExceptionInfo == NULL ? NULL : pExceptionInfo->ExceptionRecord,
4182 pExceptionInfo == NULL ? NULL : pExceptionInfo->ContextRecord,
4183 0);
4184 STRESS_LOG0(LF_CORDB, LL_INFO10, "D::RFFE: Return from RaiseFailFastException\n");
4185 }
4186 }
4187
4188 if (g_pDebugInterface)
4189 {
4190 // if execution resumed here then we may or may not be attached
4191 // either way we need to end the attach process and unblock any other
4192 // threads which were waiting for the attach here to complete
4193 g_pDebugInterface->PostJitAttach();
4194 }
4195
4196
4197 if (IsDebuggerPresent())
4198 {
4199 result = FaultReportResultDebug;
4200 jitAttachRequested = FALSE;
4201 }
4202
4203 switch(result)
4204 {
4205 case FaultReportResultAbort:
4206 {
4207 // We couldn't launch watson properly. First fall-back to OS error-reporting
4208 // so that we don't break native apps.
4209 EFaultRepRetVal r = frrvErr;
4210
4211 if (pExceptionInfo != NULL)
4212 {
4213 GCX_PREEMP();
4214
4215 if (pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_STACK_OVERFLOW)
4216 {
4217 r = DoReportFault(pExceptionInfo);
4218 }
4219 else
4220 {
4221 // Since the StackOverflow handler also calls us, we must keep our stack budget
4222 // to a minimum. Thus, we will launch a thread to do the actual work.
4223 FaultReportInfo fri;
4224 fri.m_fDoReportFault = TRUE;
4225 fri.m_pExceptionInfo = pExceptionInfo;
4226 // DoFaultCreateThreadReportCallback will overwrite this - if it doesn't, we'll assume it failed.
4227 fri.m_faultRepRetValResult = frrvErr;
4228
4229 // Stack overflow case - we don't have enough stack on our own thread so let the debugger
4230 // helper thread do the work.
4231 if (!g_pDebugInterface || FAILED(g_pDebugInterface->RequestFavor(DoFaultReportDoFavorCallback, &fri)))
4232 {
4233 // If we can't initialize the debugger helper thread or we are running on the debugger helper
4234 // thread, give it up. We don't have enough stack space.
4235 }
4236
4237 r = fri.m_faultRepRetValResult;
4238 }
4239 }
4240
4241 if ((r == frrvErr) || (r == frrvErrNoDW) || (r == frrvErrTimeout))
4242 {
4243 // If we don't have an exception record, or otherwise can't use OS error
4244 // reporting then offer the old "press OK to terminate, cancel to debug"
4245 // dialog as a futher fallback.
4246 if (g_pDebugInterface && g_pDebugInterface->FallbackJITAttachPrompt())
4247 {
4248 // User requested to launch the debugger
4249 shouldNotifyDebugger = TRUE;
4250 }
4251 }
4252 else if (r == frrvLaunchDebugger)
4253 {
4254 // User requested to launch the debugger
4255 shouldNotifyDebugger = TRUE;
4256 }
4257 break;
4258 }
4259 case FaultReportResultQuit:
4260 // No debugger, just exit normally
4261 break;
4262 case FaultReportResultDebug:
4263 // JIT attach a debugger here.
4264 shouldNotifyDebugger = TRUE;
4265 break;
4266 default:
4267 UNREACHABLE_MSG("Unknown FaultReportResult");
4268 break;
4269 }
4270 }
4271 // When the debugger thread detects that the debugger process is abruptly terminated, and triggers
4272 // a failfast error, CORDebuggerAttached() will be TRUE, but IsDebuggerPresent() will be FALSE.
4273 // If IsDebuggerPresent() is FALSE, do not try to notify the deubgger.
4274 else if (CORDebuggerAttached() && IsDebuggerPresent())
4275#else
4276 }
4277 else if (CORDebuggerAttached())
4278#endif // !FEATURE_PAL
4279 {
4280 // Already debugging with a managed debugger. Should let that debugger know.
4281 LOG((LF_EH, LL_INFO100, "WatsonLastChance: Managed debugger already attached at sp %p ...\n", GetCurrentSP()));
4282
4283 // The managed EH subsystem ignores native breakpoints and single step exceptions. These exceptions are
4284 // not considered managed, and the managed debugger should not be notified. Moreover, we won't have
4285 // created a managed exception object at this point.
4286 if (tore.GetType() != TypeOfReportedError::NativeBreakpoint)
4287 {
4288 shouldNotifyDebugger = TRUE;
4289 }
4290 }
4291
4292#ifndef FEATURE_PAL
4293 DisableOSWatson();
4294#endif // !FEATURE_PAL
4295
4296 if (!shouldNotifyDebugger)
4297 {
4298 LOG((LF_EH, LL_INFO100, "WatsonLastChance: should not notify debugger. Returning EXCEPTION_CONTINUE_SEARCH\n"));
4299 return EXCEPTION_CONTINUE_SEARCH;
4300 }
4301
4302 // If no debugger interface, we can't notify the debugger.
4303 if (g_pDebugInterface == NULL)
4304 {
4305 LOG((LF_EH, LL_INFO100, "WatsonLastChance: No debugger interface. Returning EXCEPTION_CONTINUE_SEARCH\n"));
4306 return EXCEPTION_CONTINUE_SEARCH;
4307 }
4308
4309 LOG((LF_EH, LL_INFO10, "WatsonLastChance: Notifying debugger\n"));
4310
4311 switch (tore.GetType())
4312 {
4313 case TypeOfReportedError::FatalError:
4314 #ifdef MDA_SUPPORTED
4315 {
4316 MdaFatalExecutionEngineError * pMDA = MDA_GET_ASSISTANT_EX(FatalExecutionEngineError);
4317
4318 if ((pMDA != NULL) && (pExceptionInfo != NULL) && (pExceptionInfo->ExceptionRecord != NULL))
4319 {
4320 TADDR addr = (TADDR) pExceptionInfo->ExceptionRecord->ExceptionAddress;
4321 HRESULT hrError = pExceptionInfo->ExceptionRecord->ExceptionCode;
4322 pMDA->ReportFEEE(addr, hrError);
4323 }
4324 }
4325 #endif // MDA_SUPPORTED
4326
4327 if (pThread != NULL)
4328 {
4329 NotifyDebuggerLastChance(pThread, pExceptionInfo, jitAttachRequested);
4330
4331 // If the registed debugger is not a managed debugger, we need to stop the debugger here.
4332 if (!CORDebuggerAttached() && IsDebuggerPresent())
4333 {
4334 DebugBreak();
4335 }
4336 }
4337 else
4338 {
4339 g_pDebugInterface->LaunchDebuggerForUser(GetThread(), pExceptionInfo, FALSE, FALSE);
4340 }
4341
4342 return EXCEPTION_CONTINUE_SEARCH;
4343
4344 case TypeOfReportedError::UnhandledException:
4345 case TypeOfReportedError::NativeBreakpoint:
4346 // Notify the debugger only if this is a managed thread.
4347 if (pThread != NULL)
4348 {
4349 return NotifyDebuggerLastChance(pThread, pExceptionInfo, jitAttachRequested);
4350 }
4351 else
4352 {
4353 g_pDebugInterface->JitAttach(pThread, pExceptionInfo, FALSE, FALSE);
4354
4355 // return EXCEPTION_CONTINUE_SEARCH, so OS's UEF will reraise the unhandled exception for debuggers
4356 return EXCEPTION_CONTINUE_SEARCH;
4357 }
4358
4359 case TypeOfReportedError::UserBreakpoint:
4360 g_pDebugInterface->LaunchDebuggerForUser(pThread, pExceptionInfo, TRUE, FALSE);
4361
4362 return EXCEPTION_CONTINUE_EXECUTION;
4363
4364 case TypeOfReportedError::NativeThreadUnhandledException:
4365 g_pDebugInterface->JitAttach(pThread, pExceptionInfo, FALSE, FALSE);
4366
4367 // return EXCEPTION_CONTINUE_SEARCH, so OS's UEF will reraise the unhandled exception for debuggers
4368 return EXCEPTION_CONTINUE_SEARCH;
4369
4370 default:
4371 _ASSERTE(!"Unknown case in WatsonLastChance");
4372 return EXCEPTION_CONTINUE_SEARCH;
4373 }
4374
4375 UNREACHABLE();
4376} // LONG WatsonLastChance()
4377
4378//---------------------------------------------------------------------------------------
4379//
4380// This is just a simple helper to do some basic checking to see if an exception is intercepted.
4381// It checks that we are on a managed thread and that an exception is indeed in progress.
4382//
4383// Return Value:
4384// true iff we are on a managed thread and an exception is in flight
4385//
4386
4387bool CheckThreadExceptionStateForInterception()
4388{
4389 LIMITED_METHOD_CONTRACT;
4390
4391 Thread* pThread = GetThread();
4392
4393 if (pThread == NULL)
4394 {
4395 return false;
4396 }
4397
4398 if (!pThread->IsExceptionInProgress())
4399 {
4400 return false;
4401 }
4402
4403 return true;
4404}
4405#endif
4406
4407//===========================================================================================
4408//
4409// UNHANDLED EXCEPTION HANDLING
4410//
4411
4412static Volatile<BOOL> fReady = 0;
4413static SpinLock initLock;
4414
4415void DECLSPEC_NORETURN RaiseDeadLockException()
4416{
4417 STATIC_CONTRACT_THROWS;
4418 STATIC_CONTRACT_SO_TOLERANT;
4419
4420// Disable the "initialization of static local vars is no thread safe" error
4421#ifdef _MSC_VER
4422#pragma warning(disable: 4640)
4423#endif
4424 CHECK_LOCAL_STATIC_VAR(static SString s);
4425#ifdef _MSC_VER
4426#pragma warning(default : 4640)
4427#endif
4428 if (!fReady)
4429 {
4430 WCHAR name[256];
4431 HRESULT hr = S_OK;
4432 {
4433 FAULT_NOT_FATAL();
4434 GCX_COOP();
4435 hr = UtilLoadStringRC(IDS_EE_THREAD_DEADLOCK_VICTIM, name, sizeof(name)/sizeof(WCHAR), 1);
4436 }
4437 initLock.Init(LOCK_TYPE_DEFAULT);
4438 SpinLockHolder __spinLockHolder(&initLock);
4439 if (!fReady)
4440 {
4441 if (SUCCEEDED(hr))
4442 {
4443 s.Set(name);
4444 fReady = 1;
4445 }
4446 else
4447 {
4448 ThrowHR(hr);
4449 }
4450 }
4451 }
4452
4453 ThrowHR(HOST_E_DEADLOCK, s);
4454}
4455
4456//******************************************************************************
4457//
4458// ExceptionIsAlwaysSwallowed
4459//
4460// Determine whether an exception is of a type that it should always
4461// be swallowed, even when exceptions otherwise are left to go unhandled.
4462// (For Whidbey, ThreadAbort, RudeThreadAbort, or AppDomainUnload exception)
4463//
4464// Parameters:
4465// pExceptionInfo EXCEPTION_POINTERS for current exception
4466//
4467// Returns:
4468// true If the exception is of a type that is always swallowed.
4469//
4470BOOL ExceptionIsAlwaysSwallowed(EXCEPTION_POINTERS *pExceptionInfo)
4471{
4472 BOOL isSwallowed = false;
4473
4474 // The exception code must be ours, if it is one of our Exceptions.
4475 if (IsComPlusException(pExceptionInfo->ExceptionRecord))
4476 {
4477 // Our exception code. Get the current exception from the thread.
4478 Thread *pThread = GetThread();
4479 if (pThread)
4480 {
4481 OBJECTREF throwable;
4482
4483 GCX_COOP();
4484 if ((throwable = pThread->GetThrowable()) == NULL)
4485 {
4486 throwable = pThread->LastThrownObject();
4487 }
4488 //@todo: could throwable be NULL here?
4489 isSwallowed = IsExceptionOfType(kThreadAbortException, &throwable);
4490 }
4491 }
4492
4493 return isSwallowed;
4494} // BOOL ExceptionIsAlwaysSwallowed()
4495
4496//
4497// UserBreakpointFilter is used to ensure that we get a popup on user breakpoints (DebugBreak(), hard-coded int 3,
4498// etc.) as soon as possible.
4499//
4500LONG UserBreakpointFilter(EXCEPTION_POINTERS* pEP)
4501{
4502 CONTRACTL
4503 {
4504 NOTHROW;
4505 GC_NOTRIGGER;
4506 MODE_ANY;
4507 FORBID_FAULT;
4508 SO_TOLERANT;
4509 }
4510 CONTRACTL_END;
4511
4512#ifdef DEBUGGING_SUPPORTED
4513 // Invoke the unhandled exception filter, bypassing any further first pass exception processing and treating
4514 // user breakpoints as if they're unhandled exceptions right away.
4515 //
4516 // @todo: The InternalUnhandledExceptionFilter can trigger.
4517 CONTRACT_VIOLATION(GCViolation | ThrowsViolation | ModeViolation | FaultViolation | FaultNotFatal);
4518
4519#ifdef FEATURE_PAL
4520 int result = COMUnhandledExceptionFilter(pEP);
4521#else
4522 int result = UnhandledExceptionFilter(pEP);
4523#endif
4524
4525 if (result == EXCEPTION_CONTINUE_SEARCH)
4526 {
4527 // A debugger got attached. Instead of allowing the exception to continue up, and hope for the
4528 // second-chance, we cause it to happen again. The debugger snags all int3's on first-chance. NOTE: the
4529 // InternalUnhandledExceptionFilter allowed GC's to occur, but it may be the case that some managed frames
4530 // may have been unprotected. Therefore, you may have GC holes if you attempt to continue execution from
4531 // here.
4532 return EXCEPTION_CONTINUE_EXECUTION;
4533 }
4534#endif // DEBUGGING_SUPPORTED
4535
4536 if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast))
4537 {
4538 // Fire an ETW FailFast event
4539 FireEtwFailFast(W("StatusBreakpoint"),
4540 (const PVOID)((pEP && pEP->ContextRecord) ? GetIP(pEP->ContextRecord) : 0),
4541 ((pEP && pEP->ExceptionRecord) ? pEP->ExceptionRecord->ExceptionCode : 0),
4542 STATUS_BREAKPOINT,
4543 GetClrInstanceId());
4544 }
4545
4546 // Otherwise, we termintate the process.
4547 TerminateProcess(GetCurrentProcess(), STATUS_BREAKPOINT);
4548
4549 // Shouldn't get here ...
4550 return EXCEPTION_CONTINUE_EXECUTION;
4551} // LONG UserBreakpointFilter()
4552
4553//******************************************************************************
4554//
4555// DefaultCatchFilter
4556//
4557// The old default except filter (v1.0/v1.1) . For user breakpoints, call out to UserBreakpointFilter()
4558// but otherwise return EXCEPTION_EXECUTE_HANDLER, to swallow the exception.
4559//
4560// Parameters:
4561// pExceptionInfo EXCEPTION_POINTERS for current exception
4562// pv A constant as an INT_PTR. Must be COMPLUS_EXCEPTION_EXECUTE_HANDLER.
4563//
4564// Returns:
4565// EXCEPTION_EXECUTE_HANDLER Generally returns this to swallow the exception.
4566//
4567// IMPORTANT!! READ ME!!
4568//
4569// This filter is very similar to DefaultCatchNoSwallowFilter, except when unhandled
4570// exception policy/config dictate swallowing the exception.
4571// If you make any changes to this function, look to see if the other one also needs
4572// the same change.
4573//
4574LONG DefaultCatchFilter(EXCEPTION_POINTERS *ep, PVOID pv)
4575{
4576 CONTRACTL
4577 {
4578 NOTHROW;
4579 GC_NOTRIGGER;
4580 MODE_ANY;
4581 FORBID_FAULT;
4582 SO_TOLERANT;
4583 }
4584 CONTRACTL_END;
4585
4586 //
4587 // @TODO: this seems like a strong candidate for elimination due to duplication with
4588 // our vectored exception handler.
4589 //
4590
4591 DefaultCatchFilterParam *pParam;
4592 pParam = (DefaultCatchFilterParam *) pv;
4593
4594 // the only valid parameter for DefaultCatchFilter so far
4595 _ASSERTE(pParam->pv == COMPLUS_EXCEPTION_EXECUTE_HANDLER);
4596
4597 PEXCEPTION_RECORD er = ep->ExceptionRecord;
4598 DWORD code = er->ExceptionCode;
4599
4600 if (code == STATUS_SINGLE_STEP || code == STATUS_BREAKPOINT)
4601 {
4602 return UserBreakpointFilter(ep);
4603 }
4604
4605 // return EXCEPTION_EXECUTE_HANDLER to swallow the exception.
4606 return EXCEPTION_EXECUTE_HANDLER;
4607} // LONG DefaultCatchFilter()
4608
4609
4610//******************************************************************************
4611//
4612// DefaultCatchNoSwallowFilter
4613//
4614// The new default except filter (v2.0). For user breakpoints, call out to UserBreakpointFilter().
4615// Otherwise consults host policy and config file to return EXECUTE_HANDLER / CONTINUE_SEARCH.
4616//
4617// Parameters:
4618// pExceptionInfo EXCEPTION_POINTERS for current exception
4619// pv A constant as an INT_PTR. Must be COMPLUS_EXCEPTION_EXECUTE_HANDLER.
4620//
4621// Returns:
4622// EXCEPTION_CONTINUE_SEARCH Generally returns this to let the exception go unhandled.
4623// EXCEPTION_EXECUTE_HANDLER May return this to swallow the exception.
4624//
4625// IMPORTANT!! READ ME!!
4626//
4627// This filter is very similar to DefaultCatchFilter, except when unhandled
4628// exception policy/config dictate swallowing the exception.
4629// If you make any changes to this function, look to see if the other one also needs
4630// the same change.
4631//
4632LONG DefaultCatchNoSwallowFilter(EXCEPTION_POINTERS *ep, PVOID pv)
4633{
4634 CONTRACTL
4635 {
4636 THROWS;
4637 GC_TRIGGERS;
4638 MODE_ANY;
4639 }
4640 CONTRACTL_END;
4641
4642 DefaultCatchFilterParam *pParam; pParam = (DefaultCatchFilterParam *) pv;
4643
4644 // the only valid parameter for DefaultCatchFilter so far
4645 _ASSERTE(pParam->pv == COMPLUS_EXCEPTION_EXECUTE_HANDLER);
4646
4647 PEXCEPTION_RECORD er = ep->ExceptionRecord;
4648 DWORD code = er->ExceptionCode;
4649
4650 if (code == STATUS_SINGLE_STEP || code == STATUS_BREAKPOINT)
4651 {
4652 return UserBreakpointFilter(ep);
4653 }
4654
4655 // If host policy or config file says "swallow"...
4656 if (SwallowUnhandledExceptions())
4657 { // ...return EXCEPTION_EXECUTE_HANDLER to swallow the exception.
4658 return EXCEPTION_EXECUTE_HANDLER;
4659 }
4660
4661 // If the exception is of a type that is always swallowed (ThreadAbort, AppDomainUnload)...
4662 if (ExceptionIsAlwaysSwallowed(ep))
4663 { // ...return EXCEPTION_EXECUTE_HANDLER to swallow the exception.
4664 return EXCEPTION_EXECUTE_HANDLER;
4665 }
4666
4667 // Otherwise, continue search. i.e. let the exception go unhandled (at least for now).
4668 return EXCEPTION_CONTINUE_SEARCH;
4669} // LONG DefaultCatchNoSwallowFilter()
4670
4671// Note: This is used only for CoreCLR on WLC.
4672//
4673// We keep a pointer to the previous unhandled exception filter. After we install, we use
4674// this to call the previous guy. When we un-install, we put them back. Putting them back
4675// is a bug -- we have no guarantee that the DLL unload order matches the DLL load order -- we
4676// may in fact be putting back a pointer to a DLL that has been unloaded.
4677//
4678
4679// initialize to -1 because NULL won't detect difference between us not having installed our handler
4680// yet and having installed it but the original handler was NULL.
4681static LPTOP_LEVEL_EXCEPTION_FILTER g_pOriginalUnhandledExceptionFilter = (LPTOP_LEVEL_EXCEPTION_FILTER)-1;
4682#define FILTER_NOT_INSTALLED (LPTOP_LEVEL_EXCEPTION_FILTER) -1
4683
4684
4685BOOL InstallUnhandledExceptionFilter() {
4686 STATIC_CONTRACT_NOTHROW;
4687 STATIC_CONTRACT_GC_NOTRIGGER;
4688 STATIC_CONTRACT_MODE_ANY;
4689 STATIC_CONTRACT_FORBID_FAULT;
4690
4691#ifndef FEATURE_PAL
4692 // We will be here only for CoreCLR on WLC since we dont
4693 // register UEF for SL.
4694 if (g_pOriginalUnhandledExceptionFilter == FILTER_NOT_INSTALLED) {
4695
4696 #pragma prefast(push)
4697 #pragma prefast(suppress:28725, "Calling to SetUnhandledExceptionFilter is intentional in this case.")
4698 g_pOriginalUnhandledExceptionFilter = SetUnhandledExceptionFilter(COMUnhandledExceptionFilter);
4699 #pragma prefast(pop)
4700
4701 // make sure is set (ie. is not our special value to indicate unset)
4702 LOG((LF_EH, LL_INFO10, "InstallUnhandledExceptionFilter registered UEF with OS for CoreCLR!\n"));
4703 }
4704 _ASSERTE(g_pOriginalUnhandledExceptionFilter != FILTER_NOT_INSTALLED);
4705#endif // !FEATURE_PAL
4706
4707 // All done - successfully!
4708 return TRUE;
4709}
4710
4711void UninstallUnhandledExceptionFilter() {
4712 STATIC_CONTRACT_NOTHROW;
4713 STATIC_CONTRACT_GC_NOTRIGGER;
4714 STATIC_CONTRACT_MODE_ANY;
4715 STATIC_CONTRACT_FORBID_FAULT;
4716
4717#ifndef FEATURE_PAL
4718 // We will be here only for CoreCLR on WLC or on Mac SL.
4719 if (g_pOriginalUnhandledExceptionFilter != FILTER_NOT_INSTALLED) {
4720
4721 #pragma prefast(push)
4722 #pragma prefast(suppress:28725, "Calling to SetUnhandledExceptionFilter is intentional in this case.")
4723 SetUnhandledExceptionFilter(g_pOriginalUnhandledExceptionFilter);
4724 #pragma prefast(pop)
4725
4726 g_pOriginalUnhandledExceptionFilter = FILTER_NOT_INSTALLED;
4727 LOG((LF_EH, LL_INFO10, "UninstallUnhandledExceptionFilter unregistered UEF from OS for CoreCLR!\n"));
4728 }
4729#endif // !FEATURE_PAL
4730}
4731
4732//
4733// Update the current throwable on the thread if necessary. If we're looking at one of our exceptions, and if the
4734// current throwable on the thread is NULL, then we'll set it to something more useful based on the
4735// LastThrownObject.
4736//
4737BOOL UpdateCurrentThrowable(PEXCEPTION_RECORD pExceptionRecord)
4738{
4739 STATIC_CONTRACT_THROWS;
4740 STATIC_CONTRACT_MODE_ANY;
4741 STATIC_CONTRACT_GC_TRIGGERS;
4742
4743 BOOL useLastThrownObject = FALSE;
4744
4745 Thread* pThread = GetThread();
4746
4747 // GetThrowable needs cooperative.
4748 GCX_COOP();
4749
4750 if ((pThread->GetThrowable() == NULL) && (pThread->LastThrownObject() != NULL))
4751 {
4752 // If GetThrowable is NULL and LastThrownObject is not, use lastThrownObject.
4753 // In current (June 05) implementation, this is only used to pass to
4754 // NotifyAppDomainsOfUnhandledException, which needs to get a throwable
4755 // from somewhere, with which to notify the AppDomains.
4756 useLastThrownObject = TRUE;
4757
4758 if (IsComPlusException(pExceptionRecord))
4759 {
4760#ifndef WIN64EXCEPTIONS
4761 OBJECTREF oThrowable = pThread->LastThrownObject();
4762
4763 // @TODO: we have a problem on Win64 where we won't have any place to
4764 // store the throwable on an unhandled exception. Currently this
4765 // only effects the managed debugging services as they will try
4766 // to inspect the thread to see what the throwable is on an unhandled
4767 // exception.. (but clearly it needs to be fixed asap)
4768 // We have the same problem in EEPolicy::LogFatalError().
4769 LOG((LF_EH, LL_INFO100, "UpdateCurrentThrowable: setting throwable to %s\n", (oThrowable == NULL) ? "NULL" : oThrowable->GetMethodTable()->GetDebugClassName()));
4770 pThread->SafeSetThrowables(oThrowable);
4771#endif // WIN64EXCEPTIONS
4772 }
4773 }
4774
4775 return useLastThrownObject;
4776}
4777
4778//
4779// COMUnhandledExceptionFilter is used to catch all unhandled exceptions.
4780// The debugger will either handle the exception, attach a debugger, or
4781// notify an existing attached debugger.
4782//
4783
4784struct SaveIPFilterParam
4785{
4786 SLOT ExceptionEIP;
4787};
4788
4789LONG SaveIPFilter(EXCEPTION_POINTERS* ep, LPVOID pv)
4790{
4791 WRAPPER_NO_CONTRACT;
4792
4793 SaveIPFilterParam *pParam = (SaveIPFilterParam *) pv;
4794 pParam->ExceptionEIP = (SLOT)GetIP(ep->ContextRecord);
4795 DefaultCatchFilterParam param(COMPLUS_EXCEPTION_EXECUTE_HANDLER);
4796 return DefaultCatchFilter(ep, &param);
4797}
4798
4799//------------------------------------------------------------------------------
4800// Description
4801// Does not call any previous UnhandledExceptionFilter. The assumption is that
4802// either it is inappropriate to call it (because we have elected to rip the
4803// process without transitioning completely to the base of the thread), or
4804// the caller has already consulted the previously installed UnhandledExceptionFilter.
4805//
4806// So we know we are ripping and Watson is appropriate.
4807//
4808// **** Note*****
4809// This is a stack-sensitive function if we have an unhandled SO.
4810// Do not allocate more than a few bytes on the stack or we risk taking an
4811// AV while trying to throw up Watson.
4812
4813// Parameters
4814// pExceptionInfo -- information about the exception that caused the error.
4815// If the error is not the result of an exception, pass NULL for this
4816// parameter
4817//
4818// Returns
4819// EXCEPTION_CONTINUE_SEARCH -- we've done anything we will with the exception.
4820// As far as the runtime is concerned, the process is doomed.
4821// EXCEPTION_CONTINUE_EXECUTION -- means a debugger "caught" the exception and
4822// wants to continue running.
4823// EXCEPTION_EXECUTE_HANDLER -- CoreCLR only, and only when not running as a UEF.
4824// Returned only if the host has asked us to swallow unhandled exceptions on
4825// managed threads in an AD they (the host) creates.
4826//------------------------------------------------------------------------------
4827LONG InternalUnhandledExceptionFilter_Worker(
4828 EXCEPTION_POINTERS *pExceptionInfo) // Information about the exception
4829{
4830 STATIC_CONTRACT_THROWS;
4831 STATIC_CONTRACT_GC_TRIGGERS;
4832 STATIC_CONTRACT_MODE_ANY;
4833
4834#ifdef _DEBUG
4835 static int fBreakOnUEF = -1;
4836 if (fBreakOnUEF==-1) fBreakOnUEF = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUEF);
4837 _ASSERTE(!fBreakOnUEF);
4838#endif
4839
4840 STRESS_LOG2(LF_EH, LL_INFO10, "In InternalUnhandledExceptionFilter_Worker, Exception = %x, sp = %p\n",
4841 pExceptionInfo->ExceptionRecord->ExceptionCode, GetCurrentSP());
4842
4843 // If we can't enter the EE, done.
4844 if (g_fForbidEnterEE)
4845 {
4846 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: g_fForbidEnterEE is TRUE\n"));
4847 return EXCEPTION_CONTINUE_SEARCH;
4848 }
4849
4850
4851 if (GetEEPolicy()->GetActionOnFailure(FAIL_FatalRuntime) == eDisableRuntime)
4852 {
4853 ETaskType type = ::GetCurrentTaskType();
4854 if (type != TT_UNKNOWN && type != TT_USER)
4855 {
4856 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: calling EEPolicy::HandleFatalError\n"));
4857 EEPolicy::HandleFatalError(COR_E_EXECUTIONENGINE, (UINT_PTR)GetIP(pExceptionInfo->ContextRecord), NULL, pExceptionInfo);
4858 }
4859 }
4860
4861 // We don't do anything when this is called from an unmanaged thread.
4862 Thread *pThread = GetThread();
4863
4864#ifdef _DEBUG
4865 static bool bBreakOnUncaught = false;
4866 static int fBreakOnUncaught = 0;
4867
4868 if (!bBreakOnUncaught)
4869 {
4870 fBreakOnUncaught = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException);
4871 bBreakOnUncaught = true;
4872 }
4873 if (fBreakOnUncaught != 0)
4874 {
4875 if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW)
4876 {
4877 // if we've got an uncaught SO, we don't have enough stack to pop a debug break. So instead,
4878 // loop infinitely and we can attach a debugger at that point and break in.
4879 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Infinite loop on uncaught SO\n"));
4880 for ( ;; )
4881 {
4882 }
4883 }
4884 else
4885 {
4886 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: ASSERTING on uncaught\n"));
4887 _ASSERTE(!"BreakOnUnCaughtException");
4888 }
4889 }
4890#endif
4891
4892#ifdef _DEBUG_ADUNLOAD
4893 printf("%x InternalUnhandledExceptionFilter_Worker: Called for %x\n",
4894 ((pThread == NULL) ? NULL : pThread->GetThreadId()), pExceptionInfo->ExceptionRecord->ExceptionCode);
4895 fflush(stdout);
4896#endif
4897
4898 // This shouldn't be possible, but MSVC re-installs us... for now, just bail if this happens.
4899 if (g_fNoExceptions)
4900 {
4901 return EXCEPTION_CONTINUE_SEARCH;
4902 }
4903
4904 // Are we looking at a stack overflow here?
4905 if ((pThread != NULL) && !pThread->DetermineIfGuardPagePresent())
4906 {
4907 g_fForbidEnterEE = true;
4908 }
4909
4910#ifdef DEBUGGING_SUPPORTED
4911
4912 // Mark that this exception has gone unhandled. At the moment only the debugger will
4913 // ever look at this flag. This should come before any user-visible side effect of an exception
4914 // being unhandled as seen from managed code or from a debugger. These include the
4915 // managed unhandled notification callback, execution of catch/finally clauses,
4916 // receiving the managed debugger unhandled exception event,
4917 // the OS sending the debugger 2nd pass native exception notification, etc.
4918 //
4919 // This needs to be done before the check for TSNC_ProcessedUnhandledException because it is perfectly
4920 // legitimate (though rare) for the debugger to be inspecting exceptions which are nested in finally
4921 // clauses that run after an unhandled exception has already occurred on the thread
4922 if ((pThread != NULL) && pThread->IsExceptionInProgress())
4923 {
4924 LOG((LF_EH, LL_INFO1000, "InternalUnhandledExceptionFilter_Worker: Set unhandled exception flag at %p\n",
4925 pThread->GetExceptionState()->GetFlags() ));
4926 pThread->GetExceptionState()->GetFlags()->SetUnhandled();
4927 }
4928#endif
4929
4930 // If we have already done unhandled exception processing for this thread, then
4931 // simply return back. See comment in threads.h for details for the flag
4932 // below.
4933 //
4934 if (pThread && (pThread->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException) || pThread->HasThreadStateNC(Thread::TSNC_AppDomainContainUnhandled)))
4935 {
4936 // This assert shouldnt be hit in CoreCLR since:
4937 //
4938 // 1) It has no concept of managed entry point that is invoked by the shim. You can
4939 // only run managed code via hosting APIs that will run code in non-default domains.
4940 //
4941 // 2) Managed threads cannot be created in DefaultDomain since no user code executes
4942 // in default domain.
4943 //
4944 // So, if this is hit, something is not right!
4945 if (pThread->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))
4946 {
4947 _ASSERTE(!"How come a thread with TSNC_ProcessedUnhandledException state entered the UEF on CoreCLR?");
4948 }
4949
4950 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: have already processed unhandled exception for this thread.\n"));
4951 return EXCEPTION_CONTINUE_SEARCH;
4952 }
4953
4954 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Handling\n"));
4955
4956 struct Param : SaveIPFilterParam
4957 {
4958 EXCEPTION_POINTERS *pExceptionInfo;
4959 Thread *pThread;
4960 LONG retval;
4961 BOOL fIgnore;
4962 }; Param param;
4963
4964 param.ExceptionEIP = 0;
4965 param.pExceptionInfo = pExceptionInfo;
4966 param.pThread = pThread;
4967 param.retval = EXCEPTION_CONTINUE_SEARCH; // Result of UEF filter.
4968
4969 // Is this a particular kind of exception that we'd like to ignore?
4970 param.fIgnore = ((param.pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) ||
4971 (param.pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP));
4972
4973 PAL_TRY(Param *, pParam, &param)
4974 {
4975 // If fIgnore, then this is some sort of breakpoint, not a "normal" unhandled exception. But, the
4976 // breakpoint is due to an int3 or debugger step instruction, not due to calling Debugger.Break()
4977 TypeOfReportedError tore = pParam->fIgnore ? TypeOfReportedError::NativeBreakpoint : TypeOfReportedError::UnhandledException;
4978
4979 //
4980 // If this exception is on a thread without managed code, then report this as a NativeThreadUnhandledException
4981 //
4982 // The thread object may exist if there was once managed code on the stack, but if the exception never
4983 // bubbled thru managed code, ie no managed code is on its stack, then this is a native unhandled exception
4984 //
4985 // Ignore breakpoints and single-step.
4986 if (!pParam->fIgnore)
4987 { // Possibly interesting exception. Is there no Thread at all? Or, is there a Thread,
4988 // but with no exception at all on it?
4989 if ((pParam->pThread == NULL) ||
4990 (pParam->pThread->IsThrowableNull() && pParam->pThread->IsLastThrownObjectNull()) )
4991 { // Whatever this exception is, we don't know about it. Treat as Native.
4992 tore = TypeOfReportedError::NativeThreadUnhandledException;
4993 }
4994 }
4995
4996 // If there is no throwable on the thread, go ahead and update from the last thrown exception if possible.
4997 // Note: don't do this for exceptions that we're going to ignore below anyway...
4998 BOOL useLastThrownObject = FALSE;
4999 if (!pParam->fIgnore && (pParam->pThread != NULL))
5000 {
5001 useLastThrownObject = UpdateCurrentThrowable(pParam->pExceptionInfo->ExceptionRecord);
5002 }
5003
5004#ifdef DEBUGGING_SUPPORTED
5005
5006 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Notifying Debugger...\n"));
5007
5008 // If we are using the throwable in LastThrownObject, mark that it is now unhandled
5009 if ((pParam->pThread != NULL) && useLastThrownObject)
5010 {
5011 LOG((LF_EH, LL_INFO1000, "InternalUnhandledExceptionFilter_Worker: Set lto is unhandled\n"));
5012 pParam->pThread->MarkLastThrownObjectUnhandled();
5013 }
5014
5015 //
5016 // We don't want the managed debugger to try to "intercept" breakpoints
5017 // or singlestep exceptions.
5018 // TODO: why does the exception handling code need to set this? Shouldn't the debugger code
5019 // be able to determine what it can/should intercept?
5020 if ((pParam->pThread != NULL) && pParam->pThread->IsExceptionInProgress() && pParam->fIgnore)
5021 {
5022 pParam->pThread->GetExceptionState()->GetFlags()->SetDebuggerInterceptNotPossible();
5023 }
5024
5025
5026 if (pParam->pThread != NULL)
5027 {
5028 BOOL fIsProcessTerminating = TRUE;
5029
5030 // In CoreCLR, we can be asked to not let an exception go unhandled on managed threads in a given AppDomain.
5031 // If the exception reaches the top of the thread's stack, we simply deliver AppDomain's UnhandledException event and
5032 // return back to the filter, instead of letting the process terminate because of unhandled exception.
5033
5034 // Below is how we perform the check:
5035 //
5036 // 1) The flag is specified on the AD when it is created by the host and all managed threads created
5037 // in such an AD will inherit the flag. For non-finalizer and non-threadpool threads, we check the flag against the thread.
5038 // 2) The finalizer thread always switches to the AD of the object that is going to be finalized. Thus,
5039 // while it wont have the flag specified, the AD it switches to will.
5040 // 3) The threadpool thread also switches to the correct AD before executing the request. The thread wont have the
5041 // flag specified, but the AD it switches to will.
5042
5043 // This code must only be exercised when running as a normal filter; returning
5044 // EXCEPTION_EXECUTE_HANDLER is not valid if this code is being invoked from
5045 // the UEF.
5046 // Fortunately, we should never get into this case, since the thread flag about
5047 // ignoring unhandled exceptions cannot be set on the default domain.
5048
5049 if (IsFinalizerThread() || (pParam->pThread->IsThreadPoolThread()))
5050 fIsProcessTerminating = !(pParam->pThread->GetDomain()->IgnoreUnhandledExceptions());
5051 else
5052 fIsProcessTerminating = !(pParam->pThread->HasThreadStateNC(Thread::TSNC_IgnoreUnhandledExceptions));
5053
5054#ifndef FEATURE_PAL
5055 // Setup the watson bucketing details for UE processing.
5056 // do this before notifying appdomains of the UE so if an AD attempts to
5057 // retrieve the bucket params in the UE event handler it gets the correct data.
5058 SetupWatsonBucketsForUEF(useLastThrownObject);
5059#endif // !FEATURE_PAL
5060
5061 // Send notifications to the AppDomains.
5062 NotifyAppDomainsOfUnhandledException(pParam->pExceptionInfo, NULL, useLastThrownObject, fIsProcessTerminating /*isTerminating*/);
5063
5064 // If the process is not terminating, then return back to the filter and ask it to execute
5065 if (!fIsProcessTerminating)
5066 {
5067 pParam->retval = EXCEPTION_EXECUTE_HANDLER;
5068 goto lDone;
5069 }
5070 }
5071 else
5072 {
5073 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Not collecting bucket information as thread object does not exist\n"));
5074 }
5075
5076 // AppDomain.UnhandledException event could have thrown an exception that would have gone unhandled in managed code.
5077 // The runtime swallows all such exceptions. Hence, if we are not using LastThrownObject and the current LastThrownObject
5078 // is not the same as the one in active exception tracker (if available), then update the last thrown object.
5079 if ((pParam->pThread != NULL) && (!useLastThrownObject))
5080 {
5081 GCX_COOP_NO_DTOR();
5082
5083 OBJECTREF oThrowable = pParam->pThread->GetThrowable();
5084 if ((oThrowable != NULL) && (pParam->pThread->LastThrownObject() != oThrowable))
5085 {
5086 pParam->pThread->SafeSetLastThrownObject(oThrowable);
5087 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Resetting the LastThrownObject as it appears to have changed.\n"));
5088 }
5089
5090 GCX_COOP_NO_DTOR_END();
5091 }
5092
5093 // Launch Watson and see if we want to debug the process
5094 //
5095 // Note that we need to do this before "ignoring" exceptions like
5096 // breakpoints and single step exceptions
5097 //
5098
5099 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Launching Watson at sp %p ...\n", GetCurrentSP()));
5100
5101 if (WatsonLastChance(pParam->pThread, pParam->pExceptionInfo, tore) == EXCEPTION_CONTINUE_EXECUTION)
5102 {
5103 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: debugger ==> EXCEPTION_CONTINUE_EXECUTION\n"));
5104 pParam->retval = EXCEPTION_CONTINUE_EXECUTION;
5105 goto lDone;
5106 }
5107
5108 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: ... returned.\n"));
5109#endif // DEBUGGING_SUPPORTED
5110
5111
5112 //
5113 // Except for notifying debugger, ignore exception if unmanaged, or
5114 // if it's a debugger-generated exception or user breakpoint exception.
5115 //
5116 if (tore.GetType() == TypeOfReportedError::NativeThreadUnhandledException)
5117 {
5118 pParam->retval = EXCEPTION_CONTINUE_SEARCH;
5119#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL)
5120 DoReportForUnhandledNativeException(pParam->pExceptionInfo);
5121#endif
5122 goto lDone;
5123 }
5124
5125 if (pParam->fIgnore)
5126 {
5127 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker, ignoring the exception\n"));
5128 pParam->retval = EXCEPTION_CONTINUE_SEARCH;
5129#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL)
5130 DoReportForUnhandledNativeException(pParam->pExceptionInfo);
5131#endif
5132 goto lDone;
5133 }
5134
5135 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Calling DefaultCatchHandler\n"));
5136
5137 // Call our default catch handler to do the managed unhandled exception work.
5138 DefaultCatchHandler(pParam->pExceptionInfo, NULL, useLastThrownObject,
5139 TRUE /*isTerminating*/, FALSE /*isThreadBaseFIlter*/, FALSE /*sendAppDomainEvents*/, TRUE /* sendWindowsEventLog */);
5140
5141lDone: ;
5142 }
5143 PAL_EXCEPT_FILTER (SaveIPFilter)
5144 {
5145 // Should never get here.
5146#ifdef _DEBUG
5147 char buffer[200];
5148 sprintf_s(buffer, 200, "\nInternal error: Uncaught exception was thrown from IP = %p in UnhandledExceptionFilter_Worker on thread 0x%08x\n",
5149 param.ExceptionEIP, ((GetThread() == NULL) ? NULL : GetThread()->GetThreadId()));
5150 PrintToStdErrA(buffer);
5151 _ASSERTE(!"Unexpected exception in UnhandledExceptionFilter_Worker");
5152#endif
5153 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE)
5154 }
5155 PAL_ENDTRY;
5156
5157 //if (param.fIgnore)
5158 //{
5159 // VC's try/catch ignores breakpoint or single step exceptions. We can not continue running.
5160 // TerminateProcess(GetCurrentProcess(), pExceptionInfo->ExceptionRecord->ExceptionCode);
5161 //}
5162
5163 return param.retval;
5164} // LONG InternalUnhandledExceptionFilter_Worker()
5165
5166//------------------------------------------------------------------------------
5167// Description
5168// Calls our InternalUnhandledExceptionFilter for Watson at the appropriate
5169// place in the chain.
5170//
5171// For non-side-by-side CLR's, we call everyone else's UEF first.
5172//
5173// For side-by-side CLR's, we call our own filter first. This is primary
5174// so Whidbey's UEF won't put up a second dialog box. In exchange,
5175// side-by-side CLR's won't put up UI's unless the EH really came
5176// from that instance's managed code.
5177//
5178// Parameters
5179// pExceptionInfo -- information about the exception that caused the error.
5180// If the error is not the result of an exception, pass NULL for this
5181// parameter
5182//
5183// Returns
5184// EXCEPTION_CONTINUE_SEARCH -- we've done anything we will with the exception.
5185// As far as the runtime is concerned, the process is doomed.
5186// EXCEPTION_CONTINUE_EXECUTION -- means a debugger "caught" the exception and
5187// wants to continue running.
5188//------------------------------------------------------------------------------
5189LONG InternalUnhandledExceptionFilter(
5190 EXCEPTION_POINTERS *pExceptionInfo) // Information about the exception
5191{
5192 STATIC_CONTRACT_THROWS;
5193 STATIC_CONTRACT_GC_TRIGGERS;
5194 STATIC_CONTRACT_MODE_ANY;
5195 // We don't need to be SO-robust for an unhandled exception
5196 SO_NOT_MAINLINE_FUNCTION;
5197
5198 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter: at sp %p.\n", GetCurrentSP()));
5199
5200 // Side-by-side UEF: Calls ours first, then the rest (unless we put up a UI for
5201 // the exception.)
5202
5203 LONG retval = InternalUnhandledExceptionFilter_Worker(pExceptionInfo); // Result of UEF filter.
5204
5205 // Keep looking, or done?
5206 if (retval != EXCEPTION_CONTINUE_SEARCH)
5207 { // done.
5208 return retval;
5209 }
5210
5211 BOOL fShouldOurUEFDisplayUI = ShouldOurUEFDisplayUI(pExceptionInfo);
5212
5213 // If this is a managed exception thrown by this instance of the CLR, the exception is no one's
5214 // business but ours (nudge, nudge: Whidbey). Break the UEF chain at this point.
5215 if (fShouldOurUEFDisplayUI)
5216 {
5217 return retval;
5218 }
5219
5220 // Chaining back to previous UEF handler could be a potential security risk. See
5221 // http://uninformed.org/index.cgi?v=4&a=5&p=1 for details. We are not alone in
5222 // stopping the chain - CRT (as of Orcas) is also doing that.
5223 //
5224 // The change below applies to a thread that starts in native mode and transitions to managed.
5225
5226 // Let us assume the process loaded two CoreCLRs, C1 and C2, in that order. Thus, in the UEF chain
5227 // (assuming no other entity setup their UEF), C2?s UEF will be the topmost.
5228 //
5229 // Now, assume the stack looks like the following (stack grows down):
5230 //
5231 // Native frame
5232 // Managed Frame (C1)
5233 // Managed Frame (C2)
5234 // Managed Frame (C1)
5235 // Managed Frame (C2)
5236 // Managed Frame (C1)
5237 //
5238 // Suppose an exception is thrown in C1 instance in the last managed frame and it goes unhandled. Eventually
5239 // it will reach the OS which will invoke the UEF. Note that the topmost UEF belongs to C2 instance and it
5240 // will start processing the exception. C2?s UEF could return EXCEPTION_CONTINUE_SEARCH to indicate
5241 // that we should handoff the processing to the last installed UEF. In the example above, we would handoff
5242 // the control to the UEF of the CoreCLR instance that actually threw the exception today. In reality, it
5243 // could be some unknown code too.
5244 //
5245 // Not chaining back to the last UEF, in the case of this example, would imply that certain notifications
5246 // (e.g. Unhandled Exception Notification to the AppDomain) specific to the instance that raised the exception
5247 // will not get fired. However, similar behavior can happen today if another UEF sits between
5248 // C1 and C2 and that may not callback to C1 or perhaps just terminate process.
5249 //
5250 // For CoreCLR, this will not be an issue. See
5251 // http://sharepoint/sites/clros/Shared%20Documents/Design%20Documents/EH/Chaining%20in%20%20UEF%20-%20One%20Pager.docx
5252 // for details.
5253 //
5254 // Note: Also see the conditional UEF registration with the OS in EEStartupHelper.
5255
5256 // We would be here only on CoreCLR for WLC since we dont register
5257 // the UEF with the OS for SL.
5258 if (g_pOriginalUnhandledExceptionFilter != FILTER_NOT_INSTALLED
5259 && g_pOriginalUnhandledExceptionFilter != NULL)
5260 {
5261 STRESS_LOG1(LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter: Not chaining back to previous UEF at address %p on CoreCLR!\n", g_pOriginalUnhandledExceptionFilter);
5262 }
5263
5264 return retval;
5265
5266} // LONG InternalUnhandledExceptionFilter()
5267
5268// This filter is used to trigger unhandled exception processing for the entrypoint thread
5269// incase an exception goes unhandled from it. This makes us independent of the OS
5270// UEF mechanism to invoke our registered UEF to trigger CLR specific unhandled exception
5271// processing since that can be skipped if another UEF registered over ours and not chain back.
5272LONG EntryPointFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID _pData)
5273{
5274 CONTRACTL
5275 {
5276 THROWS;
5277 GC_TRIGGERS;
5278 MODE_ANY;
5279 SO_TOLERANT;
5280 }
5281 CONTRACTL_END;
5282
5283 LONG ret = -1;
5284
5285 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return EXCEPTION_CONTINUE_SEARCH;);
5286
5287 // Invoke the UEF worker to perform unhandled exception processing
5288 ret = InternalUnhandledExceptionFilter_Worker (pExceptionInfo);
5289
5290 Thread* pThread = GetThread();
5291 if (pThread)
5292 {
5293 // Set the flag that we have done unhandled exception processing for this thread
5294 // so that we dont duplicate the effort in the UEF.
5295 //
5296 // For details on this flag, refer to threads.h.
5297 LOG((LF_EH, LL_INFO100, "EntryPointFilter: setting TSNC_ProcessedUnhandledException\n"));
5298 pThread->SetThreadStateNC(Thread::TSNC_ProcessedUnhandledException);
5299 }
5300
5301
5302 END_SO_INTOLERANT_CODE;
5303
5304 return ret;
5305}
5306
5307//------------------------------------------------------------------------------
5308// Description
5309// The actual UEF. Defers to InternalUnhandledExceptionFilter.
5310//
5311// Updated to be in its own code segment named CLR_UEF_SECTION_NAME to prevent
5312// "VirtualProtect" calls from affecting its pages and thus, its
5313// invocation. For details, see the comment within the implementation of
5314// CExecutionEngine::ClrVirtualProtect.
5315//
5316// Parameters
5317// pExceptionInfo -- information about the exception
5318//
5319// Returns
5320// the result of calling InternalUnhandledExceptionFilter
5321//------------------------------------------------------------------------------
5322#if !defined(FEATURE_PAL)
5323#pragma code_seg(push, uef, CLR_UEF_SECTION_NAME)
5324#endif // !FEATURE_PAL
5325LONG __stdcall COMUnhandledExceptionFilter( // EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION
5326 EXCEPTION_POINTERS *pExceptionInfo) // Information about the exception.
5327{
5328 STATIC_CONTRACT_THROWS;
5329 STATIC_CONTRACT_GC_TRIGGERS;
5330 STATIC_CONTRACT_MODE_ANY;
5331 // We don't need to be SO-robust for an unhandled exception
5332 SO_NOT_MAINLINE_FUNCTION;
5333
5334 LONG retVal = EXCEPTION_CONTINUE_SEARCH;
5335
5336 // Incase of unhandled exceptions on managed threads, we kick in our UE processing at the thread base and also invoke
5337 // UEF callbacks that various runtimes have registered with us. Once the callbacks return, we return back to the OS
5338 // to give other registered UEFs a chance to do their custom processing.
5339 //
5340 // If the topmost UEF registered with the OS belongs to mscoruef.dll (or someone chained back to its UEF callback),
5341 // it will start invoking the UEF callbacks (which is this function, COMUnhandledExceptionFiler) registered by
5342 // various runtimes again.
5343 //
5344 // Thus, check if this UEF has already been invoked in context of this thread and runtime and if so, dont invoke it again.
5345 if (GetThread() && (GetThread()->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException) ||
5346 GetThread()->HasThreadStateNC(Thread::TSNC_AppDomainContainUnhandled)))
5347 {
5348 LOG((LF_EH, LL_INFO10, "Exiting COMUnhandledExceptionFilter since we have already done UE processing for this thread!\n"));
5349 return retVal;
5350 }
5351
5352
5353 retVal = InternalUnhandledExceptionFilter(pExceptionInfo);
5354
5355 // If thread object exists, mark that this thread has done unhandled exception processing
5356 if (GetThread())
5357 {
5358 LOG((LF_EH, LL_INFO100, "COMUnhandledExceptionFilter: setting TSNC_ProcessedUnhandledException\n"));
5359 GetThread()->SetThreadStateNC(Thread::TSNC_ProcessedUnhandledException);
5360 }
5361
5362 return retVal;
5363} // LONG __stdcall COMUnhandledExceptionFilter()
5364#if !defined(FEATURE_PAL)
5365#pragma code_seg(pop, uef)
5366#endif // !FEATURE_PAL
5367
5368void PrintStackTraceToStdout();
5369
5370static SString GetExceptionMessageWrapper(Thread* pThread, OBJECTREF throwable)
5371{
5372 STATIC_CONTRACT_THROWS;
5373 STATIC_CONTRACT_MODE_COOPERATIVE;
5374 STATIC_CONTRACT_GC_TRIGGERS;
5375
5376 StackSString result;
5377
5378 INSTALL_NESTED_EXCEPTION_HANDLER(pThread->GetFrame());
5379 GetExceptionMessage(throwable, result);
5380 UNINSTALL_NESTED_EXCEPTION_HANDLER();
5381
5382 return result;
5383}
5384
5385void STDMETHODCALLTYPE
5386DefaultCatchHandlerExceptionMessageWorker(Thread* pThread,
5387 OBJECTREF throwable,
5388 __inout_ecount(buf_size) WCHAR *buf,
5389 const int buf_size,
5390 BOOL sendWindowsEventLog)
5391{
5392 GCPROTECT_BEGIN(throwable);
5393 if (throwable != NULL)
5394 {
5395 PrintToStdErrA("\n");
5396
5397 if (FAILED(UtilLoadResourceString(CCompRC::Error, IDS_EE_UNHANDLED_EXCEPTION, buf, buf_size)))
5398 {
5399 wcsncpy_s(buf, buf_size, SZ_UNHANDLED_EXCEPTION, SZ_UNHANDLED_EXCEPTION_CHARLEN);
5400 }
5401
5402 PrintToStdErrW(buf);
5403 PrintToStdErrA(" ");
5404
5405 SString message = GetExceptionMessageWrapper(pThread, throwable);
5406
5407 if (!message.IsEmpty())
5408 {
5409 NPrintToStdErrW(message, message.GetCount());
5410 }
5411
5412 PrintToStdErrA("\n");
5413
5414#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL)
5415 // Send the log to Windows Event Log
5416 if (sendWindowsEventLog && ShouldLogInEventLog())
5417 {
5418 EX_TRY
5419 {
5420 EventReporter reporter(EventReporter::ERT_UnhandledException);
5421
5422 if (IsException(throwable->GetMethodTable()))
5423 {
5424 if (!message.IsEmpty())
5425 {
5426 reporter.AddDescription(message);
5427 }
5428 reporter.Report();
5429 }
5430 else
5431 {
5432 StackSString s;
5433 TypeString::AppendType(s, TypeHandle(throwable->GetMethodTable()), TypeString::FormatNamespace | TypeString::FormatFullInst);
5434 reporter.AddDescription(s);
5435 LogCallstackForEventReporter(reporter);
5436 }
5437 }
5438 EX_CATCH
5439 {
5440 }
5441 EX_END_CATCH(SwallowAllExceptions);
5442 }
5443#endif
5444 }
5445 GCPROTECT_END();
5446}
5447
5448//******************************************************************************
5449// DefaultCatchHandler -- common processing for otherwise uncaught exceptions.
5450//******************************************************************************
5451void STDMETHODCALLTYPE
5452DefaultCatchHandler(PEXCEPTION_POINTERS pExceptionPointers,
5453 OBJECTREF *pThrowableIn,
5454 BOOL useLastThrownObject,
5455 BOOL isTerminating,
5456 BOOL isThreadBaseFilter,
5457 BOOL sendAppDomainEvents,
5458 BOOL sendWindowsEventLog)
5459{
5460 CONTRACTL
5461 {
5462 THROWS;
5463 GC_TRIGGERS;
5464 MODE_ANY;
5465 }
5466 CONTRACTL_END;
5467
5468 // <TODO> The strings in here should be translatable.</TODO>
5469 LOG((LF_EH, LL_INFO10, "In DefaultCatchHandler\n"));
5470
5471#if defined(_DEBUG)
5472 static bool bHaveInitialized_BreakOnUncaught = false;
5473 enum BreakOnUncaughtAction {
5474 breakOnNone = 0, // Default.
5475 breakOnAll = 1, // Always break.
5476 breakSelective = 2, // Break on exceptions application can catch,
5477 // but not ThreadAbort, AppdomainUnload
5478 breakOnMax = 2
5479 };
5480 static DWORD breakOnUncaught = breakOnNone;
5481
5482 if (!bHaveInitialized_BreakOnUncaught)
5483 {
5484 breakOnUncaught = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException);
5485 if (breakOnUncaught > breakOnMax)
5486 { // Could turn it off completely, or turn into legal value. Since it is debug code, be accommodating.
5487 breakOnUncaught = breakOnAll;
5488 }
5489 bHaveInitialized_BreakOnUncaught = true;
5490 }
5491
5492 if (breakOnUncaught == breakOnAll)
5493 {
5494 _ASSERTE(!"BreakOnUnCaughtException");
5495 }
5496
5497 int suppressSelectiveBreak = false; // to filter for the case where breakOnUncaught == "2"
5498#endif
5499
5500 Thread *pThread = GetThread();
5501
5502 // The following reduces a window for a race during shutdown.
5503 if (!pThread)
5504 {
5505 _ASSERTE(g_fEEShutDown);
5506 return;
5507 }
5508
5509 _ASSERTE(pThread);
5510
5511 ThreadPreventAsyncHolder prevAsync;
5512
5513 GCX_COOP();
5514
5515 OBJECTREF throwable;
5516
5517 if (pThrowableIn != NULL)
5518 {
5519 throwable = *pThrowableIn;
5520 }
5521 else if (useLastThrownObject)
5522 {
5523 throwable = pThread->LastThrownObject();
5524 }
5525 else
5526 {
5527 throwable = pThread->GetThrowable();
5528 }
5529
5530 // If we've got no managed object, then we can't send an event or print a message, so we just return.
5531 if (throwable == NULL)
5532 {
5533#ifdef LOGGING
5534 if (!pThread->IsRudeAbortInitiated())
5535 {
5536 LOG((LF_EH, LL_INFO10, "Unhandled exception, throwable == NULL\n"));
5537 }
5538#endif
5539
5540 return;
5541 }
5542
5543#ifdef _DEBUG
5544 DWORD unbreakableLockCount = 0;
5545 // Do not care about lock check for unhandled exception.
5546 while (pThread->HasUnbreakableLock())
5547 {
5548 pThread->DecUnbreakableLockCount();
5549 unbreakableLockCount ++;
5550 }
5551 BOOL fOwnsSpinLock = pThread->HasThreadStateNC(Thread::TSNC_OwnsSpinLock);
5552 if (fOwnsSpinLock)
5553 {
5554 pThread->ResetThreadStateNC(Thread::TSNC_OwnsSpinLock);
5555 }
5556#endif
5557
5558 GCPROTECT_BEGIN(throwable);
5559 //BOOL IsStackOverflow = (throwable->GetMethodTable() == g_pStackOverflowExceptionClass);
5560 BOOL IsOutOfMemory = (throwable->GetMethodTable() == g_pOutOfMemoryExceptionClass);
5561
5562 // Notify the AppDomain that we have taken an unhandled exception. Can't notify of stack overflow -- guard
5563 // page is not yet reset.
5564 BOOL SentEvent = FALSE;
5565
5566 // Send up the unhandled exception appdomain event.
5567 if (sendAppDomainEvents)
5568 {
5569 SentEvent = NotifyAppDomainsOfUnhandledException(pExceptionPointers, &throwable, useLastThrownObject, isTerminating);
5570 }
5571
5572 const int buf_size = 128;
5573 WCHAR buf[buf_size] = {0};
5574
5575 // See detailed explanation of this flag in threads.cpp. But the basic idea is that we already
5576 // reported the exception in the AppDomain where it went unhandled, so we don't need to report
5577 // it at the process level.
5578 // Print the unhandled exception message.
5579 if (!pThread->HasThreadStateNC(Thread::TSNC_AppDomainContainUnhandled))
5580 {
5581 EX_TRY
5582 {
5583 EX_TRY
5584 {
5585 // If this isn't ThreadAbortException, we want to print a stack trace to indicate why this thread abruptly
5586 // terminated. Exceptions kill threads rarely enough that an uncached name check is reasonable.
5587 BOOL dump = TRUE;
5588
5589 if (/*IsStackOverflow ||*/
5590 !pThread->DetermineIfGuardPagePresent() ||
5591 IsOutOfMemory)
5592 {
5593 // We have to be very careful. If we walk off the end of the stack, the process will just
5594 // die. e.g. IsAsyncThreadException() and Exception.ToString both consume too much stack -- and can't
5595 // be called here.
5596 dump = FALSE;
5597 PrintToStdErrA("\n");
5598
5599 if (FAILED(UtilLoadStringRC(IDS_EE_UNHANDLED_EXCEPTION, buf, buf_size)))
5600 {
5601 wcsncpy_s(buf, COUNTOF(buf), SZ_UNHANDLED_EXCEPTION, SZ_UNHANDLED_EXCEPTION_CHARLEN);
5602 }
5603
5604 PrintToStdErrW(buf);
5605
5606 if (IsOutOfMemory)
5607 {
5608 PrintToStdErrA(" OutOfMemoryException.\n");
5609 }
5610 else
5611 {
5612 PrintToStdErrA(" StackOverflowException.\n");
5613 }
5614 }
5615 else if (!CanRunManagedCode(LoaderLockCheck::None))
5616 {
5617 // Well, if we can't enter the runtime, we very well can't get the exception message.
5618 dump = FALSE;
5619 }
5620 else if (SentEvent || IsAsyncThreadException(&throwable))
5621 {
5622 // We don't print anything on async exceptions, like ThreadAbort.
5623 dump = FALSE;
5624 INDEBUG(suppressSelectiveBreak=TRUE);
5625 }
5626
5627 // Finally, should we print the message?
5628 if (dump)
5629 {
5630 // this is stack heavy because of the CQuickWSTRBase, so we break it out
5631 // and don't have to carry the weight through our other code paths.
5632 DefaultCatchHandlerExceptionMessageWorker(pThread, throwable, buf, buf_size, sendWindowsEventLog);
5633 }
5634 }
5635 EX_CATCH
5636 {
5637 LOG((LF_EH, LL_INFO10, "Exception occurred while processing uncaught exception\n"));
5638 UtilLoadStringRC(IDS_EE_EXCEPTION_TOSTRING_FAILED, buf, buf_size);
5639 PrintToStdErrA("\n ");
5640 PrintToStdErrW(buf);
5641 PrintToStdErrA("\n");
5642 }
5643 EX_END_CATCH(SwallowAllExceptions);
5644 }
5645 EX_CATCH
5646 { // If we got here, we can't even print the localized error message. Print non-localized.
5647 LOG((LF_EH, LL_INFO10, "Exception occurred while logging processing uncaught exception\n"));
5648 PrintToStdErrA("\n Error: Can't print exception string because Exception.ToString() failed.\n");
5649 }
5650 EX_END_CATCH(SwallowAllExceptions);
5651 }
5652
5653#if defined(_DEBUG)
5654 if ((breakOnUncaught == breakSelective) && !suppressSelectiveBreak)
5655 {
5656 _ASSERTE(!"BreakOnUnCaughtException");
5657 }
5658#endif // defined(_DEBUG)
5659
5660 FlushLogging(); // Flush any logging output
5661 GCPROTECT_END();
5662
5663#ifdef _DEBUG
5664 // Do not care about lock check for unhandled exception.
5665 while (unbreakableLockCount)
5666 {
5667 pThread->IncUnbreakableLockCount();
5668 unbreakableLockCount --;
5669 }
5670 if (fOwnsSpinLock)
5671 {
5672 pThread->SetThreadStateNC(Thread::TSNC_OwnsSpinLock);
5673 }
5674#endif
5675} // DefaultCatchHandler()
5676
5677
5678//******************************************************************************
5679// NotifyAppDomainsOfUnhandledException -- common processing for otherwise uncaught exceptions.
5680//******************************************************************************
5681BOOL NotifyAppDomainsOfUnhandledException(
5682 PEXCEPTION_POINTERS pExceptionPointers,
5683 OBJECTREF *pThrowableIn,
5684 BOOL useLastThrownObject,
5685 BOOL isTerminating)
5686{
5687 CONTRACTL
5688 {
5689 THROWS;
5690 GC_TRIGGERS;
5691 MODE_ANY;
5692 }
5693 CONTRACTL_END;
5694
5695#ifdef _DEBUG
5696 static int fBreakOnNotify = -1;
5697 if (fBreakOnNotify==-1) fBreakOnNotify = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnNotify);
5698 _ASSERTE(!fBreakOnNotify);
5699#endif
5700
5701 BOOL SentEvent = FALSE;
5702
5703 LOG((LF_EH, LL_INFO10, "In NotifyAppDomainsOfUnhandledException\n"));
5704
5705 Thread *pThread = GetThread();
5706
5707 // The following reduces a window for a race during shutdown.
5708 if (!pThread)
5709 {
5710 _ASSERTE(g_fEEShutDown);
5711 return FALSE;
5712 }
5713
5714 // See detailed explanation of this flag in threads.cpp. But the basic idea is that we already
5715 // reported the exception in the AppDomain where it went unhandled, so we don't need to report
5716 // it at the process level.
5717 if (pThread->HasThreadStateNC(Thread::TSNC_AppDomainContainUnhandled))
5718 return FALSE;
5719
5720 ThreadPreventAsyncHolder prevAsync;
5721
5722 GCX_COOP();
5723
5724 OBJECTREF throwable;
5725
5726 if (pThrowableIn != NULL)
5727 {
5728 throwable = *pThrowableIn;
5729 }
5730 else if (useLastThrownObject)
5731 {
5732 throwable = pThread->LastThrownObject();
5733 }
5734 else
5735 {
5736 throwable = pThread->GetThrowable();
5737 }
5738
5739 // If we've got no managed object, then we can't send an event, so we just return.
5740 if (throwable == NULL)
5741 {
5742 return FALSE;
5743 }
5744
5745#ifdef _DEBUG
5746 DWORD unbreakableLockCount = 0;
5747 // Do not care about lock check for unhandled exception.
5748 while (pThread->HasUnbreakableLock())
5749 {
5750 pThread->DecUnbreakableLockCount();
5751 unbreakableLockCount ++;
5752 }
5753 BOOL fOwnsSpinLock = pThread->HasThreadStateNC(Thread::TSNC_OwnsSpinLock);
5754 if (fOwnsSpinLock)
5755 {
5756 pThread->ResetThreadStateNC(Thread::TSNC_OwnsSpinLock);
5757 }
5758#endif
5759
5760 GCPROTECT_BEGIN(throwable);
5761 //BOOL IsStackOverflow = (throwable->GetMethodTable() == g_pStackOverflowExceptionClass);
5762
5763 // Notify the AppDomain that we have taken an unhandled exception. Can't notify of stack overflow -- guard
5764 // page is not yet reset.
5765
5766 // Send up the unhandled exception appdomain event.
5767 //
5768 // If we can't run managed code, we can't deliver the event. Nor do we attempt to delieve the event in stack
5769 // overflow or OOM conditions.
5770 if (/*!IsStackOverflow &&*/
5771 pThread->DetermineIfGuardPagePresent() &&
5772 CanRunManagedCode(LoaderLockCheck::None))
5773 {
5774
5775 // x86 only
5776#if !defined(WIN64EXCEPTIONS)
5777 // If the Thread object's exception state's exception pointers
5778 // is null, use the passed-in pointer.
5779 BOOL bSetPointers = FALSE;
5780
5781 ThreadExceptionState* pExceptionState = pThread->GetExceptionState();
5782
5783 if (pExceptionState->GetExceptionPointers() == NULL)
5784 {
5785 bSetPointers = TRUE;
5786 pExceptionState->SetExceptionPointers(pExceptionPointers);
5787 }
5788
5789#endif // !defined(WIN64EXCEPTIONS)
5790
5791 INSTALL_NESTED_EXCEPTION_HANDLER(pThread->GetFrame());
5792
5793 // This guy will never throw, but it will need a spot to store
5794 // any nested exceptions it might find.
5795 SentEvent = AppDomain::OnUnhandledException(&throwable, isTerminating);
5796
5797 UNINSTALL_NESTED_EXCEPTION_HANDLER();
5798
5799#if !defined(WIN64EXCEPTIONS)
5800
5801 if (bSetPointers)
5802 {
5803 pExceptionState->SetExceptionPointers(NULL);
5804 }
5805
5806#endif // !defined(WIN64EXCEPTIONS)
5807
5808 }
5809
5810 GCPROTECT_END();
5811
5812#ifdef _DEBUG
5813 // Do not care about lock check for unhandled exception.
5814 while (unbreakableLockCount)
5815 {
5816 pThread->IncUnbreakableLockCount();
5817 unbreakableLockCount --;
5818 }
5819 if (fOwnsSpinLock)
5820 {
5821 pThread->SetThreadStateNC(Thread::TSNC_OwnsSpinLock);
5822 }
5823#endif
5824
5825 return SentEvent;
5826
5827} // NotifyAppDomainsOfUnhandledException()
5828
5829
5830//******************************************************************************
5831//
5832// ThreadBaseExceptionFilter_Worker
5833//
5834// The return from the function can be EXCEPTION_CONTINUE_SEARCH to let an
5835// exception go unhandled. This is the default behaviour (starting in v2.0),
5836// but can be overridden by hosts or by config file.
5837// When the behaviour is overridden, the return will be EXCEPTION_EXECUTE_HANDLER
5838// to swallow the exception.
5839// Note that some exceptions are always swallowed: ThreadAbort, and AppDomainUnload.
5840//
5841// Parameters:
5842// pExceptionInfo EXCEPTION_POINTERS for current exception
5843// _location A constant as an INT_PTR. Tells the context from whence called.
5844// swallowing Are we swallowing unhandled exceptions based on policy?
5845//
5846// Returns:
5847// EXCEPTION_CONTINUE_SEARCH Generally returns this to let the exception go unhandled.
5848// EXCEPTION_EXECUTE_HANDLER May return this to swallow the exception.
5849//
5850static LONG ThreadBaseExceptionFilter_Worker(PEXCEPTION_POINTERS pExceptionInfo,
5851 PVOID pvParam,
5852 BOOL swallowing)
5853{
5854 CONTRACTL
5855 {
5856 THROWS;
5857 GC_TRIGGERS;
5858 MODE_ANY;
5859 }
5860 CONTRACTL_END;
5861
5862 LOG((LF_EH, LL_INFO100, "ThreadBaseExceptionFilter_Worker: Enter\n"));
5863
5864 ThreadBaseExceptionFilterParam *pParam = (ThreadBaseExceptionFilterParam *) pvParam;
5865 UnhandledExceptionLocation location = pParam->location;
5866
5867 _ASSERTE(!g_fNoExceptions);
5868
5869 Thread* pThread = GetThread();
5870 _ASSERTE(pThread);
5871
5872#ifdef _DEBUG
5873 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException) &&
5874 !(swallowing && (SwallowUnhandledExceptions() || ExceptionIsAlwaysSwallowed(pExceptionInfo))) &&
5875 !(location == ClassInitUnhandledException && pThread->IsRudeAbortInitiated()))
5876 _ASSERTE(!"BreakOnUnCaughtException");
5877#endif
5878
5879 BOOL doDefault = ((location != ClassInitUnhandledException) &&
5880 (pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_BREAKPOINT) &&
5881 (pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_SINGLE_STEP));
5882
5883 if (swallowing)
5884 {
5885 // The default handling for versions v1.0 and v1.1 was to swallow unhandled exceptions.
5886 // With v2.0, the default is to let them go unhandled. Hosts & config files can modify the default
5887 // to retain the v1.1 behaviour.
5888 // Should we swallow this exception, or let it continue up and be unhandled?
5889 if (!SwallowUnhandledExceptions())
5890 {
5891 // No, don't swallow unhandled exceptions...
5892
5893 // ...except if the exception is of a type that is always swallowed (ThreadAbort, AppDomainUnload)...
5894 if (ExceptionIsAlwaysSwallowed(pExceptionInfo))
5895 { // ...return EXCEPTION_EXECUTE_HANDLER to swallow the exception anyway.
5896 return EXCEPTION_EXECUTE_HANDLER;
5897 }
5898
5899 #ifdef _DEBUG
5900 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException))
5901 _ASSERTE(!"BreakOnUnCaughtException");
5902 #endif
5903
5904 // ...so, continue search. i.e. let the exception go unhandled.
5905 return EXCEPTION_CONTINUE_SEARCH;
5906 }
5907 }
5908
5909#ifdef DEBUGGING_SUPPORTED
5910 // If there's a debugger (and not doing a thread abort), give the debugger a shot at the exception.
5911 // If the debugger is going to try to continue the exception, it will return ContinueException (which
5912 // we see here as EXCEPTION_CONTINUE_EXECUTION).
5913 if (!pThread->IsAbortRequested())
5914 {
5915 // TODO: do we really need this check? I don't think we do
5916 if(CORDebuggerAttached())
5917 {
5918 if (NotifyDebuggerLastChance(pThread, pExceptionInfo, FALSE) == EXCEPTION_CONTINUE_EXECUTION)
5919 {
5920 LOG((LF_EH, LL_INFO100, "ThreadBaseExceptionFilter_Worker: EXCEPTION_CONTINUE_EXECUTION\n"));
5921 return EXCEPTION_CONTINUE_EXECUTION;
5922 }
5923 }
5924 }
5925#endif // DEBUGGING_SUPPORTED
5926
5927 // Do default handling, but ignore breakpoint exceptions and class init exceptions
5928 if (doDefault)
5929 {
5930 LOG((LF_EH, LL_INFO100, "ThreadBaseExceptionFilter_Worker: Calling DefaultCatchHandler\n"));
5931
5932 BOOL useLastThrownObject = UpdateCurrentThrowable(pExceptionInfo->ExceptionRecord);
5933
5934 DefaultCatchHandler(pExceptionInfo,
5935 NULL,
5936 useLastThrownObject,
5937 FALSE,
5938 location == ManagedThread || location == ThreadPoolThread || location == FinalizerThread);
5939 }
5940
5941 // Return EXCEPTION_EXECUTE_HANDLER to swallow the exception.
5942 return (swallowing
5943 ? EXCEPTION_EXECUTE_HANDLER
5944 : EXCEPTION_CONTINUE_SEARCH);
5945} // LONG ThreadBaseExceptionFilter_Worker()
5946
5947
5948// This is the filter for new managed threads, for threadpool threads, and for
5949// running finalizer methods.
5950LONG ThreadBaseExceptionSwallowingFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pvParam)
5951{
5952 return ThreadBaseExceptionFilter_Worker(pExceptionInfo, pvParam, /*swallowing=*/true);
5953}
5954
5955// This was the filter for new managed threads in v1.0 and v1.1. Now used
5956// for delegate invoke, various things in the thread pool, and the
5957// class init handler.
5958LONG ThreadBaseExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pvParam)
5959{
5960 return ThreadBaseExceptionFilter_Worker(pExceptionInfo, pvParam, /*swallowing=*/false);
5961}
5962
5963
5964// This is the filter that we install when transitioning an AppDomain at the base of a managed
5965// thread. Nothing interesting will get swallowed after us. So we never decide to continue
5966// the search. Instead, we let it go unhandled and get the Watson report and debugging
5967// experience before the AD transition has an opportunity to catch/rethrow and lose all the
5968// relevant information.
5969LONG ThreadBaseExceptionAppDomainFilter(EXCEPTION_POINTERS *pExceptionInfo, PVOID pvParam)
5970{
5971 LONG ret = ThreadBaseExceptionSwallowingFilter(pExceptionInfo, pvParam);
5972
5973 if (ret != EXCEPTION_CONTINUE_SEARCH)
5974 return ret;
5975
5976 // Consider the exception to be unhandled
5977 return InternalUnhandledExceptionFilter_Worker(pExceptionInfo);
5978}
5979
5980// Filter for calls out from the 'vm' to native code, if there's a possibility of SEH exceptions
5981// in the native code.
5982LONG CallOutFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pv)
5983{
5984 CallOutFilterParam *pParam = static_cast<CallOutFilterParam *>(pv);
5985
5986 _ASSERTE(pParam->OneShot && (pParam->OneShot == TRUE || pParam->OneShot == FALSE));
5987
5988 if (pParam->OneShot == TRUE)
5989 {
5990 pParam->OneShot = FALSE;
5991
5992 // Replace whatever SEH exception is in flight, with an SEHException derived from
5993 // CLRException. But if the exception already looks like one of ours, let it
5994 // go past since LastThrownObject should already represent it.
5995 if ((!IsComPlusException(pExceptionInfo->ExceptionRecord)) &&
5996 (pExceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_MSVC))
5997 PAL_CPP_THROW(SEHException *, new SEHException(pExceptionInfo->ExceptionRecord,
5998 pExceptionInfo->ContextRecord));
5999 }
6000 return EXCEPTION_CONTINUE_SEARCH;
6001}
6002
6003
6004//==========================================================================
6005// Convert the format string used by sprintf to the format used by String.Format.
6006// Using the managed formatting routine avoids bogus access violations
6007// that happen for long strings in Win32's FormatMessage.
6008//
6009// Note: This is not general purpose routine. It handles only cases found
6010// in TypeLoadException and FileLoadException.
6011//==========================================================================
6012static BOOL GetManagedFormatStringForResourceID(CCompRC::ResourceCategory eCategory, UINT32 resId, SString & converted)
6013{
6014 STANDARD_VM_CONTRACT;
6015
6016 StackSString temp;
6017 if (!temp.LoadResource(eCategory, resId))
6018 return FALSE;
6019
6020 SString::Iterator itr = temp.Begin();
6021 while (*itr)
6022 {
6023 WCHAR c = *itr++;
6024 switch (c) {
6025 case '%':
6026 {
6027 WCHAR fmt = *itr++;
6028 if (fmt >= '1' && fmt <= '9') {
6029 converted.Append(W("{"));
6030 converted.Append(fmt - 1); // the managed args start at 0
6031 converted.Append(W("}"));
6032 }
6033 else
6034 if (fmt == '%') {
6035 converted.Append(W("%"));
6036 }
6037 else {
6038 _ASSERTE(!"Unexpected formating string: %s");
6039 }
6040 }
6041 break;
6042 case '{':
6043 converted.Append(W("{{"));
6044 break;
6045 case '}':
6046 converted.Append(W("}}"));
6047 break;
6048 default:
6049 converted.Append(c);
6050 break;
6051 }
6052 }
6053 return TRUE;
6054}
6055
6056//==========================================================================
6057// Private helper for TypeLoadException.
6058//==========================================================================
6059void QCALLTYPE GetTypeLoadExceptionMessage(UINT32 resId, QCall::StringHandleOnStack retString)
6060{
6061 QCALL_CONTRACT;
6062
6063 BEGIN_QCALL;
6064
6065 StackSString format;
6066 GetManagedFormatStringForResourceID(CCompRC::Error, resId ? resId : IDS_CLASSLOAD_GENERAL, format);
6067 retString.Set(format);
6068
6069 END_QCALL;
6070}
6071
6072
6073
6074//==========================================================================
6075// Private helper for FileLoadException and FileNotFoundException.
6076//==========================================================================
6077
6078void QCALLTYPE GetFileLoadExceptionMessage(UINT32 hr, QCall::StringHandleOnStack retString)
6079{
6080 QCALL_CONTRACT;
6081
6082 BEGIN_QCALL;
6083
6084 StackSString format;
6085 GetManagedFormatStringForResourceID(CCompRC::Error, GetResourceIDForFileLoadExceptionHR(hr), format);
6086 retString.Set(format);
6087
6088 END_QCALL;
6089}
6090
6091//==========================================================================
6092// Private helper for FileLoadException and FileNotFoundException.
6093//==========================================================================
6094void QCALLTYPE FileLoadException_GetMessageForHR(UINT32 hresult, QCall::StringHandleOnStack retString)
6095{
6096 QCALL_CONTRACT;
6097
6098 BEGIN_QCALL;
6099
6100 BOOL bNoGeekStuff = FALSE;
6101 switch ((HRESULT)hresult)
6102 {
6103 // These are not usually app errors - as long
6104 // as the message is reasonably clear, we can live without the hex code stuff.
6105 case COR_E_FILENOTFOUND:
6106 case __HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND):
6107 case __HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND):
6108 case __HRESULT_FROM_WIN32(ERROR_INVALID_NAME):
6109 case __HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME):
6110 case __HRESULT_FROM_WIN32(ERROR_BAD_NETPATH):
6111 case __HRESULT_FROM_WIN32(ERROR_DLL_NOT_FOUND):
6112 case CTL_E_FILENOTFOUND:
6113 case COR_E_DLLNOTFOUND:
6114 case COR_E_PATHTOOLONG:
6115 case E_ACCESSDENIED:
6116 case COR_E_BADIMAGEFORMAT:
6117 case COR_E_NEWER_RUNTIME:
6118 case COR_E_ASSEMBLYEXPECTED:
6119 bNoGeekStuff = TRUE;
6120 break;
6121 }
6122
6123 SString s;
6124 GetHRMsg((HRESULT)hresult, s, bNoGeekStuff);
6125 retString.Set(s);
6126
6127 END_QCALL;
6128}
6129
6130
6131#define ValidateSigBytes(_size) do { if ((_size) > csig) COMPlusThrow(kArgumentException, W("Argument_BadSigFormat")); csig -= (_size); } while (false)
6132
6133//==========================================================================
6134// Unparses an individual type.
6135//==========================================================================
6136const BYTE *UnparseType(const BYTE *pType, DWORD& csig, StubLinker *psl)
6137{
6138 CONTRACTL
6139 {
6140 THROWS;
6141 GC_NOTRIGGER;
6142 MODE_ANY;
6143 INJECT_FAULT(ThrowOutOfMemory();); // Emitting data to the StubLinker can throw OOM.
6144 }
6145 CONTRACTL_END;
6146
6147 LPCUTF8 pName = NULL;
6148
6149 ValidateSigBytes(sizeof(BYTE));
6150 switch ( (CorElementType) *(pType++) ) {
6151 case ELEMENT_TYPE_VOID:
6152 psl->EmitUtf8("void");
6153 break;
6154
6155 case ELEMENT_TYPE_BOOLEAN:
6156 psl->EmitUtf8("boolean");
6157 break;
6158
6159 case ELEMENT_TYPE_CHAR:
6160 psl->EmitUtf8("char");
6161 break;
6162
6163 case ELEMENT_TYPE_U1:
6164 psl->EmitUtf8("unsigned ");
6165 //fallthru
6166 case ELEMENT_TYPE_I1:
6167 psl->EmitUtf8("byte");
6168 break;
6169
6170 case ELEMENT_TYPE_U2:
6171 psl->EmitUtf8("unsigned ");
6172 //fallthru
6173 case ELEMENT_TYPE_I2:
6174 psl->EmitUtf8("short");
6175 break;
6176
6177 case ELEMENT_TYPE_U4:
6178 psl->EmitUtf8("unsigned ");
6179 //fallthru
6180 case ELEMENT_TYPE_I4:
6181 psl->EmitUtf8("int");
6182 break;
6183
6184 case ELEMENT_TYPE_I:
6185 psl->EmitUtf8("native int");
6186 break;
6187 case ELEMENT_TYPE_U:
6188 psl->EmitUtf8("native unsigned");
6189 break;
6190
6191 case ELEMENT_TYPE_U8:
6192 psl->EmitUtf8("unsigned ");
6193 //fallthru
6194 case ELEMENT_TYPE_I8:
6195 psl->EmitUtf8("long");
6196 break;
6197
6198
6199 case ELEMENT_TYPE_R4:
6200 psl->EmitUtf8("float");
6201 break;
6202
6203 case ELEMENT_TYPE_R8:
6204 psl->EmitUtf8("double");
6205 break;
6206
6207 case ELEMENT_TYPE_STRING:
6208 psl->EmitUtf8(g_StringName);
6209 break;
6210
6211 case ELEMENT_TYPE_VAR:
6212 case ELEMENT_TYPE_OBJECT:
6213 psl->EmitUtf8(g_ObjectName);
6214 break;
6215
6216 case ELEMENT_TYPE_PTR:
6217 pType = UnparseType(pType, csig, psl);
6218 psl->EmitUtf8("*");
6219 break;
6220
6221 case ELEMENT_TYPE_BYREF:
6222 pType = UnparseType(pType, csig, psl);
6223 psl->EmitUtf8("&");
6224 break;
6225
6226 case ELEMENT_TYPE_VALUETYPE:
6227 case ELEMENT_TYPE_CLASS:
6228 pName = (LPCUTF8)pType;
6229 while (true) {
6230 ValidateSigBytes(sizeof(CHAR));
6231 if (*(pType++) == '\0')
6232 break;
6233 }
6234 psl->EmitUtf8(pName);
6235 break;
6236
6237 case ELEMENT_TYPE_SZARRAY:
6238 {
6239 pType = UnparseType(pType, csig, psl);
6240 psl->EmitUtf8("[]");
6241 }
6242 break;
6243
6244 case ELEMENT_TYPE_ARRAY:
6245 {
6246 pType = UnparseType(pType, csig, psl);
6247 ValidateSigBytes(sizeof(DWORD));
6248 DWORD rank = GET_UNALIGNED_VAL32(pType);
6249 pType += sizeof(DWORD);
6250 if (rank)
6251 {
6252 ValidateSigBytes(sizeof(UINT32));
6253 UINT32 nsizes = GET_UNALIGNED_VAL32(pType); // Get # of sizes
6254 ValidateSigBytes(nsizes * sizeof(UINT32));
6255 pType += 4 + nsizes*4;
6256 ValidateSigBytes(sizeof(UINT32));
6257 UINT32 nlbounds = GET_UNALIGNED_VAL32(pType); // Get # of lower bounds
6258 ValidateSigBytes(nlbounds * sizeof(UINT32));
6259 pType += 4 + nlbounds*4;
6260
6261
6262 while (rank--) {
6263 psl->EmitUtf8("[]");
6264 }
6265
6266}
6267
6268 }
6269 break;
6270
6271 case ELEMENT_TYPE_TYPEDBYREF:
6272 psl->EmitUtf8("&");
6273 break;
6274
6275 case ELEMENT_TYPE_FNPTR:
6276 psl->EmitUtf8("ftnptr");
6277 break;
6278
6279 default:
6280 psl->EmitUtf8("?");
6281 break;
6282 }
6283
6284 return pType;
6285 }
6286
6287
6288
6289//==========================================================================
6290// Helper for MissingMemberException.
6291//==========================================================================
6292static STRINGREF MissingMemberException_FormatSignature_Internal(I1ARRAYREF* ppPersistedSig)
6293{
6294 CONTRACTL
6295 {
6296 THROWS;
6297 GC_TRIGGERS;
6298 MODE_COOPERATIVE;
6299 INJECT_FAULT(ThrowOutOfMemory(););
6300 }
6301 CONTRACTL_END;
6302
6303 STRINGREF pString = NULL;
6304
6305 DWORD csig = 0;
6306 const BYTE *psig = 0;
6307 StubLinker *psl = NULL;
6308 StubHolder<Stub> pstub;
6309
6310 if ((*ppPersistedSig) != NULL)
6311 csig = (*ppPersistedSig)->GetNumComponents();
6312
6313 if (csig == 0)
6314 {
6315 return StringObject::NewString("Unknown signature");
6316 }
6317
6318 psig = (const BYTE*)_alloca(csig);
6319 CopyMemory((BYTE*)psig,
6320 (*ppPersistedSig)->GetDirectPointerToNonObjectElements(),
6321 csig);
6322
6323 {
6324 GCX_PREEMP();
6325
6326 StubLinker sl;
6327 psl = &sl;
6328 pstub = NULL;
6329
6330 ValidateSigBytes(sizeof(UINT32));
6331 UINT32 cconv = GET_UNALIGNED_VAL32(psig);
6332 psig += 4;
6333
6334 if (cconv == IMAGE_CEE_CS_CALLCONV_FIELD) {
6335 psig = UnparseType(psig, csig, psl);
6336 } else {
6337 ValidateSigBytes(sizeof(UINT32));
6338 UINT32 nargs = GET_UNALIGNED_VAL32(psig);
6339 psig += 4;
6340
6341 // Unparse return type
6342 psig = UnparseType(psig, csig, psl);
6343 psl->EmitUtf8("(");
6344 while (nargs--) {
6345 psig = UnparseType(psig, csig, psl);
6346 if (nargs) {
6347 psl->EmitUtf8(", ");
6348 }
6349 }
6350 psl->EmitUtf8(")");
6351 }
6352 psl->Emit8('\0');
6353
6354 pstub = psl->Link(NULL);
6355 }
6356
6357 pString = StringObject::NewString( (LPCUTF8)(pstub->GetEntryPoint()) );
6358 return pString;
6359}
6360
6361FCIMPL1(Object*, MissingMemberException_FormatSignature, I1Array* pPersistedSigUNSAFE)
6362{
6363 FCALL_CONTRACT;
6364
6365 STRINGREF pString = NULL;
6366 I1ARRAYREF pPersistedSig = (I1ARRAYREF) pPersistedSigUNSAFE;
6367 HELPER_METHOD_FRAME_BEGIN_RET_1(pPersistedSig);
6368
6369 pString = MissingMemberException_FormatSignature_Internal(&pPersistedSig);
6370
6371 HELPER_METHOD_FRAME_END();
6372 return OBJECTREFToObject(pString);
6373}
6374FCIMPLEND
6375
6376// Check if there is a pending exception or the thread is already aborting. Returns 0 if yes.
6377// Otherwise, sets the thread up for generating an abort and returns address of ThrowControlForThread
6378// It is the caller's responsibility to set up Thread::m_OSContext prior to this call. This is used as
6379// the context for checking if a ThreadAbort is allowed, and also as the context for the ThreadAbortException
6380// itself.
6381LPVOID COMPlusCheckForAbort(UINT_PTR uTryCatchResumeAddress)
6382{
6383 CONTRACTL
6384 {
6385 NOTHROW;
6386 GC_NOTRIGGER;
6387 MODE_ANY;
6388 SO_TOLERANT;
6389 }
6390 CONTRACTL_END;
6391
6392 // Initialize the return address
6393 LPVOID pRetAddress = 0;
6394
6395 Thread* pThread = GetThread();
6396
6397 if ((!pThread->IsAbortRequested()) || // if no abort has been requested
6398 (!pThread->IsRudeAbort() &&
6399 (pThread->GetThrowable() != NULL)) ) // or if there is a pending exception
6400 {
6401 goto exit;
6402 }
6403
6404 // Reverse COM interop IL stubs map all exceptions to HRESULTs and must not propagate Thread.Abort
6405 // to their unmanaged callers.
6406 if (uTryCatchResumeAddress != NULL)
6407 {
6408 MethodDesc * pMDResumeMethod = ExecutionManager::GetCodeMethodDesc((PCODE)uTryCatchResumeAddress);
6409 if (pMDResumeMethod->IsILStub())
6410 goto exit;
6411 }
6412
6413 // else we must produce an abort
6414 if ((pThread->GetThrowable() == NULL) &&
6415 (pThread->IsAbortInitiated()))
6416 {
6417 // Oops, we just swallowed an abort, must restart the process
6418 pThread->ResetAbortInitiated();
6419 }
6420
6421 // Question: Should we also check for (pThread->m_PreventAsync == 0)
6422
6423#if !defined(WIN64EXCEPTIONS) && defined(FEATURE_STACK_PROBE)
6424 // On Win64, this function is called by our exception handling code which has probed.
6425 // But on X86, this is called from JIT code directly. We probe here so that
6426 // we can restore the state of the thread below.
6427 if (GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) == eRudeUnloadAppDomain)
6428 {
6429 // In case of SO, we will skip the managed code.
6430 CONTRACT_VIOLATION(ThrowsViolation);
6431 RetailStackProbe(ADJUST_PROBE(DEFAULT_ENTRY_PROBE_AMOUNT), pThread);
6432 }
6433#endif // !WIN64EXCEPTIONS && FEATURE_STACK_PROBE
6434
6435 pThread->SetThrowControlForThread(Thread::InducedThreadRedirectAtEndOfCatch);
6436 if (!pThread->ReadyForAbort())
6437 {
6438 pThread->ResetThrowControlForThread();
6439 goto exit;
6440 }
6441 pThread->SetThrowControlForThread(Thread::InducedThreadStop);
6442
6443 pRetAddress = (LPVOID)THROW_CONTROL_FOR_THREAD_FUNCTION;
6444
6445exit:
6446
6447#ifndef FEATURE_PAL
6448
6449 // Only proceed if Watson is enabled - CoreCLR may have it disabled.
6450 if (IsWatsonEnabled())
6451 {
6452 BOOL fClearUEWatsonBucketTracker = TRUE;
6453 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pThread->GetExceptionState()->GetUEWatsonBucketTracker();
6454
6455 if (pRetAddress && pThread->IsAbortRequested())
6456 {
6457 // Since we are going to reraise the thread abort exception, we would like to assert that
6458 // the buckets present in the UE tracker are the ones which were setup TAE was first raised.
6459 //
6460 // However, these buckets could come from across AD transition as well and thus, would be
6461 // marked for "Captured at AD transition". Thus, we cannot just assert them to be only from
6462 // TAE raise.
6463 //
6464 // We try to preserve buckets incase there is another catch that may catch the exception we reraise
6465 // and it attempts to FailFast using the TA exception object. In such a case,
6466 // we should maintain the original exception point's bucket details.
6467 if (pUEWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL)
6468 {
6469 _ASSERTE(pUEWatsonBucketTracker->CapturedForThreadAbort() || pUEWatsonBucketTracker->CapturedAtADTransition());
6470 fClearUEWatsonBucketTracker = FALSE;
6471 }
6472#ifdef _DEBUG
6473 else
6474 {
6475 // If we are here and UE Watson bucket tracker is empty,
6476 // then it is possible that a thread abort was signalled when the catch was executing
6477 // and thus, hijack for TA from here is not a reraise but an initial raise.
6478 //
6479 // However, if we have partial details, then something is really not right.
6480 if (!((pUEWatsonBucketTracker->RetrieveWatsonBucketIp() == NULL) &&
6481 (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)))
6482 {
6483 _ASSERTE(!"How come TA is being [re]raised and we have incomplete watson bucket details?");
6484 }
6485 }
6486#endif // _DEBUG
6487 }
6488
6489 if (fClearUEWatsonBucketTracker)
6490 {
6491 // Clear the UE watson bucket tracker for future use since it does not have anything
6492 // useful for us right now.
6493 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
6494 LOG((LF_EH, LL_INFO100, "COMPlusCheckForAbort - Cleared UE watson bucket tracker since TAE was not being reraised.\n"));
6495 }
6496 }
6497
6498#endif // !FEATURE_PAL
6499
6500 return pRetAddress;
6501}
6502
6503
6504BOOL IsThreadHijackedForThreadStop(Thread* pThread, EXCEPTION_RECORD* pExceptionRecord)
6505{
6506 CONTRACTL
6507 {
6508 NOTHROW;
6509 GC_NOTRIGGER;
6510 MODE_ANY;
6511 FORBID_FAULT;
6512 SO_TOLERANT;
6513 }
6514 CONTRACTL_END;
6515
6516 if (IsComPlusException(pExceptionRecord))
6517 {
6518 if (pThread->ThrewControlForThread() == Thread::InducedThreadStop)
6519 {
6520 LOG((LF_EH, LL_INFO100, "Asynchronous Thread Stop or Abort\n"));
6521 return TRUE;
6522 }
6523 }
6524 else if (IsStackOverflowException(pThread, pExceptionRecord))
6525 {
6526 // SO happens before we are able to change the state to InducedThreadStop, but
6527 // we are still in our hijack routine.
6528 if (pThread->ThrewControlForThread() == Thread::InducedThreadRedirect)
6529 {
6530 LOG((LF_EH, LL_INFO100, "Asynchronous Thread Stop or Abort caused by SO\n"));
6531 return TRUE;
6532 }
6533 }
6534 return FALSE;
6535}
6536
6537// We sometimes move a thread's execution so it will throw an exception for us.
6538// But then we have to treat the exception as if it came from the instruction
6539// the thread was originally running.
6540//
6541// NOTE: This code depends on the fact that there are no register-based data dependencies
6542// between a try block and a catch, fault, or finally block. If there were, then we need
6543// to preserve more of the register context.
6544
6545void AdjustContextForThreadStop(Thread* pThread,
6546 CONTEXT* pContext)
6547{
6548 CONTRACTL
6549 {
6550 NOTHROW;
6551 GC_NOTRIGGER;
6552 MODE_ANY;
6553 FORBID_FAULT;
6554 SO_TOLERANT;
6555 }
6556 CONTRACTL_END;
6557
6558 _ASSERTE(pThread->m_OSContext);
6559
6560#ifndef WIN64EXCEPTIONS
6561 SetIP(pContext, GetIP(pThread->m_OSContext));
6562 SetSP(pContext, (GetSP(pThread->m_OSContext)));
6563
6564 if (GetFP(pThread->m_OSContext) != 0) // ebp = 0 implies that we got here with the right values for ebp
6565 {
6566 SetFP(pContext, GetFP(pThread->m_OSContext));
6567 }
6568
6569 // We might have been interrupted execution at a point where the jit has roots in
6570 // registers. We just need to store a "safe" value in here so that the collector
6571 // doesn't trap. We're not going to use these objects after the exception.
6572 //
6573 // Only callee saved registers are going to be reported by the faulting excepiton frame.
6574#if defined(_TARGET_X86_)
6575 // Ebx,esi,edi are important. Eax,ecx,edx are not.
6576 pContext->Ebx = 0;
6577 pContext->Edi = 0;
6578 pContext->Esi = 0;
6579#else
6580 PORTABILITY_ASSERT("AdjustContextForThreadStop");
6581#endif
6582
6583#else // !WIN64EXCEPTIONS
6584 CopyOSContext(pContext, pThread->m_OSContext);
6585#if defined(_TARGET_ARM_) && defined(_DEBUG)
6586 // Make sure that the thumb bit is set on the IP of the original abort context we just restored.
6587 PCODE controlPC = GetIP(pContext);
6588 _ASSERTE(controlPC & THUMB_CODE);
6589#endif // _TARGET_ARM_
6590#endif // !WIN64EXCEPTIONS
6591
6592 pThread->ResetThrowControlForThread();
6593
6594 // Should never get here if we're already throwing an exception.
6595 _ASSERTE(!pThread->IsExceptionInProgress() || pThread->IsRudeAbort());
6596
6597 // Should never get here if we're already abort initiated.
6598 _ASSERTE(!pThread->IsAbortInitiated() || pThread->IsRudeAbort());
6599
6600 if (pThread->IsAbortRequested())
6601 {
6602 pThread->SetAbortInitiated(); // to prevent duplicate aborts
6603 }
6604}
6605
6606// Create a COM+ exception , stick it in the thread.
6607OBJECTREF
6608CreateCOMPlusExceptionObject(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord, BOOL bAsynchronousThreadStop)
6609{
6610 CONTRACTL
6611 {
6612 NOTHROW;
6613 GC_TRIGGERS;
6614 MODE_COOPERATIVE;
6615 FORBID_FAULT;
6616 SO_TOLERANT;
6617 }
6618 CONTRACTL_END;
6619
6620 _ASSERTE(GetThread() == pThread);
6621
6622 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
6623
6624 OBJECTREF result = 0;
6625
6626 DWORD COMPlusExceptionCode = (bAsynchronousThreadStop
6627 ? kThreadAbortException
6628 : MapWin32FaultToCOMPlusException(pExceptionRecord));
6629
6630 if (exceptionCode == STATUS_NO_MEMORY)
6631 {
6632 result = CLRException::GetBestOutOfMemoryException();
6633 }
6634 else if (IsStackOverflowException(pThread, pExceptionRecord))
6635 {
6636 result = CLRException::GetPreallocatedStackOverflowException();
6637 }
6638 else if (bAsynchronousThreadStop && pThread->IsAbortRequested() && pThread->IsRudeAbort())
6639 {
6640 result = CLRException::GetPreallocatedRudeThreadAbortException();
6641 }
6642 else
6643 {
6644 EX_TRY
6645 {
6646 // We need to disable the backout stack validation at this point since CreateThrowable can
6647 // take arbitrarily large amounts of stack for different exception types; however we know
6648 // for a fact that we will never go through this code path if the exception is a stack
6649 // overflow exception since we already handled that case above with the pre-allocated SO exception.
6650 DISABLE_BACKOUT_STACK_VALIDATION;
6651
6652 FAULT_NOT_FATAL();
6653
6654 ThreadPreventAsyncHolder preventAsync;
6655 ResetProcessorStateHolder procState;
6656
6657 INSTALL_UNWIND_AND_CONTINUE_HANDLER;
6658
6659 GCPROTECT_BEGIN(result)
6660
6661 EEException e((RuntimeExceptionKind)COMPlusExceptionCode);
6662 result = e.CreateThrowable();
6663
6664 // EEException is "one size fits all". But AV needs some more information.
6665 if (COMPlusExceptionCode == kAccessViolationException)
6666 {
6667 SetExceptionAVParameters(result, pExceptionRecord);
6668 }
6669
6670 GCPROTECT_END();
6671
6672 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
6673 }
6674 EX_CATCH
6675 {
6676 // If we get an exception trying to build the managed exception object, then go ahead and return the
6677 // thrown object as the result of this function. This is preferable to letting the exception try to
6678 // percolate up through the EH code, and it effectively replaces the thrown exception with this
6679 // exception.
6680 result = GET_THROWABLE();
6681 }
6682 EX_END_CATCH(SwallowAllExceptions);
6683 }
6684
6685 return result;
6686}
6687
6688LONG FilterAccessViolation(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
6689{
6690 CONTRACTL
6691 {
6692 THROWS;
6693 GC_NOTRIGGER;
6694 MODE_ANY;
6695 FORBID_FAULT;
6696 }
6697 CONTRACTL_END;
6698
6699 if (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
6700 return EXCEPTION_EXECUTE_HANDLER;
6701
6702 return EXCEPTION_CONTINUE_SEARCH;
6703}
6704
6705/*
6706 * IsContinuableException
6707 *
6708 * Returns whether this is an exception the EE knows how to intercept and continue from.
6709 *
6710 * Parameters:
6711 * pThread - The thread the exception occurred on.
6712 *
6713 * Returns:
6714 * TRUE if the exception on the thread is interceptable or not.
6715 *
6716 * Notes:
6717 * Conditions for an interceptable exception:
6718 * 1) must be on a managed thread
6719 * 2) an exception must be in progress
6720 * 3) a managed exception object must have been created
6721 * 4) the thread must not be aborting
6722 * 5) the exception must not be a breakpoint, a single step, or a stack overflow
6723 * 6) the exception dispatch must be in the first pass
6724 * 7) the exception must not be a fatal error, as determined by the EE policy (see LogFatalError())
6725 */
6726bool IsInterceptableException(Thread *pThread)
6727{
6728 CONTRACTL
6729 {
6730 MODE_ANY;
6731 NOTHROW;
6732 GC_NOTRIGGER;
6733 }
6734 CONTRACTL_END;
6735
6736 return ((pThread != NULL) &&
6737 (!pThread->IsAbortRequested()) &&
6738 (pThread->IsExceptionInProgress()) &&
6739 (!pThread->IsThrowableNull())
6740
6741#ifdef DEBUGGING_SUPPORTED
6742 &&
6743 pThread->GetExceptionState()->IsDebuggerInterceptable()
6744#endif
6745
6746 );
6747}
6748
6749// Determines whether we hit an DO_A_GC_HERE marker in JITted code, and returns the
6750// appropriate exception code, or zero if the code is not a GC marker.
6751DWORD GetGcMarkerExceptionCode(LPVOID ip)
6752{
6753#if defined(HAVE_GCCOVER)
6754 WRAPPER_NO_CONTRACT;
6755
6756 if (GCStress<cfg_any>::IsEnabled() && IsGcCoverageInterrupt(ip))
6757 {
6758 return STATUS_CLR_GCCOVER_CODE;
6759 }
6760#else // defined(HAVE_GCCOVER)
6761 LIMITED_METHOD_CONTRACT;
6762#endif // defined(HAVE_GCCOVER)
6763 return 0;
6764}
6765
6766// Did we hit an DO_A_GC_HERE marker in JITted code?
6767bool IsGcMarker(CONTEXT* pContext, EXCEPTION_RECORD *pExceptionRecord)
6768{
6769 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
6770#ifdef HAVE_GCCOVER
6771 WRAPPER_NO_CONTRACT;
6772
6773 if (GCStress<cfg_any>::IsEnabled())
6774 {
6775#if defined(GCCOVER_TOLERATE_SPURIOUS_AV)
6776
6777 // We sometimes can't suspend the EE to update the GC marker instruction so
6778 // we update it directly without suspending. This can sometimes yield
6779 // a STATUS_ACCESS_VIOLATION instead of STATUS_CLR_GCCOVER_CODE. In
6780 // this case we let the AV through and retry the instruction as hopefully
6781 // the race will have resolved. We'll track the IP of the instruction
6782 // that generated an AV so we don't mix up a real AV with a "fake" AV.
6783 //
6784 // See comments in function DoGcStress for more details on this race.
6785 //
6786 // Note these "fake" AVs will be reported by the kernel as reads from
6787 // address 0xF...F so we also use that as a screen.
6788 Thread* pThread = GetThread();
6789 if (exceptionCode == STATUS_ACCESS_VIOLATION &&
6790 GCStress<cfg_instr>::IsEnabled() &&
6791 pExceptionRecord->ExceptionInformation[0] == 0 &&
6792 pExceptionRecord->ExceptionInformation[1] == ~0 &&
6793 pThread->GetLastAVAddress() != (LPVOID)GetIP(pContext) &&
6794 !IsIPInEE((LPVOID)GetIP(pContext)))
6795 {
6796 pThread->SetLastAVAddress((LPVOID)GetIP(pContext));
6797 return true;
6798 }
6799#endif // defined(GCCOVER_TOLERATE_SPURIOUS_AV)
6800
6801 if (exceptionCode == STATUS_CLR_GCCOVER_CODE)
6802 {
6803 if (OnGcCoverageInterrupt(pContext))
6804 {
6805 return true;
6806 }
6807
6808 {
6809 // ExecutionManager::IsManagedCode takes a spinlock. Since this is in a debug-only
6810 // check, we'll allow the lock.
6811 CONTRACT_VIOLATION(TakesLockViolation);
6812
6813 // Should never be in managed code.
6814 CONSISTENCY_CHECK_MSG(!ExecutionManager::IsManagedCode(GetIP(pContext)), "hit privileged instruction!");
6815 }
6816 }
6817 }
6818#else
6819 LIMITED_METHOD_CONTRACT;
6820#endif // HAVE_GCCOVER
6821 return false;
6822}
6823
6824#ifndef FEATURE_PAL
6825
6826// Return true if the access violation is well formed (has two info parameters
6827// at the end)
6828static inline BOOL
6829IsWellFormedAV(EXCEPTION_RECORD *pExceptionRecord)
6830{
6831 LIMITED_METHOD_CONTRACT;
6832
6833 #define NUM_AV_PARAMS 2
6834
6835 if (pExceptionRecord->NumberParameters == NUM_AV_PARAMS)
6836 {
6837 return TRUE;
6838 }
6839 else
6840 {
6841 return FALSE;
6842 }
6843}
6844
6845static inline BOOL
6846IsDebuggerFault(EXCEPTION_RECORD *pExceptionRecord,
6847 CONTEXT *pContext,
6848 DWORD exceptionCode,
6849 Thread *pThread)
6850{
6851 LIMITED_METHOD_CONTRACT;
6852
6853#ifdef DEBUGGING_SUPPORTED
6854 SO_NOT_MAINLINE_FUNCTION;
6855
6856#ifdef _TARGET_ARM_
6857 // On ARM we don't have any reliable hardware support for single stepping so it is emulated in software.
6858 // The implementation will end up throwing an EXCEPTION_BREAKPOINT rather than an EXCEPTION_SINGLE_STEP
6859 // and leaves other aspects of the thread context in an invalid state. Therefore we use this opportunity
6860 // to fixup the state before any other part of the system uses it (we do it here since only the debugger
6861 // uses single step functionality).
6862
6863 // First ask the emulation itself whether this exception occurred while single stepping was enabled. If so
6864 // it will fix up the context to be consistent again and return true. If so and the exception was
6865 // EXCEPTION_BREAKPOINT then we translate it to EXCEPTION_SINGLE_STEP (otherwise we leave it be, e.g. the
6866 // instruction stepped caused an access violation). since this is called from our VEH there might not
6867 // be a thread object so we must check pThread first.
6868 if ((pThread != NULL) && pThread->HandleSingleStep(pContext, exceptionCode) && (exceptionCode == EXCEPTION_BREAKPOINT))
6869 {
6870 exceptionCode = EXCEPTION_SINGLE_STEP;
6871 pExceptionRecord->ExceptionCode = EXCEPTION_SINGLE_STEP;
6872 pExceptionRecord->ExceptionAddress = (PVOID)pContext->Pc;
6873 }
6874#endif // _TARGET_ARM_
6875
6876 // Is this exception really meant for the COM+ Debugger? Note: we will let the debugger have a chance if there
6877 // is a debugger attached to any part of the process. It is incorrect to consider whether or not the debugger
6878 // is attached the the thread's current app domain at this point.
6879
6880 // Even if a debugger is not attached, we must let the debugger handle the exception in case it's coming from a
6881 // patch-skipper.
6882 if ((!IsComPlusException(pExceptionRecord)) &&
6883 (GetThread() != NULL) &&
6884 (g_pDebugInterface != NULL) &&
6885 g_pDebugInterface->FirstChanceNativeException(pExceptionRecord,
6886 pContext,
6887 exceptionCode,
6888 pThread))
6889 {
6890 LOG((LF_EH | LF_CORDB, LL_INFO1000, "IsDebuggerFault - it's the debugger's fault\n"));
6891 return true;
6892 }
6893#endif // DEBUGGING_SUPPORTED
6894 return false;
6895}
6896
6897#endif // FEATURE_PAL
6898
6899#ifdef WIN64EXCEPTIONS
6900
6901#ifndef _TARGET_X86_
6902EXTERN_C void JIT_MemSet_End();
6903EXTERN_C void JIT_MemCpy_End();
6904
6905EXTERN_C void JIT_WriteBarrier_End();
6906EXTERN_C void JIT_CheckedWriteBarrier_End();
6907EXTERN_C void JIT_ByRefWriteBarrier_End();
6908#endif // _TARGET_X86_
6909
6910#if defined(_TARGET_AMD64_) && defined(_DEBUG)
6911EXTERN_C void JIT_WriteBarrier_Debug();
6912EXTERN_C void JIT_WriteBarrier_Debug_End();
6913#endif
6914
6915#ifdef _TARGET_ARM_
6916EXTERN_C void FCallMemcpy_End();
6917#endif
6918
6919// Check if the passed in instruction pointer is in one of the
6920// JIT helper functions.
6921bool IsIPInMarkedJitHelper(UINT_PTR uControlPc)
6922{
6923 LIMITED_METHOD_CONTRACT;
6924
6925#define CHECK_RANGE(name) \
6926 if (GetEEFuncEntryPoint(name) <= uControlPc && uControlPc < GetEEFuncEntryPoint(name##_End)) return true;
6927
6928#ifndef _TARGET_X86_
6929 CHECK_RANGE(JIT_MemSet)
6930 CHECK_RANGE(JIT_MemCpy)
6931
6932 CHECK_RANGE(JIT_WriteBarrier)
6933 CHECK_RANGE(JIT_CheckedWriteBarrier)
6934 CHECK_RANGE(JIT_ByRefWriteBarrier)
6935#else
6936#ifdef FEATURE_PAL
6937 CHECK_RANGE(JIT_WriteBarrierGroup)
6938 CHECK_RANGE(JIT_PatchedWriteBarrierGroup)
6939#endif // FEATURE_PAL
6940#endif // _TARGET_X86_
6941
6942#if defined(_TARGET_AMD64_) && defined(_DEBUG)
6943 CHECK_RANGE(JIT_WriteBarrier_Debug)
6944#endif
6945
6946#ifdef _TARGET_ARM_
6947 CHECK_RANGE(FCallMemcpy)
6948#endif
6949
6950 return false;
6951}
6952#endif // WIN64EXCEPTIONS
6953
6954// Returns TRUE if caller should resume execution.
6955BOOL
6956AdjustContextForWriteBarrier(
6957 EXCEPTION_RECORD *pExceptionRecord,
6958 CONTEXT *pContext)
6959{
6960 WRAPPER_NO_CONTRACT;
6961
6962#ifdef FEATURE_DATABREAKPOINT
6963
6964 // If pExceptionRecord is null, it means it is called from EEDbgInterfaceImpl::AdjustContextForWriteBarrierForDebugger()
6965 // This is called only when a data breakpoint is hitm which could be inside a JIT write barrier helper and required
6966 // this logic to help unwind out of it. For the x86, not patched case, we assume the IP lies within the region where we
6967 // have already saved the registers on the stack, and therefore the code unwind those registers as well. This is not true
6968 // for the usual AV case where the registers are not saved yet.
6969
6970 if (pExceptionRecord == nullptr)
6971 {
6972 PCODE ip = GetIP(pContext);
6973#if defined(_TARGET_X86_)
6974 bool withinWriteBarrierGroup = ((ip >= (PCODE) JIT_WriteBarrierGroup) && (ip <= (PCODE) JIT_WriteBarrierGroup_End));
6975 bool withinPatchedWriteBarrierGroup = ((ip >= (PCODE) JIT_PatchedWriteBarrierGroup) && (ip <= (PCODE) JIT_PatchedWriteBarrierGroup_End));
6976 if (withinWriteBarrierGroup || withinPatchedWriteBarrierGroup)
6977 {
6978 DWORD* esp = (DWORD*)pContext->Esp;
6979 if (withinWriteBarrierGroup)
6980 {
6981#if defined(WRITE_BARRIER_CHECK)
6982 pContext->Ebp = *esp++;
6983 pContext->Ecx = *esp++;
6984#endif
6985 }
6986 pContext->Eip = *esp++;
6987 pContext->Esp = (DWORD)esp;
6988 return TRUE;
6989 }
6990#elif defined(_TARGET_AMD64_)
6991 if (IsIPInMarkedJitHelper((UINT_PTR)ip))
6992 {
6993 Thread::VirtualUnwindToFirstManagedCallFrame(pContext);
6994 return TRUE;
6995 }
6996#else
6997 #error Not supported
6998#endif
6999 return FALSE;
7000 }
7001
7002#endif // FEATURE_DATABREAKPOINT
7003
7004#if defined(_TARGET_X86_) && !defined(PLATFORM_UNIX)
7005 void* f_IP = (void *)GetIP(pContext);
7006
7007 if (((f_IP >= (void *) JIT_WriteBarrierGroup) && (f_IP <= (void *) JIT_WriteBarrierGroup_End)) ||
7008 ((f_IP >= (void *) JIT_PatchedWriteBarrierGroup) && (f_IP <= (void *) JIT_PatchedWriteBarrierGroup_End)))
7009 {
7010 // set the exception IP to be the instruction that called the write barrier
7011 void* callsite = (void *)GetAdjustedCallAddress(*dac_cast<PTR_PCODE>(GetSP(pContext)));
7012 pExceptionRecord->ExceptionAddress = callsite;
7013 SetIP(pContext, (PCODE)callsite);
7014
7015 // put ESP back to what it was before the call.
7016 SetSP(pContext, PCODE((BYTE*)GetSP(pContext) + sizeof(void*)));
7017 }
7018 return FALSE;
7019#elif defined(WIN64EXCEPTIONS) // _TARGET_X86_ && !PLATFORM_UNIX
7020 void* f_IP = dac_cast<PTR_VOID>(GetIP(pContext));
7021
7022 CONTEXT tempContext;
7023 CONTEXT* pExceptionContext = pContext;
7024
7025 BOOL fExcluded = IsIPInMarkedJitHelper((UINT_PTR)f_IP);
7026
7027 if (fExcluded)
7028 {
7029 bool fShouldHandleManagedFault = false;
7030
7031 if (pContext != &tempContext)
7032 {
7033 tempContext = *pContext;
7034 pContext = &tempContext;
7035 }
7036
7037 Thread::VirtualUnwindToFirstManagedCallFrame(pContext);
7038
7039#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
7040 // We had an AV in the writebarrier that needs to be treated
7041 // as originating in managed code. At this point, the stack (growing
7042 // from left->right) looks like this:
7043 //
7044 // ManagedFunc -> Native_WriteBarrierInVM -> AV
7045 //
7046 // We just performed an unwind from the write-barrier
7047 // and now have the context in ManagedFunc. Since
7048 // ManagedFunc called into the write-barrier, the return
7049 // address in the unwound context corresponds to the
7050 // instruction where the call will return.
7051 //
7052 // On ARM, just like we perform ControlPC adjustment
7053 // during exception dispatch (refer to ExceptionTracker::InitializeCrawlFrame),
7054 // we will need to perform the corresponding adjustment of IP
7055 // we got from unwind above, so as to indicate that the AV
7056 // happened "before" the call to the writebarrier and not at
7057 // the instruction at which the control will return.
7058 PCODE ControlPCPostAdjustment = GetIP(pContext) - STACKWALK_CONTROLPC_ADJUST_OFFSET;
7059
7060 // Now we save the address back into the context so that it gets used
7061 // as the faulting address.
7062 SetIP(pContext, ControlPCPostAdjustment);
7063#endif // _TARGET_ARM_ || _TARGET_ARM64_
7064
7065 // Unwind the frame chain - On Win64, this is required since we may handle the managed fault and to do so,
7066 // we will replace the exception context with the managed context and "continue execution" there. Thus, we do not
7067 // want any explicit frames active below the resumption SP.
7068 //
7069 // Question: Why do we unwind before determining whether we will handle the exception or not?
7070 UnwindFrameChain(GetThread(), (Frame*)GetSP(pContext));
7071 fShouldHandleManagedFault = ShouldHandleManagedFault(pExceptionRecord,pContext,
7072 NULL, // establisher frame (x86 only)
7073 NULL // pThread (x86 only)
7074 );
7075
7076 if (fShouldHandleManagedFault)
7077 {
7078 ReplaceExceptionContextRecord(pExceptionContext, pContext);
7079 pExceptionRecord->ExceptionAddress = dac_cast<PTR_VOID>(GetIP(pContext));
7080 return TRUE;
7081 }
7082 }
7083
7084 return FALSE;
7085#else // WIN64EXCEPTIONS
7086 PORTABILITY_ASSERT("AdjustContextForWriteBarrier");
7087 return FALSE;
7088#endif // ELSE
7089}
7090
7091#if defined(USE_FEF) && !defined(FEATURE_PAL)
7092
7093struct SavedExceptionInfo
7094{
7095 EXCEPTION_RECORD m_ExceptionRecord;
7096 CONTEXT m_ExceptionContext;
7097 CrstStatic m_Crst;
7098
7099 void SaveExceptionRecord(EXCEPTION_RECORD *pExceptionRecord)
7100 {
7101 LIMITED_METHOD_CONTRACT;
7102 size_t erSize = offsetof(EXCEPTION_RECORD, ExceptionInformation) +
7103 pExceptionRecord->NumberParameters * sizeof(pExceptionRecord->ExceptionInformation[0]);
7104 memcpy(&m_ExceptionRecord, pExceptionRecord, erSize);
7105
7106 }
7107
7108 void SaveContext(CONTEXT *pContext)
7109 {
7110 LIMITED_METHOD_CONTRACT;
7111#ifdef CONTEXT_EXTENDED_REGISTERS
7112
7113 size_t contextSize = offsetof(CONTEXT, ExtendedRegisters);
7114 if ((pContext->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS)
7115 contextSize += sizeof(pContext->ExtendedRegisters);
7116 memcpy(&m_ExceptionContext, pContext, contextSize);
7117
7118#else // !CONTEXT_EXTENDED_REGISTERS
7119
7120 size_t contextSize = sizeof(CONTEXT);
7121 memcpy(&m_ExceptionContext, pContext, contextSize);
7122
7123#endif // !CONTEXT_EXTENDED_REGISTERS
7124 }
7125
7126 DEBUG_NOINLINE void Enter()
7127 {
7128 WRAPPER_NO_CONTRACT;
7129 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
7130 m_Crst.Enter();
7131 }
7132
7133 DEBUG_NOINLINE void Leave()
7134 {
7135 WRAPPER_NO_CONTRACT;
7136 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
7137 m_Crst.Leave();
7138 }
7139
7140 void Init()
7141 {
7142 WRAPPER_NO_CONTRACT;
7143 m_Crst.Init(CrstSavedExceptionInfo, CRST_UNSAFE_ANYMODE);
7144 }
7145};
7146
7147SavedExceptionInfo g_SavedExceptionInfo; // Globals are guaranteed zero-init;
7148
7149void InitSavedExceptionInfo()
7150{
7151 g_SavedExceptionInfo.Init();
7152}
7153
7154EXTERN_C VOID FixContextForFaultingExceptionFrame (
7155 EXCEPTION_RECORD* pExceptionRecord,
7156 CONTEXT *pContextRecord)
7157{
7158 WRAPPER_NO_CONTRACT;
7159
7160 // don't copy parm args as have already supplied them on the throw
7161 memcpy((void*) pExceptionRecord,
7162 (void*) &g_SavedExceptionInfo.m_ExceptionRecord,
7163 offsetof(EXCEPTION_RECORD, ExceptionInformation)
7164 );
7165
7166 ReplaceExceptionContextRecord(pContextRecord, &g_SavedExceptionInfo.m_ExceptionContext);
7167
7168 g_SavedExceptionInfo.Leave();
7169
7170 GetThread()->ResetThreadStateNC(Thread::TSNC_DebuggerIsManagedException);
7171}
7172
7173EXTERN_C VOID __fastcall
7174LinkFrameAndThrow(FaultingExceptionFrame* pFrame)
7175{
7176 WRAPPER_NO_CONTRACT;
7177
7178 *(TADDR*)pFrame = FaultingExceptionFrame::GetMethodFrameVPtr();
7179 *pFrame->GetGSCookiePtr() = GetProcessGSCookie();
7180
7181 pFrame->InitAndLink(&g_SavedExceptionInfo.m_ExceptionContext);
7182
7183 GetThread()->SetThreadStateNC(Thread::TSNC_DebuggerIsManagedException);
7184
7185 ULONG argcount = g_SavedExceptionInfo.m_ExceptionRecord.NumberParameters;
7186 ULONG flags = g_SavedExceptionInfo.m_ExceptionRecord.ExceptionFlags;
7187 ULONG code = g_SavedExceptionInfo.m_ExceptionRecord.ExceptionCode;
7188 ULONG_PTR* args = &g_SavedExceptionInfo.m_ExceptionRecord.ExceptionInformation[0];
7189
7190 RaiseException(code, flags, argcount, args);
7191}
7192
7193void SetNakedThrowHelperArgRegistersInContext(CONTEXT* pContext)
7194{
7195#if defined(_TARGET_AMD64_)
7196 pContext->Rcx = (UINT_PTR)GetIP(pContext);
7197#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
7198 // Save the original IP in LR
7199 pContext->Lr = (DWORD)GetIP(pContext);
7200#else
7201 PORTABILITY_WARNING("NakedThrowHelper argument not defined");
7202#endif
7203}
7204
7205EXTERN_C VOID STDCALL NakedThrowHelper(VOID);
7206
7207void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord,
7208 CONTEXT* pContext,
7209 EXCEPTION_REGISTRATION_RECORD* pEstablisherFrame,
7210 Thread* pThread)
7211{
7212 WRAPPER_NO_CONTRACT;
7213
7214 // Ok. Now we have a brand new fault in jitted code.
7215 g_SavedExceptionInfo.Enter();
7216 g_SavedExceptionInfo.SaveExceptionRecord(pExceptionRecord);
7217 g_SavedExceptionInfo.SaveContext(pContext);
7218
7219 SetNakedThrowHelperArgRegistersInContext(pContext);
7220
7221 SetIP(pContext, GetEEFuncEntryPoint(NakedThrowHelper));
7222}
7223
7224#else // USE_FEF && !FEATURE_PAL
7225
7226void InitSavedExceptionInfo()
7227{
7228}
7229
7230#endif // USE_FEF && !FEATURE_PAL
7231
7232//
7233// Init a new frame
7234//
7235void FaultingExceptionFrame::Init(CONTEXT *pContext)
7236{
7237 WRAPPER_NO_CONTRACT;
7238#ifndef WIN64EXCEPTIONS
7239#ifdef _TARGET_X86_
7240 CalleeSavedRegisters *pRegs = GetCalleeSavedRegisters();
7241#define CALLEE_SAVED_REGISTER(regname) pRegs->regname = pContext->regname;
7242 ENUM_CALLEE_SAVED_REGISTERS();
7243#undef CALLEE_SAVED_REGISTER
7244 m_ReturnAddress = ::GetIP(pContext);
7245 m_Esp = (DWORD)GetSP(pContext);
7246#else // _TARGET_X86_
7247 PORTABILITY_ASSERT("FaultingExceptionFrame::Init");
7248#endif // _TARGET_???_ (ELSE)
7249#else // !WIN64EXCEPTIONS
7250 m_ReturnAddress = ::GetIP(pContext);
7251 CopyOSContext(&m_ctx, pContext);
7252#endif // !WIN64EXCEPTIONS
7253}
7254
7255//
7256// Init and Link in a new frame
7257//
7258void FaultingExceptionFrame::InitAndLink(CONTEXT *pContext)
7259{
7260 WRAPPER_NO_CONTRACT;
7261
7262 Init(pContext);
7263
7264 Push();
7265}
7266
7267
7268bool ShouldHandleManagedFault(
7269 EXCEPTION_RECORD* pExceptionRecord,
7270 CONTEXT* pContext,
7271 EXCEPTION_REGISTRATION_RECORD* pEstablisherFrame,
7272 Thread* pThread)
7273{
7274 CONTRACTL
7275 {
7276 NOTHROW;
7277 GC_NOTRIGGER;
7278 SO_TOLERANT;
7279 MODE_ANY;
7280 }
7281 CONTRACTL_END;
7282
7283 // If we get a faulting instruction inside managed code, we're going to
7284 // 1. Allocate the correct exception object, store it in the thread.
7285 // 2. Save the EIP in the thread.
7286 // 3. Change the EIP to our throw helper
7287 // 4. Resume execution.
7288 //
7289 // The helper will push a frame for us, and then throw the correct managed exception.
7290 //
7291 // Is this exception really meant for the COM+ Debugger? Note: we will let the debugger have a chance if there is a
7292 // debugger attached to any part of the process. It is incorrect to consider whether or not the debugger is attached
7293 // the the thread's current app domain at this point.
7294
7295
7296 // A managed exception never comes from managed code, and we can ignore all breakpoint
7297 // exceptions.
7298 //
7299 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
7300 if (IsComPlusException(pExceptionRecord)
7301 || exceptionCode == STATUS_BREAKPOINT
7302 || exceptionCode == STATUS_SINGLE_STEP)
7303 {
7304 return false;
7305 }
7306
7307#ifdef _DEBUG
7308 // This is a workaround, but it's debug-only as is gc stress 4.
7309 // The problem is that if we get an exception with this code that
7310 // didn't come from GCStress=4, then we won't push a FeF and will
7311 // end up with a gc hole and potential crash.
7312 if (exceptionCode == STATUS_CLR_GCCOVER_CODE)
7313 return false;
7314#endif // _DEBUG
7315
7316#ifndef WIN64EXCEPTIONS
7317 // If there's any frame below the ESP of the exception, then we can forget it.
7318 if (pThread->m_pFrame < dac_cast<PTR_VOID>(GetSP(pContext)))
7319 return false;
7320
7321 // If we're a subsequent handler forget it.
7322 EXCEPTION_REGISTRATION_RECORD* pBottomMostHandler = pThread->GetExceptionState()->m_currentExInfo.m_pBottomMostHandler;
7323 if (pBottomMostHandler != NULL && pEstablisherFrame > pBottomMostHandler)
7324 {
7325 return false;
7326 }
7327#endif // WIN64EXCEPTIONS
7328
7329 {
7330 // If it's not a fault in jitted code, forget it.
7331
7332 // ExecutionManager::IsManagedCode takes a spinlock. Since we're in the middle of throwing,
7333 // we'll allow the lock, even if a caller didn't expect it.
7334 CONTRACT_VIOLATION(TakesLockViolation);
7335
7336 if (!ExecutionManager::IsManagedCode(GetIP(pContext)))
7337 return false;
7338 }
7339
7340 // caller should call HandleManagedFault and resume execution.
7341 return true;
7342}
7343
7344#ifndef FEATURE_PAL
7345
7346LONG WINAPI CLRVectoredExceptionHandlerPhase2(PEXCEPTION_POINTERS pExceptionInfo);
7347
7348enum VEH_ACTION
7349{
7350 VEH_NO_ACTION = 0,
7351 VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION,
7352 VEH_CONTINUE_EXECUTION,
7353 VEH_CONTINUE_SEARCH,
7354 VEH_EXECUTE_HANDLER
7355};
7356
7357
7358VEH_ACTION WINAPI CLRVectoredExceptionHandlerPhase3(PEXCEPTION_POINTERS pExceptionInfo);
7359
7360LONG WINAPI CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
7361{
7362 // It is not safe to execute code inside VM after we shutdown EE. One example is DisablePreemptiveGC
7363 // will block forever.
7364 if (g_fForbidEnterEE)
7365 {
7366 return EXCEPTION_CONTINUE_SEARCH;
7367 }
7368
7369 //
7370 // For images ngen'd with FEATURE_LAZY_COW_PAGES, the .data section will be read-only. Any writes to that data need to be
7371 // preceded by a call to EnsureWritablePages. This code is here to catch the ones we forget.
7372 //
7373#ifdef FEATURE_LAZY_COW_PAGES
7374 if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION &&
7375 IsWellFormedAV(pExceptionInfo->ExceptionRecord) &&
7376 pExceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1 /* means this was a failed write */)
7377 {
7378 void* location = (void*)pExceptionInfo->ExceptionRecord->ExceptionInformation[1];
7379
7380 if (IsInReadOnlyLazyCOWPage(location))
7381 {
7382#ifdef _DEBUG
7383 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DebugAssertOnMissedCOWPage))
7384 _ASSERTE_MSG(false, "Writes to NGen'd data must be protected by EnsureWritablePages.");
7385#endif
7386
7387#pragma push_macro("VirtualQuery")
7388#undef VirtualQuery
7389 MEMORY_BASIC_INFORMATION mbi;
7390 if (!::VirtualQuery(location, &mbi, sizeof(mbi)))
7391 {
7392 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_OUTOFMEMORY);
7393 }
7394#pragma pop_macro("VirtualQuery")
7395
7396 bool executable = (mbi.Protect == PAGE_EXECUTE_READ) ||
7397 (mbi.Protect == PAGE_EXECUTE_READWRITE) ||
7398 (mbi.Protect == PAGE_EXECUTE_READ) ||
7399 (mbi.Protect == PAGE_EXECUTE_WRITECOPY);
7400
7401 if (!(executable ? EnsureWritableExecutablePagesNoThrow(location, 1) : EnsureWritablePagesNoThrow(location, 1)))
7402 {
7403 // Note that this failfast is very rare. It will only be hit in the theoretical cases there is
7404 // missing EnsureWritablePages probe (there should be none when we ship), and the OS run into OOM
7405 // exactly at the point when we executed the code with the missing probe.
7406 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_OUTOFMEMORY);
7407 }
7408
7409 return EXCEPTION_CONTINUE_EXECUTION;
7410 }
7411 }
7412#endif //FEATURE_LAZY_COW_PAGES
7413
7414
7415 //
7416 // DO NOT USE CONTRACTS HERE AS THIS ROUTINE MAY NEVER RETURN. You can use
7417 // static contracts, but currently this is all WRAPPER_NO_CONTRACT.
7418 //
7419
7420
7421 //
7422 // READ THIS!
7423 //
7424 //
7425 // You cannot put any code in here that allocates during an out-of-memory handling.
7426 // This routine runs before *any* other handlers, including __try. Thus, if you
7427 // allocate anything in this routine then it will throw out-of-memory and end up
7428 // right back here.
7429 //
7430 // There are various things that allocate that you may not expect to allocate. One
7431 // instance of this is STRESS_LOG. It allocates the log buffer if the thread does
7432 // not already have one allocated. Thus, if we OOM during the setting up of the
7433 // thread, the log buffer will not be allocated and this will try to do so. Thus,
7434 // all STRESS_LOGs in here need to be after you have guaranteed the allocation has
7435 // already occurred.
7436 //
7437
7438 Thread *pThread;
7439
7440 {
7441 MAYBE_FAULT_FORBID_NO_ALLOC((pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_NO_MEMORY));
7442
7443 pThread = GetThread();
7444
7445 //
7446 // Since we are in an OOM situation, we test the thread object before logging since if the
7447 // thread exists we know the log buffer has been allocated already.
7448 //
7449 if (pThread != NULL)
7450 {
7451 CantAllocHolder caHolder;
7452 STRESS_LOG4(LF_EH, LL_INFO100, "In CLRVectoredExceptionHandler, Exception = %x, Context = %p, IP = %p SP = %p\n",
7453 pExceptionInfo->ExceptionRecord->ExceptionCode, pExceptionInfo->ContextRecord,
7454 GetIP(pExceptionInfo->ContextRecord), GetSP(pExceptionInfo->ContextRecord));
7455 }
7456
7457 }
7458
7459 // We need to unhijack the thread here if it is not unhijacked already. On x86 systems,
7460 // we do this in Thread::StackWalkFramesEx, but on amd64 systems we have the OS walk the
7461 // stack for us. If we leave CLRVectoredExceptionHandler with a thread still hijacked,
7462 // the operating system will not be able to walk the stack and not find the handlers for
7463 // the exception. It is safe to unhijack the thread in this case for two reasons:
7464 // 1. pThread refers to *this* thread.
7465 // 2. If another thread tries to hijack this thread, it will see we are not in managed
7466 // code (and thus won't try to hijack us).
7467#if defined(WIN64EXCEPTIONS) && defined(FEATURE_HIJACK)
7468 if (pThread != NULL)
7469 {
7470 pThread->UnhijackThreadNoAlloc();
7471 }
7472#endif // defined(WIN64EXCEPTIONS) && defined(FEATURE_HIJACK)
7473
7474 if (IsSOExceptionCode(pExceptionInfo->ExceptionRecord->ExceptionCode))
7475 {
7476 //
7477 // Not an Out-of-memory situation, so no need for a forbid fault region here
7478 //
7479 return EXCEPTION_CONTINUE_SEARCH;
7480 }
7481
7482 LONG retVal = 0;
7483
7484#ifdef FEATURE_STACK_PROBE
7485 // See if we've got enough stack to handle this exception
7486
7487 // There isn't much stack left to attempt to report an exception. Let's trigger a hard
7488 // SO, so we clear the guard page and give us at least another page of stack to work with.
7489
7490 if (pThread && !pThread->IsStackSpaceAvailable(ADJUST_PROBE(1)))
7491 {
7492 DontCallDirectlyForceStackOverflow();
7493 }
7494#endif // FEATURE_STACK_PROBE
7495
7496 // We can't probe here, because we won't return from the CLRVectoredExceptionHandlerPhase2
7497 // on WIN64
7498 //
7499
7500 if (pThread)
7501 {
7502 FAULT_FORBID_NO_ALLOC();
7503 CantAllocHolder caHolder;
7504 }
7505
7506 retVal = CLRVectoredExceptionHandlerPhase2(pExceptionInfo);
7507
7508 //
7509 //END_ENTRYPOINT_VOIDRET;
7510 //
7511 return retVal;
7512}
7513
7514LONG WINAPI CLRVectoredExceptionHandlerPhase2(PEXCEPTION_POINTERS pExceptionInfo)
7515{
7516 //
7517 // DO NOT USE CONTRACTS HERE AS THIS ROUTINE MAY NEVER RETURN. You can use
7518 // static contracts, but currently this is all WRAPPER_NO_CONTRACT.
7519 //
7520
7521 //
7522 // READ THIS!
7523 //
7524 //
7525 // You cannot put any code in here that allocates during an out-of-memory handling.
7526 // This routine runs before *any* other handlers, including __try. Thus, if you
7527 // allocate anything in this routine then it will throw out-of-memory and end up
7528 // right back here.
7529 //
7530 // There are various things that allocate that you may not expect to allocate. One
7531 // instance of this is STRESS_LOG. It allocates the log buffer if the thread does
7532 // not already have one allocated. Thus, if we OOM during the setting up of the
7533 // thread, the log buffer will not be allocated and this will try to do so. Thus,
7534 // all STRESS_LOGs in here need to be after you have guaranteed the allocation has
7535 // already occurred.
7536 //
7537
7538 PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
7539 VEH_ACTION action;
7540
7541 {
7542 MAYBE_FAULT_FORBID_NO_ALLOC((pExceptionRecord->ExceptionCode == STATUS_NO_MEMORY));
7543 CantAllocHolder caHolder;
7544
7545 action = CLRVectoredExceptionHandlerPhase3(pExceptionInfo);
7546 }
7547
7548 if (action == VEH_CONTINUE_EXECUTION)
7549 {
7550 return EXCEPTION_CONTINUE_EXECUTION;
7551 }
7552
7553 if (action == VEH_CONTINUE_SEARCH)
7554 {
7555 return EXCEPTION_CONTINUE_SEARCH;
7556 }
7557
7558 if (action == VEH_EXECUTE_HANDLER)
7559 {
7560 return EXCEPTION_EXECUTE_HANDLER;
7561 }
7562
7563#if defined(WIN64EXCEPTIONS)
7564
7565 if (action == VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION)
7566 {
7567 //
7568 // If the exception context was unwound by Phase3 then
7569 // we'll jump here to save the managed context and resume execution at
7570 // NakedThrowHelper. This needs to be done outside of any holder's
7571 // scope, because HandleManagedFault may not return.
7572 //
7573 HandleManagedFault(pExceptionInfo->ExceptionRecord,
7574 pExceptionInfo->ContextRecord,
7575 NULL, // establisher frame (x86 only)
7576 NULL // pThread (x86 only)
7577 );
7578 return EXCEPTION_CONTINUE_EXECUTION;
7579 }
7580
7581#endif // defined(WIN64EXCEPTIONS)
7582
7583
7584 //
7585 // In OOM situations, this call better not fault.
7586 //
7587 {
7588 MAYBE_FAULT_FORBID_NO_ALLOC((pExceptionRecord->ExceptionCode == STATUS_NO_MEMORY));
7589 CantAllocHolder caHolder;
7590
7591 // Give the debugger a chance. Note that its okay for this call to trigger a GC, since the debugger will take
7592 // special steps to make that okay.
7593 //
7594 // @TODO: I'd love a way to call into the debugger with GCX_NOTRIGGER still in scope, and force them to make
7595 // the choice to break the no-trigger region after taking all necessary precautions.
7596 if (IsDebuggerFault(pExceptionRecord, pExceptionInfo->ContextRecord, pExceptionRecord->ExceptionCode, GetThread()))
7597 {
7598 return EXCEPTION_CONTINUE_EXECUTION;
7599 }
7600 }
7601
7602 //
7603 // No reason to put a forbid fault region here as the exception code is not STATUS_NO_MEMORY.
7604 //
7605
7606 // Handle a user breakpoint. Note that its okay for the UserBreakpointFilter to trigger a GC, since we're going
7607 // to either a) terminate the process, or b) let a user attach an unmanaged debugger, and debug knowing that
7608 // managed state may be messed up.
7609 if ((pExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) ||
7610 (pExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP))
7611 {
7612 // A breakpoint outside managed code and outside the runtime will have to be handled by some
7613 // other piece of code.
7614
7615 BOOL fExternalException = FALSE;
7616
7617 BEGIN_SO_INTOLERANT_CODE_NOPROBE;
7618
7619 {
7620 // ExecutionManager::IsManagedCode takes a spinlock. Since we're in the middle of throwing,
7621 // we'll allow the lock, even if a caller didn't expect it.
7622 CONTRACT_VIOLATION(TakesLockViolation);
7623
7624 fExternalException = (!ExecutionManager::IsManagedCode(GetIP(pExceptionInfo->ContextRecord)) &&
7625 !IsIPInModule(g_pMSCorEE, GetIP(pExceptionInfo->ContextRecord)));
7626 }
7627
7628 END_SO_INTOLERANT_CODE_NOPROBE;
7629
7630 if (fExternalException)
7631 {
7632 // The breakpoint was not ours. Someone else can handle it. (Or if not, we'll get it again as
7633 // an unhandled exception.)
7634 return EXCEPTION_CONTINUE_SEARCH;
7635 }
7636
7637 // The breakpoint was from managed or the runtime. Handle it.
7638 return UserBreakpointFilter(pExceptionInfo);
7639 }
7640
7641#if defined(WIN64EXCEPTIONS)
7642 BOOL fShouldHandleManagedFault;
7643
7644 {
7645 MAYBE_FAULT_FORBID_NO_ALLOC((pExceptionRecord->ExceptionCode == STATUS_NO_MEMORY));
7646 CantAllocHolder caHolder;
7647 fShouldHandleManagedFault = ShouldHandleManagedFault(pExceptionInfo->ExceptionRecord,
7648 pExceptionInfo->ContextRecord,
7649 NULL, // establisher frame (x86 only)
7650 NULL // pThread (x86 only)
7651 );
7652 }
7653
7654 if (fShouldHandleManagedFault)
7655 {
7656 //
7657 // HandleManagedFault may never return, so we cannot use a forbid fault region around it.
7658 //
7659 HandleManagedFault(pExceptionInfo->ExceptionRecord,
7660 pExceptionInfo->ContextRecord,
7661 NULL, // establisher frame (x86 only)
7662 NULL // pThread (x86 only)
7663 );
7664 return EXCEPTION_CONTINUE_EXECUTION;
7665}
7666#endif // defined(WIN64EXCEPTIONS)
7667
7668 return EXCEPTION_EXECUTE_HANDLER;
7669}
7670
7671/*
7672 * CLRVectoredExceptionHandlerPhase3
7673 *
7674 * This routine does some basic processing on the exception, making decisions about common
7675 * exception types and whether to continue them or not. It has side-effects, in that it may
7676 * adjust the context in the exception.
7677 *
7678 * Parameters:
7679 * pExceptionInfo - pointer to the exception
7680 *
7681 * Returns:
7682 * VEH_NO_ACTION - This indicates that Phase3 has no specific action to take and that further
7683 * processing of this exception should continue.
7684 * VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION - This indicates that the caller should call HandleMandagedException
7685 * immediately.
7686 * VEH_CONTINUE_EXECUTION - Caller should return EXCEPTION_CONTINUE_EXECUTION.
7687 * VEH_CONTINUE_SEARCH - Caller should return EXCEPTION_CONTINUE_SEARCH;
7688 * VEH_EXECUTE_HANDLER - Caller should return EXCEPTION_EXECUTE_HANDLER.
7689 *
7690 * Note that in all cases the context in the exception may have been adjusted.
7691 *
7692 */
7693
7694VEH_ACTION WINAPI CLRVectoredExceptionHandlerPhase3(PEXCEPTION_POINTERS pExceptionInfo)
7695{
7696 //
7697 // DO NOT USE CONTRACTS HERE AS THIS ROUTINE MAY NEVER RETURN. You can use
7698 // static contracts, but currently this is all WRAPPER_NO_CONTRACT.
7699 //
7700
7701 //
7702 // READ THIS!
7703 //
7704 //
7705 // You cannot put any code in here that allocates during an out-of-memory handling.
7706 // This routine runs before *any* other handlers, including __try. Thus, if you
7707 // allocate anything in this routine then it will throw out-of-memory and end up
7708 // right back here.
7709 //
7710 // There are various things that allocate that you may not expect to allocate. One
7711 // instance of this is STRESS_LOG. It allocates the log buffer if the thread does
7712 // not already have one allocated. Thus, if we OOM during the setting up of the
7713 // thread, the log buffer will not be allocated and this will try to do so. Thus,
7714 // all STRESS_LOGs in here need to be after you have guaranteed the allocation has
7715 // already occurred.
7716 //
7717
7718 // Handle special cases which are common amongst all filters.
7719 PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
7720 PCONTEXT pContext = pExceptionInfo->ContextRecord;
7721 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
7722
7723 // Its extremely important that no one trigger a GC in here. This is called from CPFH_FirstPassHandler, in
7724 // cases where we've taken an unmanaged exception on a managed thread (AV, divide by zero, etc.) but
7725 // _before_ we've done our work to erect a FaultingExceptionFrame. Thus, the managed frames are
7726 // unprotected. We setup a GCX_NOTRIGGER holder in this scope to prevent us from messing this up. Note
7727 // that the scope of this is limited, since there are times when its okay to trigger even in this special
7728 // case. The debugger is a good example: if it gets a breakpoint in managed code, it has the smarts to
7729 // prevent the GC before enabling GC, thus its okay for it to trigger.
7730
7731 GCX_NOTRIGGER();
7732
7733#ifdef USE_REDIRECT_FOR_GCSTRESS
7734 // NOTE: this is effectively ifdef (_TARGET_AMD64_ || _TARGET_ARM_), and does not actually trigger
7735 // a GC. This will redirect the exception context to a stub which will
7736 // push a frame and cause GC.
7737 if (IsGcMarker(pContext, pExceptionRecord))
7738 {
7739 return VEH_CONTINUE_EXECUTION;;
7740 }
7741#endif // USE_REDIRECT_FOR_GCSTRESS
7742
7743#if defined(FEATURE_HIJACK) && !defined(PLATFORM_UNIX)
7744#ifdef _TARGET_X86_
7745 CPFH_AdjustContextForThreadSuspensionRace(pContext, GetThread());
7746#endif // _TARGET_X86_
7747#endif // FEATURE_HIJACK && !PLATFORM_UNIX
7748
7749 // Some other parts of the EE use exceptions in their own nefarious ways. We do some up-front processing
7750 // here to fix up the exception if needed.
7751 if (exceptionCode == STATUS_ACCESS_VIOLATION)
7752 {
7753 if (IsWellFormedAV(pExceptionRecord))
7754 {
7755 if (AdjustContextForWriteBarrier(pExceptionRecord, pContext))
7756 {
7757 // On x86, AdjustContextForWriteBarrier simply backs up AV's
7758 // in write barrier helpers into the calling frame, so that
7759 // the subsequent logic here sees a managed fault.
7760 //
7761 // On 64-bit, some additional work is required..
7762#ifdef WIN64EXCEPTIONS
7763 return VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION;
7764#endif // defined(WIN64EXCEPTIONS)
7765 }
7766 else if (AdjustContextForVirtualStub(pExceptionRecord, pContext))
7767 {
7768#ifdef WIN64EXCEPTIONS
7769 return VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION;
7770#endif
7771 }
7772
7773 // Remember the EIP for stress debugging purposes.
7774 g_LastAccessViolationEIP = (void*) ::GetIP(pContext);
7775
7776 // Note: we have a holder, called AVInRuntimeImplOkayHolder, that tells us that its okay to have an
7777 // AV in the Runtime's implementation in certain places. So, if its okay to have an AV at this
7778 // time, then skip the check for whether or not the AV is in our impl.
7779 // AVs are ok on the Helper thread (for which there is no pThread object,
7780 // and so the AVInRuntime holder doesn't work.
7781 Thread *pThread = GetThread();
7782
7783 bool fAVisOk =
7784 (IsDbgHelperSpecialThread() || IsETWRundownSpecialThread() ||
7785 ((pThread != NULL) && (pThread->AVInRuntimeImplOkay())) );
7786
7787
7788 // It is unnecessary to check this on second pass as we would have torn down
7789 // the process on the first pass. Also, the context record is not reliable
7790 // on second pass and this subjects us to false positives.
7791 if ((!fAVisOk) && !(pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING))
7792 {
7793 PCODE ip = (PCODE)GetIP(pContext);
7794 if (IsIPInModule(g_pMSCorEE, ip) || IsIPInModule(GCHeapUtilities::GetGCModule(), ip))
7795 {
7796 CONTRACT_VIOLATION(ThrowsViolation|FaultViolation|SOToleranceViolation);
7797
7798 //
7799 // If you're debugging, set the debugger to catch first-chance AV's, then simply hit F5 or
7800 // 'go' and continue after the assert. We'll recgonize that a debugger is attached, and
7801 // return EXCEPTION_CONTINUE_EXECUTION. You'll re-execute the faulting instruction, and the
7802 // debugger will stop at the AV. The value of EXCEPTION_CONTINUE_EXECUTION is -1, just in
7803 // case you need to verify the return value for some reason. If you need to actually debug
7804 // the failure path, then set your IP around the check below.
7805 //
7806 // You can also use Windbg's .cxr command to set the context to pContext.
7807 //
7808#if defined(_DEBUG)
7809 const char * pStack = "<stack not available>";
7810 StackScratchBuffer buffer;
7811 SString sStack;
7812 if (GetStackTraceAtContext(sStack, pContext))
7813 {
7814 pStack = sStack.GetANSI(buffer);
7815 }
7816
7817 DWORD tid = GetCurrentThreadId();
7818
7819 BOOL debuggerPresentBeforeAssert = IsDebuggerPresent();
7820
7821
7822 CONSISTENCY_CHECK_MSGF(false, ("AV in clr at this callstack:\n------\n%s\n-----\n.AV on tid=0x%x (%d), cxr=%p, exr=%p\n",
7823 pStack, tid, tid, pContext, pExceptionRecord));
7824
7825 // @todo - this may not be what we want for interop-debugging...
7826 //
7827 // If there was no debugger before the assert, but there is one now, then go ahead and
7828 // return EXCEPTION_CONTINUE_EXECUTION to re-execute the faulting instruction. This is
7829 // supposed to be a nice little feature for CLR devs who attach debuggers on the "Av in
7830 // mscorwks" assert above. Since this is only for that case, its only in debug builds.
7831 if (!debuggerPresentBeforeAssert && IsDebuggerPresent())
7832 {
7833 return VEH_CONTINUE_EXECUTION;;
7834 }
7835#endif // defined(_DEBUG)
7836
7837 EEPOLICY_HANDLE_FATAL_ERROR_USING_EXCEPTION_INFO(COR_E_EXECUTIONENGINE, pExceptionInfo);
7838 }
7839 }
7840 }
7841 }
7842 else if (exceptionCode == BOOTUP_EXCEPTION_COMPLUS)
7843 {
7844 // Don't handle a boot exception
7845 return VEH_CONTINUE_SEARCH;
7846 }
7847
7848 return VEH_NO_ACTION;
7849}
7850
7851#endif // !FEATURE_PAL
7852
7853BOOL IsIPInEE(void *ip)
7854{
7855 WRAPPER_NO_CONTRACT;
7856
7857#if defined(FEATURE_PREJIT) && !defined(FEATURE_PAL)
7858 if ((TADDR)ip > g_runtimeLoadedBaseAddress &&
7859 (TADDR)ip < g_runtimeLoadedBaseAddress + g_runtimeVirtualSize)
7860 {
7861 return TRUE;
7862 }
7863 else
7864#endif // FEATURE_PREJIT && !FEATURE_PAL
7865 {
7866 return FALSE;
7867 }
7868}
7869
7870#if defined(FEATURE_HIJACK) && (!defined(_TARGET_X86_) || defined(FEATURE_PAL))
7871
7872// This function is used to check if the specified IP is in the prolog or not.
7873bool IsIPInProlog(EECodeInfo *pCodeInfo)
7874{
7875 CONTRACTL
7876 {
7877 NOTHROW;
7878 GC_NOTRIGGER;
7879 MODE_ANY;
7880 }
7881 CONTRACTL_END;
7882
7883 bool fInsideProlog = true;
7884
7885 _ASSERTE(pCodeInfo->IsValid());
7886
7887#ifdef _TARGET_AMD64_
7888
7889 // Optimized version for AMD64 that doesn't need to go through the GC info decoding
7890 PTR_RUNTIME_FUNCTION funcEntry = pCodeInfo->GetFunctionEntry();
7891
7892 // We should always get a function entry for a managed method
7893 _ASSERTE(funcEntry != NULL);
7894
7895 // Get the unwindInfo from the function entry
7896 PUNWIND_INFO pUnwindInfo = (PUNWIND_INFO)(pCodeInfo->GetModuleBase() + funcEntry->UnwindData);
7897
7898 // Check if the specified IP is beyond the prolog or not.
7899 DWORD prologLen = pUnwindInfo->SizeOfProlog;
7900
7901#else // _TARGET_AMD64_
7902
7903 GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
7904
7905#ifdef USE_GC_INFO_DECODER
7906
7907 GcInfoDecoder gcInfoDecoder(
7908 gcInfoToken,
7909 DECODE_PROLOG_LENGTH
7910 );
7911
7912 DWORD prologLen = gcInfoDecoder.GetPrologSize();
7913
7914#else // USE_GC_INFO_DECODER
7915
7916 size_t prologLen;
7917 pCodeInfo->GetCodeManager()->IsInPrologOrEpilog(0, gcInfoToken, &prologLen);
7918
7919#endif // USE_GC_INFO_DECODER
7920
7921#endif // _TARGET_AMD64_
7922
7923 if (pCodeInfo->GetRelOffset() >= prologLen)
7924 {
7925 fInsideProlog = false;
7926 }
7927
7928 return fInsideProlog;
7929}
7930
7931// This function is used to check if the specified IP is in the epilog or not.
7932bool IsIPInEpilog(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, BOOL *pSafeToInjectThreadAbort)
7933{
7934 CONTRACTL
7935 {
7936 NOTHROW;
7937 GC_NOTRIGGER;
7938 MODE_ANY;
7939 PRECONDITION(pContextToCheck != NULL);
7940 PRECONDITION(ExecutionManager::IsManagedCode(GetIP(pContextToCheck)));
7941 PRECONDITION(pSafeToInjectThreadAbort != NULL);
7942 }
7943 CONTRACTL_END;
7944
7945 TADDR ipToCheck = GetIP(pContextToCheck);
7946
7947 _ASSERTE(pCodeInfo->IsValid());
7948
7949 // The Codeinfo should correspond to the IP we are interested in.
7950 _ASSERTE(PCODEToPINSTR(ipToCheck) == pCodeInfo->GetCodeAddress());
7951
7952 // By default, assume its safe to inject the abort.
7953 *pSafeToInjectThreadAbort = TRUE;
7954
7955 // If we are inside a prolog, then we are obviously not in the epilog.
7956 // Its safe to inject the abort here.
7957 if (IsIPInProlog(pCodeInfo))
7958 {
7959 return false;
7960 }
7961
7962 // We are not inside the prolog. We could either be in the middle of the method body or
7963 // inside the epilog. While unwindInfo contains the prolog length, it does not contain the
7964 // epilog length.
7965 //
7966 // Thus, to determine if we are inside the epilog, we use a property of RtlVirtualUnwind.
7967 // When invoked for an IP, it will return a NULL for personality routine in only two scenarios:
7968 //
7969 // 1) The unwindInfo does not contain any personality routine information, OR
7970 // 2) The IP is in prolog or epilog.
7971 //
7972 // For jitted code, (1) is not applicable since we *always* emit details of the managed personality routine
7973 // in the unwindInfo. Thus, since we have already determined that we are not inside the prolog, if performing
7974 // RtlVirtualUnwind against "ipToCheck" results in a NULL personality routine, it implies that we are inside
7975 // the epilog.
7976
7977 DWORD_PTR imageBase = 0;
7978 CONTEXT tempContext;
7979 PVOID HandlerData;
7980 DWORD_PTR establisherFrame = 0;
7981 PEXCEPTION_ROUTINE personalityRoutine = NULL;
7982
7983 // Lookup the function entry for the IP
7984 PTR_RUNTIME_FUNCTION funcEntry = pCodeInfo->GetFunctionEntry();
7985
7986 // We should always get a function entry for a managed method
7987 _ASSERTE(funcEntry != NULL);
7988
7989 imageBase = pCodeInfo->GetModuleBase();
7990
7991 ZeroMemory(&tempContext, sizeof(CONTEXT));
7992 CopyOSContext(&tempContext, pContextToCheck);
7993 KNONVOLATILE_CONTEXT_POINTERS ctxPtrs;
7994 ZeroMemory(&ctxPtrs, sizeof(ctxPtrs));
7995
7996 personalityRoutine = RtlVirtualUnwind(UNW_FLAG_EHANDLER, // HandlerType
7997 imageBase,
7998 ipToCheck,
7999 funcEntry,
8000 &tempContext,
8001 &HandlerData,
8002 &establisherFrame,
8003 &ctxPtrs);
8004
8005 bool fIsInEpilog = false;
8006
8007 if (personalityRoutine == NULL)
8008 {
8009 // We are in epilog.
8010 fIsInEpilog = true;
8011
8012#ifdef _TARGET_AMD64_
8013 // Check if context pointers has returned the address of the stack location in the hijacked function
8014 // from where RBP was restored. If the address is NULL, then it implies that RBP has been popped off.
8015 // Since JIT64 ensures that pop of RBP is the last instruction before ret/jmp, it implies its not safe
8016 // to inject an abort @ this point as EstablisherFrame (which will be based
8017 // of RBP for managed code since that is the FramePointer register, as indicated in the UnwindInfo)
8018 // will be off and can result in bad managed exception dispatch.
8019 if (ctxPtrs.Rbp == NULL)
8020#endif
8021 {
8022 *pSafeToInjectThreadAbort = FALSE;
8023 }
8024 }
8025
8026 return fIsInEpilog;
8027}
8028
8029#endif // FEATURE_HIJACK && (!_TARGET_X86_ || FEATURE_PAL)
8030
8031#define EXCEPTION_VISUALCPP_DEBUGGER ((DWORD) (1<<30 | 0x6D<<16 | 5000))
8032
8033#if defined(_TARGET_X86_)
8034
8035// This holder is used to capture the FPU state, reset it to what the CLR expects
8036// and then restore the original state that was captured.
8037//
8038// FPU has a set of exception masks which the CLR expects to be always set,
8039// implying that any corresponding condition will *not* result in FPU raising
8040// an exception.
8041//
8042// However, native code (e.g. high precision math libs) can change this mask.
8043// Thus, when control enters the CLR (e.g. via exception dispatch into the VEH),
8044// we will end up using floating point instructions that could satify the exception mask
8045// condition and raise an exception. This could result in an infinite loop, resulting in
8046// SO.
8047//
8048// We use this holder to protect applicable parts of the runtime from running into such cases.
8049extern "C" void CaptureFPUContext(BYTE *pFPBUBuf);
8050extern "C" void RestoreFPUContext(BYTE *pFPBUBuf);
8051
8052// This is FPU specific and only applicable to x86 on Windows.
8053class FPUStateHolder
8054{
8055 // Capturing FPU state requires a 28byte buffer
8056 BYTE m_bufFPUState[28];
8057
8058public:
8059 FPUStateHolder()
8060 {
8061 LIMITED_METHOD_CONTRACT;
8062
8063 BYTE *pFPUBuf = m_bufFPUState;
8064
8065 // Save the FPU state using the non-waiting instruction
8066 // so that FPU may not raise an exception incase the
8067 // exception masks are unset in the FPU Control Word
8068 CaptureFPUContext(pFPUBuf);
8069
8070 // Reset the FPU state
8071 ResetCurrentContext();
8072 }
8073
8074 ~FPUStateHolder()
8075 {
8076 LIMITED_METHOD_CONTRACT;
8077
8078 BYTE *pFPUBuf = m_bufFPUState;
8079
8080 // Restore the capture FPU state
8081 RestoreFPUContext(pFPUBuf);
8082 }
8083};
8084
8085#endif // defined(_TARGET_X86_)
8086
8087#ifndef FEATURE_PAL
8088
8089LONG WINAPI CLRVectoredExceptionHandlerShim(PEXCEPTION_POINTERS pExceptionInfo)
8090{
8091 //
8092 // HandleManagedFault will take a Crst that causes an unbalanced
8093 // notrigger scope, and this contract will whack the thread's
8094 // ClrDebugState to what it was on entry in the dtor, which causes
8095 // us to assert when we finally release the Crst later on.
8096 //
8097// CONTRACTL
8098// {
8099// NOTHROW;
8100// GC_NOTRIGGER;
8101// MODE_ANY;
8102// }
8103// CONTRACTL_END;
8104
8105 //
8106 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING
8107 //
8108 // o This function should not call functions that acquire
8109 // synchronization objects or allocate memory, because this
8110 // can cause problems. <-- quoteth MSDN -- probably for
8111 // the same reason as we cannot use LOG(); we'll recurse
8112 // into a stack overflow.
8113 //
8114 // o You cannot use LOG() in here because that will trigger an
8115 // exception which will cause infinite recursion with this
8116 // function. We work around this by ignoring all non-error
8117 // exception codes, which serves as the base of the recursion.
8118 // That way, we can LOG() from the rest of the function
8119 //
8120 // The same goes for any function called by this
8121 // function.
8122 //
8123 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING
8124 //
8125
8126 // If exceptions (or runtime) have been disabled, then simply return.
8127 if (g_fForbidEnterEE || g_fNoExceptions)
8128 {
8129 return EXCEPTION_CONTINUE_SEARCH;
8130 }
8131
8132 // WARNING
8133 //
8134 // We must preserve this so that GCStress=4 eh processing doesnt kill last error.
8135 // Note that even GetThread below can affect the LastError.
8136 // Keep this in mind when adding code above this line!
8137 //
8138 // WARNING
8139 DWORD dwLastError = GetLastError();
8140
8141#if defined(_TARGET_X86_)
8142 // Capture the FPU state before we do anything involving floating point instructions
8143 FPUStateHolder captureFPUState;
8144#endif // defined(_TARGET_X86_)
8145
8146#ifdef FEATURE_INTEROP_DEBUGGING
8147 // For interop debugging we have a fancy exception queueing stunt. When the debugger
8148 // initially gets the first chance exception notification it may not know whether to
8149 // continue it handled or unhandled, but it must continue the process to allow the
8150 // in-proc helper thread to work. What it does is continue the exception unhandled which
8151 // will let the thread immediately execute to this point. Inside this worker the thread
8152 // will block until the debugger knows how to continue the exception. If it decides the
8153 // exception was handled then we immediately resume execution as if the exeption had never
8154 // even been allowed to run into this handler. If it is unhandled then we keep processing
8155 // this handler
8156 //
8157 // WARNING: This function could potentially throw an exception, however it should only
8158 // be able to do so when an interop debugger is attached
8159 if(g_pDebugInterface != NULL)
8160 {
8161 if(g_pDebugInterface->FirstChanceSuspendHijackWorker(pExceptionInfo->ContextRecord,
8162 pExceptionInfo->ExceptionRecord) == EXCEPTION_CONTINUE_EXECUTION)
8163 return EXCEPTION_CONTINUE_EXECUTION;
8164 }
8165#endif
8166
8167
8168 DWORD dwCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
8169 if (dwCode == DBG_PRINTEXCEPTION_C || dwCode == EXCEPTION_VISUALCPP_DEBUGGER)
8170 {
8171 return EXCEPTION_CONTINUE_SEARCH;
8172 }
8173
8174#if defined(_TARGET_X86_)
8175 if (dwCode == EXCEPTION_BREAKPOINT || dwCode == EXCEPTION_SINGLE_STEP)
8176 {
8177 // For interop debugging, debugger bashes our managed exception handler.
8178 // Interop debugging does not work with real vectored exception handler :(
8179 return EXCEPTION_CONTINUE_SEARCH;
8180 }
8181#endif
8182
8183 bool bIsGCMarker = false;
8184
8185#ifdef USE_REDIRECT_FOR_GCSTRESS
8186 // This is AMD64 & ARM specific as the macro above is defined for AMD64 & ARM only
8187 bIsGCMarker = IsGcMarker(pExceptionInfo->ContextRecord, pExceptionInfo->ExceptionRecord);
8188#elif defined(_TARGET_X86_) && defined(HAVE_GCCOVER)
8189 // This is the equivalent of the check done in COMPlusFrameHandler, incase the exception is
8190 // seen by VEH first on x86.
8191 bIsGCMarker = IsGcMarker(pExceptionInfo->ContextRecord, pExceptionInfo->ExceptionRecord);
8192#endif // USE_REDIRECT_FOR_GCSTRESS
8193
8194 // Do not update the TLS with exception details for exceptions pertaining to GCStress
8195 // as they are continueable in nature.
8196 if (!bIsGCMarker)
8197 {
8198 SaveCurrentExceptionInfo(pExceptionInfo->ExceptionRecord, pExceptionInfo->ContextRecord);
8199 }
8200
8201
8202 LONG result = EXCEPTION_CONTINUE_SEARCH;
8203
8204 // If we cannot obtain a Thread object, then we have no business processing any
8205 // exceptions on this thread. Indeed, even checking to see if the faulting
8206 // address is in JITted code is problematic if we have no Thread object, since
8207 // this thread will bypass all our locks.
8208 Thread *pThread = GetThread();
8209
8210 if (pThread)
8211 {
8212 // Fiber-friendly Vectored Exception Handling:
8213 // Check if the current and the cached stack-base match.
8214 // If they don't match then probably the thread is running on a different Fiber
8215 // than during the initialization of the Thread-object.
8216 void* stopPoint = pThread->GetCachedStackBase();
8217 void* currentStackBase = Thread::GetStackUpperBound();
8218 if (currentStackBase != stopPoint)
8219 {
8220 CantAllocHolder caHolder;
8221 STRESS_LOG2(LF_EH, LL_INFO100, "In CLRVectoredExceptionHandler: mismatch of cached and current stack-base indicating use of Fibers, return with EXCEPTION_CONTINUE_SEARCH: current = %p; cache = %p\n",
8222 currentStackBase, stopPoint);
8223 return EXCEPTION_CONTINUE_SEARCH;
8224 }
8225 }
8226
8227
8228 // Also check if the exception was in the EE or not
8229 BOOL fExceptionInEE = FALSE;
8230 if (!pThread)
8231 {
8232 // Check if the exception was in EE only if Thread object isnt available.
8233 // This will save us from unnecessary checks
8234 fExceptionInEE = IsIPInEE(pExceptionInfo->ExceptionRecord->ExceptionAddress);
8235 }
8236
8237 // We are going to process the exception only if one of the following conditions is true:
8238 //
8239 // 1) We have a valid Thread object (implies exception on managed thread)
8240 // 2) Not a valid Thread object but the IP is in the execution engine (implies native thread within EE faulted)
8241 if (pThread || fExceptionInEE)
8242 {
8243 if (!bIsGCMarker)
8244 result = CLRVectoredExceptionHandler(pExceptionInfo);
8245 else
8246 result = EXCEPTION_CONTINUE_EXECUTION;
8247
8248 if (EXCEPTION_EXECUTE_HANDLER == result)
8249 {
8250 result = EXCEPTION_CONTINUE_SEARCH;
8251 }
8252
8253#ifdef _DEBUG
8254#ifndef WIN64EXCEPTIONS
8255 {
8256 CantAllocHolder caHolder;
8257
8258 PEXCEPTION_REGISTRATION_RECORD pRecord = GetCurrentSEHRecord();
8259 while (pRecord != EXCEPTION_CHAIN_END)
8260 {
8261 STRESS_LOG2(LF_EH, LL_INFO10000, "CLRVectoredExceptionHandlerShim: FS:0 %p:%p\n",
8262 pRecord, pRecord->Handler);
8263 pRecord = pRecord->Next;
8264 }
8265 }
8266#endif // WIN64EXCEPTIONS
8267
8268 {
8269 // The call to "CLRVectoredExceptionHandler" above can return EXCEPTION_CONTINUE_SEARCH
8270 // for different scenarios like StackOverFlow/SOFT_SO, or if it is forbidden to enter the EE.
8271 // Thus, if we dont have a Thread object for the thread that has faulted and we came this far
8272 // because the fault was in MSCORWKS, then we work with the frame chain below only if we have
8273 // valid Thread object.
8274
8275 if (pThread)
8276 {
8277 CantAllocHolder caHolder;
8278
8279 TADDR* sp;
8280 sp = (TADDR*)&sp;
8281 DWORD count = 0;
8282 void* stopPoint = pThread->GetCachedStackBase();
8283 // If Frame chain is corrupted, we may get AV while accessing frames, and this function will be
8284 // called recursively. We use Frame chain to limit our search range. It is not disaster if we
8285 // can not use it.
8286 if (!(dwCode == STATUS_ACCESS_VIOLATION &&
8287 IsIPInEE(pExceptionInfo->ExceptionRecord->ExceptionAddress)))
8288 {
8289 // Find the stop point (most jitted function)
8290 Frame* pFrame = pThread->GetFrame();
8291 for(;;)
8292 {
8293 // skip GC frames
8294 if (pFrame == 0 || pFrame == (Frame*) -1)
8295 break;
8296
8297 Frame::ETransitionType type = pFrame->GetTransitionType();
8298 if (type == Frame::TT_M2U || type == Frame::TT_InternalCall)
8299 {
8300 stopPoint = pFrame;
8301 break;
8302 }
8303 pFrame = pFrame->Next();
8304 }
8305 }
8306 STRESS_LOG0(LF_EH, LL_INFO100, "CLRVectoredExceptionHandlerShim: stack");
8307 while (count < 20 && sp < stopPoint)
8308 {
8309 if (IsIPInEE((BYTE*)*sp))
8310 {
8311 STRESS_LOG1(LF_EH, LL_INFO100, "%pK\n", *sp);
8312 count ++;
8313 }
8314 sp += 1;
8315 }
8316 }
8317 }
8318#endif // _DEBUG
8319
8320#ifndef WIN64EXCEPTIONS
8321 {
8322 CantAllocHolder caHolder;
8323 STRESS_LOG1(LF_EH, LL_INFO1000, "CLRVectoredExceptionHandlerShim: returning %d\n", result);
8324 }
8325#endif // WIN64EXCEPTIONS
8326
8327 }
8328
8329 SetLastError(dwLastError);
8330
8331 return result;
8332}
8333
8334#endif // !FEATURE_PAL
8335
8336// Contains the handle to the registered VEH
8337static PVOID g_hVectoredExceptionHandler = NULL;
8338
8339void CLRAddVectoredHandlers(void)
8340{
8341#ifndef FEATURE_PAL
8342
8343 // We now install a vectored exception handler on all supporting Windows architectures.
8344 g_hVectoredExceptionHandler = AddVectoredExceptionHandler(TRUE, (PVECTORED_EXCEPTION_HANDLER)CLRVectoredExceptionHandlerShim);
8345 if (g_hVectoredExceptionHandler == NULL)
8346 {
8347 LOG((LF_EH, LL_INFO100, "CLRAddVectoredHandlers: AddVectoredExceptionHandler() failed\n"));
8348 COMPlusThrowHR(E_FAIL);
8349 }
8350
8351 LOG((LF_EH, LL_INFO100, "CLRAddVectoredHandlers: AddVectoredExceptionHandler() succeeded\n"));
8352#endif // !FEATURE_PAL
8353}
8354
8355// This function removes the vectored exception and continue handler registration
8356// from the OS.
8357void CLRRemoveVectoredHandlers(void)
8358{
8359 CONTRACTL
8360 {
8361 NOTHROW;
8362 GC_NOTRIGGER;
8363 MODE_ANY;
8364 }
8365 CONTRACTL_END;
8366#ifndef FEATURE_PAL
8367
8368 // Unregister the vectored exception handler if one is registered (and we can).
8369 if (g_hVectoredExceptionHandler != NULL)
8370 {
8371 // Unregister the vectored exception handler
8372 if (RemoveVectoredExceptionHandler(g_hVectoredExceptionHandler) == FALSE)
8373 {
8374 LOG((LF_EH, LL_INFO100, "CLRRemoveVectoredHandlers: RemoveVectoredExceptionHandler() failed.\n"));
8375 }
8376 else
8377 {
8378 LOG((LF_EH, LL_INFO100, "CLRRemoveVectoredHandlers: RemoveVectoredExceptionHandler() succeeded.\n"));
8379 }
8380 }
8381#endif // !FEATURE_PAL
8382}
8383
8384//
8385// This does the work of the Unwind and Continue Hanlder inside the catch clause of that handler. The stack has not
8386// been unwound when this is called. Keep that in mind when deciding where to put new code :)
8387//
8388void UnwindAndContinueRethrowHelperInsideCatch(Frame* pEntryFrame, Exception* pException)
8389{
8390 STATIC_CONTRACT_NOTHROW;
8391 STATIC_CONTRACT_GC_TRIGGERS;
8392 STATIC_CONTRACT_MODE_ANY;
8393 STATIC_CONTRACT_SO_TOLERANT;
8394
8395 Thread* pThread = GetThread();
8396
8397 GCX_COOP();
8398
8399 LOG((LF_EH, LL_INFO1000, "UNWIND_AND_CONTINUE inside catch, unwinding frame chain\n"));
8400
8401 // This SetFrame is OK because we will not have frames that require ExceptionUnwind in strictly unmanaged EE
8402 // code chunks which is all that an UnC handler can guard.
8403 //
8404 // @todo: we'd rather use UnwindFrameChain, but there is a concern: some of the ExceptionUnwind methods on some
8405 // of the Frame types do a great deal of work; load classes, throw exceptions, etc. We need to decide on some
8406 // policy here. Do we want to let such funcitons throw, etc.? Right now, we believe that there are no such
8407 // frames on the stack to be unwound, so the SetFrame is alright (see the first comment above.) At the very
8408 // least, we should add some way to assert that.
8409 pThread->SetFrame(pEntryFrame);
8410
8411#ifdef _DEBUG
8412 if (!NingenEnabled())
8413 {
8414 CONTRACT_VIOLATION(ThrowsViolation);
8415 BEGIN_SO_INTOLERANT_CODE(pThread);
8416 // Call CLRException::GetThrowableFromException to force us to retrieve the THROWABLE
8417 // while we are still within the context of the catch block. This will help diagnose
8418 // cases where the last thrown object is NULL.
8419 OBJECTREF orThrowable = CLRException::GetThrowableFromException(pException);
8420 CONSISTENCY_CHECK(orThrowable != NULL);
8421 END_SO_INTOLERANT_CODE;
8422 }
8423#endif
8424}
8425
8426//
8427// This does the work of the Unwind and Continue Hanlder after the catch clause of that handler. The stack has been
8428// unwound by the time this is called. Keep that in mind when deciding where to put new code :)
8429//
8430VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFrame, Exception* pException)
8431{
8432 STATIC_CONTRACT_THROWS;
8433 STATIC_CONTRACT_GC_TRIGGERS;
8434 STATIC_CONTRACT_MODE_ANY;
8435 STATIC_CONTRACT_SO_TOLERANT;
8436
8437 // We really should probe before switching to cooperative mode, although there's no chance
8438 // we'll SO in doing that as we've just caught an exception. We can't probe just
8439 // yet though, because we want to avoid reprobing on an SO exception and we need to switch
8440 // to cooperative to check the throwable for an SO as well as the pException object (as the
8441 // pException could be a LastThrownObjectException.) Blech.
8442 CONTRACT_VIOLATION(SOToleranceViolation);
8443
8444 GCX_COOP();
8445
8446 LOG((LF_EH, LL_INFO1000, "UNWIND_AND_CONTINUE caught and will rethrow\n"));
8447
8448 OBJECTREF orThrowable = NingenEnabled() ? NULL : CLRException::GetThrowableFromException(pException);
8449 LOG((LF_EH, LL_INFO1000, "UNWIND_AND_CONTINUE got throwable %p\n",
8450 OBJECTREFToObject(orThrowable)));
8451
8452 Exception::Delete(pException);
8453
8454 if (orThrowable != NULL && g_CLRPolicyRequested)
8455 {
8456 if (orThrowable->GetMethodTable() == g_pOutOfMemoryExceptionClass)
8457 {
8458 EEPolicy::HandleOutOfMemory();
8459 }
8460 else if (orThrowable->GetMethodTable() == g_pStackOverflowExceptionClass)
8461 {
8462#ifdef FEATURE_STACK_PROBE
8463 EEPolicy::HandleSoftStackOverflow();
8464#else
8465 /* The parameters of the function do not matter here */
8466 EEPolicy::HandleStackOverflow(SOD_UnmanagedFrameHandler, NULL);
8467#endif
8468 }
8469 }
8470
8471 RaiseTheExceptionInternalOnly(orThrowable, FALSE);
8472}
8473
8474void SaveCurrentExceptionInfo(PEXCEPTION_RECORD pRecord, PCONTEXT pContext)
8475{
8476 CONTRACTL
8477 {
8478 NOTHROW;
8479 GC_NOTRIGGER;
8480 MODE_ANY;
8481 SO_TOLERANT;
8482 }
8483 CONTRACTL_END;
8484
8485 if ((pRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)))
8486 {
8487 // If exception is unwinding the stack, the ExceptionCode may have been changed to
8488 // STATUS_UNWIND if RtlUnwind is called with a NULL ExceptionRecord.
8489 // Since we have captured exception info in the first pass, we don't need to capture it again.
8490 return;
8491 }
8492
8493 if (CExecutionEngine::CheckThreadStateNoCreate(TlsIdx_PEXCEPTION_RECORD))
8494 {
8495 BOOL fSave = TRUE;
8496 if (!IsSOExceptionCode(pRecord->ExceptionCode))
8497 {
8498 DWORD dwLastExceptionCode = (DWORD)(SIZE_T) (ClrFlsGetValue(TlsIdx_EXCEPTION_CODE));
8499 if (IsSOExceptionCode(dwLastExceptionCode))
8500 {
8501 PEXCEPTION_RECORD lastRecord =
8502 static_cast<PEXCEPTION_RECORD> (ClrFlsGetValue(TlsIdx_PEXCEPTION_RECORD));
8503
8504 // We are trying to see if C++ is attempting a rethrow of a SO exception. If so,
8505 // we want to prevent updating the exception details in the TLS. This is a workaround,
8506 // as explained below.
8507 if (pRecord->ExceptionCode == EXCEPTION_MSVC)
8508 {
8509 // This is a workaround.
8510 // When C++ rethrows, C++ internally gets rid of the new exception record after
8511 // unwinding stack, and present the original exception record to the thread.
8512 // When we get VC's support to obtain exception record in try/catch, we will replace
8513 // this code.
8514 if (pRecord < lastRecord)
8515 {
8516 // For the C++ rethrow workaround, ensure that the last exception record is still valid and as we expect it to be.
8517 //
8518 // Its possible that we are still below the address of last exception record
8519 // but since the execution stack could have changed, simply comparing its address
8520 // with the address of the current exception record may not be enough.
8521 //
8522 // Thus, ensure that its still valid and holds the exception code we expect it to
8523 // have (i.e. value in dwLastExceptionCode).
8524 if ((lastRecord != NULL) && (lastRecord->ExceptionCode == dwLastExceptionCode))
8525 {
8526 fSave = FALSE;
8527 }
8528 }
8529 }
8530 }
8531 }
8532 if (fSave)
8533 {
8534 ClrFlsSetValue(TlsIdx_EXCEPTION_CODE, (void*)(size_t)(pRecord->ExceptionCode));
8535 ClrFlsSetValue(TlsIdx_PEXCEPTION_RECORD, pRecord);
8536 ClrFlsSetValue(TlsIdx_PCONTEXT, pContext);
8537 }
8538 }
8539}
8540
8541#ifndef DACCESS_COMPILE
8542//******************************************************************************
8543//
8544// NotifyOfCHFFilterWrapper
8545//
8546// Helper function to deliver notifications of CatchHandlerFound inside a
8547// EX_TRY/EX_CATCH.
8548//
8549// Parameters:
8550// pExceptionInfo - the pExceptionInfo passed to a filter function.
8551// pCatcherStackAddr - a Frame* from the PAL_TRY/PAL_EXCEPT_FILTER site.
8552//
8553// Return:
8554// always returns EXCEPTION_CONTINUE_SEARCH.
8555//
8556//******************************************************************************
8557LONG NotifyOfCHFFilterWrapper(
8558 EXCEPTION_POINTERS *pExceptionInfo, // the pExceptionInfo passed to a filter function.
8559 PVOID pParam) // contains a Frame* from the PAL_TRY/PAL_EXCEPT_FILTER site.
8560{
8561 LIMITED_METHOD_CONTRACT;
8562
8563 PVOID pCatcherStackAddr = ((NotifyOfCHFFilterWrapperParam *)pParam)->pFrame;
8564 ULONG ret = EXCEPTION_CONTINUE_SEARCH;
8565
8566 // We are here to send an event notification to the debugger and to the appdomain. To
8567 // determine if it is safe to send these notifications, check the following:
8568 // 1) The thread object has been set up.
8569 // 2) The thread has an exception on it.
8570 // 3) The exception is the same as the one this filter is called on.
8571 Thread *pThread = GetThread();
8572 if ( (pThread == NULL) ||
8573 (pThread->GetExceptionState()->GetContextRecord() == NULL) ||
8574 (GetSP(pThread->GetExceptionState()->GetContextRecord()) != GetSP(pExceptionInfo->ContextRecord) ) )
8575 {
8576 LOG((LF_EH, LL_INFO1000, "NotifyOfCHFFilterWrapper: not sending notices. pThread: %0x8", pThread));
8577 if (pThread)
8578 {
8579 LOG((LF_EH, LL_INFO1000, ", Thread SP: %0x8, Exception SP: %08x",
8580 pThread->GetExceptionState()->GetContextRecord() ? GetSP(pThread->GetExceptionState()->GetContextRecord()) : NULL,
8581 pExceptionInfo->ContextRecord ? GetSP(pExceptionInfo->ContextRecord) : NULL ));
8582 }
8583 LOG((LF_EH, LL_INFO1000, "\n"));
8584 return ret;
8585 }
8586
8587 if (g_pDebugInterface)
8588 {
8589 // It looks safe, so make the debugger notification.
8590 ret = g_pDebugInterface->NotifyOfCHFFilter(pExceptionInfo, pCatcherStackAddr);
8591 }
8592
8593 return ret;
8594} // LONG NotifyOfCHFFilterWrapper()
8595
8596// This filter will be used process exceptions escaping out of AD transition boundaries
8597// that are not at the base of the managed thread. Those are handled in ThreadBaseRedirectingFilter.
8598// This will be invoked when an exception is going unhandled from the called AppDomain.
8599//
8600// This can be used to do last moment work before the exception gets caught by the EX_CATCH setup
8601// at the AD transition point.
8602LONG AppDomainTransitionExceptionFilter(
8603 EXCEPTION_POINTERS *pExceptionInfo, // the pExceptionInfo passed to a filter function.
8604 PVOID pParam)
8605{
8606 // Ideally, we would be NOTHROW here. However, NotifyOfCHFFilterWrapper calls into
8607 // NotifyOfCHFFilter that is THROWS. Thus, to prevent contract violation,
8608 // we abide by the rules and be THROWS.
8609 //
8610 // Same rationale for GC_TRIGGERS as well.
8611 CONTRACTL
8612 {
8613 GC_TRIGGERS;
8614 MODE_ANY;
8615 THROWS;
8616 }
8617 CONTRACTL_END;
8618
8619 ULONG ret = EXCEPTION_CONTINUE_SEARCH;
8620
8621 // First, call into NotifyOfCHFFilterWrapper
8622 ret = NotifyOfCHFFilterWrapper(pExceptionInfo, pParam);
8623
8624#ifndef FEATURE_PAL
8625 // Setup the watson bucketing details if the escaping
8626 // exception is preallocated.
8627 if (SetupWatsonBucketsForEscapingPreallocatedExceptions())
8628 {
8629 // Set the flag that these were captured at AD Transition
8630 DEBUG_STMT(GetThread()->GetExceptionState()->GetUEWatsonBucketTracker()->SetCapturedAtADTransition());
8631 }
8632
8633 // Attempt to capture buckets for non-preallocated exceptions just before the AppDomain transition boundary
8634 {
8635 GCX_COOP();
8636 OBJECTREF oThrowable = GetThread()->GetThrowable();
8637 if ((oThrowable != NULL) && (CLRException::IsPreallocatedExceptionObject(oThrowable) == FALSE))
8638 {
8639 SetupWatsonBucketsForNonPreallocatedExceptions();
8640 }
8641 }
8642#endif // !FEATURE_PAL
8643
8644 return ret;
8645} // LONG AppDomainTransitionExceptionFilter()
8646
8647// This filter will be used process exceptions escaping out of dynamic reflection invocation as
8648// unhandled and will eventually be caught in the VM to be made as inner exception of
8649// TargetInvocationException that will be thrown from the VM.
8650LONG ReflectionInvocationExceptionFilter(
8651 EXCEPTION_POINTERS *pExceptionInfo, // the pExceptionInfo passed to a filter function.
8652 PVOID pParam)
8653{
8654 // Ideally, we would be NOTHROW here. However, NotifyOfCHFFilterWrapper calls into
8655 // NotifyOfCHFFilter that is THROWS. Thus, to prevent contract violation,
8656 // we abide by the rules and be THROWS.
8657 //
8658 // Same rationale for GC_TRIGGERS as well.
8659 CONTRACTL
8660 {
8661 GC_TRIGGERS;
8662 MODE_ANY;
8663 THROWS;
8664 }
8665 CONTRACTL_END;
8666
8667 ULONG ret = EXCEPTION_CONTINUE_SEARCH;
8668
8669 // First, call into NotifyOfCHFFilterWrapper
8670 ret = NotifyOfCHFFilterWrapper(pExceptionInfo, pParam);
8671
8672#ifndef FEATURE_PAL
8673 // Setup the watson bucketing details if the escaping
8674 // exception is preallocated.
8675 if (SetupWatsonBucketsForEscapingPreallocatedExceptions())
8676 {
8677 // Set the flag that these were captured during Reflection Invocation
8678 DEBUG_STMT(GetThread()->GetExceptionState()->GetUEWatsonBucketTracker()->SetCapturedAtReflectionInvocation());
8679 }
8680
8681 // Attempt to capture buckets for non-preallocated exceptions just before the ReflectionInvocation boundary
8682 {
8683 GCX_COOP();
8684 OBJECTREF oThrowable = GetThread()->GetThrowable();
8685 if ((oThrowable != NULL) && (CLRException::IsPreallocatedExceptionObject(oThrowable) == FALSE))
8686 {
8687 SetupWatsonBucketsForNonPreallocatedExceptions();
8688 }
8689 }
8690#endif // !FEATURE_PAL
8691
8692 // If the application has opted into triggering a failfast when a CorruptedStateException enters the Reflection system,
8693 // then do the needful.
8694 if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_FailFastOnCorruptedStateException) == 1)
8695 {
8696 // Get the thread and the managed exception object - they must exist at this point
8697 Thread *pCurThread = GetThread();
8698 _ASSERTE(pCurThread != NULL);
8699
8700 // Get the thread exception state
8701 ThreadExceptionState * pCurTES = pCurThread->GetExceptionState();
8702 _ASSERTE(pCurTES != NULL);
8703
8704 // Get the exception tracker for the current exception
8705#ifdef WIN64EXCEPTIONS
8706 PTR_ExceptionTracker pEHTracker = pCurTES->GetCurrentExceptionTracker();
8707#elif _TARGET_X86_
8708 PTR_ExInfo pEHTracker = pCurTES->GetCurrentExceptionTracker();
8709#else // !(_WIN64 || _TARGET_X86_)
8710#error Unsupported platform
8711#endif // _WIN64
8712
8713#ifdef FEATURE_CORRUPTING_EXCEPTIONS
8714 if (pEHTracker->GetCorruptionSeverity() == ProcessCorrupting)
8715 {
8716 EEPolicy::HandleFatalError(COR_E_FAILFAST, reinterpret_cast<UINT_PTR>(pExceptionInfo->ExceptionRecord->ExceptionAddress), NULL, pExceptionInfo);
8717 }
8718#endif // FEATURE_CORRUPTING_EXCEPTIONS
8719 }
8720
8721 return ret;
8722} // LONG ReflectionInvocationExceptionFilter()
8723
8724#endif // !DACCESS_COMPILE
8725
8726#ifdef _DEBUG
8727bool DebugIsEECxxExceptionPointer(void* pv)
8728{
8729 CONTRACTL
8730 {
8731 DISABLED(NOTHROW);
8732 GC_NOTRIGGER;
8733 MODE_ANY;
8734 DEBUG_ONLY;
8735 }
8736 CONTRACTL_END;
8737
8738 if (pv == NULL)
8739 {
8740 return false;
8741 }
8742
8743 // check whether the memory is readable in no-throw way
8744 if (!isMemoryReadable((TADDR)pv, sizeof(UINT_PTR)))
8745 {
8746 return false;
8747 }
8748
8749 bool retVal = false;
8750
8751 EX_TRY
8752 {
8753 UINT_PTR vtbl = *(UINT_PTR*)pv;
8754
8755 // ex.h
8756
8757 HRException boilerplate1;
8758 COMException boilerplate2;
8759 SEHException boilerplate3;
8760
8761 // clrex.h
8762
8763 CLRException boilerplate4;
8764 CLRLastThrownObjectException boilerplate5;
8765 EEException boilerplate6;
8766 EEMessageException boilerplate7;
8767 EEResourceException boilerplate8;
8768
8769 // EECOMException::~EECOMException calls FreeExceptionData, which is GC_TRIGGERS,
8770 // but it won't trigger in this case because EECOMException's members remain NULL.
8771 CONTRACT_VIOLATION(GCViolation);
8772 EECOMException boilerplate9;
8773
8774 EEFieldException boilerplate10;
8775 EEMethodException boilerplate11;
8776 EEArgumentException boilerplate12;
8777 EETypeLoadException boilerplate13;
8778 EEFileLoadException boilerplate14;
8779 ObjrefException boilerplate15;
8780
8781 UINT_PTR ValidVtbls[] =
8782 {
8783 *((TADDR*)&boilerplate1),
8784 *((TADDR*)&boilerplate2),
8785 *((TADDR*)&boilerplate3),
8786 *((TADDR*)&boilerplate4),
8787 *((TADDR*)&boilerplate5),
8788 *((TADDR*)&boilerplate6),
8789 *((TADDR*)&boilerplate7),
8790 *((TADDR*)&boilerplate8),
8791 *((TADDR*)&boilerplate9),
8792 *((TADDR*)&boilerplate10),
8793 *((TADDR*)&boilerplate11),
8794 *((TADDR*)&boilerplate12),
8795 *((TADDR*)&boilerplate13),
8796 *((TADDR*)&boilerplate14),
8797 *((TADDR*)&boilerplate15)
8798 };
8799
8800 const int nVtbls = sizeof(ValidVtbls) / sizeof(ValidVtbls[0]);
8801
8802 for (int i = 0; i < nVtbls; i++)
8803 {
8804 if (vtbl == ValidVtbls[i])
8805 {
8806 retVal = true;
8807 break;
8808 }
8809 }
8810 }
8811 EX_CATCH
8812 {
8813 // Swallow any exception out of the exception constructors above and simply return false.
8814 }
8815 EX_END_CATCH(SwallowAllExceptions);
8816
8817 return retVal;
8818}
8819
8820void *DebugGetCxxException(EXCEPTION_RECORD* pExceptionRecord);
8821
8822bool DebugIsEECxxException(EXCEPTION_RECORD* pExceptionRecord)
8823{
8824 return DebugIsEECxxExceptionPointer(DebugGetCxxException(pExceptionRecord));
8825}
8826
8827//
8828// C++ EH cracking material gleaned from the debugger:
8829// (DO NOT USE THIS KNOWLEDGE IN NON-DEBUG CODE!!!)
8830//
8831// EHExceptionRecord::EHParameters
8832// [0] magicNumber : uint
8833// [1] pExceptionObject : void*
8834// [2] pThrowInfo : ThrowInfo*
8835
8836#ifdef _WIN64
8837#define NUM_CXX_EXCEPTION_PARAMS 4
8838#else
8839#define NUM_CXX_EXCEPTION_PARAMS 3
8840#endif
8841
8842void *DebugGetCxxException(EXCEPTION_RECORD* pExceptionRecord)
8843{
8844 WRAPPER_NO_CONTRACT;
8845
8846 bool fExCodeIsCxx = (EXCEPTION_MSVC == pExceptionRecord->ExceptionCode);
8847 bool fExHasCorrectNumParams = (NUM_CXX_EXCEPTION_PARAMS == pExceptionRecord->NumberParameters);
8848
8849 if (fExCodeIsCxx && fExHasCorrectNumParams)
8850 {
8851 void** ppException = (void**)pExceptionRecord->ExceptionInformation[1];
8852
8853 if (NULL == ppException)
8854 {
8855 return NULL;
8856 }
8857
8858 return *ppException;
8859
8860 }
8861
8862 CONSISTENCY_CHECK_MSG(!fExCodeIsCxx || fExHasCorrectNumParams, "We expected an EXCEPTION_MSVC exception to have 3 parameters. Did the CRT change its exception format?");
8863
8864 return NULL;
8865}
8866
8867#endif // _DEBUG
8868
8869#endif // #ifndef DACCESS_COMPILE
8870
8871BOOL IsException(MethodTable *pMT) {
8872 CONTRACTL
8873 {
8874 NOTHROW;
8875 GC_NOTRIGGER;
8876 MODE_ANY;
8877 SO_TOLERANT;
8878 SUPPORTS_DAC;
8879 }
8880 CONTRACTL_END;
8881
8882 ASSERT(g_pExceptionClass != NULL);
8883
8884 while (pMT != NULL && pMT != g_pExceptionClass) {
8885 pMT = pMT->GetParentMethodTable();
8886 }
8887
8888 return pMT != NULL;
8889} // BOOL IsException()
8890
8891// Returns TRUE iff calling get_StackTrace on an exception of the given type ends up
8892// executing some other code than just Exception.get_StackTrace.
8893BOOL ExceptionTypeOverridesStackTraceGetter(PTR_MethodTable pMT)
8894{
8895 CONTRACTL
8896 {
8897 THROWS;
8898 GC_NOTRIGGER;
8899 MODE_ANY;
8900 SO_TOLERANT;
8901 SUPPORTS_DAC;
8902 }
8903 CONTRACTL_END;
8904
8905 _ASSERTE(IsException(pMT));
8906
8907 if (pMT == g_pExceptionClass)
8908 {
8909 // if the type is System.Exception, it certainly doesn't override anything
8910 return FALSE;
8911 }
8912
8913 // find the slot corresponding to get_StackTrace
8914 for (DWORD slot = g_pObjectClass->GetNumVirtuals(); slot < g_pExceptionClass->GetNumVirtuals(); slot++)
8915 {
8916 MethodDesc *pMD = g_pExceptionClass->GetMethodDescForSlot(slot);
8917 LPCUTF8 name = pMD->GetName();
8918
8919 if (name != NULL && strcmp(name, "get_StackTrace") == 0)
8920 {
8921 // see if the slot is overriden by pMT
8922 MethodDesc *pDerivedMD = pMT->GetMethodDescForSlot(slot);
8923 return (pDerivedMD != pMD);
8924 }
8925 }
8926
8927 // there must be get_StackTrace on System.Exception
8928 UNREACHABLE();
8929}
8930
8931// Removes source file names/paths and line information from a stack trace generated
8932// by Environment.GetStackTrace.
8933void StripFileInfoFromStackTrace(SString &ssStackTrace)
8934{
8935 CONTRACTL
8936 {
8937 THROWS;
8938 GC_NOTRIGGER;
8939 MODE_ANY;
8940 SUPPORTS_DAC;
8941 }
8942 CONTRACTL_END;
8943
8944 SString::Iterator i = ssStackTrace.Begin();
8945 SString::Iterator end;
8946 int countBracket = 0;
8947 int position = 0;
8948
8949 while (i < ssStackTrace.End())
8950 {
8951 if (i[0] == W('('))
8952 {
8953 countBracket ++;
8954 }
8955 else if (i[0] == W(')'))
8956 {
8957 if (countBracket == 1)
8958 {
8959 end = i + 1;
8960 SString::Iterator j = i + 1;
8961 while (j < ssStackTrace.End())
8962 {
8963 if (j[0] == W('\r') || j[0] == W('\n'))
8964 {
8965 break;
8966 }
8967 j++;
8968 }
8969 if (j > end)
8970 {
8971 ssStackTrace.Delete(end,j-end);
8972 i = ssStackTrace.Begin() + position;
8973 }
8974 }
8975 countBracket --;
8976 }
8977 i ++;
8978 position ++;
8979 }
8980 ssStackTrace.Truncate(end);
8981}
8982
8983#ifdef _DEBUG
8984//==============================================================================
8985// This function will set a thread state indicating if an exception is escaping
8986// the last CLR personality routine on the stack in a reverse pinvoke scenario.
8987//
8988// If the exception continues to go unhandled, it will eventually reach the OS
8989// that will start invoking the UEFs. Since CLR registers its UEF only to handle
8990// unhandled exceptions on such reverse pinvoke threads, we will assert this
8991// state in our UEF to ensure it does not get called for any other reason.
8992//
8993// This function should be called only if the personality routine returned
8994// EXCEPTION_CONTINUE_SEARCH.
8995//==============================================================================
8996void SetReversePInvokeEscapingUnhandledExceptionStatus(BOOL fIsUnwinding,
8997#if defined(_TARGET_X86_)
8998 EXCEPTION_REGISTRATION_RECORD * pEstablisherFrame
8999#elif defined(WIN64EXCEPTIONS)
9000 ULONG64 pEstablisherFrame
9001#else
9002#error Unsupported platform
9003#endif
9004 )
9005{
9006#ifndef DACCESS_COMPILE
9007
9008 LIMITED_METHOD_CONTRACT;
9009
9010 Thread *pCurThread = GetThread();
9011 _ASSERTE(pCurThread);
9012
9013 if (pCurThread->GetExceptionState()->IsExceptionInProgress())
9014 {
9015 if (!fIsUnwinding)
9016 {
9017 // Get the top-most Frame of this thread.
9018 Frame* pCurFrame = pCurThread->GetFrame();
9019 Frame* pTopMostFrame = pCurFrame;
9020 while (pCurFrame && (pCurFrame != FRAME_TOP))
9021 {
9022 pTopMostFrame = pCurFrame;
9023 pCurFrame = pCurFrame->PtrNextFrame();
9024 }
9025
9026 // Is the exception escaping the last CLR personality routine on the stack of a
9027 // reverse pinvoke thread?
9028 if (((pTopMostFrame == NULL) || (pTopMostFrame == FRAME_TOP)) ||
9029 ((void *)(pEstablisherFrame) > (void *)(pTopMostFrame)))
9030 {
9031 LOG((LF_EH, LL_INFO100, "SetReversePInvokeEscapingUnhandledExceptionStatus: setting Ex_RPInvokeEscapingException\n"));
9032 // Set the flag on the thread indicating the exception is escaping the
9033 // top most reverse pinvoke exception handler.
9034 pCurThread->GetExceptionState()->GetFlags()->SetReversePInvokeEscapingException();
9035 }
9036 }
9037 else
9038 {
9039 // Since we are unwinding, simply unset the flag indicating escaping unhandled exception
9040 // if it was set.
9041 if (pCurThread->GetExceptionState()->GetFlags()->ReversePInvokeEscapingException())
9042 {
9043 LOG((LF_EH, LL_INFO100, "SetReversePInvokeEscapingUnhandledExceptionStatus: unsetting Ex_RPInvokeEscapingException\n"));
9044 pCurThread->GetExceptionState()->GetFlags()->ResetReversePInvokeEscapingException();
9045 }
9046 }
9047 }
9048 else
9049 {
9050 LOG((LF_EH, LL_INFO100, "SetReversePInvokeEscapingUnhandledExceptionStatus: not setting Ex_RPInvokeEscapingException since no exception is in progress.\n"));
9051 }
9052#endif // !DACCESS_COMPILE
9053}
9054
9055#endif // _DEBUG
9056
9057#ifndef FEATURE_PAL
9058
9059// This function will capture the watson buckets for the current exception object that is:
9060//
9061// 1) Non-preallocated
9062// 2) Already contains the IP for watson bucketing
9063BOOL SetupWatsonBucketsForNonPreallocatedExceptions(OBJECTREF oThrowable /* = NULL */)
9064{
9065#ifndef DACCESS_COMPILE
9066
9067 // CoreCLR may have watson bucketing conditionally enabled.
9068 if (!IsWatsonEnabled())
9069 {
9070 return FALSE;
9071 }
9072
9073 CONTRACTL
9074 {
9075 GC_TRIGGERS;
9076 MODE_COOPERATIVE;
9077 NOTHROW;
9078 PRECONDITION(GetThread() != NULL);
9079 }
9080 CONTRACTL_END;
9081
9082 // By default, assume we didnt get the buckets
9083 BOOL fSetupWatsonBuckets = FALSE;
9084
9085 Thread * pThread = GetThread();
9086
9087 struct
9088 {
9089 OBJECTREF oThrowable;
9090 } gc;
9091 ZeroMemory(&gc, sizeof(gc));
9092 GCPROTECT_BEGIN(gc);
9093
9094 // Get the throwable to be used
9095 gc.oThrowable = (oThrowable != NULL) ? oThrowable : pThread->GetThrowable();
9096 if (gc.oThrowable == NULL)
9097 {
9098 // If we have no throwable, then simply return back.
9099 //
9100 // We could be here because the VM may have raised an exception,
9101 // and not managed code, for its internal usage (e.g. TA to end the
9102 // threads when unloading an AppDomain). Thus, there would be no throwable
9103 // present since the exception has not been seen by the runtime's
9104 // personality routine.
9105 //
9106 // Hence, we have no work to do here.
9107 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForNonPreallocatedExceptions - No throwable available.\n"));
9108 goto done;
9109 }
9110
9111 // The exception object should be non-preallocated
9112 _ASSERTE(!CLRException::IsPreallocatedExceptionObject(gc.oThrowable));
9113
9114 if (((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent() == FALSE)
9115 {
9116 // Attempt to capture the watson buckets since they are not present.
9117 UINT_PTR ip = ((EXCEPTIONREF)gc.oThrowable)->GetIPForWatsonBuckets();
9118 if (ip != NULL)
9119 {
9120 // Attempt to capture the buckets
9121 PTR_VOID pBuckets = GetBucketParametersForManagedException(ip, TypeOfReportedError::UnhandledException, pThread, &gc.oThrowable);
9122 if (pBuckets != NULL)
9123 {
9124 // Got the buckets - save them to the exception object
9125 fSetupWatsonBuckets = FALSE;
9126 EX_TRY
9127 {
9128 fSetupWatsonBuckets = CopyWatsonBucketsToThrowable(pBuckets, gc.oThrowable);
9129 }
9130 EX_CATCH
9131 {
9132 // OOM can bring us here
9133 fSetupWatsonBuckets = FALSE;
9134 }
9135 EX_END_CATCH(SwallowAllExceptions);
9136
9137 if (!fSetupWatsonBuckets)
9138 {
9139 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForNonPreallocatedExceptions - Unable to copy buckets to throwable likely due to OOM.\n"));
9140 }
9141 else
9142 {
9143 // Clear the saved IP since we have captured the buckets
9144 ((EXCEPTIONREF)gc.oThrowable)->SetIPForWatsonBuckets(NULL);
9145 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForNonPreallocatedExceptions - Buckets copied to throwable.\n"));
9146 }
9147 FreeBucketParametersForManagedException(pBuckets);
9148 }
9149 else
9150 {
9151 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForNonPreallocatedExceptions - Unable to capture buckets from IP likely due to OOM.\n"));
9152 }
9153 }
9154 else
9155 {
9156 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForNonPreallocatedExceptions - No IP available to capture buckets from.\n"));
9157 }
9158 }
9159
9160done:;
9161 GCPROTECT_END();
9162
9163 return fSetupWatsonBuckets;
9164#else // DACCESS_COMPILE
9165 return FALSE;
9166#endif // !DACCESS_COMPILE
9167}
9168
9169// When exceptions are escaping out of various transition boundaries,
9170// we will need to capture bucket details for the original exception
9171// before the exception goes across the boundary to the caller.
9172//
9173// Examples of such boundaries include:
9174//
9175// 1) AppDomain transition boundaries (these are physical transition boundaries)
9176// 2) Dynamic method invocation in Reflection (these are logical transition boundaries).
9177//
9178// This function will capture the bucketing details in the UE tracker so that
9179// they can be used once we cross over.
9180BOOL SetupWatsonBucketsForEscapingPreallocatedExceptions()
9181{
9182#ifndef DACCESS_COMPILE
9183
9184 // CoreCLR may have watson bucketing conditionally enabled.
9185 if (!IsWatsonEnabled())
9186 {
9187 return FALSE;
9188 }
9189
9190 CONTRACTL
9191 {
9192 GC_NOTRIGGER;
9193 MODE_ANY;
9194 NOTHROW;
9195 PRECONDITION(GetThread() != NULL);
9196 }
9197 CONTRACTL_END;
9198
9199 // By default, assume we didnt get the buckets
9200 BOOL fSetupWatsonBuckets = FALSE;
9201 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker;
9202
9203 Thread * pThread = GetThread();
9204
9205 // If the exception going unhandled is preallocated, then capture the Watson buckets in the UE Watson
9206 // bucket tracker provided its not already populated.
9207 //
9208 // Switch to COOP mode
9209 GCX_COOP();
9210
9211 struct
9212 {
9213 OBJECTREF oThrowable;
9214 } gc;
9215 ZeroMemory(&gc, sizeof(gc));
9216 GCPROTECT_BEGIN(gc);
9217
9218 // Get the throwable corresponding to the escaping exception
9219 gc.oThrowable = pThread->GetThrowable();
9220 if (gc.oThrowable == NULL)
9221 {
9222 // If we have no throwable, then simply return back.
9223 //
9224 // We could be here because the VM may have raised an exception,
9225 // and not managed code, for its internal usage (e.g. TA to end the
9226 // threads when unloading an AppDomain). Thus, there would be no throwable
9227 // present since the exception has not been seen by the runtime's
9228 // personality routine.
9229 //
9230 // Hence, we have no work to do here.
9231 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForEscapingPreallocatedExceptions - No throwable available.\n"));
9232 goto done;
9233 }
9234
9235 // Is the exception preallocated? We are not going to process non-preallocated exception objects since
9236 // they already have the watson buckets in them.
9237 //
9238 // We skip thread abort as well since we track them in the UE watson bucket tracker at
9239 // throw time itself.
9240 if (!((CLRException::IsPreallocatedExceptionObject(gc.oThrowable)) &&
9241 !IsThrowableThreadAbortException(gc.oThrowable)))
9242 {
9243 // Its either not preallocated or a thread abort exception,
9244 // neither of which we need to process.
9245 goto done;
9246 }
9247
9248 // The UE watson bucket tracker could be non-empty if there were earlier transitions
9249 // on the threads stack before the exception got raised.
9250 pUEWatsonBucketTracker = pThread->GetExceptionState()->GetUEWatsonBucketTracker();
9251 _ASSERTE(pUEWatsonBucketTracker != NULL);
9252
9253 // Proceed to capture bucketing details only if the UE watson bucket tracker is empty.
9254 if((pUEWatsonBucketTracker->RetrieveWatsonBucketIp() == NULL) && (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL))
9255 {
9256 // Get the Watson Bucket tracker for this preallocated exception
9257 PTR_EHWatsonBucketTracker pCurWatsonBucketTracker = GetWatsonBucketTrackerForPreallocatedException(gc.oThrowable, FALSE);
9258
9259 if (pCurWatsonBucketTracker != NULL)
9260 {
9261 // If the tracker exists, we must have the throw site IP
9262 _ASSERTE(pCurWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL);
9263
9264 // Init the UE Watson bucket tracker
9265 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9266
9267 // Copy the Bucket details to the UE watson bucket tracker
9268 pUEWatsonBucketTracker->CopyEHWatsonBucketTracker(*(pCurWatsonBucketTracker));
9269
9270 // If the buckets dont exist, capture them now
9271 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
9272 {
9273 pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.oThrowable);
9274 }
9275
9276 // If the IP was in managed code, we will have the buckets.
9277 if(pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9278 {
9279 fSetupWatsonBuckets = TRUE;
9280 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForEscapingPreallocatedExceptions - Captured watson buckets for preallocated exception at transition.\n"));
9281 }
9282 else
9283 {
9284 // IP was likely in native code - hence, watson helper functions couldnt get us the buckets
9285 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForEscapingPreallocatedExceptions - Watson buckets not found for IP. IP likely in native code.\n"));
9286
9287 // Clear the UE tracker
9288 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9289 }
9290 }
9291 else
9292 {
9293 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForEscapingPreallocatedExceptions - Watson bucket tracker for preallocated exception not found. Exception likely thrown in native code.\n"));
9294 }
9295 }
9296
9297done:;
9298 GCPROTECT_END();
9299
9300 return fSetupWatsonBuckets;
9301#else // DACCESS_COMPILE
9302 return FALSE;
9303#endif // !DACCESS_COMPILE
9304}
9305
9306// This function is invoked from the UEF worker to setup the watson buckets
9307// for the exception going unhandled, if details are available. See
9308// implementation below for specifics.
9309void SetupWatsonBucketsForUEF(BOOL fUseLastThrownObject)
9310{
9311#ifndef DACCESS_COMPILE
9312
9313 // CoreCLR may have watson bucketing conditionally enabled.
9314 if (!IsWatsonEnabled())
9315 {
9316 return;
9317 }
9318
9319 CONTRACTL
9320 {
9321 GC_TRIGGERS;
9322 MODE_ANY;
9323 NOTHROW;
9324 PRECONDITION(GetThread() != NULL);
9325 }
9326 CONTRACTL_END;
9327
9328 Thread *pThread = GetThread();
9329
9330 PTR_EHWatsonBucketTracker pCurWatsonBucketTracker = NULL;
9331 ThreadExceptionState *pExState = pThread->GetExceptionState();
9332 _ASSERTE(pExState != NULL);
9333
9334 // If the exception tracker exists, then copy the bucketing details
9335 // from it to the UE Watson Bucket tracker.
9336 //
9337 // On 64bit, the EH system allocates the EHTracker only in the case of an exception.
9338 // Thus, assume a reverse pinvoke thread transitions to managed code from native,
9339 // does some work in managed and returns back to native code.
9340 //
9341 // In the native code, it has an exception that goes unhandled and the OS
9342 // ends up invoking our UEF, and thus, we land up here.
9343 //
9344 // In such a case, on 64bit, we wont have an exception tracker since there
9345 // was no managed exception active. On 32bit, we will have a tracker
9346 // but there wont be an IP corresponding to the throw site since exception
9347 // was raised in native code.
9348 //
9349 // But if the tracker exists, simply copy the bucket details to the UE Watson Bucket
9350 // tracker for use by the "WatsonLastChance" path.
9351 BOOL fDoWeHaveWatsonBuckets = FALSE;
9352 if (pExState->GetCurrentExceptionTracker() != NULL)
9353 {
9354 // Check the exception state if we have Watson bucket details
9355 fDoWeHaveWatsonBuckets = pExState->GetFlags()->GotWatsonBucketDetails();
9356 }
9357
9358 // Switch to COOP mode before working with the throwable
9359 GCX_COOP();
9360
9361 // Get the throwable we are going to work with
9362 struct
9363 {
9364 OBJECTREF oThrowable;
9365 } gc;
9366 ZeroMemory(&gc, sizeof(gc));
9367 GCPROTECT_BEGIN(gc);
9368
9369 gc.oThrowable = fUseLastThrownObject ? pThread->LastThrownObject() : pThread->GetThrowable();
9370 BOOL fThrowableExists = (gc.oThrowable != NULL);
9371 BOOL fIsThrowablePreallocated = !fThrowableExists ? FALSE : CLRException::IsPreallocatedExceptionObject(gc.oThrowable);
9372
9373 if ((!fDoWeHaveWatsonBuckets) && fThrowableExists)
9374 {
9375 // Check the throwable if it has buckets - this could be the scenario
9376 // of native code calling into a non-default domain and thus, have an AD
9377 // transition in between that could reraise the exception but that would
9378 // never be seen by our exception handler. Thus, there wont be any tracker
9379 // or tracker state.
9380 //
9381 // Invocation of entry point on WLC via reverse pinvoke is an example.
9382 if (!fIsThrowablePreallocated)
9383 {
9384 fDoWeHaveWatsonBuckets = ((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent();
9385 if (!fDoWeHaveWatsonBuckets)
9386 {
9387 // If buckets are not present, then we may have IP to capture the buckets from.
9388 fDoWeHaveWatsonBuckets = ((EXCEPTIONREF)gc.oThrowable)->IsIPForWatsonBucketsPresent();
9389 }
9390 }
9391 else
9392 {
9393 // Get the watson bucket tracker for the preallocated exception
9394 PTR_EHWatsonBucketTracker pCurWBTracker = GetWatsonBucketTrackerForPreallocatedException(gc.oThrowable, FALSE);
9395
9396 // We would have buckets if we have the IP
9397 if (pCurWBTracker && (pCurWBTracker->RetrieveWatsonBucketIp() != NULL))
9398 {
9399 fDoWeHaveWatsonBuckets = TRUE;
9400 }
9401 }
9402 }
9403
9404 if (fDoWeHaveWatsonBuckets)
9405 {
9406 // Get the UE Watson bucket tracker
9407 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
9408
9409 // Clear any existing information
9410 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9411
9412 if (fIsThrowablePreallocated)
9413 {
9414 // Get the watson bucket tracker for the preallocated exception
9415 PTR_EHWatsonBucketTracker pCurWBTracker = GetWatsonBucketTrackerForPreallocatedException(gc.oThrowable, FALSE);
9416
9417 if (pCurWBTracker != NULL)
9418 {
9419 // We should be having an IP for this exception at this point
9420 _ASSERTE(pCurWBTracker->RetrieveWatsonBucketIp() != NULL);
9421
9422 // Copy the existing bucketing details to the UE tracker
9423 pUEWatsonBucketTracker->CopyEHWatsonBucketTracker(*(pCurWBTracker));
9424
9425 // Get the buckets if we dont already have them since we
9426 // dont want to overwrite existing bucket information (e.g.
9427 // from an AD transition)
9428 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
9429 {
9430 pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.oThrowable);
9431 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9432 {
9433 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForUEF: Collected watson bucket information for preallocated exception\n"));
9434 }
9435 else
9436 {
9437 // If we are here, then one of the following could have happened:
9438 //
9439 // 1) pCurWBTracker had buckets but we couldnt copy them over to pUEWatsonBucketTracker due to OOM, or
9440 // 2) pCurWBTracker's IP was in native code; thus pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson()
9441 // couldnt get us the watson buckets.
9442 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForUEF: Unable to collect watson bucket information for preallocated exception due to OOM or IP being in native code.\n"));
9443 }
9444 }
9445 }
9446 else
9447 {
9448 // We likely had an OOM earlier (while copying the bucket information) if we are here
9449 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForUEF: Watson bucket tracker for preallocated exception not found.\n"));
9450 }
9451 }
9452 else
9453 {
9454 // Throwable is not preallocated - get the bucket details from it for use by Watson
9455 _ASSERTE_MSG(((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent() ||
9456 ((EXCEPTIONREF)gc.oThrowable)->IsIPForWatsonBucketsPresent(),
9457 "How come we dont have watson buckets (or IP) for a non-preallocated exception in the UEF?");
9458
9459 if ((((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent() == FALSE) &&
9460 ((EXCEPTIONREF)gc.oThrowable)->IsIPForWatsonBucketsPresent())
9461 {
9462 // Capture the buckets using the IP we have.
9463 SetupWatsonBucketsForNonPreallocatedExceptions(gc.oThrowable);
9464 }
9465
9466 if (((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent())
9467 {
9468 pUEWatsonBucketTracker->CopyBucketsFromThrowable(gc.oThrowable);
9469 }
9470
9471 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
9472 {
9473 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForUEF: Unable to copy watson buckets from regular exception throwable (%p), likely due to OOM.\n",
9474 OBJECTREFToObject(gc.oThrowable)));
9475 }
9476 }
9477 }
9478 else
9479 {
9480 // We dont have the watson buckets; exception was in native code that we dont care about
9481 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForUEF: We dont have watson buckets - likely an exception in native code.\n"));
9482 }
9483
9484 GCPROTECT_END();
9485#endif // !DACCESS_COMPILE
9486}
9487
9488// Given a throwable, this function will return a BOOL indicating
9489// if it corresponds to any of the following thread abort exception
9490// objects:
9491//
9492// 1) Regular allocated ThreadAbortException
9493// 2) Preallocated ThreadAbortException
9494// 3) Preallocated RudeThreadAbortException
9495BOOL IsThrowableThreadAbortException(OBJECTREF oThrowable)
9496{
9497#ifndef DACCESS_COMPILE
9498 CONTRACTL
9499 {
9500 GC_NOTRIGGER;
9501 MODE_COOPERATIVE;
9502 NOTHROW;
9503 PRECONDITION(GetThread() != NULL);
9504 PRECONDITION(oThrowable != NULL);
9505 }
9506 CONTRACTL_END;
9507
9508 BOOL fIsTAE = FALSE;
9509
9510 struct
9511 {
9512 OBJECTREF oThrowable;
9513 } gc;
9514 ZeroMemory(&gc, sizeof(gc));
9515 GCPROTECT_BEGIN(gc);
9516
9517 gc.oThrowable = oThrowable;
9518
9519 fIsTAE = (IsExceptionOfType(kThreadAbortException,&(gc.oThrowable)) || // regular TAE
9520 ((g_pPreallocatedThreadAbortException != NULL) &&
9521 (gc.oThrowable == CLRException::GetPreallocatedThreadAbortException())) ||
9522 ((g_pPreallocatedRudeThreadAbortException != NULL) &&
9523 (gc.oThrowable == CLRException::GetPreallocatedRudeThreadAbortException())));
9524
9525 GCPROTECT_END();
9526
9527 return fIsTAE;
9528
9529#else // DACCESS_COMPILE
9530 return FALSE;
9531#endif // !DACCESS_COMPILE
9532}
9533
9534// Given a throwable, this function will walk the exception tracker
9535// list to return the tracker, if available, corresponding to the preallocated
9536// exception object.
9537//
9538// The caller can also specify the starting EHTracker to walk the list from.
9539// If not specified, this will default to the current exception tracker active
9540// on the thread.
9541#if defined(WIN64EXCEPTIONS)
9542PTR_ExceptionTracker GetEHTrackerForPreallocatedException(OBJECTREF oPreAllocThrowable,
9543 PTR_ExceptionTracker pStartingEHTracker)
9544#elif _TARGET_X86_
9545PTR_ExInfo GetEHTrackerForPreallocatedException(OBJECTREF oPreAllocThrowable,
9546 PTR_ExInfo pStartingEHTracker)
9547#else
9548#error Unsupported platform
9549#endif
9550{
9551 CONTRACTL
9552 {
9553 GC_NOTRIGGER;
9554 MODE_COOPERATIVE;
9555 NOTHROW;
9556 PRECONDITION(GetThread() != NULL);
9557 PRECONDITION(oPreAllocThrowable != NULL);
9558 PRECONDITION(CLRException::IsPreallocatedExceptionObject(oPreAllocThrowable));
9559 PRECONDITION(IsWatsonEnabled());
9560 }
9561 CONTRACTL_END;
9562
9563 // Get the reference to the current exception tracker
9564#if defined(WIN64EXCEPTIONS)
9565 PTR_ExceptionTracker pEHTracker = (pStartingEHTracker != NULL) ? pStartingEHTracker : GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
9566#elif _TARGET_X86_
9567 PTR_ExInfo pEHTracker = (pStartingEHTracker != NULL) ? pStartingEHTracker : GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
9568#else // !(_WIN64 || _TARGET_X86_)
9569#error Unsupported platform
9570#endif // _WIN64
9571
9572 BOOL fFoundTracker = FALSE;
9573
9574 struct
9575 {
9576 OBJECTREF oPreAllocThrowable;
9577 } gc;
9578 ZeroMemory(&gc, sizeof(gc));
9579 GCPROTECT_BEGIN(gc);
9580
9581 gc.oPreAllocThrowable = oPreAllocThrowable;
9582
9583 // Start walking the list to find the tracker correponding
9584 // to the preallocated exception object.
9585 while (pEHTracker != NULL)
9586 {
9587 if (pEHTracker->GetThrowable() == gc.oPreAllocThrowable)
9588 {
9589 // found the tracker - break out.
9590 fFoundTracker = TRUE;
9591 break;
9592 }
9593
9594 // move to the previous tracker...
9595 pEHTracker = pEHTracker->GetPreviousExceptionTracker();
9596 }
9597
9598 GCPROTECT_END();
9599
9600 return fFoundTracker ? pEHTracker : NULL;
9601}
9602
9603// This function will return the pointer to EHWatsonBucketTracker corresponding to the
9604// preallocated exception object. If none is found, it will return NULL.
9605PTR_EHWatsonBucketTracker GetWatsonBucketTrackerForPreallocatedException(OBJECTREF oPreAllocThrowable,
9606 BOOL fCaptureBucketsIfNotPresent,
9607 BOOL fStartSearchFromPreviousTracker /*= FALSE*/)
9608{
9609#ifndef DACCESS_COMPILE
9610 CONTRACTL
9611 {
9612 GC_NOTRIGGER;
9613 MODE_COOPERATIVE;
9614 NOTHROW;
9615 PRECONDITION(GetThread() != NULL);
9616 PRECONDITION(oPreAllocThrowable != NULL);
9617 PRECONDITION(CLRException::IsPreallocatedExceptionObject(oPreAllocThrowable));
9618 PRECONDITION(IsWatsonEnabled());
9619 }
9620 CONTRACTL_END;
9621
9622 PTR_EHWatsonBucketTracker pWBTracker = NULL;
9623
9624 struct
9625 {
9626 OBJECTREF oPreAllocThrowable;
9627 } gc;
9628 ZeroMemory(&gc, sizeof(gc));
9629 GCPROTECT_BEGIN(gc);
9630
9631 gc.oPreAllocThrowable = oPreAllocThrowable;
9632
9633 // Before doing anything, check if this is a thread abort exception. If it is,
9634 // then simply return the reference to the UE watson bucket tracker since it
9635 // tracks the bucketing details for all types of TAE.
9636 if (IsThrowableThreadAbortException(gc.oPreAllocThrowable))
9637 {
9638 pWBTracker = GetThread()->GetExceptionState()->GetUEWatsonBucketTracker();
9639 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Setting UE Watson Bucket Tracker to be returned for preallocated ThreadAbortException.\n"));
9640 goto doValidation;
9641 }
9642
9643 {
9644 // Find the reference to the exception tracker corresponding to the preallocated exception,
9645 // starting the search from the current exception tracker (2nd arg of NULL specifies that).
9646 #if defined(WIN64EXCEPTIONS)
9647 PTR_ExceptionTracker pEHTracker = NULL;
9648 PTR_ExceptionTracker pPreviousEHTracker = NULL;
9649
9650#elif _TARGET_X86_
9651 PTR_ExInfo pEHTracker = NULL;
9652 PTR_ExInfo pPreviousEHTracker = NULL;
9653#else // !(_WIN64 || _TARGET_X86_)
9654#error Unsupported platform
9655#endif // _WIN64
9656
9657 if (fStartSearchFromPreviousTracker)
9658 {
9659 // Get the exception tracker previous to the current one
9660 pPreviousEHTracker = GetThread()->GetExceptionState()->GetCurrentExceptionTracker()->GetPreviousExceptionTracker();
9661
9662 // If there is no previous tracker to start from, then simply abort the search attempt.
9663 // If we couldnt find the exception tracker, then buckets are not available
9664 if (pPreviousEHTracker == NULL)
9665 {
9666 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Couldnt find the previous EHTracker to start the search from.\n"));
9667 pWBTracker = NULL;
9668 goto done;
9669 }
9670 }
9671
9672 pEHTracker = GetEHTrackerForPreallocatedException(gc.oPreAllocThrowable, pPreviousEHTracker);
9673
9674 // If we couldnt find the exception tracker, then buckets are not available
9675 if (pEHTracker == NULL)
9676 {
9677 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Couldnt find EHTracker for preallocated exception object.\n"));
9678 pWBTracker = NULL;
9679 goto done;
9680 }
9681
9682 // Get the Watson Bucket Tracker from the exception tracker
9683 pWBTracker = pEHTracker->GetWatsonBucketTracker();
9684 }
9685doValidation:
9686 _ASSERTE(pWBTracker != NULL);
9687
9688 // Incase of an OOM, we may not have an IP in the Watson bucket tracker. A scenario
9689 // would be default domain calling to AD 2 that calls into AD 3.
9690 //
9691 // AD 3 has an exception that is represented by a preallocated exception object. The
9692 // exception goes unhandled and reaches AD2/AD3 transition boundary. The bucketing details
9693 // from AD3 are copied to UETracker and once the exception is reraised in AD2, we will
9694 // enter SetupInitialThrowBucketingDetails to copy the bucketing details to the active
9695 // exception tracker.
9696 //
9697 // This copy operation could fail due to OOM and the active exception tracker in AD 2,
9698 // for the preallocated exception object, will not have any bucketing details. If the
9699 // exception remains unhandled in AD 2, then just before it reaches DefDomain/AD2 boundary,
9700 // we will attempt to capture the bucketing details in AppDomainTransitionExceptionFilter,
9701 // that will bring us here.
9702 //
9703 // In such a case, the active exception tracker will not have any bucket details for the
9704 // preallocated exception. In such a case, if the IP does not exist, we will return NULL
9705 // indicating that we couldnt find the Watson bucket tracker, since returning a tracker
9706 // that does not have any bucketing details will be of no use to the caller.
9707 if (pWBTracker->RetrieveWatsonBucketIp() != NULL)
9708 {
9709 // Check if the buckets exist or not..
9710 PTR_VOID pBuckets = pWBTracker->RetrieveWatsonBuckets();
9711
9712 // If they dont exist and we have been asked to collect them,
9713 // then do so.
9714 if (pBuckets == NULL)
9715 {
9716 if (fCaptureBucketsIfNotPresent)
9717 {
9718 pWBTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, GetThread(), &gc.oPreAllocThrowable);
9719
9720 // Check if we have the buckets now
9721 if (pWBTracker->RetrieveWatsonBuckets() != NULL)
9722 {
9723 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Captured watson buckets for preallocated exception object.\n"));
9724 }
9725 else
9726 {
9727 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Unable to capture watson buckets for preallocated exception object due to OOM.\n"));
9728 }
9729 }
9730 else
9731 {
9732 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Found IP but no buckets for preallocated exception object.\n"));
9733 }
9734 }
9735 else
9736 {
9737 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Buckets already exist for preallocated exception object.\n"));
9738 }
9739 }
9740 else
9741 {
9742 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Returning NULL EHWatsonBucketTracker since bucketing IP does not exist. This is likely due to an earlier OOM.\n"));
9743 pWBTracker = NULL;
9744 }
9745
9746done:;
9747
9748 GCPROTECT_END();
9749
9750 // Return the Watson bucket tracker
9751 return pWBTracker;
9752#else // DACCESS_COMPILE
9753 return NULL;
9754#endif // !DACCESS_COMPILE
9755}
9756
9757// Given an exception object, this function will attempt to look up
9758// the watson buckets for it and set them up against the thread
9759// for use by FailFast mechanism.
9760// Return TRUE when it succeeds or Waston is disabled on CoreCLR
9761// Return FALSE when refException neither has buckets nor has inner exception
9762BOOL SetupWatsonBucketsForFailFast(EXCEPTIONREF refException)
9763{
9764 BOOL fResult = TRUE;
9765
9766#ifndef DACCESS_COMPILE
9767 // On CoreCLR, Watson may not be enabled. Thus, we should
9768 // skip this.
9769 if (!IsWatsonEnabled())
9770 {
9771 return fResult;
9772 }
9773
9774 CONTRACTL
9775 {
9776 GC_TRIGGERS;
9777 MODE_ANY;
9778 NOTHROW;
9779 PRECONDITION(GetThread() != NULL);
9780 PRECONDITION(refException != NULL);
9781 PRECONDITION(IsWatsonEnabled());
9782 }
9783 CONTRACTL_END;
9784
9785 // Switch to COOP mode
9786 GCX_COOP();
9787
9788 struct
9789 {
9790 OBJECTREF refException;
9791 OBJECTREF oInnerMostExceptionThrowable;
9792 } gc;
9793 ZeroMemory(&gc, sizeof(gc));
9794 GCPROTECT_BEGIN(gc);
9795 gc.refException = refException;
9796
9797 Thread *pThread = GetThread();
9798
9799 // If we dont already have the bucketing details for the exception
9800 // being thrown, then get them.
9801 ThreadExceptionState *pExState = pThread->GetExceptionState();
9802
9803 // Check if the exception object is preallocated or not
9804 BOOL fIsPreallocatedException = CLRException::IsPreallocatedExceptionObject(gc.refException);
9805
9806 // Get the WatsonBucketTracker where bucketing details will be copied to
9807 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
9808
9809 // Check if this is a thread abort exception of any kind.
9810 // See IsThrowableThreadAbortException implementation for details.
9811 BOOL fIsThreadAbortException = IsThrowableThreadAbortException(gc.refException);
9812
9813 if (fIsPreallocatedException)
9814 {
9815 // If the exception being used to FailFast is preallocated,
9816 // then it cannot have any inner exception. Thus, try to
9817 // find the watson bucket tracker corresponding to this exception.
9818 //
9819 // Also, capture the buckets if we dont have them already.
9820 PTR_EHWatsonBucketTracker pTargetWatsonBucketTracker = GetWatsonBucketTrackerForPreallocatedException(gc.refException, TRUE);
9821 if ((pTargetWatsonBucketTracker != NULL) && (!fIsThreadAbortException))
9822 {
9823 // Buckets are not captured proactively for preallocated exception objects. We only
9824 // save the IP in the watson bucket tracker (see SetupInitialThrowBucketingDetails for
9825 // details).
9826 //
9827 // Thus, if, say in DefDomain, a preallocated exception is thrown and we enter
9828 // the catch block and invoke the FailFast API with the reference to the preallocated
9829 // exception object, we will have the IP but not the buckets. In such a case,
9830 // capture the buckets before proceeding ahead.
9831 if (pTargetWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
9832 {
9833 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForFailFast - Collecting watson bucket details for preallocated exception.\n"));
9834 pTargetWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.refException);
9835 }
9836
9837 // Copy the buckets to the UE tracker
9838 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9839 pUEWatsonBucketTracker->CopyEHWatsonBucketTracker(*pTargetWatsonBucketTracker);
9840 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9841 {
9842 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForFailFast - Collected watson bucket details for preallocated exception in UE tracker.\n"));
9843 }
9844 else
9845 {
9846 // If we are here, then the copy operation above had an OOM, resulting
9847 // in no buckets for us.
9848 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForFailFast - Unable to collect watson bucket details for preallocated exception due to out of memory.\n"));
9849
9850 // Make sure the tracker is clean.
9851 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9852 }
9853 }
9854 else
9855 {
9856 // For TAE, UE watson bucket tracker is the one that tracks the buckets. It *may*
9857 // not have the bucket details if FailFast is being invoked from outside the
9858 // managed EH clauses. But if invoked from within the active EH clause for the exception,
9859 // UETracker will have the bucketing details (see SetupInitialThrowBucketingDetails for details).
9860 if (fIsThreadAbortException && (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL))
9861 {
9862 _ASSERTE(pTargetWatsonBucketTracker == pUEWatsonBucketTracker);
9863 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForFailFast - UE tracker already watson bucket details for preallocated thread abort exception.\n"));
9864 }
9865 else
9866 {
9867 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForFailFast - Unable to find bucket details for preallocated %s exception.\n",
9868 fIsThreadAbortException?"rude/thread abort":""));
9869
9870 // Make sure the tracker is clean.
9871 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9872 }
9873 }
9874 }
9875 else
9876 {
9877 // Since the exception object is not preallocated, start by assuming
9878 // that we dont need to check it for watson buckets
9879 BOOL fCheckThrowableForWatsonBuckets = FALSE;
9880
9881 // Get the innermost exception object (if any)
9882 gc.oInnerMostExceptionThrowable = ((EXCEPTIONREF)gc.refException)->GetBaseException();
9883 if (gc.oInnerMostExceptionThrowable != NULL)
9884 {
9885 if (CLRException::IsPreallocatedExceptionObject(gc.oInnerMostExceptionThrowable))
9886 {
9887 // If the inner most exception being used to FailFast is preallocated,
9888 // try to find the watson bucket tracker corresponding to it.
9889 //
9890 // Also, capture the buckets if we dont have them already.
9891 PTR_EHWatsonBucketTracker pTargetWatsonBucketTracker =
9892 GetWatsonBucketTrackerForPreallocatedException(gc.oInnerMostExceptionThrowable, TRUE);
9893
9894 if (pTargetWatsonBucketTracker != NULL)
9895 {
9896 if (pTargetWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
9897 {
9898 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Capturing Watson bucket details for preallocated inner exception.\n"));
9899 pTargetWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.oInnerMostExceptionThrowable);
9900 }
9901
9902 // Copy the details to the UE tracker
9903 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9904 pUEWatsonBucketTracker->CopyEHWatsonBucketTracker(*pTargetWatsonBucketTracker);
9905 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9906 {
9907 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Watson bucket details collected for preallocated inner exception.\n"));
9908 }
9909 else
9910 {
9911 // If we are here, copy operation failed likely due to OOM
9912 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Unable to copy watson bucket details for preallocated inner exception.\n"));
9913
9914 // Keep the UETracker clean
9915 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9916 }
9917 }
9918 else
9919 {
9920 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Unable to find bucket details for preallocated inner exception.\n"));
9921
9922 // Keep the UETracker clean
9923 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9924
9925 // Since we couldnt find the watson bucket tracker for the the inner most exception,
9926 // try to look for the buckets in the throwable.
9927 fCheckThrowableForWatsonBuckets = TRUE;
9928 }
9929 }
9930 else
9931 {
9932 // Inner most exception is not preallocated.
9933 //
9934 // If it has the IP but not the buckets, then capture them now.
9935 if ((((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() == FALSE) &&
9936 (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent()))
9937 {
9938 SetupWatsonBucketsForNonPreallocatedExceptions(gc.oInnerMostExceptionThrowable);
9939 }
9940
9941 // If it has the buckets, copy them over to the current Watson bucket tracker
9942 if (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent())
9943 {
9944 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9945 pUEWatsonBucketTracker->CopyBucketsFromThrowable(gc.oInnerMostExceptionThrowable);
9946 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9947 {
9948 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Got watson buckets from regular innermost exception.\n"));
9949 }
9950 else
9951 {
9952 // Copy operation can fail due to OOM
9953 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Unable to copy watson buckets from regular innermost exception, likely due to OOM.\n"));
9954 }
9955 }
9956 else
9957 {
9958 // Since the inner most exception didnt have the buckets,
9959 // try to look for them in the throwable
9960 fCheckThrowableForWatsonBuckets = TRUE;
9961 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Neither exception object nor its inner exception has watson buckets.\n"));
9962 }
9963 }
9964 }
9965 else
9966 {
9967 // There is no innermost exception - try to look for buckets
9968 // in the throwable
9969 fCheckThrowableForWatsonBuckets = TRUE;
9970 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Innermost exception does not exist\n"));
9971 }
9972
9973 if (fCheckThrowableForWatsonBuckets)
9974 {
9975 // Since we have not found buckets anywhere, try to look for them
9976 // in the throwable.
9977 if ((((EXCEPTIONREF)gc.refException)->AreWatsonBucketsPresent() == FALSE) &&
9978 (((EXCEPTIONREF)gc.refException)->IsIPForWatsonBucketsPresent()))
9979 {
9980 // Capture the buckets from the IP.
9981 SetupWatsonBucketsForNonPreallocatedExceptions(gc.refException);
9982 }
9983
9984 if (((EXCEPTIONREF)gc.refException)->AreWatsonBucketsPresent())
9985 {
9986 // Copy the buckets to the current watson bucket tracker
9987 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9988 pUEWatsonBucketTracker->CopyBucketsFromThrowable(gc.refException);
9989 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9990 {
9991 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Watson buckets copied from the exception object.\n"));
9992 }
9993 else
9994 {
9995 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Unable to copy Watson buckets copied from the exception object, likely due to OOM.\n"));
9996 }
9997 }
9998 else
9999 {
10000 fResult = FALSE;
10001 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Exception object neither has buckets nor has inner exception.\n"));
10002 }
10003 }
10004 }
10005
10006 GCPROTECT_END();
10007
10008#endif // !DACCESS_COMPILE
10009
10010 return fResult;
10011}
10012
10013// This function will setup the bucketing details in the exception
10014// tracker or the throwable, if they are not already setup.
10015//
10016// This is called when an exception is thrown (or raised):
10017//
10018// 1) from outside the confines of managed EH clauses, OR
10019// 2) from within the confines of managed EH clauses but the
10020// exception does not have bucketing details with it, OR
10021// 3) When an exception is reraised at AD transition boundary
10022// after it has been marshalled over to the returning AD.
10023void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
10024{
10025#ifndef DACCESS_COMPILE
10026
10027 // On CoreCLR, Watson may not be enabled. Thus, we should
10028 // skip this.
10029 if (!IsWatsonEnabled())
10030 {
10031 return;
10032 }
10033
10034 CONTRACTL
10035 {
10036 GC_TRIGGERS;
10037 MODE_ANY;
10038 NOTHROW;
10039 PRECONDITION(GetThread() != NULL);
10040 PRECONDITION(!(GetThread()->GetExceptionState()->GetFlags()->GotWatsonBucketDetails()));
10041 PRECONDITION(adjustedIp != NULL);
10042 PRECONDITION(IsWatsonEnabled());
10043 }
10044 CONTRACTL_END;
10045
10046 Thread *pThread = GetThread();
10047
10048 // If we dont already have the bucketing details for the exception
10049 // being thrown, then get them.
10050 ThreadExceptionState *pExState = pThread->GetExceptionState();
10051
10052 // Ensure that the exception tracker exists
10053 _ASSERTE(pExState->GetCurrentExceptionTracker() != NULL);
10054
10055 // Switch to COOP mode
10056 GCX_COOP();
10057
10058 // Get the throwable for the exception being thrown
10059 struct
10060 {
10061 OBJECTREF oCurrentThrowable;
10062 OBJECTREF oInnerMostExceptionThrowable;
10063 } gc;
10064 ZeroMemory(&gc, sizeof(gc));
10065
10066 GCPROTECT_BEGIN(gc);
10067
10068 gc.oCurrentThrowable = pExState->GetThrowable();
10069
10070 // Check if the exception object is preallocated or not
10071 BOOL fIsPreallocatedException = CLRException::IsPreallocatedExceptionObject(gc.oCurrentThrowable);
10072
10073 // Get the WatsonBucketTracker for the current exception
10074 PTR_EHWatsonBucketTracker pWatsonBucketTracker = pExState->GetCurrentExceptionTracker()->GetWatsonBucketTracker();
10075
10076 // Get the innermost exception object (if any)
10077 gc.oInnerMostExceptionThrowable = ((EXCEPTIONREF)gc.oCurrentThrowable)->GetBaseException();
10078
10079 // By default, assume that no watson bucketing details are available and inner exception
10080 // is not preallocated
10081 BOOL fAreBucketingDetailsPresent = FALSE;
10082 BOOL fIsInnerExceptionPreallocated = FALSE;
10083
10084 // Check if this is a thread abort exception of any kind. See IsThrowableThreadAbortException implementation for details.
10085 // We shouldnt use the thread state as well to determine if it is a TAE since, in cases like throwing a cached exception
10086 // as part of type initialization failure, we could throw a TAE but the thread will not be in abort state (which is expected).
10087 BOOL fIsThreadAbortException = IsThrowableThreadAbortException(gc.oCurrentThrowable);
10088
10089 // If we are here, then this was a new exception raised
10090 // from outside the managed EH clauses (fault/finally/catch).
10091 //
10092 // The throwable *may* have the bucketing details already
10093 // if this exception was raised when it was crossing over
10094 // an AD transition boundary. Those are stored in UE watson bucket
10095 // tracker by AppDomainTransitionExceptionFilter.
10096 if (fIsPreallocatedException)
10097 {
10098 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
10099 fAreBucketingDetailsPresent = ((pUEWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL) &&
10100 (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL));
10101
10102 // If they are present, copy them over to the watson tracker for the exception
10103 // being processed.
10104 if (fAreBucketingDetailsPresent)
10105 {
10106#ifdef _DEBUG
10107 // Under OOM scenarios, its possible that when we are raising a threadabort,
10108 // the throwable may get converted to preallocated OOM object when RaiseTheExceptionInternalOnly
10109 // invokes Thread::SafeSetLastThrownObject. We check if this is the current case and use it in
10110 // our validation below.
10111 BOOL fIsPreallocatedOOMExceptionForTA = FALSE;
10112 if ((!fIsThreadAbortException) && pUEWatsonBucketTracker->CapturedForThreadAbort())
10113 {
10114 fIsPreallocatedOOMExceptionForTA = (gc.oCurrentThrowable == CLRException::GetPreallocatedOutOfMemoryException());
10115 if (fIsPreallocatedOOMExceptionForTA)
10116 {
10117 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Got preallocated OOM throwable for buckets captured for thread abort.\n"));
10118 }
10119 }
10120#endif // _DEBUG
10121 // These should have been captured at AD transition OR
10122 // could be bucketing details of preallocated [rude] thread abort exception.
10123 _ASSERTE(pUEWatsonBucketTracker->CapturedAtADTransition() ||
10124 ((fIsThreadAbortException || fIsPreallocatedOOMExceptionForTA) && pUEWatsonBucketTracker->CapturedForThreadAbort()));
10125
10126 if (!fIsThreadAbortException)
10127 {
10128 // The watson bucket tracker for the exceptiong being raised should be empty at this point
10129 // since we are here because of a cross AD reraise of the original exception.
10130 _ASSERTE((pWatsonBucketTracker->RetrieveWatsonBucketIp() == NULL) && (pWatsonBucketTracker->RetrieveWatsonBuckets() == NULL));
10131
10132 // Copy the buckets over to it
10133 pWatsonBucketTracker->CopyEHWatsonBucketTracker(*(pUEWatsonBucketTracker));
10134 if (pWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
10135 {
10136 // If we dont have buckets after the copy operation, its due to us running out of
10137 // memory.
10138 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Unable to copy watson buckets from cross AD rethrow, likely due to out of memory.\n"));
10139 }
10140 else
10141 {
10142 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Copied watson buckets from cross AD rethrow.\n"));
10143 }
10144 }
10145 else
10146 {
10147 // Thread abort watson bucket details are already present in the
10148 // UE watson bucket tracker.
10149 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Already have watson buckets for preallocated thread abort reraise.\n"));
10150 }
10151 }
10152 else if (fIsThreadAbortException)
10153 {
10154 // This is a preallocated thread abort exception.
10155 UINT_PTR ip = pUEWatsonBucketTracker->RetrieveWatsonBucketIp();
10156 if (ip != NULL)
10157 {
10158 // Since we have the IP, assert that this was the one setup
10159 // for ThreadAbort. This is for the reraise scenario where
10160 // the original exception was non-preallocated TA but the
10161 // reraise resulted in preallocated TA.
10162 //
10163 // In this case, we will update the ip to be used as the
10164 // one we have. The control flow below will automatically
10165 // endup using it.
10166 _ASSERTE(pUEWatsonBucketTracker->CapturedForThreadAbort());
10167 adjustedIp = ip;
10168 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Setting an existing IP (%p) to be used for capturing buckets for preallocated thread abort.\n", ip));
10169 goto phase1;
10170 }
10171 }
10172
10173 if (!fAreBucketingDetailsPresent || !fIsThreadAbortException)
10174 {
10175 // Clear the UE Watson bucket tracker so that its usable
10176 // in future. We dont clear this for ThreadAbort since
10177 // the UE watson bucket tracker carries bucketing details
10178 // for the same, unless the UE tracker is not containing them
10179 // already.
10180 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
10181 }
10182 }
10183 else
10184 {
10185 // The exception object is not preallocated
10186 fAreBucketingDetailsPresent = ((EXCEPTIONREF)gc.oCurrentThrowable)->AreWatsonBucketsPresent();
10187 if (!fAreBucketingDetailsPresent)
10188 {
10189 // If buckets are not present, check if the bucketing IP is present.
10190 fAreBucketingDetailsPresent = ((EXCEPTIONREF)gc.oCurrentThrowable)->IsIPForWatsonBucketsPresent();
10191 }
10192
10193 // If throwable does not have buckets and this is a thread abort exception,
10194 // then this maybe a reraise of the original thread abort.
10195 //
10196 // We can also be here if an exception was caught at AppDomain transition and
10197 // in the returning domain, a non-preallocated TAE was raised. In such a case,
10198 // the UE tracker flags could indicate the exception is from AD transition.
10199 // This is similar to preallocated case above.
10200 //
10201 // Check the UE Watson bucket tracker if it has the buckets and if it does,
10202 // copy them over to the current throwable.
10203 if (!fAreBucketingDetailsPresent && fIsThreadAbortException)
10204 {
10205 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
10206 UINT_PTR ip = pUEWatsonBucketTracker->RetrieveWatsonBucketIp();
10207 if (ip != NULL)
10208 {
10209 // Confirm that we had the buckets captured for thread abort
10210 _ASSERTE(pUEWatsonBucketTracker->CapturedForThreadAbort() || pUEWatsonBucketTracker->CapturedAtADTransition());
10211
10212 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
10213 {
10214 // Copy the buckets to the current throwable - CopyWatsonBucketsToThrowable
10215 // can throw in OOM. However, since the current function is called as part of
10216 // setting up the stack trace, where we bail out incase of OOM, we will do
10217 // no different here as well.
10218 BOOL fCopiedBuckets = TRUE;
10219 EX_TRY
10220 {
10221 CopyWatsonBucketsToThrowable(pUEWatsonBucketTracker->RetrieveWatsonBuckets());
10222 _ASSERTE(((EXCEPTIONREF)gc.oCurrentThrowable)->AreWatsonBucketsPresent());
10223 }
10224 EX_CATCH
10225 {
10226 fCopiedBuckets = FALSE;
10227 }
10228 EX_END_CATCH(SwallowAllExceptions);
10229
10230 if (fCopiedBuckets)
10231 {
10232 // Since the throwable has the buckets, set the flag that indicates so
10233 fAreBucketingDetailsPresent = TRUE;
10234 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Setup watson buckets for thread abort reraise.\n"));
10235 }
10236 }
10237 else
10238 {
10239 // Copy the faulting IP from the UE tracker to the exception object. This was setup in COMPlusCheckForAbort
10240 // for non-preallocated exceptions.
10241 ((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(ip);
10242 fAreBucketingDetailsPresent = TRUE;
10243 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Setup watson bucket IP for thread abort reraise.\n"));
10244 }
10245 }
10246 else
10247 {
10248 // Clear the UE Watson bucket tracker so that its usable
10249 // in future.
10250 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
10251 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Didnt find watson buckets for thread abort - likely being raised.\n"));
10252 }
10253 }
10254 }
10255
10256phase1:
10257 if (fAreBucketingDetailsPresent)
10258 {
10259 // Since we already have the buckets, simply bail out
10260 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Already had watson ip/buckets.\n"));
10261 goto done;
10262 }
10263
10264 // Check if an inner most exception exists and if it does, examine
10265 // it for watson bucketing details.
10266 if (gc.oInnerMostExceptionThrowable != NULL)
10267 {
10268 // Preallocated exception objects do not have inner exception objects.
10269 // Thus, if we are here, then the current throwable cannot be
10270 // a preallocated exception object.
10271 _ASSERTE(!fIsPreallocatedException);
10272
10273 fIsInnerExceptionPreallocated = CLRException::IsPreallocatedExceptionObject(gc.oInnerMostExceptionThrowable);
10274
10275 // If we are here, then this was a "throw" with inner exception
10276 // outside of any managed EH clauses.
10277 //
10278 // If the inner exception object is preallocated, then we will need to create the
10279 // watson buckets since we are outside the managed EH clauses with no exception tracking
10280 // information relating to the inner exception.
10281 //
10282 // But if the inner exception object was not preallocated, create new watson buckets
10283 // only if inner exception does not have them.
10284 if (fIsInnerExceptionPreallocated)
10285 {
10286 fAreBucketingDetailsPresent = FALSE;
10287 }
10288 else
10289 {
10290 // Do we have either the IP for Watson buckets or the buckets themselves?
10291 fAreBucketingDetailsPresent = (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
10292 ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());
10293 }
10294 }
10295
10296 if (!fAreBucketingDetailsPresent)
10297 {
10298 // Collect the bucketing details since they are not already present
10299 pWatsonBucketTracker->SaveIpForWatsonBucket(adjustedIp);
10300
10301 if (!fIsPreallocatedException || fIsThreadAbortException)
10302 {
10303 if (!fIsPreallocatedException)
10304 {
10305 // Save the IP for Watson bucketing in the exception object for non-preallocated exception
10306 // objects
10307 ((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(adjustedIp);
10308
10309 // Save the IP in the UE tracker as well for TAE if an abort is in progress
10310 // since when we attempt reraise, the exception object is not available. Otherwise,
10311 // treat the exception like a regular non-preallocated exception and not do anything else.
10312 if (fIsThreadAbortException && pThread->IsAbortInitiated())
10313 {
10314 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
10315
10316 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
10317 pUEWatsonBucketTracker->SaveIpForWatsonBucket(adjustedIp);
10318
10319 // Set the flag that we captured the IP for Thread abort
10320 DEBUG_STMT(pUEWatsonBucketTracker->SetCapturedForThreadAbort());
10321 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Saved bucket IP for initial thread abort raise.\n"));
10322 }
10323 }
10324 else
10325 {
10326 // Create the buckets proactively for preallocated threadabort exception
10327 pWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.oCurrentThrowable);
10328 PTR_VOID pUnmanagedBuckets = pWatsonBucketTracker->RetrieveWatsonBuckets();
10329 if(pUnmanagedBuckets != NULL)
10330 {
10331 // Copy the details over to the UE Watson bucket tracker so that we can use them if the exception
10332 // is "reraised" after invoking the catch block.
10333 //
10334 // Since we can be here for preallocated threadabort exception when UE Tracker is simply
10335 // carrying the IP (that has been copied to pWatsonBucketTracker and buckets captured for it),
10336 // we will need to clear UE tracker so that we can copy over the captured buckets.
10337 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
10338 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
10339
10340 // Copy over the buckets from the current tracker that captured them.
10341 pUEWatsonBucketTracker->CopyEHWatsonBucketTracker(*(pWatsonBucketTracker));
10342
10343 // Buckets should be present now (unless the copy operation had an OOM)
10344 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
10345 {
10346 // Set the flag that we captured buckets for Thread abort
10347 DEBUG_STMT(pUEWatsonBucketTracker->SetCapturedForThreadAbort());
10348 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Saved buckets for Watson Bucketing for initial thread abort raise.\n"));
10349 }
10350 else
10351 {
10352 // If we are here, then the bucket copy operation (above) failed due to OOM.
10353 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Unable to save buckets for Watson Bucketing for initial thread abort raise, likely due to OOM.\n"));
10354 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
10355 }
10356 }
10357 else
10358 {
10359 // Watson helper function can bail out on us under OOM scenarios and return a NULL.
10360 // We cannot do much in such a case.
10361 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - No buckets were captured and returned to us for initial thread abort raise. Likely encountered an OOM.\n"));
10362 }
10363
10364 // Clear the buckets since we no longer need them
10365 pWatsonBucketTracker->ClearWatsonBucketDetails();
10366 }
10367 }
10368 else
10369 {
10370 // We have already saved the throw site IP for bucketing the non-ThreadAbort preallocated exceptions
10371 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Saved IP (%p) for Watson Bucketing for a preallocated exception\n", adjustedIp));
10372 }
10373 }
10374 else
10375 {
10376 // The inner exception object should be having either the IP for watson bucketing or the buckets themselves.
10377 // We shall copy over, whatever is available, to the current exception object.
10378 _ASSERTE(gc.oInnerMostExceptionThrowable != NULL);
10379 _ASSERTE(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
10380 ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());
10381
10382 if (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent())
10383 {
10384 EX_TRY
10385 {
10386 // Copy the bucket details from innermost exception to the current exception object.
10387 CopyWatsonBucketsFromThrowableToCurrentThrowable(gc.oInnerMostExceptionThrowable);
10388 }
10389 EX_CATCH
10390 {
10391 }
10392 EX_END_CATCH(SwallowAllExceptions);
10393
10394 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Copied watson bucket details from the innermost exception\n"));
10395 }
10396 else
10397 {
10398 // Copy the IP to the current exception object
10399 ((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetIPForWatsonBuckets());
10400 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Copied watson bucket IP from the innermost exception\n"));
10401 }
10402 }
10403
10404done:
10405 // Set the flag that we have got the bucketing details
10406 pExState->GetFlags()->SetGotWatsonBucketDetails();
10407
10408 GCPROTECT_END();
10409
10410#endif // !DACCESS_COMPILE
10411}
10412
10413// This function is a wrapper to copy the watson bucket byte[] from the specified
10414// throwable to the current throwable.
10415void CopyWatsonBucketsFromThrowableToCurrentThrowable(OBJECTREF oThrowableFrom)
10416{
10417#ifndef DACCESS_COMPILE
10418
10419 CONTRACTL
10420 {
10421 GC_TRIGGERS;
10422 MODE_COOPERATIVE;
10423 THROWS;
10424 PRECONDITION(oThrowableFrom != NULL);
10425 PRECONDITION(!CLRException::IsPreallocatedExceptionObject(oThrowableFrom));
10426 PRECONDITION(((EXCEPTIONREF)oThrowableFrom)->AreWatsonBucketsPresent());
10427 PRECONDITION(IsWatsonEnabled());
10428 }
10429 CONTRACTL_END;
10430
10431 struct
10432 {
10433 OBJECTREF oThrowableFrom;
10434 } _gc;
10435
10436 ZeroMemory(&_gc, sizeof(_gc));
10437 GCPROTECT_BEGIN(_gc);
10438 _gc.oThrowableFrom = oThrowableFrom;
10439
10440 // Copy the watson buckets to the current throwable by NOT passing
10441 // the second argument that will default to NULL.
10442 //
10443 // CopyWatsonBucketsBetweenThrowables will pass that NULL to
10444 // CopyWatsonBucketsToThrowables that will make it copy the buckets
10445 // to the current throwable.
10446 CopyWatsonBucketsBetweenThrowables(_gc.oThrowableFrom);
10447
10448 GCPROTECT_END();
10449
10450#endif // !DACCESS_COMPILE
10451}
10452
10453// This function will copy the watson bucket byte[] from the source
10454// throwable to the destination throwable.
10455//
10456// If the destination throwable is NULL, it will result in the buckets
10457// being copied to the current throwable.
10458void CopyWatsonBucketsBetweenThrowables(OBJECTREF oThrowableFrom, OBJECTREF oThrowableTo /*=NULL*/)
10459{
10460#ifndef DACCESS_COMPILE
10461
10462 CONTRACTL
10463 {
10464 GC_TRIGGERS;
10465 MODE_COOPERATIVE;
10466 THROWS;
10467 PRECONDITION(oThrowableFrom != NULL);
10468 PRECONDITION(!CLRException::IsPreallocatedExceptionObject(oThrowableFrom));
10469 PRECONDITION(((EXCEPTIONREF)oThrowableFrom)->AreWatsonBucketsPresent());
10470 PRECONDITION(IsWatsonEnabled());
10471 }
10472 CONTRACTL_END;
10473
10474 BOOL fRetVal = FALSE;
10475
10476 struct
10477 {
10478 OBJECTREF oFrom;
10479 OBJECTREF oTo;
10480 OBJECTREF oWatsonBuckets;
10481 } _gc;
10482
10483 ZeroMemory(&_gc, sizeof(_gc));
10484 GCPROTECT_BEGIN(_gc);
10485
10486 _gc.oFrom = oThrowableFrom;
10487 _gc.oTo = (oThrowableTo == NULL)?GetThread()->GetThrowable():oThrowableTo;
10488 _ASSERTE(_gc.oTo != NULL);
10489
10490 // The target throwable to which Watson buckets are going to be copied
10491 // shouldnt be preallocated exception object.
10492 _ASSERTE(!CLRException::IsPreallocatedExceptionObject(_gc.oTo));
10493
10494 // Size of a watson bucket
10495 DWORD size = sizeof(GenericModeBlock);
10496
10497 // Create the managed byte[] to hold the bucket details
10498 _gc.oWatsonBuckets = AllocatePrimitiveArray(ELEMENT_TYPE_U1, size);
10499 if (_gc.oWatsonBuckets == NULL)
10500 {
10501 // return failure if failed to create bucket array
10502 fRetVal = FALSE;
10503 }
10504 else
10505 {
10506 // Get the raw array data pointer of the source array
10507 U1ARRAYREF refSourceWatsonBucketArray = ((EXCEPTIONREF)_gc.oFrom)->GetWatsonBucketReference();
10508 PTR_VOID pRawSourceWatsonBucketArray = dac_cast<PTR_VOID>(refSourceWatsonBucketArray->GetDataPtr());
10509
10510 // Get the raw array data pointer to the destination array
10511 U1ARRAYREF refDestWatsonBucketArray = (U1ARRAYREF)_gc.oWatsonBuckets;
10512 PTR_VOID pRawDestWatsonBucketArray = dac_cast<PTR_VOID>(refDestWatsonBucketArray->GetDataPtr());
10513
10514 // Deep copy the bucket information to the managed array
10515 memcpyNoGCRefs(pRawDestWatsonBucketArray, pRawSourceWatsonBucketArray, size);
10516
10517 // Setup the managed field reference to point to the byte array.
10518 //
10519 // The throwable, to which the buckets are being copied to, may be
10520 // having existing buckets (e.g. when TypeInitialization exception
10521 // maybe thrown again when attempt is made to load the originally
10522 // failed type).
10523 //
10524 // This is also possible if exception object is used as singleton
10525 // and thrown by multiple threads.
10526 if (((EXCEPTIONREF)_gc.oTo)->AreWatsonBucketsPresent())
10527 {
10528 LOG((LF_EH, LL_INFO1000, "CopyWatsonBucketsBetweenThrowables: Throwable (%p) being copied to had previous buckets.\n", OBJECTREFToObject(_gc.oTo)));
10529 }
10530
10531 ((EXCEPTIONREF)_gc.oTo)->SetWatsonBucketReference(_gc.oWatsonBuckets);
10532
10533 fRetVal = TRUE;
10534 }
10535
10536 // We shouldn't be here when fRetVal is FALSE since failure to allocate the primitive
10537 // array should throw an OOM.
10538 _ASSERTE(fRetVal);
10539
10540 GCPROTECT_END();
10541#endif // !DACCESS_COMPILE
10542}
10543
10544// This function will copy the watson bucket information to the managed byte[] in
10545// the specified managed exception object.
10546//
10547// If throwable is not specified, it will be copied to the current throwable.
10548//
10549// pUnmanagedBuckets is a pointer to native memory that cannot be affected by GC.
10550BOOL CopyWatsonBucketsToThrowable(PTR_VOID pUnmanagedBuckets, OBJECTREF oTargetThrowable /*= NULL*/)
10551{
10552#ifndef DACCESS_COMPILE
10553
10554 CONTRACTL
10555 {
10556 GC_TRIGGERS;
10557 MODE_COOPERATIVE;
10558 THROWS;
10559 PRECONDITION(GetThread() != NULL);
10560 PRECONDITION(pUnmanagedBuckets != NULL);
10561 PRECONDITION(!CLRException::IsPreallocatedExceptionObject((oTargetThrowable == NULL)?GetThread()->GetThrowable():oTargetThrowable));
10562 PRECONDITION(IsWatsonEnabled());
10563 }
10564 CONTRACTL_END;
10565
10566 BOOL fRetVal = TRUE;
10567 struct
10568 {
10569 OBJECTREF oThrowable;
10570 OBJECTREF oWatsonBuckets;
10571 } _gc;
10572
10573 ZeroMemory(&_gc, sizeof(_gc));
10574 GCPROTECT_BEGIN(_gc);
10575 _gc.oThrowable = (oTargetThrowable == NULL)?GetThread()->GetThrowable():oTargetThrowable;
10576
10577 // Throwable to which buckets should be copied to, must exist.
10578 _ASSERTE(_gc.oThrowable != NULL);
10579
10580 // Size of a watson bucket
10581 DWORD size = sizeof(GenericModeBlock);
10582
10583 _gc.oWatsonBuckets = AllocatePrimitiveArray(ELEMENT_TYPE_U1, size);
10584 if (_gc.oWatsonBuckets == NULL)
10585 {
10586 // return failure if failed to create bucket array
10587 fRetVal = FALSE;
10588 }
10589 else
10590 {
10591 // Get the raw array data pointer
10592 U1ARRAYREF refWatsonBucketArray = (U1ARRAYREF)_gc.oWatsonBuckets;
10593 PTR_VOID pRawWatsonBucketArray = dac_cast<PTR_VOID>(refWatsonBucketArray->GetDataPtr());
10594
10595 // Deep copy the bucket information to the managed array
10596 memcpyNoGCRefs(pRawWatsonBucketArray, pUnmanagedBuckets, size);
10597
10598 // Setup the managed field reference to point to the byte array.
10599 //
10600 // The throwable, to which the buckets are being copied to, may be
10601 // having existing buckets (e.g. when TypeInitialization exception
10602 // maybe thrown again when attempt is made to load the originally
10603 // failed type).
10604 //
10605 // This is also possible if exception object is used as singleton
10606 // and thrown by multiple threads.
10607 if (((EXCEPTIONREF)_gc.oThrowable)->AreWatsonBucketsPresent())
10608 {
10609 LOG((LF_EH, LL_INFO1000, "CopyWatsonBucketsToThrowable: Throwable (%p) being copied to had previous buckets.\n", OBJECTREFToObject(_gc.oThrowable)));
10610 }
10611
10612 ((EXCEPTIONREF)_gc.oThrowable)->SetWatsonBucketReference(_gc.oWatsonBuckets);
10613 }
10614
10615 GCPROTECT_END();
10616
10617 return fRetVal;
10618#else // DACCESS_COMPILE
10619 return TRUE;
10620#endif // !DACCESS_COMPILE
10621}
10622
10623// This function will setup the bucketing information for nested exceptions
10624// raised. These would be any exceptions thrown from within the confines of
10625// managed EH clauses and include "rethrow" and "throw new ...".
10626//
10627// This is called from within CLR's personality routine for managed
10628// exceptions to preemptively setup the watson buckets from the ones that may
10629// already exist. If none exist already, we will automatically endup in the
10630// path (SetupInitialThrowBucketDetails) that will set up buckets for the
10631// exception being thrown.
10632void SetStateForWatsonBucketing(BOOL fIsRethrownException, OBJECTHANDLE ohOriginalException)
10633{
10634#ifndef DACCESS_COMPILE
10635
10636 // On CoreCLR, Watson may not be enabled. Thus, we should
10637 // skip this.
10638 if (!IsWatsonEnabled())
10639 {
10640 return;
10641 }
10642
10643 CONTRACTL
10644 {
10645 GC_TRIGGERS;
10646 MODE_ANY;
10647 NOTHROW;
10648 PRECONDITION(GetThread() != NULL);
10649 PRECONDITION(IsWatsonEnabled());
10650 }
10651 CONTRACTL_END;
10652
10653 // Switch to COOP mode
10654 GCX_COOP();
10655
10656 struct
10657 {
10658 OBJECTREF oCurrentThrowable;
10659 OBJECTREF oInnerMostExceptionThrowable;
10660 } gc;
10661 ZeroMemory(&gc, sizeof(gc));
10662 GCPROTECT_BEGIN(gc);
10663
10664 Thread* pThread = GetThread();
10665
10666 // Get the current exception state of the thread
10667 ThreadExceptionState* pCurExState = pThread->GetExceptionState();
10668 _ASSERTE(NULL != pCurExState);
10669
10670 // Ensure that the exception tracker exists
10671 _ASSERTE(pCurExState->GetCurrentExceptionTracker() != NULL);
10672
10673 // Get the current throwable
10674 gc.oCurrentThrowable = pThread->GetThrowable();
10675 _ASSERTE(gc.oCurrentThrowable != NULL);
10676
10677 // Is the throwable a preallocated exception object?
10678 BOOL fIsPreallocatedExceptionObject = CLRException::IsPreallocatedExceptionObject(gc.oCurrentThrowable);
10679
10680 // Copy the bucketing details from the original exception tracker if the current exception is a rethrow
10681 // AND the throwable is a preallocated exception object.
10682 //
10683 // For rethrown non-preallocated exception objects, the throwable would already have the bucketing
10684 // details inside it.
10685 if (fIsRethrownException)
10686 {
10687 if (fIsPreallocatedExceptionObject)
10688 {
10689 // Get the WatsonBucket tracker for the original exception, starting search from the previous EH tracker.
10690 // This is required so that when a preallocated exception is rethrown, then the current tracker would have
10691 // the same throwable as the original exception but no bucketing details.
10692 //
10693 // To ensure GetWatsonBucketTrackerForPreallocatedException uses the EH tracker corresponding to the original
10694 // exception to get the bucketing details, we pass TRUE as the third parameter.
10695 PTR_EHWatsonBucketTracker pPreallocWatsonBucketTracker = GetWatsonBucketTrackerForPreallocatedException(gc.oCurrentThrowable, FALSE, TRUE);
10696 if (pPreallocWatsonBucketTracker != NULL)
10697 {
10698 if (!IsThrowableThreadAbortException(gc.oCurrentThrowable))
10699 {
10700 // For non-thread abort preallocated exceptions, we copy the bucketing details
10701 // from their corresponding watson bucket tracker to the one corresponding to the
10702 // rethrow that is taking place.
10703 //
10704 // Bucketing details for preallocated exception may not be present if the exception came
10705 // from across AD transition and we attempted to copy them over from the UETracker, when
10706 // the exception was reraised in the calling AD, and the copy operation failed due to OOM.
10707 //
10708 // In such a case, when the reraised exception is caught and rethrown, we will not have
10709 // any bucketing details.
10710 if (NULL != pPreallocWatsonBucketTracker->RetrieveWatsonBucketIp())
10711 {
10712 // Copy the bucketing details now
10713 pCurExState->GetCurrentExceptionTracker()->GetWatsonBucketTracker()->CopyEHWatsonBucketTracker(*pPreallocWatsonBucketTracker);
10714 }
10715 else
10716 {
10717 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Watson bucketing details for rethrown preallocated exception not found in the EH tracker corresponding to the original exception. This is likely due to a previous OOM.\n"));
10718 LOG((LF_EH, LL_INFO1000, ">>>>>>>>>>>>>>>>>>>>>>>>>> Original WatsonBucketTracker = %p\n", pPreallocWatsonBucketTracker));
10719
10720 // Make the active tracker clear
10721 pCurExState->GetCurrentExceptionTracker()->GetWatsonBucketTracker()->ClearWatsonBucketDetails();
10722 }
10723 }
10724 #ifdef _DEBUG
10725 else
10726 {
10727 // For thread abort exceptions, the returned watson bucket tracker
10728 // would correspond to UE Watson bucket tracker and it will have
10729 // all the details.
10730 _ASSERTE(pPreallocWatsonBucketTracker == pCurExState->GetUEWatsonBucketTracker());
10731 }
10732 #endif // _DEBUG
10733 }
10734 else
10735 {
10736 // OOM can result in not having a Watson bucket tracker with valid bucketing details for a preallocated exception.
10737 // Thus, we may end up here. For details, see implementation of GetWatsonBucketTrackerForPreallocatedException.
10738 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Watson bucketing tracker for rethrown preallocated exception not found. This is likely due to a previous OOM.\n"));
10739
10740 // Make the active tracker clear
10741 pCurExState->GetCurrentExceptionTracker()->GetWatsonBucketTracker()->ClearWatsonBucketDetails();
10742 }
10743 }
10744 else
10745 {
10746 // We dont need to do anything here since the throwable would already have the bucketing
10747 // details inside it. Simply assert that the original exception object is the same as the current throwable.
10748 //
10749 // We cannot assert for Watson buckets since the original throwable may not have got them in
10750 // SetupInitialThrowBucketDetails due to OOM
10751 _ASSERTE((NULL != ohOriginalException) && (ObjectFromHandle(ohOriginalException) == gc.oCurrentThrowable));
10752 if ((((EXCEPTIONREF)gc.oCurrentThrowable)->AreWatsonBucketsPresent() == FALSE) &&
10753 (((EXCEPTIONREF)gc.oCurrentThrowable)->IsIPForWatsonBucketsPresent() == FALSE))
10754 {
10755 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Regular rethrown exception (%p) does not have Watson buckets, likely due to OOM.\n",
10756 OBJECTREFToObject(gc.oCurrentThrowable)));
10757 }
10758 }
10759
10760 // Set the flag that we have bucketing details for the exception
10761 pCurExState->GetFlags()->SetGotWatsonBucketDetails();
10762 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Using original exception details for Watson bucketing for rethrown exception.\n"));
10763 }
10764 else
10765 {
10766 // If we are here, then an exception is being thrown from within the
10767 // managed EH clauses of fault, finally or catch, with an inner exception.
10768
10769 // By default, we will create buckets based upon the exception being thrown unless
10770 // thrown exception has an inner exception that has got bucketing details
10771 BOOL fCreateBucketsForExceptionBeingThrown = TRUE;
10772
10773 // Start off by assuming that inner exception object is not preallocated
10774 BOOL fIsInnerExceptionPreallocated = FALSE;
10775
10776 // Reference to the WatsonBucket tracker for the inner exception, if it is preallocated
10777 PTR_EHWatsonBucketTracker pInnerExceptionWatsonBucketTracker = NULL;
10778
10779 // Since this is a new exception being thrown, we will check if it has buckets already or not.
10780 // This is possible when Reflection throws TargetInvocationException with an inner exception
10781 // that is preallocated exception object. In such a case, we copy the inner exception details
10782 // to the TargetInvocationException object already. This is done in InvokeImpl in ReflectionInvocation.cpp.
10783 if (((EXCEPTIONREF)gc.oCurrentThrowable)->AreWatsonBucketsPresent() ||
10784 ((EXCEPTIONREF)gc.oCurrentThrowable)->IsIPForWatsonBucketsPresent())
10785 {
10786 goto done;
10787 }
10788
10789 // If no buckets are present, then we will check if it has an innermost exception or not.
10790 // If it does, then we will make the exception being thrown use the bucketing details of the
10791 // innermost exception.
10792 //
10793 // If there is no innermost exception or if one is present without bucketing details, then
10794 // we will have bucket details based upon the exception being thrown.
10795
10796 // Get the innermost exception from the exception being thrown.
10797 gc.oInnerMostExceptionThrowable = ((EXCEPTIONREF)gc.oCurrentThrowable)->GetBaseException();
10798 if (gc.oInnerMostExceptionThrowable != NULL)
10799 {
10800 fIsInnerExceptionPreallocated = CLRException::IsPreallocatedExceptionObject(gc.oInnerMostExceptionThrowable);
10801
10802 // Preallocated exception objects do not have inner exception objects.
10803 // Thus, if we are here, then the current throwable cannot be
10804 // a preallocated exception object.
10805 _ASSERTE(!fIsPreallocatedExceptionObject);
10806
10807 // Create the new buckets only if the innermost exception object
10808 // does not have them already.
10809 if (fIsInnerExceptionPreallocated)
10810 {
10811 // If we are able to find the watson bucket tracker for the preallocated
10812 // inner exception, then we dont need to create buckets for throw site.
10813 pInnerExceptionWatsonBucketTracker = GetWatsonBucketTrackerForPreallocatedException(gc.oInnerMostExceptionThrowable, FALSE, TRUE);
10814 fCreateBucketsForExceptionBeingThrown = ((pInnerExceptionWatsonBucketTracker != NULL) &&
10815 (pInnerExceptionWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL)) ? FALSE : TRUE;
10816 }
10817 else
10818 {
10819 // Since the inner exception object is not preallocated, create
10820 // watson buckets only if it does not have them.
10821 fCreateBucketsForExceptionBeingThrown = !(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
10822 ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());
10823 }
10824 }
10825
10826 // If we are NOT going to create buckets for the thrown exception,
10827 // then copy them over from the inner exception object.
10828 //
10829 // If we have to create the buckets for the thrown exception,
10830 // we wont do that now - it will be done in StackTraceInfo::AppendElement
10831 // when we get the IP for bucketing.
10832 if (!fCreateBucketsForExceptionBeingThrown)
10833 {
10834 // Preallocated exception objects do not have inner exception objects.
10835 // Thus, if we are here, then the current throwable cannot be
10836 // a preallocated exception object.
10837 _ASSERTE(!fIsPreallocatedExceptionObject);
10838
10839 if (fIsInnerExceptionPreallocated)
10840 {
10841
10842 // We should have the inner exception watson bucket tracker
10843 _ASSERTE((pInnerExceptionWatsonBucketTracker != NULL) && (pInnerExceptionWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL));
10844
10845 // Capture the buckets for the innermost exception if they dont already exist.
10846 // Since the current throwable cannot be preallocated (see the assert above),
10847 // copy the buckets to the throwable.
10848 PTR_VOID pInnerExceptionWatsonBuckets = pInnerExceptionWatsonBucketTracker->RetrieveWatsonBuckets();
10849 if (pInnerExceptionWatsonBuckets == NULL)
10850 {
10851 // Capture the buckets since they dont exist
10852 pInnerExceptionWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.oInnerMostExceptionThrowable);
10853 pInnerExceptionWatsonBuckets = pInnerExceptionWatsonBucketTracker->RetrieveWatsonBuckets();
10854 }
10855
10856 if (pInnerExceptionWatsonBuckets == NULL)
10857 {
10858 // Couldnt capture details like due to OOM
10859 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Preallocated inner-exception's WBTracker (%p) has no bucketing details for the thrown exception, likely due to OOM.\n", pInnerExceptionWatsonBucketTracker));
10860 }
10861 else
10862 {
10863 // Copy the buckets to the current throwable
10864 BOOL fCopied = TRUE;
10865 EX_TRY
10866 {
10867 fCopied = CopyWatsonBucketsToThrowable(pInnerExceptionWatsonBuckets);
10868 _ASSERTE(fCopied);
10869 }
10870 EX_CATCH
10871 {
10872 // Dont do anything if we fail to copy the buckets - this is no different than
10873 // the native watson helper functions failing under OOM
10874 fCopied = FALSE;
10875 }
10876 EX_END_CATCH(SwallowAllExceptions);
10877 }
10878 }
10879 else
10880 {
10881 // Assert that the inner exception has the Watson buckets
10882 _ASSERTE(gc.oInnerMostExceptionThrowable != NULL);
10883 _ASSERTE(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
10884 ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());
10885
10886 if (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent())
10887 {
10888 // Copy the bucket information from the inner exception object to the current throwable
10889 EX_TRY
10890 {
10891 CopyWatsonBucketsFromThrowableToCurrentThrowable(gc.oInnerMostExceptionThrowable);
10892 }
10893 EX_CATCH
10894 {
10895 // Dont do anything if we fail to copy the buckets - this is no different than
10896 // the native watson helper functions failing under OOM
10897 }
10898 EX_END_CATCH(SwallowAllExceptions);
10899 }
10900 else
10901 {
10902 // Copy the IP for Watson bucketing to the exception object
10903 ((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetIPForWatsonBuckets());
10904 }
10905 }
10906
10907 // Set the flag that we got bucketing details for the exception
10908 pCurExState->GetFlags()->SetGotWatsonBucketDetails();
10909 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Using innermost exception details for Watson bucketing for thrown exception.\n"));
10910 }
10911done:;
10912 }
10913
10914 GCPROTECT_END();
10915
10916#endif // !DACCESS_COMPILE
10917}
10918
10919// Constructor that will do the initialization of the object
10920EHWatsonBucketTracker::EHWatsonBucketTracker()
10921{
10922 LIMITED_METHOD_CONTRACT;
10923
10924 Init();
10925}
10926
10927// Reset the fields to default values
10928void EHWatsonBucketTracker::Init()
10929{
10930 LIMITED_METHOD_CONTRACT;
10931
10932 m_WatsonUnhandledInfo.m_UnhandledIp = 0;
10933 m_WatsonUnhandledInfo.m_pUnhandledBuckets = NULL;
10934
10935 DEBUG_STMT(ResetFlags());
10936
10937 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::Init - initializing watson bucket tracker (%p)\n", this));
10938}
10939
10940// This method copies the bucketing details from the specified throwable
10941// to the current Watson Bucket tracker.
10942void EHWatsonBucketTracker::CopyBucketsFromThrowable(OBJECTREF oThrowable)
10943{
10944#ifndef DACCESS_COMPILE
10945 CONTRACTL
10946 {
10947 NOTHROW;
10948 GC_NOTRIGGER;
10949 MODE_ANY;
10950 PRECONDITION(oThrowable != NULL);
10951 PRECONDITION(((EXCEPTIONREF)oThrowable)->AreWatsonBucketsPresent());
10952 PRECONDITION(IsWatsonEnabled());
10953 }
10954 CONTRACTL_END;
10955
10956 GCX_COOP();
10957
10958 struct
10959 {
10960 OBJECTREF oFrom;
10961 } _gc;
10962
10963 ZeroMemory(&_gc, sizeof(_gc));
10964 GCPROTECT_BEGIN(_gc);
10965
10966 _gc.oFrom = oThrowable;
10967
10968 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Copying bucketing details from throwable (%p) to tracker (%p)\n",
10969 OBJECTREFToObject(_gc.oFrom), this));
10970
10971 // Watson bucket is a "GenericModeBlock" type. Set up an empty GenericModeBlock
10972 // to hold the bucket parameters.
10973 GenericModeBlock *pgmb = new (nothrow) GenericModeBlock;
10974 if (pgmb == NULL)
10975 {
10976 // If we are unable to allocate memory to hold the WatsonBucket, then
10977 // reset the IP and bucket pointer to NULL and bail out
10978 SaveIpForWatsonBucket(NULL);
10979 m_WatsonUnhandledInfo.m_pUnhandledBuckets = NULL;
10980 }
10981 else
10982 {
10983 // Get the raw array data pointer
10984 U1ARRAYREF refWatsonBucketArray = ((EXCEPTIONREF)_gc.oFrom)->GetWatsonBucketReference();
10985 PTR_VOID pRawWatsonBucketArray = dac_cast<PTR_VOID>(refWatsonBucketArray->GetDataPtr());
10986
10987 // Copy over the details to our new allocation
10988 memcpyNoGCRefs(pgmb, pRawWatsonBucketArray, sizeof(GenericModeBlock));
10989
10990 // and save the address where the buckets were copied
10991 _ASSERTE(m_WatsonUnhandledInfo.m_pUnhandledBuckets == NULL);
10992 m_WatsonUnhandledInfo.m_pUnhandledBuckets = pgmb;
10993 }
10994
10995 GCPROTECT_END();
10996
10997 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Copied Watson Buckets from throwable to (%p)\n",
10998 m_WatsonUnhandledInfo.m_pUnhandledBuckets));
10999#endif // !DACCESS_COMPILE
11000}
11001
11002// This method copies the bucketing details from the specified Watson Bucket tracker
11003// to the current one.
11004void EHWatsonBucketTracker::CopyEHWatsonBucketTracker(const EHWatsonBucketTracker& srcTracker)
11005{
11006#ifndef DACCESS_COMPILE
11007 CONTRACTL
11008 {
11009 NOTHROW;
11010 GC_NOTRIGGER;
11011 MODE_ANY;
11012 PRECONDITION(m_WatsonUnhandledInfo.m_UnhandledIp == 0);
11013 PRECONDITION(m_WatsonUnhandledInfo.m_pUnhandledBuckets == NULL);
11014 PRECONDITION(IsWatsonEnabled());
11015 }
11016 CONTRACTL_END;
11017
11018 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Copying bucketing details from %p to %p\n", &srcTracker, this));
11019
11020 // Copy the tracking details over from the specified tracker
11021 SaveIpForWatsonBucket(srcTracker.m_WatsonUnhandledInfo.m_UnhandledIp);
11022
11023 if (srcTracker.m_WatsonUnhandledInfo.m_pUnhandledBuckets != NULL)
11024 {
11025 // To save the bucket information, we will need to memcpy.
11026 // This is to ensure that if the original watson bucket tracker
11027 // (for original exception) is released and its memory deallocated,
11028 // the new watson bucket tracker (for rethrown exception, for e.g.)
11029 // would still have all the bucket details.
11030
11031 // Watson bucket is a "GenericModeBlock" type. Set up an empty GenericModeBlock
11032 // to hold the bucket parameters.
11033 GenericModeBlock *pgmb = new (nothrow) GenericModeBlock;
11034 if (pgmb == NULL)
11035 {
11036 // If we are unable to allocate memory to hold the WatsonBucket, then
11037 // reset the IP and bucket pointer to NULL and bail out
11038 SaveIpForWatsonBucket(NULL);
11039 m_WatsonUnhandledInfo.m_pUnhandledBuckets = NULL;
11040
11041 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Not copying buckets due to out of memory.\n"));
11042 }
11043 else
11044 {
11045 // Copy over the details to our new allocation
11046 memcpyNoGCRefs(pgmb, srcTracker.m_WatsonUnhandledInfo.m_pUnhandledBuckets, sizeof(GenericModeBlock));
11047
11048 // and save the address where the buckets were copied
11049 m_WatsonUnhandledInfo.m_pUnhandledBuckets = pgmb;
11050 }
11051 }
11052
11053 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Copied Watson Bucket to (%p)\n", m_WatsonUnhandledInfo.m_pUnhandledBuckets));
11054#endif // !DACCESS_COMPILE
11055}
11056
11057void EHWatsonBucketTracker::SaveIpForWatsonBucket(
11058 UINT_PTR ip) // The new IP.
11059{
11060#ifndef DACCESS_COMPILE
11061 CONTRACTL
11062 {
11063 NOTHROW;
11064 GC_NOTRIGGER;
11065 MODE_ANY;
11066 SO_TOLERANT;
11067 PRECONDITION(IsWatsonEnabled());
11068 }
11069 CONTRACTL_END;
11070
11071 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::SaveIpForUnhandledInfo - this = %p, IP = %p\n", this, ip));
11072
11073 // Since we are setting a new IP for tracking buckets,
11074 // clear any existing details we may hold
11075 ClearWatsonBucketDetails();
11076
11077 // Save the new IP for bucketing
11078 m_WatsonUnhandledInfo.m_UnhandledIp = ip;
11079#endif // !DACCESS_COMPILE
11080}
11081
11082UINT_PTR EHWatsonBucketTracker::RetrieveWatsonBucketIp()
11083{
11084 LIMITED_METHOD_CONTRACT;
11085
11086 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::RetrieveWatsonBucketIp - this = %p, IP = %p\n", this, m_WatsonUnhandledInfo.m_UnhandledIp));
11087
11088 return m_WatsonUnhandledInfo.m_UnhandledIp;
11089}
11090
11091// This function returns the reference to the Watson buckets tracked by the
11092// instance of WatsonBucket tracker.
11093//
11094// This is *also* invoked from the DAC when buckets are requested.
11095PTR_VOID EHWatsonBucketTracker::RetrieveWatsonBuckets()
11096{
11097#if !defined(DACCESS_COMPILE)
11098 if (!IsWatsonEnabled())
11099 {
11100 return NULL;
11101 }
11102#endif //!defined(DACCESS_COMPILE)
11103
11104 CONTRACTL
11105 {
11106 NOTHROW;
11107 GC_NOTRIGGER;
11108 MODE_ANY;
11109 SO_TOLERANT;
11110 PRECONDITION(IsWatsonEnabled());
11111 }
11112 CONTRACTL_END;
11113
11114 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::RetrieveWatsonBuckets - this = %p, bucket address = %p\n", this, m_WatsonUnhandledInfo.m_pUnhandledBuckets));
11115
11116 return m_WatsonUnhandledInfo.m_pUnhandledBuckets;
11117}
11118
11119void EHWatsonBucketTracker::ClearWatsonBucketDetails()
11120{
11121#ifndef DACCESS_COMPILE
11122
11123 if (!IsWatsonEnabled())
11124 {
11125 return;
11126 }
11127
11128 CONTRACTL
11129 {
11130 NOTHROW;
11131 GC_NOTRIGGER;
11132 MODE_ANY;
11133 SO_TOLERANT;
11134 PRECONDITION(IsWatsonEnabled());
11135 }
11136 CONTRACTL_END;
11137
11138
11139 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::ClearWatsonBucketDetails for tracker (%p)\n", this));
11140
11141 if (m_WatsonUnhandledInfo.m_pUnhandledBuckets != NULL)
11142 {
11143 FreeBucketParametersForManagedException(m_WatsonUnhandledInfo.m_pUnhandledBuckets);
11144 }
11145
11146 Init();
11147#endif // !DACCESS_COMPILE
11148}
11149
11150void EHWatsonBucketTracker::CaptureUnhandledInfoForWatson(TypeOfReportedError tore, Thread * pThread, OBJECTREF * pThrowable)
11151{
11152#ifndef DACCESS_COMPILE
11153 CONTRACTL
11154 {
11155 NOTHROW;
11156 GC_NOTRIGGER;
11157 MODE_ANY;
11158 PRECONDITION(IsWatsonEnabled());
11159 }
11160 CONTRACTL_END;
11161
11162 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CaptureUnhandledInfoForWatson capturing watson bucket details for (%p)\n", this));
11163
11164 // Only capture the bucket information if there is an IP AND we dont already have collected them.
11165 // We could have collected them from a previous AD transition and wouldnt want to overwrite them.
11166 if (m_WatsonUnhandledInfo.m_UnhandledIp != 0)
11167 {
11168 if (m_WatsonUnhandledInfo.m_pUnhandledBuckets == NULL)
11169 {
11170 // Get the bucket details since we dont have them
11171 m_WatsonUnhandledInfo.m_pUnhandledBuckets = GetBucketParametersForManagedException(m_WatsonUnhandledInfo.m_UnhandledIp, tore, pThread, pThrowable);
11172 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CaptureUnhandledInfoForWatson captured the following watson bucket details: (this = %p, bucket addr = %p)\n",
11173 this, m_WatsonUnhandledInfo.m_pUnhandledBuckets));
11174 }
11175 else
11176 {
11177 // We already have the bucket details - so no need to capture them again
11178 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CaptureUnhandledInfoForWatson already have the watson bucket details: (this = %p, bucket addr = %p)\n",
11179 this, m_WatsonUnhandledInfo.m_pUnhandledBuckets));
11180 }
11181 }
11182 else
11183 {
11184 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CaptureUnhandledInfoForWatson didnt have an IP to use for capturing watson buckets\n"));
11185 }
11186#endif // !DACCESS_COMPILE
11187}
11188#endif // !FEATURE_PAL
11189
11190// Given a throwable, this function will attempt to find an active EH tracker corresponding to it.
11191// If none found, it will return NULL
11192#ifdef WIN64EXCEPTIONS
11193PTR_ExceptionTracker GetEHTrackerForException(OBJECTREF oThrowable, PTR_ExceptionTracker pStartingEHTracker)
11194#elif _TARGET_X86_
11195PTR_ExInfo GetEHTrackerForException(OBJECTREF oThrowable, PTR_ExInfo pStartingEHTracker)
11196#else
11197#error Unsupported platform
11198#endif
11199{
11200 CONTRACTL
11201 {
11202 GC_NOTRIGGER;
11203 MODE_COOPERATIVE;
11204 NOTHROW;
11205 SO_TOLERANT;
11206 PRECONDITION(GetThread() != NULL);
11207 PRECONDITION(oThrowable != NULL);
11208 }
11209 CONTRACTL_END;
11210
11211 // Get the reference to the exception tracker to start with. If one has been provided to us,
11212 // then use it. Otherwise, start from the current one.
11213#ifdef WIN64EXCEPTIONS
11214 PTR_ExceptionTracker pEHTracker = (pStartingEHTracker != NULL) ? pStartingEHTracker : GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
11215#elif _TARGET_X86_
11216 PTR_ExInfo pEHTracker = (pStartingEHTracker != NULL) ? pStartingEHTracker : GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
11217#else
11218#error Unsupported platform
11219#endif
11220
11221 BOOL fFoundTracker = FALSE;
11222
11223 // Start walking the list to find the tracker correponding
11224 // to the exception object.
11225 while (pEHTracker != NULL)
11226 {
11227 if (pEHTracker->GetThrowable() == oThrowable)
11228 {
11229 // found the tracker - break out.
11230 fFoundTracker = TRUE;
11231 break;
11232 }
11233
11234 // move to the previous tracker...
11235 pEHTracker = pEHTracker->GetPreviousExceptionTracker();
11236 }
11237
11238 return fFoundTracker ? pEHTracker : NULL;
11239}
11240
11241#ifdef FEATURE_CORRUPTING_EXCEPTIONS
11242// -----------------------------------------------------------------------
11243// Support for CorruptedState Exceptions
11244// -----------------------------------------------------------------------
11245
11246// Given an exception code, this method returns a BOOL to indicate if the
11247// code belongs to a corrupting exception or not.
11248/* static */
11249BOOL CEHelper::IsProcessCorruptedStateException(DWORD dwExceptionCode, BOOL fCheckForSO /*= TRUE*/)
11250{
11251 CONTRACTL
11252 {
11253 NOTHROW;
11254 GC_NOTRIGGER;
11255 MODE_ANY;
11256 SO_TOLERANT;
11257 }
11258 CONTRACTL_END;
11259
11260 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11261 {
11262 return FALSE;
11263 }
11264
11265 // Call into the utilcode helper function to check if this
11266 // is a CE or not.
11267 return (::IsProcessCorruptedStateException(dwExceptionCode, fCheckForSO));
11268}
11269
11270// This is used in the VM folder version of "SET_CE_RETHROW_FLAG_FOR_EX_CATCH" (in clrex.h)
11271// to check if the managed exception caught by EX_END_CATCH is CSE or not.
11272//
11273// If you are using it from rethrow boundaries (e.g. SET_CE_RETHROW_FLAG_FOR_EX_CATCH
11274// macro that is used to automatically rethrow corrupting exceptions), then you may
11275// want to set the "fMarkForReuseIfCorrupting" to TRUE to enable propagation of the
11276// corruption severity when the reraised exception is seen by managed code again.
11277/* static */
11278BOOL CEHelper::IsLastActiveExceptionCorrupting(BOOL fMarkForReuseIfCorrupting /* = FALSE */)
11279{
11280 CONTRACTL
11281 {
11282 NOTHROW;
11283 GC_NOTRIGGER;
11284 MODE_ANY;
11285 PRECONDITION(GetThread() != NULL);
11286 }
11287 CONTRACTL_END;
11288
11289 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11290 {
11291 return FALSE;
11292 }
11293
11294 BOOL fIsCorrupting = FALSE;
11295 ThreadExceptionState *pCurTES = GetThread()->GetExceptionState();
11296
11297 // Check the corruption severity
11298 CorruptionSeverity severity = pCurTES->GetLastActiveExceptionCorruptionSeverity();
11299 fIsCorrupting = (severity == ProcessCorrupting);
11300 if (fIsCorrupting && fMarkForReuseIfCorrupting)
11301 {
11302 // Mark the corruption severity for reuse
11303 CEHelper::MarkLastActiveExceptionCorruptionSeverityForReraiseReuse();
11304 }
11305
11306 LOG((LF_EH, LL_INFO100, "CEHelper::IsLastActiveExceptionCorrupting - Using corruption severity from TES.\n"));
11307
11308 return fIsCorrupting;
11309}
11310
11311// Given a MethodDesc, this method will return a BOOL to indicate if
11312// the containing assembly was built for PreV4 runtime or not.
11313/* static */
11314BOOL CEHelper::IsMethodInPreV4Assembly(PTR_MethodDesc pMethodDesc)
11315{
11316 CONTRACTL
11317 {
11318 NOTHROW;
11319 GC_NOTRIGGER;
11320 MODE_ANY;
11321 PRECONDITION(pMethodDesc != NULL);
11322 }
11323 CONTRACTL_END;
11324
11325 // By default, assume that the containing assembly was not
11326 // built for PreV4 runtimes.
11327 BOOL fBuiltForPreV4Runtime = FALSE;
11328
11329 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11330 {
11331 return TRUE;
11332 }
11333
11334 LPCSTR pszVersion = NULL;
11335
11336 // Retrieve the manifest metadata reference since that contains
11337 // the "built-for" runtime details
11338 IMDInternalImport *pImport = pMethodDesc->GetAssembly()->GetManifestImport();
11339 if (pImport && SUCCEEDED(pImport->GetVersionString(&pszVersion)))
11340 {
11341 if (pszVersion != NULL)
11342 {
11343 // If version begins with "v1.*" or "v2.*", it was built for preV4 runtime
11344 if ((pszVersion[0] == 'v' || pszVersion[0] == 'V') &&
11345 IS_DIGIT(pszVersion[1]) &&
11346 (pszVersion[2] == '.') )
11347 {
11348 // Looks like a version. Is it lesser than v4.0 major version where we start using new behavior?
11349 fBuiltForPreV4Runtime = ((DIGIT_TO_INT(pszVersion[1]) != 0) &&
11350 (DIGIT_TO_INT(pszVersion[1]) <= HIGHEST_MAJOR_VERSION_OF_PREV4_RUNTIME));
11351 }
11352 }
11353 }
11354
11355 return fBuiltForPreV4Runtime;
11356}
11357
11358// Given a MethodDesc and CorruptionSeverity, this method will return a
11359// BOOL indicating if the method can handle those kinds of CEs or not.
11360/* static */
11361BOOL CEHelper::CanMethodHandleCE(PTR_MethodDesc pMethodDesc, CorruptionSeverity severity, BOOL fCalculateSecurityInfo /*= TRUE*/)
11362{
11363 BOOL fCanMethodHandleSeverity = FALSE;
11364
11365#ifndef DACCESS_COMPILE
11366 CONTRACTL
11367 {
11368 if (fCalculateSecurityInfo)
11369 {
11370 GC_TRIGGERS; // CEHelper::CanMethodHandleCE will invoke Security::IsMethodCritical that could endup invoking MethodTable::LoadEnclosingMethodTable that is GC_TRIGGERS
11371 }
11372 else
11373 {
11374 // See comment in COMPlusUnwindCallback for details.
11375 GC_NOTRIGGER;
11376 }
11377 // First pass requires THROWS and in 2nd we need to be due to the AppX check below where GetFusionAssemblyName can throw.
11378 THROWS;
11379 MODE_ANY;
11380 PRECONDITION(pMethodDesc != NULL);
11381 }
11382 CONTRACTL_END;
11383
11384
11385 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11386 {
11387 return TRUE;
11388 }
11389
11390 // Since the method is Security Critical, now check if it is
11391 // attributed to handle the CE or not.
11392 IMDInternalImport *pImport = pMethodDesc->GetMDImport();
11393 if (pImport != NULL)
11394 {
11395 mdMethodDef methodDef = pMethodDesc->GetMemberDef();
11396 switch(severity)
11397 {
11398 case ProcessCorrupting:
11399 fCanMethodHandleSeverity = (S_OK == pImport->GetCustomAttributeByName(
11400 methodDef,
11401 HANDLE_PROCESS_CORRUPTED_STATE_EXCEPTION_ATTRIBUTE,
11402 NULL,
11403 NULL));
11404 break;
11405 default:
11406 _ASSERTE(!"Unknown Exception Corruption Severity!");
11407 break;
11408 }
11409 }
11410#endif // !DACCESS_COMPILE
11411
11412 return fCanMethodHandleSeverity;
11413}
11414
11415// Given a MethodDesc, this method will return a BOOL to indicate if the method should be examined for exception
11416// handlers for the specified exception.
11417//
11418// This method accounts for both corrupting and non-corrupting exceptions.
11419/* static */
11420BOOL CEHelper::CanMethodHandleException(CorruptionSeverity severity, PTR_MethodDesc pMethodDesc, BOOL fCalculateSecurityInfo /*= TRUE*/)
11421{
11422 CONTRACTL
11423 {
11424 // CEHelper::CanMethodHandleCE will invoke Security::IsMethodCritical that could endup invoking MethodTable::LoadEnclosingMethodTable that is GC_TRIGGERS/THROWS
11425 if (fCalculateSecurityInfo)
11426 {
11427 GC_TRIGGERS;
11428 }
11429 else
11430 {
11431 // See comment in COMPlusUnwindCallback for details.
11432 GC_NOTRIGGER;
11433 }
11434 THROWS;
11435 MODE_ANY;
11436 PRECONDITION(pMethodDesc != NULL);
11437 }
11438 CONTRACTL_END;
11439
11440 // By default, assume that the runtime shouldn't look for exception handlers
11441 // in the method pointed by the MethodDesc
11442 BOOL fLookForExceptionHandlersInMethod = FALSE;
11443
11444 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11445 {
11446 return TRUE;
11447 }
11448
11449 // If we have been asked to use the last active corruption severity (e.g. in cases of Reflection
11450 // or COM interop), then retrieve it.
11451 if (severity == UseLast)
11452 {
11453 LOG((LF_EH, LL_INFO100, "CEHelper::CanMethodHandleException - Using LastActiveExceptionCorruptionSeverity.\n"));
11454 severity = GetThread()->GetExceptionState()->GetLastActiveExceptionCorruptionSeverity();
11455 }
11456
11457 LOG((LF_EH, LL_INFO100, "CEHelper::CanMethodHandleException - Processing CorruptionSeverity: %d.\n", severity));
11458
11459 if (severity > NotCorrupting)
11460 {
11461 // If the method lies in an assembly built for pre-V4 runtime, allow the runtime
11462 // to look for exception handler for the CE.
11463 BOOL fIsMethodInPreV4Assembly = FALSE;
11464 fIsMethodInPreV4Assembly = CEHelper::IsMethodInPreV4Assembly(pMethodDesc);
11465
11466 if (!fIsMethodInPreV4Assembly)
11467 {
11468 // Method lies in an assembly built for V4 or later runtime.
11469 LOG((LF_EH, LL_INFO100, "CEHelper::CanMethodHandleException - Method is in an assembly built for V4 or later runtime.\n"));
11470
11471 // Depending upon the corruption severity of the exception, see if the
11472 // method supports handling that.
11473 LOG((LF_EH, LL_INFO100, "CEHelper::CanMethodHandleException - Exception is corrupting.\n"));
11474
11475 // Check if the method can handle the severity specified in the exception object.
11476 fLookForExceptionHandlersInMethod = CEHelper::CanMethodHandleCE(pMethodDesc, severity, fCalculateSecurityInfo);
11477 }
11478 else
11479 {
11480 // Method is in a Pre-V4 assembly - allow it to be examined for processing the CE
11481 fLookForExceptionHandlersInMethod = TRUE;
11482 }
11483 }
11484 else
11485 {
11486 // Non-corrupting exceptions can continue to be delivered
11487 fLookForExceptionHandlersInMethod = TRUE;
11488 }
11489
11490 return fLookForExceptionHandlersInMethod;
11491}
11492
11493// Given a managed exception object, this method will return a BOOL
11494// indicating if it corresponds to a ProcessCorruptedState exception
11495// or not.
11496/* static */
11497BOOL CEHelper::IsProcessCorruptedStateException(OBJECTREF oThrowable)
11498{
11499 CONTRACTL
11500 {
11501 NOTHROW;
11502 GC_NOTRIGGER;
11503 MODE_COOPERATIVE;
11504 SO_TOLERANT;
11505 PRECONDITION(oThrowable != NULL);
11506 }
11507 CONTRACTL_END;
11508
11509 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11510 {
11511 return FALSE;
11512 }
11513
11514#ifndef DACCESS_COMPILE
11515 // If the throwable represents preallocated SO, then indicate it as a CSE
11516 if (CLRException::GetPreallocatedStackOverflowException() == oThrowable)
11517 {
11518 return TRUE;
11519 }
11520#endif // !DACCESS_COMPILE
11521
11522 // Check if we have an exception tracker for this exception
11523 // and if so, if it represents corrupting exception or not.
11524 // Get the exception tracker for the current exception
11525#ifdef WIN64EXCEPTIONS
11526 PTR_ExceptionTracker pEHTracker = GetEHTrackerForException(oThrowable, NULL);
11527#elif _TARGET_X86_
11528 PTR_ExInfo pEHTracker = GetEHTrackerForException(oThrowable, NULL);
11529#else
11530#error Unsupported platform
11531#endif
11532
11533 if (pEHTracker != NULL)
11534 {
11535 // Found the tracker for exception object - check if its CSE or not.
11536 return (pEHTracker->GetCorruptionSeverity() == ProcessCorrupting);
11537 }
11538
11539 return FALSE;
11540}
11541
11542#ifdef WIN64EXCEPTIONS
11543void CEHelper::SetupCorruptionSeverityForActiveExceptionInUnwindPass(Thread *pCurThread, PTR_ExceptionTracker pEHTracker, BOOL fIsFirstPass,
11544 DWORD dwExceptionCode)
11545{
11546#ifndef DACCESS_COMPILE
11547 CONTRACTL
11548 {
11549 NOTHROW;
11550 GC_NOTRIGGER;
11551 MODE_ANY;
11552 SO_TOLERANT;
11553 PRECONDITION(!fIsFirstPass); // This method should only be called during an unwind
11554 PRECONDITION(pCurThread != NULL);
11555 }
11556 CONTRACTL_END;
11557
11558 // <WIN64>
11559 //
11560 // Typically, exception tracker is created for an exception when the OS is in the first pass.
11561 // However, it may be created during the 2nd pass under specific cases. Managed C++ provides
11562 // such a scenario. In the following, stack grows left to right:
11563 //
11564 // CallDescrWorker -> ILStub1 -> <Native Main> -> UMThunkStub -> IL_Stub2 -> <Managed Main>
11565 //
11566 // If a CSE exception goes unhandled from managed main, it will reach the OS. The [CRT in?] OS triggers
11567 // unwind that results in invoking the personality routine of UMThunkStub, called UMThunkStubUnwindFrameChainHandler,
11568 // that releases all exception trackers below it. Thus, the tracker for the CSE, which went unhandled, is also
11569 // released. This detail is 64bit specific and the crux of this issue.
11570 //
11571 // Now, it is expected that by the time we are in the unwind pass, the corruption severity would have already been setup in the
11572 // exception tracker and thread exception state (TES) as part of the first pass, and thus, are identical.
11573 //
11574 // However, for the scenario above, when the unwind continues and reaches ILStub1, its personality routine (which is ProcessCLRException)
11575 // is invoked. It attempts to get the exception tracker corresponding to the exception. Since none exists, it creates a brand new one,
11576 // which has the exception corruption severity as NotSet.
11577 //
11578 // During the stack walk, we know (from TES) that the active exception was a CSE, and thus, ILStub1 cannot handle the exception. Prior
11579 // to bailing out, we assert that our data structures are intact by comparing the exception severity in TES with the one in the current
11580 // exception tracker. Since the tracker was recreated, it had the severity as NotSet and this does not match the severity in TES.
11581 // Thus, the assert fires. [This check is performed in ProcessManagedCallFrame.]
11582 //
11583 // To address such a case, if we have created a new exception tracker in the unwind (2nd) pass, then set its
11584 // exception corruption severity to what the TES holds currently. This will maintain the same semantic as the case
11585 // where new tracker is not created (for e.g. the exception was caught in Managed main).
11586 //
11587 // The exception is the scenario of code that uses longjmp to jump to a different context. Longjmp results in a raise
11588 // of a new exception with the longjmp exception code (0x80000026) but with ExceptionFlags set indicating unwind. When this is
11589 // seen by ProcessCLRException (64bit personality routine), it will create a new tracker in the 2nd pass.
11590 //
11591 // Longjmp outside an exceptional path does not interest us, but the one in the exceptional
11592 // path would only happen when a method attributed to handle CSE invokes it. Thus, if the longjmp happened during the 2nd pass of a CSE,
11593 // we want it to proceed (and thus, jump) as expected and not apply the CSE severity to the tracker - this is equivalent to
11594 // a catch block that handles a CSE and then does a "throw new Exception();". The new exception raised is
11595 // non-CSE in nature as well.
11596 //
11597 // http://www.nynaeve.net/?p=105 has a brief description of how exception-safe setjmp/longjmp works.
11598 //
11599 // </WIN64>
11600 if (pEHTracker->GetCorruptionSeverity() == NotSet)
11601 {
11602 // Get the thread exception state
11603 ThreadExceptionState *pCurTES = pCurThread->GetExceptionState();
11604
11605 // Set the tracker to have the same corruption severity as the last active severity unless we are dealing
11606 // with LONGJMP
11607 if (dwExceptionCode == STATUS_LONGJUMP)
11608 {
11609 pCurTES->SetLastActiveExceptionCorruptionSeverity(NotCorrupting);
11610 }
11611
11612 pEHTracker->SetCorruptionSeverity(pCurTES->GetLastActiveExceptionCorruptionSeverity());
11613 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveExceptionInUnwindPass - Setup the corruption severity in the second pass.\n"));
11614 }
11615#endif // !DACCESS_COMPILE
11616}
11617#endif // WIN64EXCEPTIONS
11618
11619// This method is invoked from the personality routine for managed code and is used to setup the
11620// corruption severity for the active exception on the thread exception state and the
11621// exception tracker corresponding to the exception.
11622/* static */
11623void CEHelper::SetupCorruptionSeverityForActiveException(BOOL fIsRethrownException, BOOL fIsNestedException, BOOL fShouldTreatExceptionAsNonCorrupting /* = FALSE */)
11624{
11625#ifndef DACCESS_COMPILE
11626 CONTRACTL
11627 {
11628 NOTHROW;
11629 GC_NOTRIGGER;
11630 MODE_COOPERATIVE;
11631 }
11632 CONTRACTL_END;
11633
11634 // Get the thread and the managed exception object - they must exist at this point
11635 Thread *pCurThread = GetThread();
11636 _ASSERTE(pCurThread != NULL);
11637
11638 OBJECTREF oThrowable = pCurThread->GetThrowable();
11639 _ASSERTE(oThrowable != NULL);
11640
11641 // Get the thread exception state
11642 ThreadExceptionState * pCurTES = pCurThread->GetExceptionState();
11643 _ASSERTE(pCurTES != NULL);
11644
11645 // Get the exception tracker for the current exception
11646#ifdef WIN64EXCEPTIONS
11647 PTR_ExceptionTracker pEHTracker = pCurTES->GetCurrentExceptionTracker();
11648#elif _TARGET_X86_
11649 PTR_ExInfo pEHTracker = pCurTES->GetCurrentExceptionTracker();
11650#else // !(_WIN64 || _TARGET_X86_)
11651#error Unsupported platform
11652#endif // _WIN64
11653
11654 _ASSERTE(pEHTracker != NULL);
11655
11656 // Get the current exception code from the tracker.
11657 PEXCEPTION_RECORD pEHRecord = pCurTES->GetExceptionRecord();
11658 _ASSERTE(pEHRecord != NULL);
11659 DWORD dwActiveExceptionCode = pEHRecord->ExceptionCode;
11660
11661 if (pEHTracker->GetCorruptionSeverity() != NotSet)
11662 {
11663 // Since the exception tracker already has the corruption severity set,
11664 // we dont have much to do. Just confirm that our assumptions are correct.
11665 _ASSERTE(pEHTracker->GetCorruptionSeverity() == pCurTES->GetLastActiveExceptionCorruptionSeverity());
11666
11667 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Current tracker already has the corruption severity set.\n"));
11668 return;
11669 }
11670
11671 // If the exception in question is to be treated as non-corrupting,
11672 // then flag it and exit.
11673 if (fShouldTreatExceptionAsNonCorrupting || g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11674 {
11675 pEHTracker->SetCorruptionSeverity(NotCorrupting);
11676 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Exception treated as non-corrupting.\n"));
11677 goto done;
11678 }
11679
11680 if (!fIsRethrownException && !fIsNestedException)
11681 {
11682 // There should be no previously active exception for this case
11683 _ASSERTE(pEHTracker->GetPreviousExceptionTracker() == NULL);
11684
11685 CorruptionSeverity severityTES = NotSet;
11686
11687 if (pCurTES->ShouldLastActiveExceptionCorruptionSeverityBeReused())
11688 {
11689 // Get the corruption severity from the ThreadExceptionState (TES) for the last active exception
11690 severityTES = pCurTES->GetLastActiveExceptionCorruptionSeverity();
11691
11692 // Incase of scenarios like AD transition or Reflection invocation,
11693 // TES would hold corruption severity of the last active exception. To propagate it
11694 // to the current exception, we will apply it to current tracker and only if the applied
11695 // severity is "NotSet", will we proceed to check the current exception for corruption
11696 // severity.
11697 pEHTracker->SetCorruptionSeverity(severityTES);
11698 }
11699
11700 // Reset TES Corruption Severity
11701 pCurTES->SetLastActiveExceptionCorruptionSeverity(NotSet);
11702
11703 if (severityTES == NotSet)
11704 {
11705 // Since the last active exception's severity was "NotSet", we will look up the
11706 // exception code and the exception object to see if the exception should be marked
11707 // corrupting.
11708 //
11709 // Since this exception was neither rethrown nor is nested, it implies that we are
11710 // outside an active exception. Thus, even if it contains inner exceptions, we wont have
11711 // corruption severity for them since that information is tracked in EH tracker and
11712 // we wont have an EH tracker for the inner most exception.
11713
11714 if (CEHelper::IsProcessCorruptedStateException(dwActiveExceptionCode) ||
11715 CEHelper::IsProcessCorruptedStateException(oThrowable))
11716 {
11717 pEHTracker->SetCorruptionSeverity(ProcessCorrupting);
11718 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Marked non-rethrow/non-nested exception as ProcessCorrupting.\n"));
11719 }
11720 else
11721 {
11722 pEHTracker->SetCorruptionSeverity(NotCorrupting);
11723 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Marked non-rethrow/non-nested exception as NotCorrupting.\n"));
11724 }
11725 }
11726 else
11727 {
11728 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Copied the corruption severity to tracker from ThreadExceptionState for non-rethrow/non-nested exception.\n"));
11729 }
11730 }
11731 else
11732 {
11733 // Its either a rethrow or nested exception
11734
11735#ifdef WIN64EXCEPTIONS
11736 PTR_ExceptionTracker pOrigEHTracker = NULL;
11737#elif _TARGET_X86_
11738 PTR_ExInfo pOrigEHTracker = NULL;
11739#else
11740#error Unsupported platform
11741#endif
11742
11743 BOOL fDoWeHaveCorruptionSeverity = FALSE;
11744
11745 if (fIsRethrownException)
11746 {
11747 // Rethrown exceptions are nested by nature (of our implementation). The
11748 // original EHTracker will exist for the exception - infact, it will be
11749 // the tracker previous to the current one. We will simply copy
11750 // its severity to the current EH tracker representing the rethrow.
11751 pOrigEHTracker = pEHTracker->GetPreviousExceptionTracker();
11752 _ASSERTE(pOrigEHTracker != NULL);
11753
11754 // Ideally, we would like have the assert below enabled. But, as may happen under OOM
11755 // stress, this can be false. Here's how it will happen:
11756 //
11757 // An exception is thrown, which is later caught and rethrown in the catch block. Rethrow
11758 // results in calling IL_Rethrow that will call RaiseTheExceptionInternalOnly to actually
11759 // raise the exception. Prior to the raise, we update the last thrown object on the thread
11760 // by calling Thread::SafeSetLastThrownObject which, internally, could have an OOM, resulting
11761 // in "changing" the throwable used to raise the exception to be preallocated OOM object.
11762 //
11763 // When the rethrow happens and CLR's exception handler for managed code sees the exception,
11764 // the exception tracker created for the rethrown exception will contain the reference to
11765 // the last thrown object, which will be the preallocated OOM object.
11766 //
11767 // Thus, though, we came here because of a rethrow, and logically, the throwable should remain
11768 // the same, it neednt be. Simply put, rethrow can result in working with a completely different
11769 // exception object than what was originally thrown.
11770 //
11771 // Hence, the assert cannot be enabled.
11772 //
11773 // Thus, we will use the EH tracker corresponding to the original exception, to get the
11774 // rethrown exception's corruption severity, only when the rethrown throwable is the same
11775 // as the original throwable. Otherwise, we will pretend that we didnt get the original tracker
11776 // and will automatically enter the path below to set the corruption severity based upon the
11777 // rethrown throwable.
11778
11779 // _ASSERTE(pOrigEHTracker->GetThrowable() == oThrowable);
11780 if (pOrigEHTracker->GetThrowable() != oThrowable)
11781 {
11782 pOrigEHTracker = NULL;
11783 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Rethrown throwable does not match the original throwable. Corruption severity will be set based upon rethrown throwable.\n"));
11784 }
11785 }
11786 else
11787 {
11788 // Get the corruption severity from the ThreadExceptionState (TES) for the last active exception
11789 CorruptionSeverity severityTES = NotSet;
11790
11791 if (pCurTES->ShouldLastActiveExceptionCorruptionSeverityBeReused())
11792 {
11793 severityTES = pCurTES->GetLastActiveExceptionCorruptionSeverity();
11794
11795 // Incase of scenarios like AD transition or Reflection invocation,
11796 // TES would hold corruption severity of the last active exception. To propagate it
11797 // to the current exception, we will apply it to current tracker and only if the applied
11798 // severity is "NotSet", will we proceed to check the current exception for corruption
11799 // severity.
11800 pEHTracker->SetCorruptionSeverity(severityTES);
11801 }
11802
11803 // Reset TES Corruption Severity
11804 pCurTES->SetLastActiveExceptionCorruptionSeverity(NotSet);
11805
11806 // If the last exception didnt have any corruption severity, proceed to look for it.
11807 if (severityTES == NotSet)
11808 {
11809 // This is a nested exception - check if it has an inner exception(s). If it does,
11810 // find the EH tracker corresponding to the innermost exception and we will copy the
11811 // corruption severity from the original tracker to the current one.
11812 OBJECTREF oInnermostThrowable = ((EXCEPTIONREF)oThrowable)->GetBaseException();
11813 if (oInnermostThrowable != NULL)
11814 {
11815 // Find the tracker corresponding to the inner most exception, starting from
11816 // the tracker previous to the current one. An EH tracker may not be found if
11817 // the code did the following inside a catch clause:
11818 //
11819 // Exception ex = new Exception("inner exception");
11820 // throw new Exception("message", ex);
11821 //
11822 // Or, an exception like AV happened in the catch clause.
11823 pOrigEHTracker = GetEHTrackerForException(oInnermostThrowable, pEHTracker->GetPreviousExceptionTracker());
11824 }
11825 }
11826 else
11827 {
11828 // We have the corruption severity from the TES. Set the flag indicating so.
11829 fDoWeHaveCorruptionSeverity = TRUE;
11830 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Copied the corruption severity to tracker from ThreadExceptionState for nested exception.\n"));
11831 }
11832 }
11833
11834 if (!fDoWeHaveCorruptionSeverity)
11835 {
11836 if (pOrigEHTracker != NULL)
11837 {
11838 // Copy the severity from the original EH tracker to the current one
11839 CorruptionSeverity origCorruptionSeverity = pOrigEHTracker->GetCorruptionSeverity();
11840 _ASSERTE(origCorruptionSeverity != NotSet);
11841 pEHTracker->SetCorruptionSeverity(origCorruptionSeverity);
11842
11843 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Copied the corruption severity (%d) from the original EH tracker for rethrown exception.\n", origCorruptionSeverity));
11844 }
11845 else
11846 {
11847 if (CEHelper::IsProcessCorruptedStateException(dwActiveExceptionCode) ||
11848 CEHelper::IsProcessCorruptedStateException(oThrowable))
11849 {
11850 pEHTracker->SetCorruptionSeverity(ProcessCorrupting);
11851 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Marked nested exception as ProcessCorrupting.\n"));
11852 }
11853 else
11854 {
11855 pEHTracker->SetCorruptionSeverity(NotCorrupting);
11856 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Marked nested exception as NotCorrupting.\n"));
11857 }
11858 }
11859 }
11860 }
11861
11862done:
11863 // Save the current exception's corruption severity in the ThreadExceptionState (TES)
11864 // for cases when we catch the managed exception in the runtime using EX_CATCH.
11865 // At such a time, all exception trackers get released (due to unwind triggered
11866 // by EX_END_CATCH) and yet we need the corruption severity information for
11867 // scenarios like AD Transition, Reflection invocation, etc.
11868 CorruptionSeverity currentSeverity = pEHTracker->GetCorruptionSeverity();
11869
11870 // We should be having a valid corruption severity at this point
11871 _ASSERTE(currentSeverity != NotSet);
11872
11873 // Save it in the TES
11874 pCurTES->SetLastActiveExceptionCorruptionSeverity(currentSeverity);
11875 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Copied the corruption severity (%d) to ThreadExceptionState.\n", currentSeverity));
11876
11877#endif // !DACCESS_COMPILE
11878}
11879
11880// CE can be caught in the VM and later reraised again. Examples of such scenarios
11881// include AD transition, COM interop, Reflection invocation, to name a few.
11882// In such cases, we want to mark the corruption severity for reuse upon reraise,
11883// implying that when the VM does a reraise of such an exception, we should use
11884// the original corruption severity for the new raised exception, instead of creating
11885// a new one for it.
11886/* static */
11887void CEHelper::MarkLastActiveExceptionCorruptionSeverityForReraiseReuse()
11888{
11889 CONTRACTL
11890 {
11891 NOTHROW;
11892 GC_NOTRIGGER;
11893 MODE_ANY;
11894 SO_TOLERANT;
11895 PRECONDITION(GetThread() != NULL);
11896 }
11897 CONTRACTL_END;
11898
11899 // If the last active exception's corruption severity is anything but
11900 // "NotSet", mark it for ReraiseReuse
11901 ThreadExceptionState *pCurTES = GetThread()->GetExceptionState();
11902 _ASSERTE(pCurTES != NULL);
11903
11904 CorruptionSeverity severityTES = pCurTES->GetLastActiveExceptionCorruptionSeverity();
11905 if (severityTES != NotSet)
11906 {
11907 pCurTES->SetLastActiveExceptionCorruptionSeverity((CorruptionSeverity)(severityTES | ReuseForReraise));
11908 }
11909}
11910
11911// This method will return a BOOL to indicate if the current exception is to be treated as
11912// non-corrupting. Currently, this returns true for NullReferenceException only.
11913/* static */
11914BOOL CEHelper::ShouldTreatActiveExceptionAsNonCorrupting()
11915{
11916 BOOL fShouldTreatAsNonCorrupting = FALSE;
11917
11918#ifndef DACCESS_COMPILE
11919 CONTRACTL
11920 {
11921 THROWS;
11922 GC_TRIGGERS;
11923 MODE_COOPERATIVE;
11924 PRECONDITION(GetThread() != NULL);
11925 }
11926 CONTRACTL_END;
11927
11928 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11929 {
11930 return TRUE;
11931 }
11932
11933 DWORD dwActiveExceptionCode = GetThread()->GetExceptionState()->GetExceptionRecord()->ExceptionCode;
11934 if (dwActiveExceptionCode == STATUS_ACCESS_VIOLATION)
11935 {
11936 // NullReference has the same exception code as AV
11937 OBJECTREF oThrowable = NULL;
11938 GCPROTECT_BEGIN(oThrowable);
11939
11940 // Get the throwable and check if it represents null reference exception
11941 oThrowable = GetThread()->GetThrowable();
11942 _ASSERTE(oThrowable != NULL);
11943 if (MscorlibBinder::GetException(kNullReferenceException) == oThrowable->GetMethodTable())
11944 {
11945 fShouldTreatAsNonCorrupting = TRUE;
11946 }
11947 GCPROTECT_END();
11948 }
11949#endif // !DACCESS_COMPILE
11950
11951 return fShouldTreatAsNonCorrupting;
11952}
11953
11954// If we were working in a nested exception scenario, reset the corruption severity to the last
11955// exception we were processing, based upon its EH tracker.
11956//
11957// If none was present, reset it to NotSet.
11958//
11959// Note: This method must be called once the exception trackers have been adjusted post catch-block execution.
11960/* static */
11961void CEHelper::ResetLastActiveCorruptionSeverityPostCatchHandler(Thread *pThread)
11962{
11963 CONTRACTL
11964 {
11965 NOTHROW;
11966 GC_NOTRIGGER;
11967 MODE_ANY;
11968 PRECONDITION(pThread != NULL);
11969 }
11970 CONTRACTL_END;
11971
11972 ThreadExceptionState *pCurTES = pThread->GetExceptionState();
11973
11974 // By this time, we would have set the correct exception tracker for the active exception domain,
11975 // if applicable. An example is throwing and catching an exception within a catch block. We will update
11976 // the LastActiveCorruptionSeverity based upon the active exception domain. If we are not in one, we will
11977 // set it to "NotSet".
11978#ifdef WIN64EXCEPTIONS
11979 PTR_ExceptionTracker pEHTracker = pCurTES->GetCurrentExceptionTracker();
11980#elif _TARGET_X86_
11981 PTR_ExInfo pEHTracker = pCurTES->GetCurrentExceptionTracker();
11982#else
11983#error Unsupported platform
11984#endif
11985
11986 if (pEHTracker)
11987 {
11988 pCurTES->SetLastActiveExceptionCorruptionSeverity(pEHTracker->GetCorruptionSeverity());
11989 }
11990 else
11991 {
11992 pCurTES->SetLastActiveExceptionCorruptionSeverity(NotSet);
11993 }
11994
11995 LOG((LF_EH, LL_INFO100, "CEHelper::ResetLastActiveCorruptionSeverityPostCatchHandler - Reset LastActiveException corruption severity to %d.\n",
11996 pCurTES->GetLastActiveExceptionCorruptionSeverity()));
11997}
11998
11999// This method will return a BOOL indicating if the target of IDispatch can handle the specified exception or not.
12000/* static */
12001BOOL CEHelper::CanIDispatchTargetHandleException()
12002{
12003 CONTRACTL
12004 {
12005 NOTHROW;
12006 GC_NOTRIGGER;
12007 MODE_ANY;
12008 PRECONDITION(GetThread() != NULL);
12009 }
12010 CONTRACTL_END;
12011
12012 // By default, assume that the target of IDispatch cannot handle the exception.
12013 BOOL fCanMethodHandleException = FALSE;
12014
12015 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
12016 {
12017 return TRUE;
12018 }
12019
12020 // IDispatch implementation in COM interop works by invoking the actual target via reflection.
12021 // Thus, a COM client could use the V4 runtime to invoke a V2 method. In such a case, a CSE
12022 // could come unhandled at the actual target invoked via reflection.
12023 //
12024 // Reflection invocation would have set a flag for us, indicating if the actual target was
12025 // enabled to handle the CE or not. If it is, then we should allow the COM client to get the
12026 // hresult from the call and not let the exception continue up the stack.
12027 ThreadExceptionState *pCurTES = GetThread()->GetExceptionState();
12028 fCanMethodHandleException = pCurTES->CanReflectionTargetHandleException();
12029
12030 // Reset the flag so that subsequent invocations work as expected.
12031 pCurTES->SetCanReflectionTargetHandleException(FALSE);
12032
12033 return fCanMethodHandleException;
12034}
12035
12036#endif // FEATURE_CORRUPTING_EXCEPTIONS
12037
12038#ifndef DACCESS_COMPILE
12039// This method will deliver the actual exception notification. Its assumed that the caller has done the necessary checks, including
12040// checking whether the delegate can be invoked for the exception's corruption severity.
12041void ExceptionNotifications::DeliverExceptionNotification(ExceptionNotificationHandlerType notificationType, OBJECTREF *pDelegate,
12042 OBJECTREF *pAppDomain, OBJECTREF *pEventArgs)
12043{
12044 CONTRACTL
12045 {
12046 THROWS;
12047 GC_TRIGGERS;
12048 MODE_COOPERATIVE;
12049 PRECONDITION(pDelegate != NULL && IsProtectedByGCFrame(pDelegate) && (*pDelegate != NULL));
12050 PRECONDITION(pEventArgs != NULL && IsProtectedByGCFrame(pEventArgs));
12051 PRECONDITION(pAppDomain != NULL && IsProtectedByGCFrame(pAppDomain));
12052 }
12053 CONTRACTL_END;
12054
12055 PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(DELEGATEREF(*pDelegate)->GetMethodPtr());
12056
12057 DECLARE_ARGHOLDER_ARRAY(args, 3);
12058
12059 args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(DELEGATEREF(*pDelegate)->GetTarget());
12060 args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(*pAppDomain);
12061 args[ARGNUM_2] = OBJECTREF_TO_ARGHOLDER(*pEventArgs);
12062
12063 CALL_MANAGED_METHOD_NORET(args);
12064}
12065
12066// To include definition of COMDelegate::GetMethodDesc
12067#include "comdelegate.h"
12068
12069// This method constructs the arguments to be passed to the exception notification event callback
12070void ExceptionNotifications::GetEventArgsForNotification(ExceptionNotificationHandlerType notificationType,
12071 OBJECTREF *pOutEventArgs, OBJECTREF *pThrowable)
12072{
12073 CONTRACTL
12074 {
12075 THROWS;
12076 GC_TRIGGERS;
12077 MODE_COOPERATIVE;
12078 PRECONDITION(notificationType != UnhandledExceptionHandler);
12079 PRECONDITION((pOutEventArgs != NULL) && IsProtectedByGCFrame(pOutEventArgs));
12080 PRECONDITION(*pOutEventArgs == NULL);
12081 PRECONDITION((pThrowable != NULL) && (*pThrowable != NULL) && IsProtectedByGCFrame(pThrowable));
12082 PRECONDITION(IsException((*pThrowable)->GetMethodTable())); // We expect a valid exception object
12083 }
12084 CONTRACTL_END;
12085
12086 MethodTable *pMTEventArgs = NULL;
12087 BinderMethodID idEventArgsCtor = METHOD__FIRSTCHANCE_EVENTARGS__CTOR;
12088
12089 EX_TRY
12090 {
12091 switch(notificationType)
12092 {
12093 case FirstChanceExceptionHandler:
12094 pMTEventArgs = MscorlibBinder::GetClass(CLASS__FIRSTCHANCE_EVENTARGS);
12095 idEventArgsCtor = METHOD__FIRSTCHANCE_EVENTARGS__CTOR;
12096 break;
12097 default:
12098 _ASSERTE(!"Invalid Exception Notification Handler!");
12099 break;
12100 }
12101
12102 // Allocate the instance of the eventargs corresponding to the notification
12103 *pOutEventArgs = AllocateObject(pMTEventArgs);
12104
12105 // Prepare to invoke the .ctor
12106 MethodDescCallSite ctor(idEventArgsCtor, pOutEventArgs);
12107
12108 // Setup the arguments to be passed to the notification specific EventArgs .ctor
12109 if (notificationType == FirstChanceExceptionHandler)
12110 {
12111 // FirstChance notification takes only a single argument: the exception object.
12112 ARG_SLOT args[] =
12113 {
12114 ObjToArgSlot(*pOutEventArgs),
12115 ObjToArgSlot(*pThrowable),
12116 };
12117
12118 ctor.Call(args);
12119 }
12120 else
12121 {
12122 // Since we have already asserted above, just set the args to NULL.
12123 *pOutEventArgs = NULL;
12124 }
12125 }
12126 EX_CATCH
12127 {
12128 // Set event args to be NULL incase of any error (e.g. OOM)
12129 *pOutEventArgs = NULL;
12130 LOG((LF_EH, LL_INFO100, "ExceptionNotifications::GetEventArgsForNotification: Setting event args to NULL due to an exception.\n"));
12131 }
12132 EX_END_CATCH(RethrowCorruptingExceptions); // Dont swallow any CSE that may come in from the .ctor.
12133}
12134
12135// This SEH filter will be invoked when an exception escapes out of the exception notification
12136// callback and enters the runtime. In such a case, we ill simply failfast.
12137static LONG ExceptionNotificationFilter(PEXCEPTION_POINTERS pExceptionInfo, LPVOID pParam)
12138{
12139 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
12140}
12141
12142#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12143// This method will return a BOOL indicating if the delegate should be invoked for the exception
12144// of the specified corruption severity.
12145BOOL ExceptionNotifications::CanDelegateBeInvokedForException(OBJECTREF *pDelegate, CorruptionSeverity severity)
12146{
12147 CONTRACTL
12148 {
12149 THROWS;
12150 GC_TRIGGERS;
12151 MODE_COOPERATIVE;
12152 PRECONDITION(pDelegate != NULL && IsProtectedByGCFrame(pDelegate) && (*pDelegate != NULL));
12153 PRECONDITION(severity > NotSet);
12154 }
12155 CONTRACTL_END;
12156
12157 // Notifications for CSE are only delivered if the delegate target follows CSE rules.
12158 BOOL fCanMethodHandleException = g_pConfig->LegacyCorruptedStateExceptionsPolicy() ? TRUE:(severity == NotCorrupting);
12159 if (!fCanMethodHandleException)
12160 {
12161 EX_TRY
12162 {
12163 // Get the MethodDesc of the delegate to be invoked
12164 MethodDesc *pMDDelegate = COMDelegate::GetMethodDesc(*pDelegate);
12165 _ASSERTE(pMDDelegate != NULL);
12166
12167 // Check the callback target and see if it is following CSE rules or not.
12168 fCanMethodHandleException = CEHelper::CanMethodHandleException(severity, pMDDelegate);
12169 }
12170 EX_CATCH
12171 {
12172 // Incase of any exceptions, pretend we cannot handle the exception
12173 fCanMethodHandleException = FALSE;
12174 LOG((LF_EH, LL_INFO100, "ExceptionNotifications::CanDelegateBeInvokedForException: Exception while trying to determine if exception notification can be invoked or not.\n"));
12175 }
12176 EX_END_CATCH(RethrowCorruptingExceptions); // Dont swallow any CSEs.
12177 }
12178
12179 return fCanMethodHandleException;
12180}
12181#endif // FEATURE_CORRUPTING_EXCEPTIONS
12182
12183// This method will make the actual delegate invocation for the exception notification to be delivered. If an
12184// exception escapes out of the notification, our filter in ExceptionNotifications::DeliverNotification will
12185// address it.
12186void ExceptionNotifications::InvokeNotificationDelegate(ExceptionNotificationHandlerType notificationType, OBJECTREF *pDelegate, OBJECTREF *pEventArgs,
12187 OBJECTREF *pAppDomain
12188#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12189 , CorruptionSeverity severity
12190#endif // FEATURE_CORRUPTING_EXCEPTIONS
12191 )
12192{
12193 CONTRACTL
12194 {
12195 THROWS;
12196 GC_TRIGGERS;
12197 MODE_COOPERATIVE;
12198 PRECONDITION(pDelegate != NULL && IsProtectedByGCFrame(pDelegate) && (*pDelegate != NULL));
12199 PRECONDITION(pEventArgs != NULL && IsProtectedByGCFrame(pEventArgs));
12200 PRECONDITION(pAppDomain != NULL && IsProtectedByGCFrame(pAppDomain));
12201#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12202 PRECONDITION(severity > NotSet);
12203#endif // FEATURE_CORRUPTING_EXCEPTIONS
12204 // Unhandled Exception Notification is delivered via Unhandled Exception Processing
12205 // mechanism.
12206 PRECONDITION(notificationType != UnhandledExceptionHandler);
12207 }
12208 CONTRACTL_END;
12209
12210#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12211 // Notifications are delivered based upon corruption severity of the exception
12212 if (!ExceptionNotifications::CanDelegateBeInvokedForException(pDelegate, severity))
12213 {
12214 LOG((LF_EH, LL_INFO100, "ExceptionNotifications::InvokeNotificationDelegate: Delegate cannot be invoked for corruption severity %d\n",
12215 severity));
12216 return;
12217 }
12218#endif // FEATURE_CORRUPTING_EXCEPTIONS
12219
12220 // We've already exercised the prestub on this delegate's COMDelegate::GetMethodDesc,
12221 // as part of wiring up a reliable event sink in the BCL. Deliver the notification.
12222 ExceptionNotifications::DeliverExceptionNotification(notificationType, pDelegate, pAppDomain, pEventArgs);
12223}
12224
12225// This method returns a BOOL to indicate if the AppDomain is ready to receive exception notifications or not.
12226BOOL ExceptionNotifications::CanDeliverNotificationToCurrentAppDomain(ExceptionNotificationHandlerType notificationType)
12227{
12228 CONTRACTL
12229 {
12230 THROWS;
12231 GC_TRIGGERS;
12232 MODE_COOPERATIVE;
12233 PRECONDITION(GetThread() != NULL);
12234 PRECONDITION(notificationType != UnhandledExceptionHandler);
12235 }
12236 CONTRACTL_END;
12237
12238 // Do we have handler(s) of the specific type wired up?
12239 if (notificationType == FirstChanceExceptionHandler)
12240 {
12241 return MscorlibBinder::GetField(FIELD__APPCONTEXT__FIRST_CHANCE_EXCEPTION)->GetStaticOBJECTREF() != NULL;
12242 }
12243 else
12244 {
12245 _ASSERTE(!"Invalid exception notification handler specified!");
12246 return FALSE;
12247 }
12248}
12249
12250// This method wraps the call to the actual 'DeliverNotificationInternal' method in an SEH filter
12251// so that if an exception escapes out of the notification callback, we will trigger failfast from
12252// our filter.
12253void ExceptionNotifications::DeliverNotification(ExceptionNotificationHandlerType notificationType,
12254 OBJECTREF *pThrowable
12255#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12256 , CorruptionSeverity severity
12257#endif // FEATURE_CORRUPTING_EXCEPTIONS
12258 )
12259{
12260 STATIC_CONTRACT_GC_TRIGGERS;
12261 STATIC_CONTRACT_NOTHROW; // NOTHROW because incase of an exception, we will FailFast.
12262 STATIC_CONTRACT_MODE_COOPERATIVE;
12263
12264 struct TryArgs
12265 {
12266 ExceptionNotificationHandlerType notificationType;
12267 OBJECTREF *pThrowable;
12268#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12269 CorruptionSeverity severity;
12270#endif // FEATURE_CORRUPTING_EXCEPTIONS
12271 } args;
12272
12273 args.notificationType = notificationType;
12274 args.pThrowable = pThrowable;
12275#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12276 args.severity = severity;
12277#endif // FEATURE_CORRUPTING_EXCEPTIONS
12278
12279 PAL_TRY(TryArgs *, pArgs, &args)
12280 {
12281 // Make the call to the actual method that will invoke the callbacks
12282 ExceptionNotifications::DeliverNotificationInternal(pArgs->notificationType,
12283 pArgs->pThrowable
12284#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12285 , pArgs->severity
12286#endif // FEATURE_CORRUPTING_EXCEPTIONS
12287 );
12288 }
12289 PAL_EXCEPT_FILTER(ExceptionNotificationFilter)
12290 {
12291 // We should never be entering this handler since there should be
12292 // no exception escaping out of a callback. If we are here,
12293 // failfast.
12294 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
12295 }
12296 PAL_ENDTRY;
12297}
12298
12299// This method will deliver the exception notification to the current AppDomain.
12300void ExceptionNotifications::DeliverNotificationInternal(ExceptionNotificationHandlerType notificationType,
12301 OBJECTREF *pThrowable
12302#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12303 , CorruptionSeverity severity
12304#endif // FEATURE_CORRUPTING_EXCEPTIONS
12305 )
12306{
12307 CONTRACTL
12308 {
12309 THROWS;
12310 GC_TRIGGERS;
12311 MODE_COOPERATIVE;
12312
12313 // Unhandled Exception Notification is delivered via Unhandled Exception Processing
12314 // mechanism.
12315 PRECONDITION(notificationType != UnhandledExceptionHandler);
12316 PRECONDITION((pThrowable != NULL) && (*pThrowable != NULL));
12317 PRECONDITION(ExceptionNotifications::CanDeliverNotificationToCurrentAppDomain(notificationType));
12318#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12319 PRECONDITION(severity > NotSet); // Exception corruption severity must be valid at this point.
12320#endif // FEATURE_CORRUPTING_EXCEPTIONS
12321 }
12322 CONTRACTL_END;
12323
12324 Thread *pCurThread = GetThread();
12325 _ASSERTE(pCurThread != NULL);
12326
12327 // Get the current AppDomain
12328 AppDomain *pCurDomain = GetAppDomain();
12329 _ASSERTE(pCurDomain != NULL);
12330
12331 struct
12332 {
12333 OBJECTREF oNotificationDelegate;
12334 PTRARRAYREF arrDelegates;
12335 OBJECTREF oInnerDelegate;
12336 OBJECTREF oEventArgs;
12337 OBJECTREF oCurrentThrowable;
12338 OBJECTREF oCurAppDomain;
12339 } gc;
12340 ZeroMemory(&gc, sizeof(gc));
12341
12342 // This will hold the MethodDesc of the callback that will be invoked.
12343 MethodDesc *pMDDelegate = NULL;
12344
12345 GCPROTECT_BEGIN(gc);
12346
12347 // Protect the throwable to be passed to the delegate callback
12348 gc.oCurrentThrowable = *pThrowable;
12349
12350 // We expect a valid exception object
12351 _ASSERTE(IsException(gc.oCurrentThrowable->GetMethodTable()));
12352
12353 // Save the reference to the current AppDomain. If the user code has
12354 // wired upto this event, then the managed AppDomain object will exist.
12355 gc.oCurAppDomain = pCurDomain->GetRawExposedObject();
12356
12357 // Get the reference to the delegate based upon the type of notification
12358 if (notificationType == FirstChanceExceptionHandler)
12359 {
12360 gc.oNotificationDelegate = MscorlibBinder::GetField(FIELD__APPCONTEXT__FIRST_CHANCE_EXCEPTION)->GetStaticOBJECTREF();
12361 }
12362 else
12363 {
12364 gc.oNotificationDelegate = NULL;
12365 _ASSERTE(!"Invalid Exception Notification Handler specified!");
12366 }
12367
12368 if (gc.oNotificationDelegate != NULL)
12369 {
12370 // Prevent any async exceptions from this moment on this thread
12371 ThreadPreventAsyncHolder prevAsync;
12372
12373 gc.oEventArgs = NULL;
12374
12375 // Get the arguments to be passed to the delegate callback. Incase of any
12376 // problem while allocating the event args, we will return a NULL.
12377 ExceptionNotifications::GetEventArgsForNotification(notificationType, &gc.oEventArgs,
12378 &gc.oCurrentThrowable);
12379
12380 // Check if there are multiple callbacks registered? If there are, we will
12381 // loop through them, invoking each one at a time. Before invoking the target,
12382 // we will check if the target can be invoked based upon the corruption severity
12383 // for the active exception that was passed to us.
12384 gc.arrDelegates = (PTRARRAYREF) ((DELEGATEREF)(gc.oNotificationDelegate))->GetInvocationList();
12385 if (gc.arrDelegates == NULL || !gc.arrDelegates->GetMethodTable()->IsArray())
12386 {
12387 ExceptionNotifications::InvokeNotificationDelegate(notificationType, &gc.oNotificationDelegate, &gc.oEventArgs,
12388 &gc.oCurAppDomain
12389#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12390 , severity
12391#endif // FEATURE_CORRUPTING_EXCEPTIONS
12392 );
12393 }
12394 else
12395 {
12396 // The _invocationCount could be less than the array size, if we are sharing
12397 // immutable arrays cleverly.
12398 UINT_PTR cnt = ((DELEGATEREF)(gc.oNotificationDelegate))->GetInvocationCount();
12399 _ASSERTE(cnt <= gc.arrDelegates->GetNumComponents());
12400
12401 for (UINT_PTR i=0; i<cnt; i++)
12402 {
12403 gc.oInnerDelegate = gc.arrDelegates->m_Array[i];
12404 ExceptionNotifications::InvokeNotificationDelegate(notificationType, &gc.oInnerDelegate, &gc.oEventArgs,
12405 &gc.oCurAppDomain
12406#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12407 , severity
12408#endif // FEATURE_CORRUPTING_EXCEPTIONS
12409 );
12410 }
12411 }
12412 }
12413
12414 GCPROTECT_END();
12415}
12416
12417void ExceptionNotifications::DeliverFirstChanceNotification()
12418{
12419 CONTRACTL
12420 {
12421 THROWS;
12422 GC_TRIGGERS;
12423 MODE_ANY;
12424 }
12425 CONTRACTL_END;
12426
12427 // We check for FirstChance notification delivery after setting up the corruption severity
12428 // so that we can determine if the callback delegate can handle CSE (or not).
12429 //
12430 // Deliver it only if not already done and someone has wiredup to receive it.
12431 //
12432 // We do this provided this is the first frame of a new exception
12433 // that was thrown or a rethrown exception. We dont want to do this
12434 // processing for subsequent frames on the stack since FirstChance notification
12435 // will be delivered only when the exception is first thrown/rethrown.
12436 ThreadExceptionState *pCurTES = GetThread()->GetExceptionState();
12437 _ASSERTE(pCurTES->GetCurrentExceptionTracker());
12438 _ASSERTE(!(pCurTES->GetCurrentExceptionTracker()->DeliveredFirstChanceNotification()));
12439 {
12440 GCX_COOP();
12441 if (ExceptionNotifications::CanDeliverNotificationToCurrentAppDomain(FirstChanceExceptionHandler))
12442 {
12443 OBJECTREF oThrowable = NULL;
12444 GCPROTECT_BEGIN(oThrowable);
12445
12446 oThrowable = pCurTES->GetThrowable();
12447 _ASSERTE(oThrowable != NULL);
12448
12449 ExceptionNotifications::DeliverNotification(FirstChanceExceptionHandler, &oThrowable
12450#ifdef FEATURE_CORRUPTING_EXCEPTIONS
12451 , pCurTES->GetCurrentExceptionTracker()->GetCorruptionSeverity()
12452#endif // FEATURE_CORRUPTING_EXCEPTIONS
12453 );
12454 GCPROTECT_END();
12455
12456 }
12457
12458 // Mark the exception tracker as having delivered the first chance notification
12459 pCurTES->GetCurrentExceptionTracker()->SetFirstChanceNotificationStatus(TRUE);
12460 }
12461}
12462
12463
12464#ifdef WIN64EXCEPTIONS
12465struct TAResetStateCallbackData
12466{
12467 // Do we have more managed code up the stack?
12468 BOOL fDoWeHaveMoreManagedCodeOnStack;
12469
12470 // StackFrame representing the crawlFrame above which
12471 // we are searching for presence of managed code.
12472 StackFrame sfSeedCrawlFrame;
12473};
12474
12475// This callback helps the 64bit EH attempt to determine if there is more managed code
12476// up the stack (or not). Currently, it is used to conditionally reset the thread abort state
12477// as the unwind passes by.
12478StackWalkAction TAResetStateCallback(CrawlFrame* pCf, void* data)
12479{
12480 CONTRACTL {
12481 NOTHROW;
12482 GC_NOTRIGGER;
12483 }
12484 CONTRACTL_END;
12485
12486 TAResetStateCallbackData *pTAResetStateCallbackData = static_cast<TAResetStateCallbackData *>(data);
12487 StackWalkAction retStatus = SWA_CONTINUE;
12488
12489 if(pCf->IsFrameless())
12490 {
12491 IJitManager* pJitManager = pCf->GetJitManager();
12492 _ASSERTE(pJitManager);
12493 if (pJitManager && (!pTAResetStateCallbackData->fDoWeHaveMoreManagedCodeOnStack))
12494 {
12495 // The stackwalker can give us a callback for the seeding CrawlFrame (or other crawlframes)
12496 // depending upon which is closer to the leaf: the seeding crawlframe or the explicit frame
12497 // specified when starting the stackwalk.
12498 //
12499 // Since we are interested in checking if there is more managed code up the stack from
12500 // the seeding crawlframe, we check if the current crawlframe is above it or not. If it is,
12501 // then we have found managed code up the stack and should stop the stack walk. Otherwise,
12502 // continue searching.
12503 StackFrame sfCurrentFrame = StackFrame::FromRegDisplay(pCf->GetRegisterSet());
12504 if (pTAResetStateCallbackData->sfSeedCrawlFrame < sfCurrentFrame)
12505 {
12506 // We have found managed code on the stack. Flag it and stop the stackwalk.
12507 pTAResetStateCallbackData->fDoWeHaveMoreManagedCodeOnStack = TRUE;
12508 retStatus = SWA_ABORT;
12509 }
12510 }
12511 }
12512
12513 return retStatus;
12514}
12515#endif // WIN64EXCEPTIONS
12516
12517// This function will reset the thread abort state against the specified thread if it is determined that
12518// there is no more managed code on the stack.
12519//
12520// Note: This function should be invoked ONLY during unwind.
12521#ifndef WIN64EXCEPTIONS
12522void ResetThreadAbortState(PTR_Thread pThread, void *pEstablisherFrame)
12523#else
12524void ResetThreadAbortState(PTR_Thread pThread, CrawlFrame *pCf, StackFrame sfCurrentStackFrame)
12525#endif
12526{
12527 CONTRACTL
12528 {
12529 NOTHROW;
12530 GC_NOTRIGGER;
12531 MODE_ANY;
12532 PRECONDITION(pThread != NULL);
12533#ifndef WIN64EXCEPTIONS
12534 PRECONDITION(pEstablisherFrame != NULL);
12535#else
12536 PRECONDITION(pCf != NULL);
12537 PRECONDITION(!sfCurrentStackFrame.IsNull());
12538#endif
12539 }
12540 CONTRACTL_END;
12541
12542 BOOL fResetThreadAbortState = FALSE;
12543
12544 if (pThread->IsAbortRequested())
12545 {
12546#ifndef WIN64EXCEPTIONS
12547 if (GetNextCOMPlusSEHRecord(static_cast<EXCEPTION_REGISTRATION_RECORD *>(pEstablisherFrame)) == EXCEPTION_CHAIN_END)
12548 {
12549 // Topmost handler and abort requested.
12550 fResetThreadAbortState = TRUE;
12551 LOG((LF_EH, LL_INFO100, "ResetThreadAbortState: Topmost handler resets abort as no more managed code beyond %p.\n", pEstablisherFrame));
12552 }
12553#else // !WIN64EXCEPTIONS
12554 // Get the active exception tracker
12555 PTR_ExceptionTracker pCurEHTracker = pThread->GetExceptionState()->GetCurrentExceptionTracker();
12556 _ASSERTE(pCurEHTracker != NULL);
12557
12558 // We will check if thread abort state needs to be reset only for the case of exception caught in
12559 // native code. This will happen when:
12560 //
12561 // 1) an unwind is triggered and
12562 // 2) current frame is the topmost frame we saw in the first pass and
12563 // 3) a thread abort is requested and
12564 // 4) we dont have address of the exception handler to be invoked.
12565 //
12566 // (1), (2) and (4) above are checked for in ExceptionTracker::ProcessOSExceptionNotification from where we call this
12567 // function.
12568
12569 // Current frame should be the topmost frame we saw in the first pass
12570 _ASSERTE(pCurEHTracker->GetTopmostStackFrameFromFirstPass() == sfCurrentStackFrame);
12571
12572 // If the exception has been caught in native code, then alongwith not having address of the handler to be
12573 // invoked, we also wont have the IL clause for the catch block and resume stack frame will be NULL as well.
12574 _ASSERTE((pCurEHTracker->GetCatchToCallPC() == NULL) &&
12575 (pCurEHTracker->GetCatchHandlerExceptionClauseToken() == NULL) &&
12576 (pCurEHTracker->GetResumeStackFrame().IsNull()));
12577
12578 // Walk the frame chain to see if there is any more managed code on the stack. If not, then this is the last managed frame
12579 // on the stack and we can reset the thread abort state.
12580 //
12581 // Get the frame from which to start the stack walk from
12582 Frame* pFrame = pCurEHTracker->GetLimitFrame();
12583
12584 // At this point, we are at the topmost frame we saw during the first pass
12585 // before the unwind began. Walk the stack using the specified crawlframe and the topmost
12586 // explicit frame to determine if we have more managed code up the stack. If none is found,
12587 // we can reset the thread abort state.
12588
12589 // Setup the data structure to be passed to the callback
12590 TAResetStateCallbackData dataCallback;
12591 dataCallback.fDoWeHaveMoreManagedCodeOnStack = FALSE;
12592
12593 // At this point, the StackFrame in CrawlFrame should represent the current frame we have been called for.
12594 // _ASSERTE(sfCurrentStackFrame == StackFrame::FromRegDisplay(pCf->GetRegisterSet()));
12595
12596 // Reference to the StackFrame beyond which we are looking for managed code.
12597 dataCallback.sfSeedCrawlFrame = sfCurrentStackFrame;
12598
12599 pThread->StackWalkFramesEx(pCf->GetRegisterSet(), TAResetStateCallback, &dataCallback, QUICKUNWIND, pFrame);
12600
12601 if (!dataCallback.fDoWeHaveMoreManagedCodeOnStack)
12602 {
12603 // There is no more managed code on the stack, so reset the thread abort state.
12604 fResetThreadAbortState = TRUE;
12605 LOG((LF_EH, LL_INFO100, "ResetThreadAbortState: Resetting thread abort state since there is no more managed code beyond stack frames:\n"));
12606 LOG((LF_EH, LL_INFO100, "sf.SP = %p ", dataCallback.sfSeedCrawlFrame.SP));
12607 }
12608#endif // !WIN64EXCEPTIONS
12609 }
12610
12611 if (fResetThreadAbortState)
12612 {
12613 pThread->EEResetAbort(Thread::TAR_Thread);
12614 }
12615}
12616#endif // !DACCESS_COMPILE
12617
12618#endif // !CROSSGEN_COMPILE
12619
12620//---------------------------------------------------------------------------------
12621//
12622//
12623// EXCEPTION THROWING HELPERS
12624//
12625//
12626//---------------------------------------------------------------------------------
12627
12628//---------------------------------------------------------------------------------
12629// Funnel-worker for THROW_BAD_FORMAT and friends.
12630//
12631// Note: The "cond" argument is there to tide us over during the transition from
12632// BAD_FORMAT_ASSERT to THROW_BAD_FORMAT. It will go away soon.
12633//---------------------------------------------------------------------------------
12634VOID ThrowBadFormatWorker(UINT resID, LPCWSTR imageName DEBUGARG(__in_z const char *cond))
12635{
12636 CONTRACTL
12637 {
12638 THROWS;
12639 GC_TRIGGERS;
12640 INJECT_FAULT(COMPlusThrowOM(););
12641 SUPPORTS_DAC;
12642 }
12643 CONTRACTL_END
12644
12645#ifndef DACCESS_COMPILE
12646 SString msgStr;
12647
12648 if ((imageName != NULL) && (imageName[0] != 0))
12649 {
12650 msgStr += W("[");
12651 msgStr += imageName;
12652 msgStr += W("] ");
12653 }
12654
12655 SString resStr;
12656 if (resID == 0 || !resStr.LoadResource(CCompRC::Optional, resID))
12657 {
12658 resStr.LoadResource(CCompRC::Error, MSG_FOR_URT_HR(COR_E_BADIMAGEFORMAT));
12659 }
12660 msgStr += resStr;
12661
12662#ifdef _DEBUG
12663 if (0 != strcmp(cond, "FALSE"))
12664 {
12665 msgStr += W(" (Failed condition: "); // this is in DEBUG only - not going to localize it.
12666 SString condStr(SString::Ascii, cond);
12667 msgStr += condStr;
12668 msgStr += W(")");
12669 }
12670#endif
12671
12672 ThrowHR(COR_E_BADIMAGEFORMAT, msgStr);
12673#endif // #ifndef DACCESS_COMPILE
12674}
12675
12676UINT GetResourceIDForFileLoadExceptionHR(HRESULT hr)
12677{
12678 switch (hr) {
12679
12680 case CTL_E_FILENOTFOUND:
12681 hr = IDS_EE_FILE_NOT_FOUND;
12682 break;
12683
12684 case (HRESULT)IDS_EE_PROC_NOT_FOUND:
12685 case (HRESULT)IDS_EE_PATH_TOO_LONG:
12686 case INET_E_OBJECT_NOT_FOUND:
12687 case INET_E_DATA_NOT_AVAILABLE:
12688 case INET_E_DOWNLOAD_FAILURE:
12689 case INET_E_UNKNOWN_PROTOCOL:
12690 case (HRESULT)IDS_INET_E_SECURITY_PROBLEM:
12691 case (HRESULT)IDS_EE_BAD_USER_PROFILE:
12692 case (HRESULT)IDS_EE_ALREADY_EXISTS:
12693 case IDS_CLASSLOAD_32BITCLRLOADING64BITASSEMBLY:
12694 break;
12695
12696 case MK_E_SYNTAX:
12697 hr = FUSION_E_INVALID_NAME;
12698 break;
12699
12700 case INET_E_CONNECTION_TIMEOUT:
12701 hr = IDS_INET_E_CONNECTION_TIMEOUT;
12702 break;
12703
12704 case INET_E_CANNOT_CONNECT:
12705 hr = IDS_INET_E_CANNOT_CONNECT;
12706 break;
12707
12708 case INET_E_RESOURCE_NOT_FOUND:
12709 hr = IDS_INET_E_RESOURCE_NOT_FOUND;
12710 break;
12711
12712 case NTE_BAD_HASH:
12713 case NTE_BAD_LEN:
12714 case NTE_BAD_KEY:
12715 case NTE_BAD_DATA:
12716 case NTE_BAD_ALGID:
12717 case NTE_BAD_FLAGS:
12718 case NTE_BAD_HASH_STATE:
12719 case NTE_BAD_UID:
12720 case NTE_FAIL:
12721 case NTE_BAD_TYPE:
12722 case NTE_BAD_VER:
12723 case NTE_BAD_SIGNATURE:
12724 case NTE_SIGNATURE_FILE_BAD:
12725 case CRYPT_E_HASH_VALUE:
12726 hr = IDS_EE_HASH_VAL_FAILED;
12727 break;
12728
12729 default:
12730 hr = IDS_EE_FILELOAD_ERROR_GENERIC;
12731 break;
12732
12733 }
12734
12735 return (UINT) hr;
12736}
12737
12738#ifndef DACCESS_COMPILE
12739
12740//==========================================================================
12741// Throw a runtime exception based on the last Win32 error (GetLastError())
12742//==========================================================================
12743VOID DECLSPEC_NORETURN RealCOMPlusThrowWin32()
12744{
12745
12746// before we do anything else...
12747 DWORD err = ::GetLastError();
12748
12749 CONTRACTL
12750 {
12751 THROWS;
12752 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
12753 MODE_ANY;
12754 }
12755 CONTRACTL_END;
12756
12757 RealCOMPlusThrowWin32(HRESULT_FROM_WIN32(err));
12758} // VOID DECLSPEC_NORETURN RealCOMPlusThrowWin32()
12759
12760//==========================================================================
12761// Throw a runtime exception based on the last Win32 error (GetLastError())
12762//==========================================================================
12763VOID DECLSPEC_NORETURN RealCOMPlusThrowWin32(HRESULT hr)
12764{
12765 CONTRACTL
12766 {
12767 THROWS;
12768 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
12769 MODE_ANY;
12770}
12771 CONTRACTL_END;
12772
12773 // Force to ApplicationException for compatibility with previous versions. We would
12774 // prefer a "Win32Exception" here.
12775 EX_THROW(EEMessageException, (kApplicationException, hr, 0 /* resid*/,
12776 NULL /* szArg1 */, NULL /* szArg2 */, NULL /* szArg3 */, NULL /* szArg4 */,
12777 NULL /* szArg5 */, NULL /* szArg6 */));
12778} // VOID DECLSPEC_NORETURN RealCOMPlusThrowWin32()
12779
12780
12781//==========================================================================
12782// Throw an OutOfMemoryError
12783//==========================================================================
12784VOID DECLSPEC_NORETURN RealCOMPlusThrowOM()
12785{
12786 CONTRACTL
12787 {
12788 THROWS;
12789 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
12790 CANNOT_TAKE_LOCK;
12791 MODE_ANY;
12792 SO_TOLERANT;
12793 SUPPORTS_DAC;
12794 }
12795 CONTRACTL_END;
12796
12797 ThrowOutOfMemory();
12798}
12799
12800//==========================================================================
12801// Throw an undecorated runtime exception.
12802//==========================================================================
12803VOID DECLSPEC_NORETURN RealCOMPlusThrow(RuntimeExceptionKind reKind)
12804{
12805 CONTRACTL
12806 {
12807 THROWS;
12808 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
12809 MODE_ANY;
12810 }
12811 CONTRACTL_END;
12812
12813 _ASSERTE((reKind != kExecutionEngineException) ||
12814 !"ExecutionEngineException shouldn't be thrown. Use EEPolicy to failfast or a better exception. The caller of this function should modify their code.");
12815
12816 EX_THROW(EEException, (reKind));
12817}
12818
12819//==========================================================================
12820// Throw a decorated runtime exception.
12821// Try using RealCOMPlusThrow(reKind, wszResourceName) instead.
12822//==========================================================================
12823VOID DECLSPEC_NORETURN RealCOMPlusThrowNonLocalized(RuntimeExceptionKind reKind, LPCWSTR wszTag)
12824{
12825 CONTRACTL
12826 {
12827 THROWS;
12828 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
12829 MODE_ANY;
12830 }
12831 CONTRACTL_END;
12832
12833 _ASSERTE((reKind != kExecutionEngineException) ||
12834 !"ExecutionEngineException shouldn't be thrown. Use EEPolicy to failfast or a better exception. The caller of this function should modify their code.");
12835
12836 EX_THROW(EEMessageException, (reKind, IDS_EE_GENERIC, wszTag));
12837}
12838
12839//==========================================================================
12840// Throw a runtime exception based on an HResult
12841//==========================================================================
12842VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, IErrorInfo* pErrInfo, Exception * pInnerException)
12843{
12844 CONTRACTL
12845 {
12846 THROWS;
12847 GC_TRIGGERS; // because of IErrorInfo
12848 MODE_ANY;
12849 }
12850 CONTRACTL_END;
12851
12852 _ASSERTE (FAILED(hr));
12853
12854 // Though we would like to assert this, it can happen in the following scenario:
12855 //
12856 // MgdCode --RCW-> COM --CCW-> MgdCode2
12857 //
12858 // If MgdCode2 throws EEE, when it reaches the RCW, it will invoking MarshalNative::ThrowExceptionForHr and thus,
12859 // reach here. Hence, we will need to keep the assert off, until user code is stopped for creating an EEE.
12860
12861 //_ASSERTE((hr != COR_E_EXECUTIONENGINE) ||
12862 // !"ExecutionEngineException shouldn't be thrown. Use EEPolicy to failfast or a better exception. The caller of this function should modify their code.");
12863
12864#ifndef CROSSGEN_COMPILE
12865#ifdef FEATURE_COMINTEROP
12866 // check for complus created IErrorInfo pointers
12867 if (pErrInfo != NULL)
12868 {
12869 GCX_COOP();
12870 {
12871 OBJECTREF oRetVal = NULL;
12872 GCPROTECT_BEGIN(oRetVal);
12873 GetExceptionForHR(hr, pErrInfo, &oRetVal);
12874 _ASSERTE(oRetVal != NULL);
12875 RealCOMPlusThrow(oRetVal);
12876 GCPROTECT_END ();
12877 }
12878 }
12879#endif // FEATURE_COMINTEROP
12880
12881 if (pErrInfo != NULL)
12882 {
12883 if (pInnerException == NULL)
12884 {
12885 EX_THROW(EECOMException, (hr, pErrInfo, true, NULL, FALSE));
12886 }
12887 else
12888 {
12889 EX_THROW_WITH_INNER(EECOMException, (hr, pErrInfo, true, NULL, FALSE), pInnerException);
12890 }
12891 }
12892 else
12893#endif // CROSSGEN_COMPILE
12894 {
12895 if (pInnerException == NULL)
12896 {
12897 EX_THROW(EEMessageException, (hr));
12898 }
12899 else
12900 {
12901 EX_THROW_WITH_INNER(EEMessageException, (hr), pInnerException);
12902 }
12903 }
12904}
12905
12906VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr)
12907{
12908 CONTRACTL
12909 {
12910 THROWS;
12911 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
12912 MODE_ANY;
12913 }
12914 CONTRACTL_END;
12915
12916
12917 // ! COMPlusThrowHR(hr) no longer snags the IErrorInfo off the TLS (Too many places
12918 // ! call this routine where no IErrorInfo was set by the prior call.)
12919 // !
12920 // ! If you actually want to pull IErrorInfo off the TLS, call
12921 // !
12922 // ! COMPlusThrowHR(hr, kGetErrorInfo)
12923
12924 RealCOMPlusThrowHR(hr, (IErrorInfo*)NULL);
12925}
12926
12927
12928VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, tagGetErrorInfo)
12929{
12930 CONTRACTL
12931 {
12932 THROWS;
12933 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
12934 MODE_ANY;
12935 }
12936 CONTRACTL_END;
12937
12938 // Get an IErrorInfo if one is available.
12939 IErrorInfo *pErrInfo = NULL;
12940
12941#ifndef CROSSGEN_COMPILE
12942 if (SafeGetErrorInfo(&pErrInfo) != S_OK)
12943 pErrInfo = NULL;
12944#endif
12945
12946 // Throw the exception.
12947 RealCOMPlusThrowHR(hr, pErrInfo);
12948}
12949
12950
12951
12952VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, UINT resID, LPCWSTR wszArg1,
12953 LPCWSTR wszArg2, LPCWSTR wszArg3, LPCWSTR wszArg4,
12954 LPCWSTR wszArg5, LPCWSTR wszArg6)
12955{
12956 CONTRACTL
12957 {
12958 THROWS;
12959 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
12960 MODE_ANY;
12961 }
12962 CONTRACTL_END;
12963
12964 _ASSERTE (FAILED(hr));
12965
12966 // Though we would like to assert this, it can happen in the following scenario:
12967 //
12968 // MgdCode --RCW-> COM --CCW-> MgdCode2
12969 //
12970 // If MgdCode2 throws EEE, when it reaches the RCW, it will invoking MarshalNative::ThrowExceptionForHr and thus,
12971 // reach here. Hence, we will need to keep the assert off, until user code is stopped for creating an EEE.
12972
12973 //_ASSERTE((hr != COR_E_EXECUTIONENGINE) ||
12974 // !"ExecutionEngineException shouldn't be thrown. Use EEPolicy to failfast or a better exception. The caller of this function should modify their code.");
12975
12976 EX_THROW(EEMessageException,
12977 (hr, resID, wszArg1, wszArg2, wszArg3, wszArg4, wszArg5, wszArg6));
12978}
12979
12980//==========================================================================
12981// Throw a decorated runtime exception with a localized message.
12982// Queries the ResourceManager for a corresponding resource value.
12983//==========================================================================
12984VOID DECLSPEC_NORETURN RealCOMPlusThrow(RuntimeExceptionKind reKind, LPCWSTR wszResourceName, Exception * pInnerException)
12985{
12986 CONTRACTL
12987 {
12988 THROWS;
12989 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
12990 MODE_ANY;
12991 PRECONDITION(CheckPointer(wszResourceName));
12992 }
12993 CONTRACTL_END;
12994
12995 _ASSERTE((reKind != kExecutionEngineException) ||
12996 !"ExecutionEngineException shouldn't be thrown. Use EEPolicy to failfast or a better exception. The caller of this function should modify their code.");
12997 //
12998 // For some reason, the compiler complains about unreachable code if
12999 // we don't split the new from the throw. So we're left with this
13000 // unnecessarily verbose syntax.
13001 //
13002
13003 if (pInnerException == NULL)
13004 {
13005 EX_THROW(EEResourceException, (reKind, wszResourceName));
13006 }
13007 else
13008 {
13009 EX_THROW_WITH_INNER(EEResourceException, (reKind, wszResourceName), pInnerException);
13010 }
13011}
13012
13013//==========================================================================
13014// Used by the classloader to record a managed exception object to explain
13015// why a classload got botched.
13016//
13017// - Can be called with gc enabled or disabled.
13018// This allows a catch-all error path to post a generic catchall error
13019// message w/out bonking more specific error messages posted by inner functions.
13020//==========================================================================
13021VOID DECLSPEC_NORETURN ThrowTypeLoadException(LPCWSTR pFullTypeName,
13022 LPCWSTR pAssemblyName,
13023 LPCUTF8 pMessageArg,
13024 UINT resIDWhy)
13025{
13026 CONTRACTL
13027 {
13028 THROWS;
13029 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13030 MODE_ANY;
13031 }
13032 CONTRACTL_END;
13033
13034 EX_THROW(EETypeLoadException, (pFullTypeName, pAssemblyName, pMessageArg, resIDWhy));
13035}
13036
13037
13038//==========================================================================
13039// Used by the classloader to post illegal layout
13040//==========================================================================
13041VOID DECLSPEC_NORETURN ThrowFieldLayoutError(mdTypeDef cl, // cl of the NStruct being loaded
13042 Module* pModule, // Module that defines the scope, loader and heap (for allocate FieldMarshalers)
13043 DWORD dwOffset, // Offset of field
13044 DWORD dwID) // Message id
13045{
13046 CONTRACTL
13047 {
13048 THROWS;
13049 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13050 MODE_ANY;
13051 }
13052 CONTRACTL_END;
13053
13054 IMDInternalImport *pInternalImport = pModule->GetMDImport(); // Internal interface for the NStruct being loaded.
13055
13056 LPCUTF8 pszName, pszNamespace;
13057 if (FAILED(pInternalImport->GetNameOfTypeDef(cl, &pszName, &pszNamespace)))
13058 {
13059 pszName = pszNamespace = "Invalid TypeDef record";
13060 }
13061
13062 CHAR offsetBuf[16];
13063 sprintf_s(offsetBuf, COUNTOF(offsetBuf), "%d", dwOffset);
13064 offsetBuf[COUNTOF(offsetBuf) - 1] = '\0';
13065
13066 pModule->GetAssembly()->ThrowTypeLoadException(pszNamespace,
13067 pszName,
13068 offsetBuf,
13069 dwID);
13070}
13071
13072//==========================================================================
13073// Throw an ArithmeticException
13074//==========================================================================
13075VOID DECLSPEC_NORETURN RealCOMPlusThrowArithmetic()
13076{
13077 CONTRACTL
13078 {
13079 THROWS;
13080 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13081 MODE_ANY;
13082 }
13083 CONTRACTL_END;
13084
13085 RealCOMPlusThrow(kArithmeticException);
13086}
13087
13088//==========================================================================
13089// Throw an ArgumentNullException
13090//==========================================================================
13091VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentNull(LPCWSTR argName, LPCWSTR wszResourceName)
13092{
13093 CONTRACTL
13094 {
13095 THROWS;
13096 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13097 MODE_ANY;
13098 PRECONDITION(CheckPointer(wszResourceName));
13099 }
13100 CONTRACTL_END;
13101
13102 EX_THROW(EEArgumentException, (kArgumentNullException, argName, wszResourceName));
13103}
13104
13105
13106VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentNull(LPCWSTR argName)
13107{
13108 CONTRACTL
13109 {
13110 THROWS;
13111 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13112 MODE_ANY;
13113 }
13114 CONTRACTL_END;
13115
13116 EX_THROW(EEArgumentException, (kArgumentNullException, argName, W("ArgumentNull_Generic")));
13117}
13118
13119
13120//==========================================================================
13121// Throw an ArgumentOutOfRangeException
13122//==========================================================================
13123VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentOutOfRange(LPCWSTR argName, LPCWSTR wszResourceName)
13124{
13125 CONTRACTL
13126 {
13127 THROWS;
13128 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13129 MODE_ANY;
13130 }
13131 CONTRACTL_END;
13132
13133 EX_THROW(EEArgumentException, (kArgumentOutOfRangeException, argName, wszResourceName));
13134}
13135
13136//==========================================================================
13137// Throw an ArgumentException
13138//==========================================================================
13139VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentException(LPCWSTR argName, LPCWSTR wszResourceName)
13140{
13141 CONTRACTL
13142 {
13143 THROWS;
13144 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13145 MODE_ANY;
13146 }
13147 CONTRACTL_END;
13148
13149 EX_THROW(EEArgumentException, (kArgumentException, argName, wszResourceName));
13150}
13151
13152//=========================================================================
13153// Used by the classloader to record a managed exception object to explain
13154// why a classload got botched.
13155//
13156// - Can be called with gc enabled or disabled.
13157// This allows a catch-all error path to post a generic catchall error
13158// message w/out bonking more specific error messages posted by inner functions.
13159//==========================================================================
13160VOID DECLSPEC_NORETURN ThrowTypeLoadException(LPCUTF8 pszNameSpace,
13161 LPCUTF8 pTypeName,
13162 LPCWSTR pAssemblyName,
13163 LPCUTF8 pMessageArg,
13164 UINT resIDWhy)
13165{
13166 CONTRACTL
13167 {
13168 THROWS;
13169 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13170 MODE_ANY;
13171 }
13172 CONTRACTL_END;
13173
13174 EX_THROW(EETypeLoadException, (pszNameSpace, pTypeName, pAssemblyName, pMessageArg, resIDWhy));
13175}
13176
13177//==========================================================================
13178// Throw a decorated runtime exception.
13179//==========================================================================
13180VOID DECLSPEC_NORETURN RealCOMPlusThrow(RuntimeExceptionKind reKind, UINT resID,
13181 LPCWSTR wszArg1, LPCWSTR wszArg2, LPCWSTR wszArg3,
13182 LPCWSTR wszArg4, LPCWSTR wszArg5, LPCWSTR wszArg6)
13183{
13184 CONTRACTL
13185 {
13186 THROWS;
13187 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13188 MODE_ANY;
13189 }
13190 CONTRACTL_END;
13191
13192 EX_THROW(EEMessageException,
13193 (reKind, resID, wszArg1, wszArg2, wszArg3, wszArg4, wszArg5, wszArg6));
13194}
13195
13196#ifdef FEATURE_COMINTEROP
13197#ifndef CROSSGEN_COMPILE
13198//==========================================================================
13199// Throw a runtime exception based on an HResult, check for error info
13200//==========================================================================
13201VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, IUnknown *iface, REFIID riid)
13202{
13203 CONTRACTL
13204 {
13205 THROWS;
13206 GC_TRIGGERS; // because of IErrorInfo
13207 MODE_ANY;
13208 }
13209 CONTRACTL_END;
13210
13211 IErrorInfo *info = NULL;
13212 {
13213 GCX_PREEMP();
13214 info = GetSupportedErrorInfo(iface, riid);
13215 }
13216 RealCOMPlusThrowHR(hr, info);
13217}
13218
13219//==========================================================================
13220// Throw a runtime exception based on an EXCEPINFO. This function will free
13221// the strings in the EXCEPINFO that is passed in.
13222//==========================================================================
13223VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(EXCEPINFO *pExcepInfo)
13224{
13225 CONTRACTL
13226 {
13227 THROWS;
13228 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13229 MODE_ANY;
13230 }
13231 CONTRACTL_END;
13232
13233 EX_THROW(EECOMException, (pExcepInfo));
13234}
13235#endif //CROSSGEN_COMPILE
13236
13237#endif // FEATURE_COMINTEROP
13238
13239
13240#ifdef FEATURE_STACK_PROBE
13241//==========================================================================
13242// Throw a StackOverflowError
13243//==========================================================================
13244VOID DECLSPEC_NORETURN RealCOMPlusThrowSO()
13245{
13246 CONTRACTL
13247 {
13248 // This should be throws... But it isn't because a SO doesn't technically
13249 // fall into the same THROW/NOTHROW conventions as the rest of the contract
13250 // infrastructure.
13251 NOTHROW;
13252
13253 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13254 SO_TOLERANT;
13255 MODE_ANY;
13256 }
13257 CONTRACTL_END;
13258
13259 // We only use BreakOnSO if we are in debug mode, so we'll only checking if the
13260 // _DEBUG flag is set.
13261#ifdef _DEBUG
13262 static int breakOnSO = -1;
13263
13264 if (breakOnSO == -1)
13265 breakOnSO = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BreakOnSO);
13266
13267 if (breakOnSO != 0)
13268 {
13269 _ASSERTE(!"SO occurred");
13270 }
13271#endif
13272
13273 ThrowStackOverflow();
13274}
13275#endif
13276
13277//==========================================================================
13278// Throw an InvalidCastException
13279//==========================================================================
13280
13281
13282VOID GetAssemblyDetailInfo(SString &sType,
13283 SString &sAssemblyDisplayName,
13284 PEAssembly *pPEAssembly,
13285 SString &sAssemblyDetailInfo)
13286{
13287 WRAPPER_NO_CONTRACT;
13288
13289 InlineSString<MAX_LONGPATH> sFormat;
13290 const WCHAR *pwzLoadContext = W("Default");
13291
13292 if (pPEAssembly->GetPath().IsEmpty())
13293 {
13294 sFormat.LoadResource(CCompRC::Debugging, IDS_EE_CANNOTCAST_HELPER_BYTE);
13295
13296 sAssemblyDetailInfo.Printf(sFormat.GetUnicode(),
13297 sType.GetUnicode(),
13298 sAssemblyDisplayName.GetUnicode(),
13299 pwzLoadContext);
13300 }
13301 else
13302 {
13303 sFormat.LoadResource(CCompRC::Debugging, IDS_EE_CANNOTCAST_HELPER_PATH);
13304
13305 sAssemblyDetailInfo.Printf(sFormat.GetUnicode(),
13306 sType.GetUnicode(),
13307 sAssemblyDisplayName.GetUnicode(),
13308 pwzLoadContext,
13309 pPEAssembly->GetPath().GetUnicode());
13310 }
13311}
13312
13313VOID CheckAndThrowSameTypeAndAssemblyInvalidCastException(TypeHandle thCastFrom,
13314 TypeHandle thCastTo)
13315{
13316 CONTRACTL {
13317 THROWS;
13318 GC_TRIGGERS;
13319 MODE_COOPERATIVE;
13320 SO_INTOLERANT;
13321 } CONTRACTL_END;
13322
13323 Module *pModuleTypeFrom = thCastFrom.GetModule();
13324 Module *pModuleTypeTo = thCastTo.GetModule();
13325
13326 if ((pModuleTypeFrom != NULL) && (pModuleTypeTo != NULL))
13327 {
13328 Assembly *pAssemblyTypeFrom = pModuleTypeFrom->GetAssembly();
13329 Assembly *pAssemblyTypeTo = pModuleTypeTo->GetAssembly();
13330
13331 _ASSERTE(pAssemblyTypeFrom != NULL);
13332 _ASSERTE(pAssemblyTypeTo != NULL);
13333
13334 PEAssembly *pPEAssemblyTypeFrom = pAssemblyTypeFrom->GetManifestFile();
13335 PEAssembly *pPEAssemblyTypeTo = pAssemblyTypeTo->GetManifestFile();
13336
13337 _ASSERTE(pPEAssemblyTypeFrom != NULL);
13338 _ASSERTE(pPEAssemblyTypeTo != NULL);
13339
13340 InlineSString<MAX_LONGPATH> sAssemblyFromDisplayName;
13341 InlineSString<MAX_LONGPATH> sAssemblyToDisplayName;
13342
13343 pPEAssemblyTypeFrom->GetDisplayName(sAssemblyFromDisplayName);
13344 pPEAssemblyTypeTo->GetDisplayName(sAssemblyToDisplayName);
13345
13346 // Found the culprit case. Now format the new exception text.
13347 InlineSString<MAX_CLASSNAME_LENGTH + 1> strCastFromName;
13348 InlineSString<MAX_CLASSNAME_LENGTH + 1> strCastToName;
13349 InlineSString<MAX_LONGPATH> sAssemblyDetailInfoFrom;
13350 InlineSString<MAX_LONGPATH> sAssemblyDetailInfoTo;
13351
13352 thCastFrom.GetName(strCastFromName);
13353 thCastTo.GetName(strCastToName);
13354
13355 SString typeA = SL(W("A"));
13356 GetAssemblyDetailInfo(typeA,
13357 sAssemblyFromDisplayName,
13358 pPEAssemblyTypeFrom,
13359 sAssemblyDetailInfoFrom);
13360 SString typeB = SL(W("B"));
13361 GetAssemblyDetailInfo(typeB,
13362 sAssemblyToDisplayName,
13363 pPEAssemblyTypeTo,
13364 sAssemblyDetailInfoTo);
13365
13366 COMPlusThrow(kInvalidCastException,
13367 IDS_EE_CANNOTCASTSAME,
13368 strCastFromName.GetUnicode(),
13369 strCastToName.GetUnicode(),
13370 sAssemblyDetailInfoFrom.GetUnicode(),
13371 sAssemblyDetailInfoTo.GetUnicode());
13372 }
13373}
13374
13375VOID RealCOMPlusThrowInvalidCastException(TypeHandle thCastFrom, TypeHandle thCastTo)
13376{
13377 CONTRACTL {
13378 THROWS;
13379 GC_TRIGGERS;
13380 MODE_COOPERATIVE;
13381 } CONTRACTL_END;
13382
13383 // Use an InlineSString with a size of MAX_CLASSNAME_LENGTH + 1 to prevent
13384 // TypeHandle::GetName from having to allocate a new block of memory. This
13385 // significantly improves the performance of throwing an InvalidCastException.
13386 InlineSString<MAX_CLASSNAME_LENGTH + 1> strCastFromName;
13387 InlineSString<MAX_CLASSNAME_LENGTH + 1> strCastToName;
13388
13389 thCastTo.GetName(strCastToName);
13390 {
13391 thCastFrom.GetName(strCastFromName);
13392 // Attempt to catch the A.T != A.T case that causes so much user confusion.
13393 if (strCastFromName.Equals(strCastToName))
13394 {
13395 CheckAndThrowSameTypeAndAssemblyInvalidCastException(thCastFrom, thCastTo);
13396 }
13397 COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST, strCastFromName.GetUnicode(), strCastToName.GetUnicode());
13398 }
13399}
13400
13401#ifndef CROSSGEN_COMPILE
13402VOID RealCOMPlusThrowInvalidCastException(OBJECTREF *pObj, TypeHandle thCastTo)
13403{
13404 CONTRACTL {
13405 THROWS;
13406 GC_TRIGGERS;
13407 MODE_COOPERATIVE;
13408 PRECONDITION(IsProtectedByGCFrame (pObj));
13409 } CONTRACTL_END;
13410
13411 TypeHandle thCastFrom = (*pObj)->GetTypeHandle();
13412#ifdef FEATURE_COMINTEROP
13413 if (thCastFrom.GetMethodTable()->IsComObjectType())
13414 {
13415 // Special case casting RCWs so we can give better error information when the
13416 // cast fails.
13417 ComObject::ThrowInvalidCastException(pObj, thCastTo.GetMethodTable());
13418 }
13419#endif
13420 COMPlusThrowInvalidCastException(thCastFrom, thCastTo);
13421}
13422#endif // CROSSGEN_COMPILE
13423
13424#endif // DACCESS_COMPILE
13425
13426#ifndef CROSSGEN_COMPILE // ???
13427#ifdef FEATURE_COMINTEROP
13428#include "comtoclrcall.h"
13429#endif // FEATURE_COMINTEROP
13430
13431// Reverse COM interop IL stubs need to catch all exceptions and translate them into HRESULTs.
13432// But we allow for CSEs to be rethrown. Our corrupting state policy gets applied to the
13433// original user-visible method that triggered the IL stub to be generated. So we must be able
13434// to map back from a given IL stub to the user-visible method. Here, we do that only when we
13435// see a 'matching' ComMethodFrame further up the stack.
13436MethodDesc * GetUserMethodForILStub(Thread * pThread, UINT_PTR uStubSP, MethodDesc * pILStubMD, Frame ** ppFrameOut)
13437{
13438 CONTRACTL
13439 {
13440 NOTHROW;
13441 GC_NOTRIGGER;
13442 MODE_ANY;
13443 PRECONDITION(pILStubMD->IsILStub());
13444 }
13445 CONTRACTL_END;
13446
13447 MethodDesc * pUserMD = pILStubMD;
13448#ifdef FEATURE_COMINTEROP
13449 DynamicMethodDesc * pDMD = pILStubMD->AsDynamicMethodDesc();
13450 if (pDMD->IsCOMToCLRStub())
13451 {
13452 // There are some differences across architectures for "which" SP is passed in.
13453 // On ARM, the SP is the SP on entry to the IL stub, on the other arches, it's
13454 // a post-prolog SP. But this doesn't matter here because the COM->CLR path
13455 // always pushes the Frame in a caller's stack frame.
13456
13457 Frame * pCurFrame = pThread->GetFrame();
13458 while ((UINT_PTR)pCurFrame < uStubSP)
13459 {
13460 pCurFrame = pCurFrame->PtrNextFrame();
13461 }
13462
13463 // The construction of the COM->CLR path ensures that our corresponding ComMethodFrame
13464 // should be present further up the stack. Normally, the ComMethodFrame in question is
13465 // simply the next stack frame; however, there are situations where there may be other
13466 // stack frames present (such as an optional ContextTransitionFrame if we switched
13467 // AppDomains, or an inlined stack frame from a QCall in the IL stub).
13468 while (pCurFrame->GetVTablePtr() != ComMethodFrame::GetMethodFrameVPtr())
13469 {
13470 pCurFrame = pCurFrame->PtrNextFrame();
13471 }
13472
13473 ComMethodFrame * pComFrame = (ComMethodFrame *)pCurFrame;
13474 _ASSERTE((UINT_PTR)pComFrame > uStubSP);
13475
13476 CONSISTENCY_CHECK_MSG(pComFrame->GetVTablePtr() == ComMethodFrame::GetMethodFrameVPtr(),
13477 "Expected to find a ComMethodFrame.");
13478
13479 ComCallMethodDesc * pCMD = pComFrame->GetComCallMethodDesc();
13480
13481 CONSISTENCY_CHECK_MSG(pILStubMD == ExecutionManager::GetCodeMethodDesc(pCMD->GetILStub()),
13482 "The ComMethodFrame that we found doesn't match the IL stub passed in.");
13483
13484 pUserMD = pCMD->GetMethodDesc();
13485 *ppFrameOut = pComFrame;
13486 }
13487#endif // FEATURE_COMINTEROP
13488 return pUserMD;
13489}
13490#endif //CROSSGEN_COMPILE
13491