diff options
Diffstat (limited to 'src/vm/clrex.h')
-rw-r--r-- | src/vm/clrex.h | 1316 |
1 files changed, 1316 insertions, 0 deletions
diff --git a/src/vm/clrex.h b/src/vm/clrex.h new file mode 100644 index 0000000000..02de452370 --- /dev/null +++ b/src/vm/clrex.h @@ -0,0 +1,1316 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// --------------------------------------------------------------------------- +// CLREx.h +// --------------------------------------------------------------------------- + + +#ifndef _CLREX_H_ +#define _CLREX_H_ + +#include <ex.h> + +#include "objecthandle.h" +#include "runtimeexceptionkind.h" +#include "interoputil.h" + +class BaseBind; +class AssemblySpec; +class PEFile; +class PEAssembly; + +struct StackTraceElement +{ + UINT_PTR ip; + UINT_PTR sp; + PTR_MethodDesc pFunc; +#if defined(FEATURE_EXCEPTIONDISPATCHINFO) + // TRUE if this element represents the last frame of the foreign + // exception stack trace. + BOOL fIsLastFrameFromForeignStackTrace; +#endif // defined(FEATURE_EXCEPTIONDISPATCHINFO) + + bool operator==(StackTraceElement const & rhs) const + { + return ip == rhs.ip + && sp == rhs.sp + && pFunc == rhs.pFunc; + } + + bool operator!=(StackTraceElement const & rhs) const + { + return !(*this == rhs); + } + + bool PartiallyEqual(StackTraceElement const & rhs) const + { + return pFunc == rhs.pFunc; + } + + void PartialAtomicUpdate(StackTraceElement const & rhs) + { + ip = rhs.ip; + } +}; + +class StackTraceInfo +{ +private: + // for building stack trace info + StackTraceElement* m_pStackTrace; // pointer to stack trace storage + unsigned m_cStackTrace; // size of stack trace storage + unsigned m_dFrameCount; // current frame in stack trace + unsigned m_cDynamicMethodItems; // number of items in the Dynamic Method array + unsigned m_dCurrentDynamicIndex; // index of the next location where the resolver object will be stored + +public: + void Init(); + BOOL IsEmpty(); + void AllocateStackTrace(); + void ClearStackTrace(); + void FreeStackTrace(); + void SaveStackTrace(BOOL bAllowAllocMem, OBJECTHANDLE hThrowable, BOOL bReplaceStack, BOOL bSkipLastElement); + BOOL AppendElement(BOOL bAllowAllocMem, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf); + + void GetLeafFrameInfo(StackTraceElement* pStackTraceElement); +}; + + +// --------------------------------------------------------------------------- +// CLRException represents an exception which has a managed representation. +// It adds the generic method GetThrowable(). +// --------------------------------------------------------------------------- +class CLRException : public Exception +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + friend class CLRLastThrownObjectException; + private: + static const int c_type = 0x434c5220; // 'CLR ' + + protected: + OBJECTHANDLE m_throwableHandle; + + void SetThrowableHandle(OBJECTHANDLE throwable); + OBJECTHANDLE GetThrowableHandle() { return m_throwableHandle; } + + + CLRException(); +public: + ~CLRException(); + + OBJECTREF GetThrowable(); + + // Dynamic type query for catchers + static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; } + virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; } + BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || Exception::IsType(type); } + + BOOL IsSameInstanceType(Exception *pException) + { + STATIC_CONTRACT_MODE_COOPERATIVE; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_NOTHROW; + + if (pException->GetInstanceType() != GetInstanceType()) + { + return FALSE; + } + OBJECTREF mine = GetThrowable(); + OBJECTREF other = ((CLRException*)pException)->GetThrowable(); + return mine != NULL && other != NULL && + mine->GetMethodTable() == other->GetMethodTable(); + } + + // Overrides + virtual BOOL IsDomainBound() + { + //@TODO special case for preallocated exceptions? + return TRUE; + } + + HRESULT GetHR(); + IErrorInfo *GetErrorInfo(); + HRESULT SetErrorInfo(); + + void GetMessage(SString &result); + + protected: + + virtual OBJECTREF CreateThrowable() { LIMITED_METHOD_CONTRACT; return NULL; } + + public: // These are really private, but are used by the exception macros + + + // Accessors for all the preallocated exception objects. + static OBJECTREF GetPreallocatedBaseException(); + static OBJECTREF GetPreallocatedOutOfMemoryException(); + static OBJECTREF GetPreallocatedRudeThreadAbortException(); + static OBJECTREF GetPreallocatedThreadAbortException(); + static OBJECTREF GetPreallocatedStackOverflowException(); + static OBJECTREF GetPreallocatedExecutionEngineException(); + + // Accessors for all the preallocated exception handles. + static OBJECTHANDLE GetPreallocatedOutOfMemoryExceptionHandle(); + static OBJECTHANDLE GetPreallocatedRudeThreadAbortExceptionHandle(); + static OBJECTHANDLE GetPreallocatedThreadAbortExceptionHandle(); + static OBJECTHANDLE GetPreallocatedStackOverflowExceptionHandle(); + static OBJECTHANDLE GetPreallocatedExecutionEngineExceptionHandle(); + static OBJECTHANDLE GetPreallocatedHandleForObject(OBJECTREF o); + + // Use these to determine if a handle or object ref is one of the preallocated handles or object refs. + static BOOL IsPreallocatedExceptionObject(OBJECTREF o); + static BOOL IsPreallocatedExceptionHandle(OBJECTHANDLE h); + + // Prefer a new OOM exception if we can make one. If we cannot, then give back the pre-allocated + // one. + static OBJECTREF GetBestOutOfMemoryException(); + + static OBJECTREF GetThrowableFromException(Exception *pException); + static OBJECTREF GetThrowableFromExceptionRecord(EXCEPTION_RECORD *pExceptionRecord); + + class HandlerState : public Exception::HandlerState + { + public: + Thread* m_pThread; + Frame* m_pFrame; + BOOL m_fPreemptiveGCDisabled; + + enum NonNullThread + { + ThreadIsNotNull + }; + + HandlerState(Thread * pThread); + HandlerState(Thread * pThread, NonNullThread dummy); + + void CleanupTry(); + void SetupCatch(INDEBUG_COMMA(__in_z const char * szFile) int lineNum); +#ifdef LOGGING // Use parent implementation that inlines into nothing in retail build + void SucceedCatch(); +#endif + void SetupFinally(); + }; +}; + +// prototype for helper function to get exception object from thread's LastThrownObject. +void GetLastThrownObjectExceptionFromThread_Internal(Exception **ppException); + + +// --------------------------------------------------------------------------- +// EEException is a CLR exception subclass which has purely unmanaged representation. +// The standard methods will not do any GC dangerous operations. Thus you +// can throw and catch such an exception without regard to GC mode. +// --------------------------------------------------------------------------- + +class EEException : public CLRException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + private: + static const int c_type = 0x45452020; // 'EE ' + + public: + const RuntimeExceptionKind m_kind; + + EEException(RuntimeExceptionKind kind); + EEException(HRESULT hr); + + // Dynamic type query for catchers + static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; } + virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; } + BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || CLRException::IsType(type); } + + BOOL IsSameInstanceType(Exception *pException) + { + WRAPPER_NO_CONTRACT; + return pException->GetInstanceType() == GetType() && ((EEException*)pException)->m_kind == m_kind; + } + + // Virtual overrides + HRESULT GetHR(); + IErrorInfo *GetErrorInfo(); + void GetMessage(SString &result); + OBJECTREF CreateThrowable(); + + // GetThrowableMessage returns a message to be stored in the throwable. + // Returns FALSE if there is no useful value. + virtual BOOL GetThrowableMessage(SString &result); + + static BOOL GetResourceMessage(UINT iResourceID, SString &result, + const SString &arg1 = SString::Empty(), const SString &arg2 = SString::Empty(), + const SString &arg3 = SString::Empty(), const SString &arg4 = SString::Empty(), + const SString &arg5 = SString::Empty(), const SString &arg6 = SString::Empty()); + + // Note: reKind-->hr is a one-to-many relationship. + // + // each reKind is associated with one or more hresults. + // every hresult is associated with exactly one reKind (with kCOMException being the catch-all.) + static RuntimeExceptionKind GetKindFromHR(HRESULT hr, bool fIsWinRtMode = false); + protected: + static HRESULT GetHRFromKind(RuntimeExceptionKind reKind); + +#ifdef _DEBUG + EEException() : m_kind(kException) + { + // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. + // We need a variant which does not allocate memory. + } +#endif // _DEBUG + + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new EEException(m_kind); + } + +}; + +// --------------------------------------------------------------------------- +// EEMessageException is an EE exception subclass composed of a type and +// an unmanaged message of some sort. +// --------------------------------------------------------------------------- + +class EEMessageException : public EEException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + private: + HRESULT m_hr; + UINT m_resID; + InlineSString<32> m_arg1; + InlineSString<32> m_arg2; + SString m_arg3; + SString m_arg4; + SString m_arg5; + SString m_arg6; + + public: + EEMessageException(RuntimeExceptionKind kind, UINT resID = 0, LPCWSTR szArg1 = NULL, LPCWSTR szArg2 = NULL, + LPCWSTR szArg3 = NULL, LPCWSTR szArg4 = NULL, LPCWSTR szArg5 = NULL, LPCWSTR szArg6 = NULL); + + EEMessageException(HRESULT hr); + + EEMessageException(HRESULT hr, bool fUseCOMException); + + EEMessageException(HRESULT hr, UINT resID, LPCWSTR szArg1 = NULL, LPCWSTR szArg2 = NULL, LPCWSTR szArg3 = NULL, + LPCWSTR szArg4 = NULL, LPCWSTR szArg5 = NULL, LPCWSTR szArg6 = NULL); + + EEMessageException(RuntimeExceptionKind kind, HRESULT hr, UINT resID, LPCWSTR szArg1 = NULL, LPCWSTR szArg2 = NULL, + LPCWSTR szArg3 = NULL, LPCWSTR szArg4 = NULL, LPCWSTR szArg5 = NULL, LPCWSTR szArg6 = NULL); + + // Virtual overrides + HRESULT GetHR(); + + BOOL GetThrowableMessage(SString &result); + + UINT GetResID(void) { LIMITED_METHOD_CONTRACT; return m_resID; } + + static BOOL IsEEMessageException(Exception *pException) + { + return (*(PVOID*)pException == GetEEMessageExceptionVPtr()); + } + + protected: + + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new EEMessageException( + m_kind, m_hr, m_resID, m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6); + } + + + private: + + static PVOID GetEEMessageExceptionVPtr() + { + CONTRACT (PVOID) + { + WRAPPER(THROWS); + WRAPPER(GC_TRIGGERS); + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + EEMessageException boilerplate(E_FAIL); + RETURN (PVOID&)boilerplate; + } + + BOOL GetResourceMessage(UINT iResourceID, SString &result); + +#ifdef _DEBUG + EEMessageException() + { + // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. + // We need a variant which does not allocate memory. + } +#endif // _DEBUG +}; + +// --------------------------------------------------------------------------- +// EEResourceException is an EE exception subclass composed of a type and +// an message using a managed exception resource. +// --------------------------------------------------------------------------- + +class EEResourceException : public EEException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + private: + InlineSString<32> m_resourceName; + + public: + EEResourceException(RuntimeExceptionKind kind, const SString &resourceName); + + // Unmanaged message text containing only the resource name (GC safe) + void GetMessage(SString &result); + + // Throwable message containig the resource contents (not GC safe) + BOOL GetThrowableMessage(SString &result); + + protected: + + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new EEResourceException(m_kind, m_resourceName); + } + +private: +#ifdef _DEBUG + EEResourceException() + { + // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. + // We need a variant which does not allocate memory. + } +#endif // _DEBUG +}; + +// --------------------------------------------------------------------------- +// EECOMException is an EE exception subclass composed of COM-generated data. +// Note that you must ensure that the COM data was not derived from a wrapper +// on a managed Exception object. (If so, you must compose the exception from +// the managed object itself.) +// --------------------------------------------------------------------------- + +struct ExceptionData +{ + HRESULT hr; + BSTR bstrDescription; + BSTR bstrSource; + BSTR bstrHelpFile; + DWORD dwHelpContext; + GUID guid; +#ifdef FEATURE_COMINTEROP + BSTR bstrRestrictedError; // Returned from IRestrictedErrorInfo::GetErrorDetails + BSTR bstrReference; // Returned from IRestrictedErrorInfo::GetReference + BSTR bstrCapabilitySid; // Returned from IRestrictedErrorInfo::GetErrorDetails + IUnknown *pRestrictedErrorInfo; // AddRef-ed RestrictedErrorInfo pointer + // We need to keep this alive as long as user need the reference + BOOL bHasLanguageRestrictedErrorInfo; +#endif // FEATURE_COMINTEROP +}; + +class EECOMException : public EEException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + private: + ExceptionData m_ED; + + public: + + EECOMException(EXCEPINFO *pExcepInfo); + EECOMException(ExceptionData *pED); + EECOMException( + HRESULT hr, + IErrorInfo *pErrInfo, + bool fUseCOMException, + IRestrictedErrorInfo *pRestrictedErrInfo, + BOOL bHasLanguageRestrictedErrorInfo + COMMA_INDEBUG(BOOL bCheckInProcCCWTearOff = TRUE)); + ~EECOMException(); + + // Virtual overrides + HRESULT GetHR(); + + BOOL GetThrowableMessage(SString &result); + OBJECTREF CreateThrowable(); + + protected: + + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new EECOMException(&m_ED); + } + +private: +#ifdef _DEBUG + EECOMException() + { + // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. + // We need a variant which does not allocate memory. + ZeroMemory(&m_ED, sizeof(m_ED)); + } +#endif // _DEBUG +}; + +// --------------------------------------------------------------------------- +// EEFieldException is an EE exception subclass composed of a field +// --------------------------------------------------------------------------- +class EEFieldException : public EEException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + private: + FieldDesc *m_pFD; + MethodDesc *m_pAccessingMD; + SString m_additionalContext; + UINT m_messageID; + + public: + EEFieldException(FieldDesc *pField); + EEFieldException(FieldDesc *pField, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID); + + BOOL GetThrowableMessage(SString &result); + virtual BOOL IsDomainBound() {return TRUE;}; +protected: + + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new EEFieldException(m_pFD, m_pAccessingMD, m_additionalContext, m_messageID); + } + +private: +#ifdef _DEBUG + EEFieldException() + { + // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. + // We need a variant which does not allocate memory. + } +#endif // _DEBUG +}; + +// --------------------------------------------------------------------------- +// EEMethodException is an EE exception subclass composed of a field +// --------------------------------------------------------------------------- + +class EEMethodException : public EEException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + private: + MethodDesc *m_pMD; + MethodDesc *m_pAccessingMD; + SString m_additionalContext; + UINT m_messageID; + + public: + EEMethodException(MethodDesc *pMethod); + EEMethodException(MethodDesc *pMethod, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID); + + BOOL GetThrowableMessage(SString &result); + virtual BOOL IsDomainBound() {return TRUE;}; + protected: + + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new EEMethodException(m_pMD, m_pAccessingMD, m_additionalContext, m_messageID); + } + +private: +#ifdef _DEBUG + EEMethodException() + { + // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. + // We need a variant which does not allocate memory. + } +#endif // _DEBUG +}; + +// --------------------------------------------------------------------------- +// EETypeAccessException is an EE exception subclass composed of a type being +// illegally accessed and the method doing the access +// --------------------------------------------------------------------------- + +class EETypeAccessException : public EEException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + private: + MethodTable *m_pMT; + MethodDesc *m_pAccessingMD; + SString m_additionalContext; + UINT m_messageID; + + public: + EETypeAccessException(MethodTable *pMT); + EETypeAccessException(MethodTable *pMT, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID); + + BOOL GetThrowableMessage(SString &result); + virtual BOOL IsDomainBound() {return TRUE;}; + protected: + + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new EETypeAccessException(m_pMT, m_pAccessingMD, m_additionalContext, m_messageID); + } + +private: +#ifdef _DEBUG + EETypeAccessException() + { + // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. + // We need a variant which does not allocate memory. + } +#endif // _DEBUG +}; + +// --------------------------------------------------------------------------- +// EEArgumentException is an EE exception subclass representing a bad argument +// exception +// --------------------------------------------------------------------------- + +class EEArgumentException : public EEException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + private: + InlineSString<32> m_argumentName; + InlineSString<32> m_resourceName; + + public: + EEArgumentException(RuntimeExceptionKind reKind, LPCWSTR pArgName, + LPCWSTR wszResourceName); + + // @todo: GetMessage + + OBJECTREF CreateThrowable(); + + protected: + + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new EEArgumentException(m_kind, m_argumentName, m_resourceName); + } + +private: +#ifdef _DEBUG + EEArgumentException() + { + // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. + // We need a variant which does not allocate memory. + } +#endif // _DEBUG +}; + +// --------------------------------------------------------------------------- +// EETypeLoadException is an EE exception subclass representing a type loading +// error +// --------------------------------------------------------------------------- + +class EETypeLoadException : public EEException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + private: + InlineSString<64> m_fullName; + SString m_pAssemblyName; + SString m_pMessageArg; + UINT m_resIDWhy; + + public: + EETypeLoadException(LPCUTF8 pszNameSpace, LPCUTF8 pTypeName, + LPCWSTR pAssemblyName, LPCUTF8 pMessageArg, UINT resIDWhy); + + EETypeLoadException(LPCWSTR pFullTypeName, + LPCWSTR pAssemblyName, LPCUTF8 pMessageArg, UINT resIDWhy); + + // virtual overrides + void GetMessage(SString &result); + OBJECTREF CreateThrowable(); + + protected: + + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new EETypeLoadException(m_fullName, m_pAssemblyName, m_pMessageArg, m_resIDWhy); + } + + private: + EETypeLoadException(const InlineSString<64> &fullName, LPCWSTR pAssemblyName, + const SString &pMessageArg, UINT resIDWhy) + : EEException(kTypeLoadException), + m_fullName(fullName), + m_pAssemblyName(pAssemblyName), + m_pMessageArg(pMessageArg), + m_resIDWhy(resIDWhy) + { + WRAPPER_NO_CONTRACT; + } + + +#ifdef _DEBUG + EETypeLoadException() + { + // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. + // We need a variant which does not allocate memory. + } +#endif // _DEBUG +}; + +// --------------------------------------------------------------------------- +// EEFileLoadException is an EE exception subclass representing a file loading +// error +// --------------------------------------------------------------------------- + +class EEFileLoadException : public EEException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + private: + SString m_name; +#ifdef FEATURE_FUSION + IFusionBindLog *m_pFusionLog; +#else + void *m_pFusionLog; +#endif + HRESULT m_hr; + + + public: + +#ifdef FEATURE_FUSION + EEFileLoadException(const SString &name, HRESULT hr, IFusionBindLog *pFusionLog = NULL, Exception *pInnerException = NULL); +#else + EEFileLoadException(const SString &name, HRESULT hr, void *pFusionLog = NULL, Exception *pInnerException = NULL); +#endif + ~EEFileLoadException(); + + // virtual overrides + HRESULT GetHR() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_hr; + } + void GetMessage(SString &result); + void GetName(SString &result); + OBJECTREF CreateThrowable(); + + static RuntimeExceptionKind GetFileLoadKind(HRESULT hr); +#ifdef FEATURE_FUSION + static void DECLSPEC_NORETURN Throw(AssemblySpec *pSpec, IFusionBindLog *pFusionLog, HRESULT hr, Exception *pInnerException = NULL); + static void DECLSPEC_NORETURN Throw(IAssembly *pIAssembly, IHostAssembly *pIHostAssembly, HRESULT hr, Exception *pInnerException = NULL); +#endif + static void DECLSPEC_NORETURN Throw(AssemblySpec *pSpec, HRESULT hr, Exception *pInnerException = NULL); + static void DECLSPEC_NORETURN Throw(PEFile *pFile, HRESULT hr, Exception *pInnerException = NULL); + static void DECLSPEC_NORETURN Throw(LPCWSTR path, HRESULT hr, Exception *pInnerException = NULL); + static void DECLSPEC_NORETURN Throw(PEAssembly *parent, const void *memory, COUNT_T size, HRESULT hr, Exception *pInnerException = NULL); + static BOOL CheckType(Exception* ex); // typeof(EEFileLoadException) + + protected: + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new EEFileLoadException(m_name, m_hr, m_pFusionLog); + } + + private: +#ifdef _DEBUG + EEFileLoadException() : m_pFusionLog(NULL) + { + // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer. + // We need a variant which does not allocate memory. + } +#endif // _DEBUG + + void SetFileName(const SString &fileName, BOOL removePath); +}; + +// ------------------------------------------------------------------------------------------------------- +// Throw/catch macros. These are derived from the generic EXCEPTION macros, +// but add extra functionality for cleaning up thread state on catches +// +// Usage: +// EX_TRY +// { +// EX_THROW(EEMessageException, (kind, L"Failure message")); +// } +// EX_CATCH +// { +// EX_RETHROW() +// } +// EX_END_CATCH(RethrowTerminalExceptions or RethrowCorruptingExceptions) +// -------------------------------------------------------------------------------------------------------- + +// In DAC builds, we don't want to override the normal utilcode exception handling. +// We're not actually running in the CLR, but we may need access to some CLR-exception +// related data structures elsewhere in this header file in order to analyze CLR +// exceptions that occurred in the target. +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + +#define GET_THROWABLE() CLRException::GetThrowableFromException(GET_EXCEPTION()) + +#ifdef FEATURE_CORRUPTING_EXCEPTIONS + +// For the VM folder, we redefine SET_CE_RETHROW_FLAG_FOR_EX_CATCH to also check the +// corruption severity when deciding whether to rethrow them or not. +// +// We also check the global override flag incase it has been set to force pre-V4 behaviour. +// +// Doing the checks for "__fCaughtSO" and "__fCaughtNonCxx" will ensure that we check for +// corruption severity only if the last exception was a managed exception that could have been rethrown in the VM. +// When "(__fCaughtSO == FALSE) && (__fCaughtNonCxx == true)" is true, it implies we are dealing with a managed exception +// inside the VM that is represented by the CLRLastThrownObjectException instance (see EX_TRY/EX_CATCH implementation in VM +// folder to see how CLRLastThrownObjectException is used). +// +// This macro also supports the following scenarios: +// +// Scenario 1 +// ---------- +// +// [VM1] -> [VM2] -> <managed code> +// +// If a managed exception is swallowed by an EX_CATCH in native function VM2, which then returns back +// to native function VM1 that throws, for example, a VM C++ exception, an EX_CATCH(RethrowCorruptingExceptions) +// in VM1 that catches the C++ exception will not rethrow since the last exception was not a managed CSE but +// a C++ exception. +// +// A variation of this is for VM2 to return back in VM1, which calls VM3 that throws a VM C++ exception that +// reaches VM1's EX_CATCH(RethrowCorruptingExceptions). VM1 shouldn't be rethrowing the exception in such a case. +// +// Scenario 2 +// ---------- +// +// [VM1 - RethrowCSE] -> [VM2 - RethrowCSE] -> [VM3 - RethrowCSE] -> <managed code> +// +// When managed code throws a CSE (e.g. TargetInvocationException flagged as CSE), [VM3] will rethrow it and we will +// enter EX_CATCH in VM2 which is supposed to rethrow it as well. But if the implementation of EX_CATCH in VM2 throws +// another VM C++ exception (e.g. EEFileLoadException) *before* rethrow policy is applied, control will reach EX_CATCH +// in VM1 that *shouldn't* rethrow (even though it has RethrowCSE as the policy) since the last exception was a VM C++ +// exception. +// +// Scenario 3 +// ---------- +// +// This is about VM throwing a managed exception that gets handled either within the VM, with or without CLR's managed code +// exception handler coming into the picture. +// +// This is explained in detail (alongwith relevant changes) in the implementation of RaiseTheException (in excep.cpp). + +#undef SET_CE_RETHROW_FLAG_FOR_EX_CATCH +#define SET_CE_RETHROW_FLAG_FOR_EX_CATCH(expr) ((expr == TRUE) && \ + (g_pConfig->LegacyCorruptedStateExceptionsPolicy() == false) && \ + (CEHelper::IsProcessCorruptedStateException(GetCurrentExceptionCode(), FALSE) || \ + ((!__state.DidCatchSO()) && (!__state.DidCatchCxx()) && \ + CEHelper::IsLastActiveExceptionCorrupting(TRUE)))) + +#endif // FEATURE_CORRUPTING_EXCEPTIONS + +#undef EX_TRY +#define EX_TRY \ + EX_TRY_CUSTOM(CLRException::HandlerState, (::GetThreadNULLOk()), CLRLastThrownObjectException) + +#undef EX_TRY_CPP_ONLY +#define EX_TRY_CPP_ONLY \ + EX_TRY_CUSTOM_CPP_ONLY(CLRException::HandlerState, (::GetThreadNULLOk()), CLRLastThrownObjectException) + +// Faster version with thread, skipping GetThread call +#define EX_TRY_THREAD(pThread) \ + EX_TRY_CUSTOM(CLRException::HandlerState, (pThread, CLRException::HandlerState::ThreadIsNotNull), CLRLastThrownObjectException) + +#if defined(_DEBUG) + // Redefine GET_EXCEPTION to validate CLRLastThrownObjectException as much as possible. + #undef GET_EXCEPTION + #define GET_EXCEPTION() (__pException == NULL ? __defaultException.Validate() : __pException.GetValue()) +#endif // _DEBUG + +// When we throw an exception, we need stay in SO-intolerant state and +// probe for sufficient stack so that we don't SO during the processing. +#undef HANDLE_SO_TOLERANCE_FOR_THROW +#define HANDLE_SO_TOLERANCE_FOR_THROW STACK_PROBE_FOR_THROW(GetThread()); + +LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv); + +// Re-define the macro to add automatic restoration of the guard page to PAL_EXCEPT and PAL_EXCEPT_FILTER and +// friends. Note: RestoreGuardPage will only do work if the guard page is not present. +#undef PAL_SEH_RESTORE_GUARD_PAGE +#define PAL_SEH_RESTORE_GUARD_PAGE \ + if (__exCode == STATUS_STACK_OVERFLOW) \ + { \ + Thread *__pThread = GetThread(); \ + if (__pThread != NULL) \ + { \ + __pThread->RestoreGuardPage(); \ + } \ + } + +#undef EX_TRY_NOCATCH +#define EX_TRY_NOCATCH(ParamType, paramDef, paramRef) \ + PAL_TRY(ParamType, __EXparam, paramRef) \ + { \ + CLRException::HandlerState __state(::GetThreadNULLOk()); \ + PAL_TRY(ParamType, paramDef, __EXparam) \ + { + +#undef EX_END_NOCATCH +#define EX_END_NOCATCH \ + ; \ + } \ + PAL_FINALLY \ + { \ + __state.CleanupTry(); \ + } \ + PAL_ENDTRY \ + } \ + PAL_EXCEPT_FILTER(CLRNoCatchHandler) \ + { \ + } \ + PAL_ENDTRY + +// +// We need a way to identify an exception in managed code that is rethrown from a new exception in managed code +// when we get into our managed EH logic. Currently, we do that by checking the GC mode. If a thread has preemptive +// GC enabled, but the IP of the exception is in mangaed code, then it must be a rethrow from unmanaged code +// (including CLR code.) Therefore, we toggle the GC mode before the rethrow to indicate that. Note: we don't do +// this if we've caught one of our internal C++ Exception objects: by definition, those don't come from managed +// code, and this allows us to continue to use EX_RETHROW in no-trigger regions. +// +#undef EX_RETHROW +#define EX_RETHROW \ + do \ + { \ + /* don't embed file names in retail to save space and avoid IP */ \ + /* a findstr /n will allow you to locate it in a pinch */ \ + STRESS_LOG1(LF_EH, LL_INFO100, \ + "EX_RETHROW " INDEBUG(__FILE__) " line %d\n", __LINE__); \ + __pException.SuppressRelease(); \ + if ((!__state.DidCatchCxx()) && (GetThread() != NULL)) \ + { \ + if (GetThread()->PreemptiveGCDisabled()) \ + { \ + LOG((LF_EH, LL_INFO10, "EX_RETHROW: going preemptive\n")); \ + GetThread()->EnablePreemptiveGC(); \ + } \ + } \ + PAL_CPP_RETHROW; \ + } while (0) + +// +// Note: we only restore the guard page if we did _not_ catch a C++ exception, since a SO exception is a SEH +// exception. +// +// We also need to restore the SO tolerance state, including restoring the cookie for the current stack guard. +// +// For VM code EX_CATCH calls CLREXception::HandleState::SetupCatch(). +// When Stack guards are disabled we will tear down the process in +// CLREXception::HandleState::SetupCatch() if there is a StackOverflow. +// So we should not reach EX_ENDTRY when there is StackOverflow. +// This change cannot be done in ex.h as for all other code +// CLREXception::HandleState::SetupCatch() is not called rather +// EXception::HandleState::SetupCatch() is called which is a nop. +// +#undef EX_ENDTRY +#define EX_ENDTRY \ + PAL_CPP_ENDTRY \ + SO_INFRASTRUCTURE_CODE(if (__state.DidCatch()) { RESTORE_SO_TOLERANCE_STATE; }) \ + SO_INFRASTRUCTURE_CODE(if (__state.DidCatchSO()) { HANDLE_STACKOVERFLOW_AFTER_CATCH; }) \ + NO_SO_INFRASTRUCTURE_CODE_ASSERTE(!__state.DidCatchSO()) \ + + +// CLRException::GetErrorInfo below invokes GetComIPFromObjectRef +// that invokes ObjHeader::GetSyncBlock which has the INJECT_FAULT contract. +// +// This EX_CATCH_HRESULT implementation can be used in functions +// that have FORBID_FAULT contracts. +// +// However, failure due to OOM (or any other potential exception) in GetErrorInfo +// implies that we couldnt get the interface pointer from the objectRef and would be +// returned NULL. +// +// Thus, the scoped use of FAULT_NOT_FATAL macro. +#undef EX_CATCH_HRESULT +#define EX_CATCH_HRESULT(_hr) \ + EX_CATCH \ + { \ + (_hr) = GET_EXCEPTION()->GetHR(); \ + { \ + FAULT_NOT_FATAL(); \ + HRESULT hrErrorInfo = GET_EXCEPTION()->SetErrorInfo(); \ + if (FAILED(hrErrorInfo)) \ + { \ + (_hr) = hrErrorInfo; \ + } \ + } \ + _ASSERTE(FAILED(_hr)); \ + } \ + EX_END_CATCH(SwallowAllExceptions) + +#endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE + +// When collecting dumps, we need to ignore errors unless the user cancels. +#define EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED \ + EX_CATCH \ + { \ + /* Swallow the exception and keep going unless COR_E_OPERATIONCANCELED */ \ + /* was thrown. Used generating dumps, where rethrow will cancel dump. */ \ + } \ + EX_END_CATCH(RethrowCancelExceptions) + +// Only use this version to wrap single source lines, or it makes debugging painful. +#define CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED(sourceCode) \ + EX_TRY \ + { \ + sourceCode \ + } \ + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED + + +//============================================================================== +// High-level macros for common uses of EX_TRY. Try using these rather +// than the raw EX_TRY constructs. +//============================================================================== + +//=================================================================================== +// Macro for defining external entrypoints such as COM interop boundaries. +// The boundary will catch all exceptions (including terminals) and convert +// them into HR/IErrorInfo pairs as appropriate. +// +// Usage: +// +// HRESULT hr; ;; BEGIN will initialize HR +// BEGIN_EXTERNAL_ENTRYPOINT(&hr) +// <do managed stuff> ;; this part will execute in cooperative GC mode +// END_EXTERNAL_ENTRYPOINT +// return hr; +// +// Comments: +// The BEGIN macro will setup a Thread if necessary. It should only be called +// in preemptive mode. If you are calling it from cooperative mode, this implies +// we are executing "external" code in cooperative mode. The Reentrancy MDA +// complains about this. +// +// Only use this macro for actual boundaries between CLR and +// outside unmanaged code. If you want to connect internal pieces +// of CLR code, use EX_TRY instead. +//=================================================================================== +#ifdef MDA_SUPPORTED +NOINLINE BOOL HasIllegalReentrancyRare(); +#define HAS_ILLEGAL_REENTRANCY() (NULL != MDA_GET_ASSISTANT(Reentrancy) && HasIllegalReentrancyRare()) +#else +#define HAS_ILLEGAL_REENTRANCY() false +#endif + +#define BEGIN_EXTERNAL_ENTRYPOINT(phresult) \ + { \ + HRESULT *__phr = (phresult); \ + *__phr = S_OK; \ + _ASSERTE(GetThread() == NULL || \ + !GetThread()->PreemptiveGCDisabled()); \ + if (HAS_ILLEGAL_REENTRANCY()) \ + { \ + *__phr = COR_E_ILLEGAL_REENTRANCY; \ + } \ + else \ + if (!CanRunManagedCode()) \ + { \ + *__phr = E_PROCESS_SHUTDOWN_REENTRY; \ + } \ + else \ + { \ + MAKE_CURRENT_THREAD_AVAILABLE_EX(GetThreadNULLOk()); \ + if (CURRENT_THREAD == NULL) \ + { \ + CURRENT_THREAD = SetupThreadNoThrow(__phr); \ + } \ + if (CURRENT_THREAD != NULL) \ + { \ + BEGIN_SO_INTOLERANT_CODE_NOTHROW(CURRENT_THREAD, *__phr = COR_E_STACKOVERFLOW); \ + EX_TRY_THREAD(CURRENT_THREAD); \ + { \ + +#define END_EXTERNAL_ENTRYPOINT \ + } \ + EX_CATCH_HRESULT(*__phr); \ + END_SO_INTOLERANT_CODE; \ + } \ + } \ + } \ + +// This macro should be used at the entry points (e.g. COM interop boundaries) +// where CE's are not expected to get swallowed. +#define END_EXTERNAL_ENTRYPOINT_RETHROW_CORRUPTING_EXCEPTIONS_EX(fCond) \ + } \ + EX_CATCH \ + { \ + *__phr = GET_EXCEPTION()->GetHR(); \ + } \ + EX_END_CATCH(RethrowCorruptingExceptionsEx(fCond)); \ + END_SO_INTOLERANT_CODE; \ + } \ + } \ + } \ + +// This macro should be used at the entry points (e.g. COM interop boundaries) +// where CE's are not expected to get swallowed. +#define END_EXTERNAL_ENTRYPOINT_RETHROW_CORRUPTING_EXCEPTIONS \ + END_EXTERNAL_ENTRYPOINT_RETHROW_CORRUPTING_EXCEPTIONS_EX(TRUE) + + + +//============================================================================== + +// --------------------------------------------------------------------------- +// Inline implementations. Pay no attention to that man behind the curtain. +// --------------------------------------------------------------------------- + +inline CLRException::CLRException() + : m_throwableHandle(NULL) +{ + LIMITED_METHOD_CONTRACT; +} + +inline void CLRException::SetThrowableHandle(OBJECTHANDLE throwable) +{ + STRESS_LOG1(LF_EH, LL_INFO100, "in CLRException::SetThrowableHandle: obj = %x\n", throwable); + m_throwableHandle = throwable; +} + +inline EEException::EEException(RuntimeExceptionKind kind) + : m_kind(kind) +{ + LIMITED_METHOD_CONTRACT; +} + +inline EEException::EEException(HRESULT hr) + : m_kind(GetKindFromHR(hr)) +{ + LIMITED_METHOD_CONTRACT; +} + +inline EEMessageException::EEMessageException(HRESULT hr) + : EEException(GetKindFromHR(hr)), + m_hr(hr), + m_resID(0) +{ + WRAPPER_NO_CONTRACT; + + m_arg1.Printf("%.8x", hr); +} + +inline EEMessageException::EEMessageException(HRESULT hr, bool fUseCOMException) + : EEException(GetKindFromHR(hr, fUseCOMException)), + m_hr(hr), + m_resID(0) +{ + WRAPPER_NO_CONTRACT; + + m_arg1.Printf("%.8x", hr); +} + +//----------------------------------------------------------------------------- +// Constructor with lots of defaults (to 0 / null) +// kind -- "clr kind" of the exception +// resid -- resource id for message +// strings -- substitution text for message +inline EEMessageException::EEMessageException(RuntimeExceptionKind kind, UINT resID, LPCWSTR szArg1, LPCWSTR szArg2, + LPCWSTR szArg3, LPCWSTR szArg4, LPCWSTR szArg5, LPCWSTR szArg6) + : EEException(kind), + m_hr(EEException::GetHRFromKind(kind)), + m_resID(resID), + m_arg1(szArg1), + m_arg2(szArg2), + m_arg3(szArg3), + m_arg4(szArg4), + m_arg5(szArg5), + m_arg6(szArg6) +{ + WRAPPER_NO_CONTRACT; +} + +//----------------------------------------------------------------------------- +// Constructor with lots of defaults (to 0 / null) +// hr -- hresult that lead to this exception +// resid -- resource id for message +// strings -- substitution text for message +inline EEMessageException::EEMessageException(HRESULT hr, UINT resID, LPCWSTR szArg1, LPCWSTR szArg2, LPCWSTR szArg3, + LPCWSTR szArg4, LPCWSTR szArg5, LPCWSTR szArg6) + : EEException(GetKindFromHR(hr)), + m_hr(hr), + m_resID(resID), + m_arg1(szArg1), + m_arg2(szArg2), + m_arg3(szArg3), + m_arg4(szArg4), + m_arg5(szArg5), + m_arg6(szArg6) +{ +} + +//----------------------------------------------------------------------------- +// Constructor with no defaults +// kind -- "clr kind" of the exception +// hr -- hresult that lead to this exception +// resid -- resource id for message +// strings -- substitution text for message +inline EEMessageException::EEMessageException(RuntimeExceptionKind kind, HRESULT hr, UINT resID, LPCWSTR szArg1, + LPCWSTR szArg2, LPCWSTR szArg3, LPCWSTR szArg4, LPCWSTR szArg5, + LPCWSTR szArg6) + : EEException(kind), + m_hr(hr), + m_resID(resID), + m_arg1(szArg1), + m_arg2(szArg2), + m_arg3(szArg3), + m_arg4(szArg4), + m_arg5(szArg5), + m_arg6(szArg6) +{ + WRAPPER_NO_CONTRACT; +} + + +inline EEResourceException::EEResourceException(RuntimeExceptionKind kind, const SString &resourceName) + : EEException(kind), + m_resourceName(resourceName) +{ + WRAPPER_NO_CONTRACT; +} + + +inline EEFieldException::EEFieldException(FieldDesc *pField) + : EEException(kFieldAccessException), + m_pFD(pField), + m_pAccessingMD(NULL), + m_messageID(0) +{ + WRAPPER_NO_CONTRACT; +} + +inline EEFieldException::EEFieldException(FieldDesc *pField, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID) + : EEException(kFieldAccessException), + m_pFD(pField), + m_pAccessingMD(pAccessingMD), + m_additionalContext(additionalContext), + m_messageID(messageID) +{ +} + +inline EEMethodException::EEMethodException(MethodDesc *pMethod) + : EEException(kMethodAccessException), + m_pMD(pMethod), + m_pAccessingMD(NULL), + m_messageID(0) +{ + WRAPPER_NO_CONTRACT; +} + +inline EEMethodException::EEMethodException(MethodDesc *pMethod, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID) + : EEException(kMethodAccessException), + m_pMD(pMethod), + m_pAccessingMD(pAccessingMD), + m_additionalContext(additionalContext), + m_messageID(messageID) +{ +} + +inline EETypeAccessException::EETypeAccessException(MethodTable *pMT) + : EEException(kTypeAccessException), + m_pMT(pMT), + m_pAccessingMD(NULL), + m_messageID(0) +{ +} + +inline EETypeAccessException::EETypeAccessException(MethodTable *pMT, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID) + : EEException(kTypeAccessException), + m_pMT(pMT), + m_pAccessingMD(pAccessingMD), + m_additionalContext(additionalContext), + m_messageID(messageID) +{ +} + +inline EEArgumentException::EEArgumentException(RuntimeExceptionKind reKind, LPCWSTR pArgName, + LPCWSTR wszResourceName) + : EEException(reKind), + m_argumentName(pArgName), + m_resourceName(wszResourceName) +{ + WRAPPER_NO_CONTRACT; +} + + +class ObjrefException : public CLRException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + public: + + ObjrefException(); + ObjrefException(OBJECTREF throwable); + + private: + static const int c_type = 0x4F522020; // 'OR ' + + public: + // Dynamic type query for catchers + static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; } + virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; } + BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || CLRException::IsType(type); } + +protected: + virtual Exception *CloneHelper() + { + WRAPPER_NO_CONTRACT; + return new ObjrefException(); + } + + virtual Exception *DomainBoundCloneHelper(); +}; + + +class CLRLastThrownObjectException : public CLRException +{ + friend bool DebugIsEECxxExceptionPointer(void* pv); + + public: + CLRLastThrownObjectException(); + + private: + static const int c_type = 0x4C544F20; // 'LTO ' + + public: + // Dynamic type query for catchers + static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; } + virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; } + BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || CLRException::IsType(type); } + + #if defined(_DEBUG) + CLRLastThrownObjectException* Validate(); + #endif // _DEBUG + + protected: + virtual Exception *CloneHelper(); + + virtual Exception *DomainBoundCloneHelper(); + + virtual OBJECTREF CreateThrowable(); +}; + +// Returns true if the HRESULT maps to the RuntimeExceptionKind (hr => kind). +bool IsHRESULTForExceptionKind(HRESULT hr, RuntimeExceptionKind kind); + +#endif // _CLREX_H_ + |