Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-10-01 00:47:24 +0300
committerScott Mosier <smosier@microsoft.com>2015-10-01 00:47:24 +0300
commitad0323ab91a7b1469b42ca5457ddd631b94294fe (patch)
tree88fae57e1ec3aae90288463dc07e58f7aebc1de8 /src/Native/Runtime/EHHelpers.cpp
parent6763d16387778f126ec510c0421783952602f8f7 (diff)
Initial population of CoreRT Runtime files.
Diffstat (limited to 'src/Native/Runtime/EHHelpers.cpp')
-rw-r--r--src/Native/Runtime/EHHelpers.cpp466
1 files changed, 466 insertions, 0 deletions
diff --git a/src/Native/Runtime/EHHelpers.cpp b/src/Native/Runtime/EHHelpers.cpp
new file mode 100644
index 000000000..a377594d6
--- /dev/null
+++ b/src/Native/Runtime/EHHelpers.cpp
@@ -0,0 +1,466 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+#ifndef DACCESS_COMPILE
+#include "commontypes.h"
+#include "daccess.h"
+#include "commonmacros.h"
+#include "assert.h"
+#include "slist.h"
+#include "gcrhinterface.h"
+#include "module.h"
+#include "varint.h"
+#include "palredhawkcommon.h"
+#include "palredhawk.h"
+#include "holder.h"
+#include "crst.h"
+#include "rwlock.h"
+#include "runtimeinstance.h"
+#include "event.h"
+#include "threadstore.h"
+#include "regdisplay.h"
+#include "stackframeiterator.h"
+#include "thread.h"
+#include "stresslog.h"
+
+// Find the module containing the given address, which is a return address from a managed function. The
+// address may be to another managed function, or it may be to an unmanaged function, or it may be to a GC
+// hijack. The address may also refer to an EEType if we've been called from RhpGetClasslibFunction. If it is
+// a GC hijack, we will recgonize that and use the real return address, updating the address passed in.
+static Module * FindModuleRespectingReturnAddressHijacks(void ** pAddress)
+{
+ RuntimeInstance * pRI = GetRuntimeInstance();
+
+ // Try looking up the module assuming the address is for code first. Fall back to a read-only data looukp
+ // if that fails. If we have data indicating that the data case is more common then we can reverse the
+ // order of checks. Finally check read/write data: generic EETypes live there since they need to be fixed
+ // up at runtime to support unification.
+ Module * pModule = pRI->FindModuleByCodeAddress(*pAddress);
+ if (pModule == NULL)
+ {
+ pModule = pRI->FindModuleByReadOnlyDataAddress(*pAddress);
+
+ if (pModule == NULL)
+ pModule = pRI->FindModuleByDataAddress(*pAddress);
+
+ if (pModule == NULL)
+ {
+ // Hmmm... we didn't find a managed module for the given PC. We have a return address in unmanaged
+ // code, but it could be because the thread is hijacked for GC suspension. If it is then we should
+ // get the real return address and try again.
+ Thread * pCurThread = ThreadStore::GetCurrentThread();
+
+ if (!pCurThread->IsHijacked())
+ {
+ // The PC isn't in a managed module, and there is no hijack in place, so we have no EH info.
+ return NULL;
+ }
+
+ // Update the PC passed in to reflect the correct return address.
+ *pAddress = pCurThread->GetHijackedReturnAddress();
+
+ pModule = pRI->FindModuleByCodeAddress(*pAddress);
+ }
+ }
+
+ return pModule;
+}
+
+COOP_PINVOKE_HELPER(Boolean, RhpEHEnumInitFromStackFrameIterator, (StackFrameIterator* pFrameIter, void ** pMethodStartAddressOut, EHEnum* pEHEnum))
+{
+ ICodeManager * pCodeManager = pFrameIter->GetCodeManager();
+ pEHEnum->m_pCodeManager = pCodeManager;
+
+ return pCodeManager->EHEnumInit(pFrameIter->GetMethodInfo(), pMethodStartAddressOut, &pEHEnum->m_state);
+}
+
+COOP_PINVOKE_HELPER(Boolean, RhpEHEnumNext, (EHEnum* pEHEnum, EHClause* pEHClause))
+{
+ return pEHEnum->m_pCodeManager->EHEnumNext(&pEHEnum->m_state, pEHClause);
+}
+
+// The EH dispatch code needs to know the original return address of a method, even if it has been hijacked.
+// We provide this here without modifying the hijack and the EHJump helper will honor the hijack when it
+// attempts to dispatch up the stack.
+COOP_PINVOKE_HELPER(void*, RhpGetUnhijackedReturnAddress, (void** ppvReturnAddressLocation))
+{
+ return ThreadStore::GetCurrentThread()->GetUnhijackedReturnAddress(ppvReturnAddressLocation);
+}
+
+//------------------------------------------------------------------------------------------------------------
+// @TODO:EXCEPTIONS: the code below is related to throwing exceptions out of Rtm. If we did not have to throw
+// out of Rtm, then we would note have to have the code below to get a classlib exception object given
+// an exception id, or the special functions to back up the MDIL THROW_* instructions, or the allocation
+// failure helper. If we could move to a world where we never throw out of Rtm, perhaps by moving parts
+// of Rtm that do need to throw out to Bartok- or Binder-generated functions, then we could remove all of this.
+//------------------------------------------------------------------------------------------------------------
+
+
+// Constants used with RhpGetClasslibFunction, to indicate which classlib function
+// we are interested in.
+// Note: make sure you change the def in rtm\System\Runtime\exceptionhandling.cs if you change this!
+enum ClasslibFunctionId
+{
+ GetRuntimeException = 0,
+ FailFast = 1,
+ UnhandledExceptionHandler = 2,
+ AppendExceptionStackFrame = 3,
+};
+
+// Unmanaged helper to locate one of two classlib-provided functions that the runtime needs to
+// implement throwing of exceptions out of Rtm, and fail-fast. This may return NULL if the classlib
+// found via the provided address does not have the necessary exports.
+COOP_PINVOKE_HELPER(void *, RhpGetClasslibFunction, (void * address, ClasslibFunctionId functionId))
+{
+ // Find the module contianing the given address, which is an address into some managed module. It could
+ // be code, or it could be an EEType. No matter what, it's an address into a managed module in some non-Rtm
+ // type system.
+ Module * pModule = FindModuleRespectingReturnAddressHijacks(&address);
+
+ // If the address isn't in a managed module then we have no classlib function.
+ if (pModule == NULL)
+ {
+ return NULL;
+ }
+
+ // Now, find the classlib module that the first module was compiled against. This one will contain definitions
+ // for the classlib functions we need here at runtime.
+ Module * pClasslibModule = pModule->GetClasslibModule();
+ ASSERT(pClasslibModule != NULL);
+
+ // Lookup the method and return it. If we don't find it, we just return NULL.
+ void * pMethod = NULL;
+
+ if (functionId == GetRuntimeException)
+ {
+ pMethod = pClasslibModule->GetClasslibRuntimeExceptionHelper();
+ }
+ else if (functionId == AppendExceptionStackFrame)
+ {
+ pMethod = pClasslibModule->GetClasslibAppendExceptionStackFrameHelper();
+ }
+ else if (functionId == FailFast)
+ {
+ pMethod = pClasslibModule->GetClasslibFailFastHelper();
+ }
+ else if (functionId == UnhandledExceptionHandler)
+ {
+ pMethod = pClasslibModule->GetClasslibUnhandledExceptionHandlerHelper();
+ }
+
+ return pMethod;
+}
+
+COOP_PINVOKE_HELPER(void, RhpValidateExInfoStack, ())
+{
+ Thread * pThisThread = ThreadStore::GetCurrentThread();
+ pThisThread->ValidateExInfoStack();
+}
+
+COOP_PINVOKE_HELPER(void, RhpClearThreadDoNotTriggerGC, ())
+{
+ Thread * pThisThread = ThreadStore::GetCurrentThread();
+
+ if (!pThisThread->IsDoNotTriggerGcSet())
+ RhFailFast();
+
+ pThisThread->ClearDoNotTriggerGc();
+}
+
+COOP_PINVOKE_HELPER(void, RhpSetThreadDoNotTriggerGC, ())
+{
+ Thread * pThisThread = ThreadStore::GetCurrentThread();
+
+ if (pThisThread->IsDoNotTriggerGcSet())
+ RhFailFast();
+
+ pThisThread->SetDoNotTriggerGc();
+}
+
+COOP_PINVOKE_HELPER(Int32, RhGetModuleFileName, (HANDLE moduleHandle, _Out_ wchar_t** pModuleNameOut))
+{
+ return PalGetModuleFileName(pModuleNameOut, moduleHandle);
+}
+
+COOP_PINVOKE_HELPER(void, RhpCopyContextFromExInfo,
+ (void * pOSContext, Int32 cbOSContext, PAL_LIMITED_CONTEXT * pPalContext))
+{
+ ASSERT(cbOSContext >= sizeof(CONTEXT));
+ CONTEXT* pContext = (CONTEXT *)pOSContext;
+#ifdef _AMD64_
+ pContext->Rip = pPalContext->IP;
+ pContext->Rsp = pPalContext->Rsp;
+ pContext->Rbp = pPalContext->Rbp;
+ pContext->Rdi = pPalContext->Rdi;
+ pContext->Rsi = pPalContext->Rsi;
+ pContext->Rax = pPalContext->Rax;
+ pContext->Rbx = pPalContext->Rbx;
+ pContext->R12 = pPalContext->R12;
+ pContext->R13 = pPalContext->R13;
+ pContext->R14 = pPalContext->R14;
+ pContext->R15 = pPalContext->R15;
+#elif defined(_X86_)
+ pContext->Eip = pPalContext->IP;
+ pContext->Esp = pPalContext->Rsp;
+ pContext->Ebp = pPalContext->Rbp;
+ pContext->Edi = pPalContext->Rdi;
+ pContext->Esi = pPalContext->Rsi;
+ pContext->Eax = pPalContext->Rax;
+ pContext->Ebx = pPalContext->Rbx;
+#elif defined(_ARM_)
+ pContext->R0 = pPalContext->R0;
+ pContext->R4 = pPalContext->R4;
+ pContext->R5 = pPalContext->R5;
+ pContext->R6 = pPalContext->R6;
+ pContext->R7 = pPalContext->R7;
+ pContext->R8 = pPalContext->R8;
+ pContext->R9 = pPalContext->R9;
+ pContext->R10 = pPalContext->R10;
+ pContext->R11 = pPalContext->R11;
+ pContext->Sp = pPalContext->SP;
+ pContext->Lr = pPalContext->LR;
+ pContext->Pc = pPalContext->IP;
+#else
+#error Not Implemented for this architecture -- RhpCopyContextFromExInfo
+#endif
+}
+
+
+#if defined(FEATURE_CLR_EH) && (defined(_AMD64_) || defined(_ARM_) || defined(_X86_))
+struct DISPATCHER_CONTEXT
+{
+ UIntNative ControlPc;
+ // N.B. There is more here (so this struct isn't the right size), but we ignore everything else
+};
+
+EXTERN_C void REDHAWK_CALLCONV RhpFailFastForPInvokeExceptionPreemp(IntNative PInvokeCallsiteReturnAddr,
+ void* pExceptionRecord, void* pContextRecord);
+EXTERN_C void REDHAWK_CALLCONV RhpFailFastForPInvokeExceptionCoop(IntNative PInvokeCallsiteReturnAddr,
+ void* pExceptionRecord, void* pContextRecord);
+Int32 __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs);
+
+EXTERN_C Int32 __stdcall RhpPInvokeExceptionGuard(PEXCEPTION_RECORD pExceptionRecord,
+ UIntNative MemoryStackFp,
+ PCONTEXT pContextRecord,
+ DISPATCHER_CONTEXT * pDispatcherContext)
+{
+#ifdef APP_LOCAL_RUNTIME
+ //
+ // When running on Windows 8.1 RTM, we cannot register our vectored exception handler, because that
+ // version of MRT100.dll does not support it. However, the binder sets this function as the personality
+ // routine for every reverse p/invoke, so we can handle hardware exceptions from managed code here.
+ //
+ EXCEPTION_POINTERS pointers;
+ pointers.ExceptionRecord = pExceptionRecord;
+ pointers.ContextRecord = pContextRecord;
+
+ if (RhpVectoredExceptionHandler(&pointers) == EXCEPTION_CONTINUE_EXECUTION)
+ return ExceptionContinueExecution;
+#endif //APP_LOCAL_RUNTIME
+
+ Thread * pThread = ThreadStore::GetCurrentThread();
+
+ // If the thread is currently in the "do not trigger GC" mode, we must not allocate, we must not reverse pinvoke, or
+ // return from a pinvoke. All of these things will deadlock with the GC and they all become increasingly likely as
+ // exception dispatch kicks off. So we just nip this in the bud as early as possible with a FailFast. The most
+ // likely case where this occurs is in our GC-callouts for Jupiter lifetime management -- in that case, we have
+ // managed code that calls to native code (without pinvoking) which might have a bug that causes an AV.
+ if (pThread->IsDoNotTriggerGcSet())
+ RhFailFast();
+
+ // We promote exceptions that were not converted to managed exceptions to a FailFast. However, we have to
+ // be careful because we got here via OS SEH infrastructure and, therefore, don't know what GC mode we're
+ // currently in. As a result, since we're calling back into managed code to handle the FailFast, we must
+ // correctly call either a NativeCallable or a RuntimeExport version of the same method.
+ if (pThread->IsCurrentThreadInCooperativeMode())
+ RhpFailFastForPInvokeExceptionCoop((IntNative)pDispatcherContext->ControlPc, pExceptionRecord, pContextRecord);
+ else
+ RhpFailFastForPInvokeExceptionPreemp((IntNative)pDispatcherContext->ControlPc, pExceptionRecord, pContextRecord);
+
+ return 0;
+}
+#else
+EXTERN_C Int32 RhpPInvokeExceptionGuard()
+{
+#ifdef FEATURE_CLR_EH
+ ASSERT_UNCONDITIONALLY("RhpPInvokeExceptionGuard NYI for this architecture!");
+#else
+ ASSERT_UNCONDITIONALLY("RhpPInvokeExceptionGuard NYI for this version of Redhawk!");
+#endif
+ RhFailFast();
+ return 0;
+}
+#endif // FEATURE_CLR_EH && (_AMD64_ || _ARM_)
+
+#ifdef FEATURE_CLR_EH
+#if defined(_AMD64_) || defined(_ARM_) || defined(_X86_)
+EXTERN_C REDHAWK_API void __fastcall RhpThrowHwEx();
+#else
+COOP_PINVOKE_HELPER(void, RhpThrowHwEx, ())
+{
+ ASSERT_UNCONDITIONALLY("RhpThrowHwEx NYI for this architecture!");
+}
+COOP_PINVOKE_HELPER(void, RhpThrowEx, ())
+{
+ ASSERT_UNCONDITIONALLY("RhpThrowEx NYI for this architecture!");
+}
+COOP_PINVOKE_HELPER(void, RhpCallCatchFunclet, ())
+{
+ ASSERT_UNCONDITIONALLY("RhpCallCatchFunclet NYI for this architecture!");
+}
+COOP_PINVOKE_HELPER(void, RhpCallFinallyFunclet, ())
+{
+ ASSERT_UNCONDITIONALLY("RhpCallFinallyFunclet NYI for this architecture!");
+}
+COOP_PINVOKE_HELPER(void, RhpCallFilterFunclet, ())
+{
+ ASSERT_UNCONDITIONALLY("RhpCallFilterFunclet NYI for this architecture!");
+}
+COOP_PINVOKE_HELPER(void, RhpRethrow, ())
+{
+ ASSERT_UNCONDITIONALLY("RhpRethrow NYI for this architecture!");
+}
+
+EXTERN_C void* RhpCallCatchFunclet2 = NULL;
+EXTERN_C void* RhpCallFinallyFunclet2 = NULL;
+EXTERN_C void* RhpCallFilterFunclet2 = NULL;
+EXTERN_C void* RhpThrowEx2 = NULL;
+EXTERN_C void* RhpThrowHwEx2 = NULL;
+EXTERN_C void* RhpRethrow2 = NULL;
+#endif
+#endif // FEATURE_CLR_EH
+
+#if defined(_AMD64_) || defined(_X86_)
+EXTERN_C void __fastcall RhpAssignRefEDX();
+EXTERN_C void __fastcall RhpCheckedAssignRefEDX();
+#define RhpAssignRefAvLocationEDX RhpAssignRefEDX
+#define RhpCheckedAssignRefAvLocationEDX RhpCheckedAssignRefEDX
+#define APPEND_REG1_TO_NAME(name) name##EDX
+#elif defined(_ARM_)
+EXTERN_C void __fastcall RhpAssignRefAvLocationR1();
+EXTERN_C void __fastcall RhpCheckedAssignRefAvLocationR1();
+#define APPEND_REG1_TO_NAME(name) name##R1
+#else
+#error "Unknown Architecture"
+#endif
+EXTERN_C void * RhpCheckedLockCmpXchgAVLocation;
+EXTERN_C void * RhpCheckedXchgAVLocation;
+EXTERN_C void * RhpCopyMultibyteDestAVLocation;
+EXTERN_C void * RhpCopyMultibyteSrcAVLocation;
+EXTERN_C void * RhpCopyMultibyteNoGCRefsDestAVLocation;
+EXTERN_C void * RhpCopyMultibyteNoGCRefsSrcAVLocation;
+
+static bool InWriteBarrierHelper(UIntNative faultingIP)
+{
+ static UIntNative writeBarrierAVLocations[] =
+ {
+ (UIntNative)&APPEND_REG1_TO_NAME(RhpAssignRefAvLocation),
+ (UIntNative)&APPEND_REG1_TO_NAME(RhpCheckedAssignRefAvLocation),
+ (UIntNative)&RhpCheckedLockCmpXchgAVLocation,
+ (UIntNative)&RhpCheckedXchgAVLocation,
+ (UIntNative)&RhpCopyMultibyteDestAVLocation,
+ (UIntNative)&RhpCopyMultibyteSrcAVLocation,
+ (UIntNative)&RhpCopyMultibyteNoGCRefsDestAVLocation,
+ (UIntNative)&RhpCopyMultibyteNoGCRefsSrcAVLocation,
+ };
+
+ // compare the IP against the list of known possible AV locations in the write barrier helpers
+ for (size_t i = 0; i < sizeof(writeBarrierAVLocations)/sizeof(writeBarrierAVLocations[0]); i++)
+ {
+ if (writeBarrierAVLocations[i] == faultingIP)
+ return true;
+ }
+ return false;
+}
+
+static UIntNative UnwindWriteBarrierToCaller(_CONTEXT * pContext)
+{
+#if defined(_DEBUG)
+ UIntNative faultingIP = pContext->GetIP();
+ ASSERT(InWriteBarrierHelper(faultingIP));
+#endif
+#if defined(_AMD64_) || defined(_X86_)
+ // simulate a ret instruction
+ UIntNative sp = pContext->GetSP(); // get the stack pointer
+ UIntNative adjustedFaultingIP = *(UIntNative *)sp - 5; // call instruction will be 6 bytes - act as if start of call instruction + 1 were the faulting IP
+ pContext->SetSP(sp+sizeof(UIntNative)); // pop the stack
+#elif defined(_ARM_)
+ UIntNative adjustedFaultingIP = pContext->GetLR() - 2; // bl instruction will be 4 bytes - act as if start of call instruction + 2 were the faulting IP
+#else
+#error "Unknown Architecture"
+#endif
+ return adjustedFaultingIP;
+}
+
+Int32 __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs)
+{
+ UIntNative faultingIP = pExPtrs->ContextRecord->GetIP();
+#ifdef FEATURE_CLR_EH
+ ICodeManager * pCodeManager = GetRuntimeInstance()->FindCodeManagerByAddress((PTR_VOID)faultingIP);
+ UIntNative faultCode = pExPtrs->ExceptionRecord->ExceptionCode;
+ if ((pCodeManager != NULL) || (faultCode == STATUS_ACCESS_VIOLATION && InWriteBarrierHelper(faultingIP)))
+ {
+ if (faultCode == STATUS_ACCESS_VIOLATION)
+ {
+ if (pExPtrs->ExceptionRecord->ExceptionInformation[1] < NULL_AREA_SIZE)
+ faultCode = STATUS_REDHAWK_NULL_REFERENCE;
+ if (pCodeManager == NULL)
+ {
+ // we were AV-ing in a write barrier helper - unwind our way to our caller
+ faultingIP = UnwindWriteBarrierToCaller(pExPtrs->ContextRecord);
+ }
+ }
+ else if (faultCode == STATUS_STACK_OVERFLOW)
+ {
+ ASSERT_UNCONDITIONALLY("managed stack overflow");
+ RhFailFast2(pExPtrs->ExceptionRecord, pExPtrs->ContextRecord);
+ }
+
+ pExPtrs->ContextRecord->SetIP((UIntNative)&RhpThrowHwEx);
+ pExPtrs->ContextRecord->SetArg0Reg(faultCode);
+ pExPtrs->ContextRecord->SetArg1Reg(faultingIP);
+
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ else
+#endif // FEATURE_CLR_EH
+ {
+ static UInt8 *s_pbRuntimeModuleLower = NULL;
+ static UInt8 *s_pbRuntimeModuleUpper = NULL;
+
+ // If this is the first time through this path then calculate the upper and lower bounds of the
+ // runtime module. Note we could be racing to calculate this but it doesn't matter since the results
+ // should always agree.
+ if ((s_pbRuntimeModuleLower == NULL) || (s_pbRuntimeModuleUpper == NULL))
+ {
+ // Get the module handle for this runtime. Do this by passing an address definitely within the
+ // module (the address of this function) to GetModuleHandleEx with the "from address" flag.
+ HANDLE hRuntimeModule = PalGetModuleHandleFromPointer(RhpVectoredExceptionHandler);
+ if (!hRuntimeModule)
+ {
+ ASSERT_UNCONDITIONALLY("Failed to locate our own module handle");
+ RhFailFast();
+ }
+
+ PalGetModuleBounds(hRuntimeModule, &s_pbRuntimeModuleLower, &s_pbRuntimeModuleUpper);
+ }
+
+ if (((UInt8*)faultingIP >= s_pbRuntimeModuleLower) && ((UInt8*)faultingIP < s_pbRuntimeModuleUpper))
+ {
+ // Generally any form of hardware exception within the runtime itself is considered a fatal error.
+ // Note this includes the managed code within the runtime. Eventually the range check above will
+ // have to get a little more complex to deal with A/Vs in runtime helpers that we want to
+ // translate into managed exceptions (e.g. null ref exceptions in the write barriers or interface
+ // invocation stubs). Until FEATURE_CLR_EH comes fully online this simpler range check is
+ // sufficient.
+ ASSERT_UNCONDITIONALLY("Hardware exception raised inside the runtime.");
+ RhFailFast2(pExPtrs->ExceptionRecord, pExPtrs->ContextRecord);
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+}
+
+
+#endif // !DACCESS_COMPILE