diff options
author | Jan Kotas <jkotas@microsoft.com> | 2016-02-18 20:38:26 +0300 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2016-03-03 23:41:21 +0300 |
commit | 70cfd4b0910c1deff3fe5de3b2ad150da9bffa91 (patch) | |
tree | 6f4f50828b2df81c89bd40476f35b03c071465fe /src/Native/Runtime/windows | |
parent | 04831187678eee22bebb262f87ecac4b9ed2fbd2 (diff) |
Exception handling on Windows
- Add CoffNativeCodeManager that is able to decode the Windows OS native unwind info
- Append EH info to the Windows OS unwind info blob in the compiler
Diffstat (limited to 'src/Native/Runtime/windows')
-rw-r--r-- | src/Native/Runtime/windows/CoffNativeCodeManager.cpp | 504 | ||||
-rw-r--r-- | src/Native/Runtime/windows/CoffNativeCodeManager.h | 89 |
2 files changed, 593 insertions, 0 deletions
diff --git a/src/Native/Runtime/windows/CoffNativeCodeManager.cpp b/src/Native/Runtime/windows/CoffNativeCodeManager.cpp new file mode 100644 index 000000000..f5993843f --- /dev/null +++ b/src/Native/Runtime/windows/CoffNativeCodeManager.cpp @@ -0,0 +1,504 @@ +// 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. +#include "common.h" + +#include <windows.h> + +#include "CommonTypes.h" +#include "CommonMacros.h" +#include "daccess.h" +#include "PalRedhawkCommon.h" +#include "regdisplay.h" +#include "ICodemanager.h" +#include "CoffNativeCodeManager.h" +#include "varint.h" + +#include "CommonMacros.inl" + +#define UBF_FUNC_KIND_MASK 0x03 +#define UBF_FUNC_KIND_ROOT 0x00 +#define UBF_FUNC_KIND_HANDLER 0x01 +#define UBF_FUNC_KIND_FILTER 0x02 + +#define UBF_FUNC_HAS_EHINFO 0x04 + +#if defined(_TARGET_AMD64_) + +// +// The following structures are defined in Windows x64 unwind info specification +// http://www.bing.com/search?q=msdn+Exception+Handling+x64 +// + +typedef union _UNWIND_CODE { + struct { + uint8_t CodeOffset; + uint8_t UnwindOp : 4; + uint8_t OpInfo : 4; + }; + + uint16_t FrameOffset; +} UNWIND_CODE, *PUNWIND_CODE; + +#define UNW_FLAG_NHANDLER 0x0 +#define UNW_FLAG_EHANDLER 0x1 +#define UNW_FLAG_UHANDLER 0x2 +#define UNW_FLAG_CHAININFO 0x4 + +typedef struct _UNWIND_INFO { + uint8_t Version : 3; + uint8_t Flags : 5; + uint8_t SizeOfProlog; + uint8_t CountOfUnwindCodes; + uint8_t FrameRegister : 4; + uint8_t FrameOffset : 4; + UNWIND_CODE UnwindCode[1]; +} UNWIND_INFO, *PUNWIND_INFO; + +typedef DPTR(struct _UNWIND_INFO) PTR_UNWIND_INFO; +typedef DPTR(union _UNWIND_CODE) PTR_UNWIND_CODE; + +#endif // _TARGET_AMD64_ + +static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFunction, /* out */ size_t * pSize) +{ +#if defined(_TARGET_AMD64_) + PTR_UNWIND_INFO pUnwindInfo(dac_cast<PTR_UNWIND_INFO>(moduleBase + pRuntimeFunction->UnwindInfoAddress)); + + size_t size = offsetof(UNWIND_INFO, UnwindCode) + sizeof(UNWIND_CODE) * pUnwindInfo->CountOfUnwindCodes; + + // Chained unwind info is not supported at this time + ASSERT((pUnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0); + + if (pUnwindInfo->Flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER)) + { + // Personality routine + size = ALIGN_UP(size, sizeof(DWORD)) + sizeof(DWORD); + } + + *pSize = size; + + return pUnwindInfo; + +#elif defined(_TARGET_ARM_) + + // if this function uses packed unwind data then at least one of the two least significant bits + // will be non-zero. if this is the case then there will be no xdata record to enumerate. + ASSERT((pRuntimeFunction->UnwindData & 0x3) == 0); + + // compute the size of the unwind info + PTR_TADDR xdata = dac_cast<PTR_TADDR>(pRuntimeFunction->UnwindData + moduleBase); + + ULONG epilogScopes = 0; + ULONG unwindWords = 0; + ULONG size = 0; + + if ((xdata[0] >> 23) != 0) + { + size = 4; + epilogScopes = (xdata[0] >> 23) & 0x1f; + unwindWords = (xdata[0] >> 28) & 0x0f; + } + else + { + size = 8; + epilogScopes = xdata[1] & 0xffff; + unwindWords = (xdata[1] >> 16) & 0xff; + } + + if (!(xdata[0] & (1 << 21))) + size += 4 * epilogScopes; + + size += 4 * unwindWords; + + if ((xdata[0] & (1 << 20)) != 0) + { + // Personality routine + size += 4; + } + + *pSize = size; + return xdata; +#else + #error unexpected target architecture +#endif +} + + +CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, UInt32 nRuntimeFunctionTable) + : m_moduleBase(moduleBase), m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable) +{ +} + +CoffNativeCodeManager::~CoffNativeCodeManager() +{ +} + +static int LookupUnwindInfoForMethod(UInt32 relativePc, + PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, + int low, + int high) +{ +#ifdef TARGET_ARM + relativePc |= THUMB_CODE; +#endif + + // Entries are sorted and terminated by sentinel value (DWORD)-1 + + // Binary search the RUNTIME_FUNCTION table + // Use linear search once we get down to a small number of elements + // to avoid Binary search overhead. + while (high - low > 10) + { + int middle = low + (high - low) / 2; + + PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle; + if (relativePc < pFunctionEntry->BeginAddress) + { + high = middle - 1; + } + else + { + low = middle; + } + } + + for (int i = low; i <= high; ++i) + { + // This is safe because of entries are terminated by sentinel value (DWORD)-1 + PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1); + + if (relativePc < pNextFunctionEntry->BeginAddress) + { + PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + i; + if (relativePc >= pFunctionEntry->BeginAddress) + { + return i; + } + break; + } + } + + return -1; +} + +struct CoffNativeMethodInfo +{ + PTR_RUNTIME_FUNCTION mainRuntimeFunction; + PTR_RUNTIME_FUNCTION runtimeFunction; + bool executionAborted; +}; + +// Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo +static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big"); + +bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC, + MethodInfo * pMethodInfoOut, + UInt32 * pCodeOffset) +{ + CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut; + + TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase; + + int MethodIndex = LookupUnwindInfoForMethod((UInt32)relativePC, m_pRuntimeFunctionTable, + 0, m_nRuntimeFunctionTable - 1); + if (MethodIndex < 0) + return false; + + PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex; + + pMethodInfo->runtimeFunction = pRuntimeFunction; + + // The runtime function could correspond to a funclet. We need to get to the + // runtime function of the main method. + for (;;) + { + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pRuntimeFunction, &unwindDataBlobSize); + + uint8_t unwindBlockFlags = *(dac_cast<DPTR(uint8_t)>(pUnwindDataBlob) + unwindDataBlobSize); + if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) == UBF_FUNC_KIND_ROOT) + break; + + pRuntimeFunction--; + } + + pMethodInfo->mainRuntimeFunction = pRuntimeFunction; + + pMethodInfo->executionAborted = false; + + *pCodeOffset = (UInt32)(relativePC - pMethodInfo->mainRuntimeFunction->BeginAddress); + + return true; +} + +bool CoffNativeCodeManager::IsFunclet(MethodInfo * pMethInfo) +{ + CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethInfo; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pMethodInfo->runtimeFunction, &unwindDataBlobSize); + + uint8_t unwindBlockFlags = *(dac_cast<DPTR(uint8_t)>(pUnwindDataBlob) + unwindDataBlobSize); + + // A funclet will have an entry in funclet to main method map + return (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT; +} + +PTR_VOID CoffNativeCodeManager::GetFramePointer(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet) +{ + // If the method has EHinfo then it is guaranteed to have a frame pointer. + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + + // Get to unwind info + PTR_UNWIND_INFO pUnwindInfo(dac_cast<PTR_UNWIND_INFO>(m_moduleBase + pNativeMethodInfo->runtimeFunction->UnwindInfoAddress)); + + if (pUnwindInfo->FrameRegister != 0) + { + return (PTR_VOID)pRegisterSet->GetFP(); + } + + return NULL; +} + +// void EnumGCRefs(PTR_VOID pGCInfo, UINT32 curOffs, REGDISPLAY * pRD, GCEnumContext * hCallback, bool executionAborted); + +void CoffNativeCodeManager::EnumGcRefs(MethodInfo * pMethodInfo, + UInt32 codeOffset, + REGDISPLAY * pRegisterSet, + GCEnumContext * hCallback) +{ + // @TODO: CORERT: PInvoke transitions + +#if 0 + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + + SIZE_T nUnwindDataSize; + PTR_VOID pUnwindData = GetUnwindDataBlob(dac_cast<TADDR>(m_pvStartRange), &pNativeMethodInfo->mainRuntimeFunction, &nUnwindDataSize); + + // GCInfo immediatelly follows unwind data + PTR_VOID pGCInfo = dac_cast<PTR_VOID>(dac_cast<TADDR>(pUnwindData) + nUnwindDataSize + 1); + + ::EnumGCRefs(pGCInfo, codeOffset, pRegisterSet, hCallback, pNativeMethodInfo->executionAborted); +#endif +} + +UIntNative CoffNativeCodeManager::GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet) +{ + // @TODO: CORERT: GetConservativeUpperBoundForOutgoingArgs + assert(false); + return false; +} + +bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, + UInt32 codeOffset, + REGDISPLAY * pRegisterSet, // in/out + PTR_VOID * ppPreviousTransitionFrame) // out +{ + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + + // @TODO: CORERT: PInvoke transitions + *ppPreviousTransitionFrame = NULL; + + CONTEXT context; + KNONVOLATILE_CONTEXT_POINTERS contextPointers; + +#ifdef _DEBUG + memset(&context, 0xDD, sizeof(context)); + memset(&contextPointers, 0xDD, sizeof(contextPointers)); +#endif + +#define FOR_EACH_NONVOLATILE_REGISTER(F) \ + F(Rax) F(Rcx) F(Rdx) F(Rbx) F(Rbp) F(Rsi) F(Rdi) F(R8) F(R9) F(R10) F(R11) F(R12) F(R13) F(R14) F(R15) + +#define REGDISPLAY_TO_CONTEXT(reg) \ + contextPointers.reg = (PDWORD64) pRegisterSet->p##reg; \ + if (pRegisterSet->p##reg != NULL) context.reg = *(pRegisterSet->p##reg); + +#define CONTEXT_TO_REGDISPLAY(reg) \ + pRegisterSet->p##reg = (PTR_UIntNative) contextPointers.reg; + + FOR_EACH_NONVOLATILE_REGISTER(REGDISPLAY_TO_CONTEXT); + + memcpy(&context.Xmm6, pRegisterSet->Xmm, sizeof(pRegisterSet->Xmm)); + + context.Rsp = pRegisterSet->SP; + context.Rip = pRegisterSet->IP; + + SIZE_T EstablisherFrame; + PVOID HandlerData; + + RtlVirtualUnwind(NULL, + dac_cast<TADDR>(m_moduleBase), + pRegisterSet->IP, + (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction, + &context, + &HandlerData, + &EstablisherFrame, + &contextPointers); + + pRegisterSet->SP = context.Rsp; + pRegisterSet->IP = context.Rip; + + pRegisterSet->pIP = PTR_PCODE(pRegisterSet->SP - sizeof(TADDR)); + + memcpy(pRegisterSet->Xmm, &context.Xmm6, sizeof(pRegisterSet->Xmm)); + + FOR_EACH_NONVOLATILE_REGISTER(CONTEXT_TO_REGDISPLAY); + +#undef FOR_EACH_NONVOLATILE_REGISTER +#undef REGDISPLAY_TO_CONTEXT +#undef CONTEXT_TO_REGDISPLAY + + return true; +} + +bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, + UInt32 codeOffset, + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind) // out +{ + // @TODO: CORERT: GetReturnAddressHijackInfo + return false; +} + +void CoffNativeCodeManager::UnsynchronizedHijackMethodLoops(MethodInfo * pMethodInfo) +{ + // @TODO: CORERT: UnsynchronizedHijackMethodLoops +} + +void CoffNativeCodeManager::RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, UInt32 * pCodeOffset) +{ + // GCInfo decoder needs to know whether execution of the method is aborted + // while querying for gc-info. But ICodeManager::EnumGCRef() doesn't receive any + // flags from mrt. Call to this method is used as a cue to mark the method info + // as execution aborted. Note - if pMethodInfo was cached, this scheme would not work. + // + // If the method has EH, then JIT will make sure the method is fully interruptible + // and we will have GC-info available at the faulting address as well. + + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + pNativeMethodInfo->executionAborted = true; + + return; +} + +struct CoffEHEnumState +{ + PTR_UInt8 pEHInfo; + UInt32 uClause; + UInt32 nClauses; +}; + +// Ensure that CoffEHEnumState fits into the space reserved by EHEnumState +static_assert(sizeof(CoffEHEnumState) <= sizeof(EHEnumState), "CoffEHEnumState too big"); + +bool CoffNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumStateOut) +{ + assert(pMethodInfo != NULL); + assert(pMethodStartAddress != NULL); + assert(pEHEnumStateOut != NULL); + + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + CoffEHEnumState * pEnumState = (CoffEHEnumState *)pEHEnumStateOut; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->mainRuntimeFunction, &unwindDataBlobSize); + + uint8_t unwindBlockFlags = *(dac_cast<DPTR(uint8_t)>(pUnwindDataBlob) + unwindDataBlobSize); + + // return if there is no EH info associated with this method + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) == 0) + { + return false; + } + + *pMethodStartAddress = dac_cast<PTR_VOID>(m_moduleBase + pNativeMethodInfo->mainRuntimeFunction->BeginAddress); + + pEnumState->pEHInfo = dac_cast<PTR_UInt8>(pUnwindDataBlob) + unwindDataBlobSize + 1; + pEnumState->uClause = 0; + pEnumState->nClauses = VarInt::ReadUnsigned(pEnumState->pEHInfo); + + return true; +} + +bool CoffNativeCodeManager::EHEnumNext(EHEnumState * pEHEnumState, EHClause * pEHClauseOut) +{ + assert(pEHEnumState != NULL); + assert(pEHClauseOut != NULL); + + CoffEHEnumState * pEnumState = (CoffEHEnumState *)pEHEnumState; + if (pEnumState->uClause >= pEnumState->nClauses) + return false; + pEnumState->uClause++; + + pEHClauseOut->m_tryStartOffset = VarInt::ReadUnsigned(pEnumState->pEHInfo); + + UInt32 tryEndDeltaAndClauseKind = VarInt::ReadUnsigned(pEnumState->pEHInfo); + pEHClauseOut->m_clauseKind = (EHClauseKind)(tryEndDeltaAndClauseKind & 0x3); + pEHClauseOut->m_tryEndOffset = pEHClauseOut->m_tryStartOffset + (tryEndDeltaAndClauseKind >> 2); + + // For each clause, we have up to 4 integers: + // 1) try start offset + // 2) (try length << 2) | clauseKind + // 3) if (typed || fault || filter) { handler start offset } + // 4a) if (typed) { type RVA } + // 4b) if (filter) { filter start offset } + // + // The first two integers have already been decoded + + switch (pEHClauseOut->m_clauseKind) + { + case EH_CLAUSE_TYPED: + pEHClauseOut->m_handlerOffset = VarInt::ReadUnsigned(pEnumState->pEHInfo); + + // Read target type + { + // @TODO: CORERT: Compress EHInfo using type table index scheme + // https://github.com/dotnet/corert/issues/972 + UInt32 typeRVA = *((PTR_UInt32&)pEnumState->pEHInfo)++; + pEHClauseOut->m_pTargetType = dac_cast<PTR_VOID>(m_moduleBase + typeRVA); + } + break; + case EH_CLAUSE_FAULT: + pEHClauseOut->m_handlerOffset = VarInt::ReadUnsigned(pEnumState->pEHInfo); + break; + case EH_CLAUSE_FILTER: + pEHClauseOut->m_handlerOffset = VarInt::ReadUnsigned(pEnumState->pEHInfo); + pEHClauseOut->m_filterOffset = VarInt::ReadUnsigned(pEnumState->pEHInfo); + break; + default: + UNREACHABLE_MSG("unexpected EHClauseKind"); + } + + return true; +} + +extern "C" bool __stdcall RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, UInt32 cbRange); + +extern "C" +bool RhpRegisterCoffModule(void * pModule) +{ + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pModule; + PIMAGE_NT_HEADERS pNTHeaders = (PIMAGE_NT_HEADERS)((TADDR)pModule + pDosHeader->e_lfanew); + + IMAGE_DATA_DIRECTORY * pRuntimeFunctions = &(pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]); + + CoffNativeCodeManager * pCoffNativeCodeManager = new (nothrow) CoffNativeCodeManager((TADDR)pModule, + dac_cast<PTR_RUNTIME_FUNCTION>((TADDR)pModule + pRuntimeFunctions->VirtualAddress), + pRuntimeFunctions->Size / sizeof(RUNTIME_FUNCTION)); + + // @TODO: CORERT: Register the code manager only for the range that contains managed code. E.g. It is required for + // proper handling of hardware exceptions in unmanaged runtime code. + // https://github.com/dotnet/corert/issues/973 + + if (!RegisterCodeManager(pCoffNativeCodeManager, pModule, pNTHeaders->OptionalHeader.SizeOfImage)) + { + delete pCoffNativeCodeManager; + return false; + } + + return true; +} diff --git a/src/Native/Runtime/windows/CoffNativeCodeManager.h b/src/Native/Runtime/windows/CoffNativeCodeManager.h new file mode 100644 index 000000000..6c7c5f6ef --- /dev/null +++ b/src/Native/Runtime/windows/CoffNativeCodeManager.h @@ -0,0 +1,89 @@ +// 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. + +#pragma once + +#if defined(_TARGET_AMD64_) +struct T_RUNTIME_FUNCTION { + uint32_t BeginAddress; + uint32_t EndAddress; + uint32_t UnwindInfoAddress; +}; +#elif defined(_TARGET_ARM_) +struct T_RUNTIME_FUNCTION { + uint32_t BeginAddress; + uint32_t UnwindData; +}; +#elif defined(_TARGET_ARM64_) +struct T_RUNTIME_FUNCTION { + uint32_t BeginAddress; + union { + uint32_t UnwindData; + struct { + uint32_t Flag : 2; + uint32_t FunctionLength : 11; + uint32_t RegF : 3; + uint32_t RegI : 4; + uint32_t H : 1; + uint32_t CR : 2; + uint32_t FrameSize : 9; + } PackedUnwindData; + }; +}; +#else +#error unexpected target architecture +#endif + +typedef DPTR(T_RUNTIME_FUNCTION) PTR_RUNTIME_FUNCTION; + +class CoffNativeCodeManager : public ICodeManager +{ + TADDR m_moduleBase; + PTR_RUNTIME_FUNCTION m_pRuntimeFunctionTable; + UInt32 m_nRuntimeFunctionTable; + +public: + CoffNativeCodeManager(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, UInt32 nRuntimeFunctionTable); + ~CoffNativeCodeManager(); + + // + // Code manager methods + // + + bool FindMethodInfo(PTR_VOID ControlPC, + MethodInfo * pMethodInfoOut, + UInt32 * pCodeOffset); + + bool IsFunclet(MethodInfo * pMethodInfo); + + PTR_VOID GetFramePointer(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet); + + void EnumGcRefs(MethodInfo * pMethodInfo, + UInt32 codeOffset, + REGDISPLAY * pRegisterSet, + GCEnumContext * hCallback); + + bool UnwindStackFrame(MethodInfo * pMethodInfo, + UInt32 codeOffset, + REGDISPLAY * pRegisterSet, // in/out + PTR_VOID * ppPreviousTransitionFrame); // out + + UIntNative GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet); + + bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, + UInt32 codeOffset, + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind); // out + + void UnsynchronizedHijackMethodLoops(MethodInfo * pMethodInfo); + + void RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, UInt32 * pCodeOffset); + + bool EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumState); + + bool EHEnumNext(EHEnumState * pEHEnumState, EHClause * pEHClause); +}; |