summaryrefslogtreecommitdiff
path: root/src/vm
diff options
context:
space:
mode:
authorHanjoung Lee <waterets@gmail.com>2017-02-24 16:02:21 +0900
committerJan Kotas <jkotas@microsoft.com>2017-02-23 23:02:21 -0800
commit77f2ad4c07b24fcfe1298dd7cd260876ef46e6c3 (patch)
treeca3ae57361dfc130e729df51bee8e13b8649d873 /src/vm
parent687e0fbfe1b753653968308d1cd17c079c5a87fc (diff)
downloadcoreclr-77f2ad4c07b24fcfe1298dd7cd260876ef46e6c3.tar.gz
coreclr-77f2ad4c07b24fcfe1298dd7cd260876ef46e6c3.tar.bz2
coreclr-77f2ad4c07b24fcfe1298dd7cd260876ef46e6c3.zip
[x86/Linux] Initial patch for EH funclet (#9601)
- Generate a simple EH funclet frame and support SP-based stack unwinding for funclets. - Introduce assembly helpers : CallEHFunclet and CallEHFilterFunclet
Diffstat (limited to 'src/vm')
-rw-r--r--src/vm/CMakeLists.txt1
-rw-r--r--src/vm/eetwain.cpp31
-rw-r--r--src/vm/exceptionhandling.cpp83
-rw-r--r--src/vm/exceptionhandling.h1
-rw-r--r--src/vm/i386/ehhelpers.S98
5 files changed, 182 insertions, 32 deletions
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index cdf5a53d1c..01537534ed 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -368,6 +368,7 @@ else(WIN32)
)
elseif(CLR_CMAKE_TARGET_ARCH_I386)
set(VM_SOURCES_WKS_ARCH_ASM
+ ${ARCH_SOURCES_DIR}/ehhelpers.S
${ARCH_SOURCES_DIR}/asmhelpers.S
${ARCH_SOURCES_DIR}/jithelp.S
${ARCH_SOURCES_DIR}/gmsasm.S
diff --git a/src/vm/eetwain.cpp b/src/vm/eetwain.cpp
index bf6e1c7aa5..989300d468 100644
--- a/src/vm/eetwain.cpp
+++ b/src/vm/eetwain.cpp
@@ -3786,10 +3786,11 @@ void UnwindEbpDoubleAlignFrameProlog(
/*****************************************************************************/
bool UnwindEbpDoubleAlignFrame(
- PREGDISPLAY pContext,
- hdrInfo * info,
- PTR_CBYTE methodStart,
- unsigned flags,
+ PREGDISPLAY pContext,
+ EECodeInfo *pCodeInfo,
+ hdrInfo *info,
+ PTR_CBYTE methodStart,
+ unsigned flags,
StackwalkCacheUnwindInfo *pUnwindInfo) // out-only, perf improvement
{
LIMITED_METHOD_CONTRACT;
@@ -3806,6 +3807,25 @@ bool UnwindEbpDoubleAlignFrame(
{
TADDR baseSP;
+#ifdef WIN64EXCEPTIONS
+ // Funclets' frame pointers(EBP) are always restored so they can access to main function's local variables.
+ // Therefore the value of EBP is invalid for unwinder so we should use ESP instead.
+ // TODO If funclet frame layout is changed from CodeGen::genFuncletProlog() and genFuncletEpilog(),
+ // we need to change here accordingly. It is likely to have changes when introducing PSPSym.
+ // TODO Currently we assume that ESP of funclet frames is always fixed but actually it could change.
+ if (pCodeInfo->IsFunclet())
+ {
+ baseSP = curESP + 12; // padding for 16byte stack alignment allocated in genFuncletProlog()
+
+ pContext->PCTAddr = baseSP;
+ pContext->ControlPC = *PTR_PCODE(pContext->PCTAddr);
+
+ pContext->SP = (DWORD)(baseSP + sizeof(TADDR));
+
+ return true;
+ }
+#else // WIN64EXCEPTIONS
+
FrameType frameType = GetHandlerFrameInfo(info, curEBP,
curESP, (DWORD) IGNORE_VAL,
&baseSP);
@@ -3860,6 +3880,7 @@ bool UnwindEbpDoubleAlignFrame(
return true;
}
+#endif // !WIN64EXCEPTIONS
}
//
@@ -3990,7 +4011,7 @@ bool UnwindStackFrame(PREGDISPLAY pContext,
* Now we know that have an EBP frame
*/
- if (!UnwindEbpDoubleAlignFrame(pContext, info, methodStart, flags, pUnwindInfo))
+ if (!UnwindEbpDoubleAlignFrame(pContext, pCodeInfo, info, methodStart, flags, pUnwindInfo))
return false;
}
diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp
index 1e51467789..d7f22d58f1 100644
--- a/src/vm/exceptionhandling.cpp
+++ b/src/vm/exceptionhandling.cpp
@@ -19,6 +19,7 @@
#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) || defined(_TARGET_X86_)
#define ADJUST_PC_UNWOUND_TO_CALL
+#define USE_CALLER_SP_IN_FUNCLET
#endif // _TARGET_ARM_ || _TARGET_ARM64_ || _TARGET_X86_
#ifndef DACCESS_COMPILE
@@ -574,7 +575,7 @@ UINT_PTR ExceptionTracker::CallCatchHandler(CONTEXT* pContextRecord, bool* pfAbo
if (!fIntercepted)
{
_ASSERTE(m_uCatchToCallPC != 0 && m_pClauseForCatchToken != NULL);
- uResumePC = CallHandler(m_uCatchToCallPC, sfStackFp, &m_ClauseForCatch, pMD, Catch ARM_ARG(pContextRecord) ARM64_ARG(pContextRecord));
+ uResumePC = CallHandler(m_uCatchToCallPC, sfStackFp, &m_ClauseForCatch, pMD, Catch X86_ARG(pContextRecord) ARM_ARG(pContextRecord) ARM64_ARG(pContextRecord));
}
else
{
@@ -1289,12 +1290,12 @@ void ExceptionTracker::InitializeCurrentContextForCrawlFrame(CrawlFrame* pcfThis
pRD->SP = sfEstablisherFrame.SP;
pRD->ControlPC = pDispatcherContext->ControlPc;
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#ifdef USE_CALLER_SP_IN_FUNCLET
pcfThisFrame->pRD->IsCallerSPValid = TRUE;
// Assert our first pass assumptions for the Arm/Arm64
_ASSERTE(sfEstablisherFrame.SP == GetSP(pDispatcherContext->ContextRecord));
-#endif // defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#endif // USE_CALLER_SP_IN_FUNCLET
}
@@ -2872,7 +2873,7 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
SetEnclosingClauseInfo(fIsFunclet,
pcfThisFrame->GetRelOffset(),
GetSP(pcfThisFrame->GetRegisterSet()->pCallerContext));
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#ifdef USE_CALLER_SP_IN_FUNCLET
// On ARM & ARM64, the OS passes us the CallerSP for the frame for which personality routine has been invoked.
// Since IL filters are invoked in the first pass, we pass this CallerSP to the filter funclet which will
// then lookup the actual frame pointer value using it since we dont have a frame pointer to pass to it
@@ -2887,11 +2888,11 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
_ASSERTE(pCurRegDisplay->IsCallerContextValid && pCurRegDisplay->IsCallerSPValid);
// 3) CallerSP is intact
_ASSERTE(GetSP(pCurRegDisplay->pCallerContext) == GetRegdisplaySP(pCurRegDisplay));
-#endif // _TARGET_ARM_ || _TARGET_ARM64_
+#endif // USE_CALLER_SP_IN_FUNCLET
{
// CallHandler expects to be in COOP mode.
GCX_COOP();
- dwResult = CallHandler(dwFilterStartPC, sf, &EHClause, pMD, Filter ARM_ARG(pCurRegDisplay->pCallerContext) ARM64_ARG(pCurRegDisplay->pCallerContext));
+ dwResult = CallHandler(dwFilterStartPC, sf, &EHClause, pMD, Filter X86_ARG(pCurRegDisplay->pCallerContext) ARM_ARG(pCurRegDisplay->pCallerContext) ARM64_ARG(pCurRegDisplay->pCallerContext));
}
}
EX_CATCH
@@ -3124,7 +3125,7 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
// Since we also forbid GC during second pass, disable it now since
// invocation of managed code can result in a GC.
ENDFORBIDGC();
- dwStatus = CallHandler(dwHandlerStartPC, sf, &EHClause, pMD, FaultFinally ARM_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext) ARM64_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext));
+ dwStatus = CallHandler(dwHandlerStartPC, sf, &EHClause, pMD, FaultFinally X86_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext) ARM_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext) ARM64_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext));
// Once we return from a funclet, forbid GC again (refer to comment before start of the loop for details)
BEGINFORBIDGC();
@@ -3192,20 +3193,57 @@ lExit:
#define OPTIONAL_SO_CLEANUP_UNWIND(pThread, pFrame) if (pThread->GetFrame() < pFrame) { UnwindFrameChain(pThread, pFrame); }
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#ifdef USE_CALLER_SP_IN_FUNCLET
// This is an assembly helper that enables us to call into EH funclets.
EXTERN_C DWORD_PTR STDCALL CallEHFunclet(Object *pThrowable, UINT_PTR pFuncletToInvoke, UINT_PTR *pFirstNonVolReg, UINT_PTR *pFuncletCallerSP);
// This is an assembly helper that enables us to call into EH filter funclets.
EXTERN_C DWORD_PTR STDCALL CallEHFilterFunclet(Object *pThrowable, TADDR CallerSP, UINT_PTR pFuncletToInvoke, UINT_PTR *pFuncletCallerSP);
-#endif // _TARGET_ARM_ || _TARGET_ARM64_
+static inline UINT_PTR CastHandlerFn(HandlerFn *pfnHandler)
+{
+#ifdef _TARGET_ARM_
+ return DataPointerToThumbCode<UINT_PTR, HandlerFn *>(pfnHandler);
+#else
+ return (UINT_PTR)pfnHandler;
+#endif
+}
+
+static inline UINT_PTR *GetFirstNonVolatileRegisterAddress(PCONTEXT pContextRecord)
+{
+#if defined(_TARGET_ARM_)
+ return (UINT_PTR*)&(pContextRecord->R4);
+#elif defined(_TARGET_ARM64_)
+ return (UINT_PTR*)&(pContextRecord->X19);
+#elif defined(_TARGET_X86_)
+ return (UINT_PTR*)&(pContextRecord->Edi);
+#else
+ PORTABILITY_ASSERT("GetFirstNonVolatileRegisterAddress");
+ return NULL;
+#endif
+}
+
+static inline TADDR GetFrameRestoreBase(PCONTEXT pContextRecord)
+{
+#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+ return GetSP(pContextRecord);
+#elif defined(_TARGET_X86_)
+ return pContextRecord->Ebp;
+#else
+ PORTABILITY_ASSERT("GetFrameRestoreBase");
+ return NULL;
+#endif
+}
+
+#endif // USE_CALLER_SP_IN_FUNCLET
+
DWORD_PTR ExceptionTracker::CallHandler(
UINT_PTR uHandlerStartPC,
StackFrame sf,
EE_ILEXCEPTION_CLAUSE* pEHClause,
MethodDesc* pMD,
EHFuncletType funcletType
+ X86_ARG(PCONTEXT pContextRecord)
ARM_ARG(PCONTEXT pContextRecord)
ARM64_ARG(PCONTEXT pContextRecord)
)
@@ -3260,7 +3298,7 @@ DWORD_PTR ExceptionTracker::CallHandler(
break;
}
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#ifdef USE_CALLER_SP_IN_FUNCLET
// Invoke the funclet. We pass throwable only when invoking the catch block.
// Since the actual caller of the funclet is the assembly helper, pass the reference
// to the CallerStackFrame instance so that it can be updated.
@@ -3269,14 +3307,9 @@ DWORD_PTR ExceptionTracker::CallHandler(
if (funcletType != EHFuncletType::Filter)
{
dwResumePC = CallEHFunclet((funcletType == EHFuncletType::Catch)?OBJECTREFToObject(throwable):(Object *)NULL,
-#ifdef _TARGET_ARM_
- DataPointerToThumbCode<UINT_PTR, HandlerFn *>(pfnHandler),
- (UINT_PTR*)&(pContextRecord->R4),
-#else
- (UINT_PTR)pfnHandler,
- &(pContextRecord->X19),
-#endif // _TARGET_ARM_
- pFuncletCallerSP);
+ CastHandlerFn(pfnHandler),
+ GetFirstNonVolatileRegisterAddress(pContextRecord),
+ pFuncletCallerSP);
}
else
{
@@ -3284,20 +3317,16 @@ DWORD_PTR ExceptionTracker::CallHandler(
// it will retrieve the framepointer for accessing the locals in the parent
// method.
dwResumePC = CallEHFilterFunclet(OBJECTREFToObject(throwable),
- GetSP(pContextRecord),
-#ifdef _TARGET_ARM_
- DataPointerToThumbCode<UINT_PTR, HandlerFn *>(pfnHandler),
-#else
- (UINT_PTR)pfnHandler,
-#endif // _TARGET_ARM_
- pFuncletCallerSP);
+ GetFrameRestoreBase(pContextRecord),
+ CastHandlerFn(pfnHandler),
+ pFuncletCallerSP);
}
-#else // defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#else // USE_CALLER_SP_IN_FUNCLET
//
// Invoke the funclet.
//
dwResumePC = pfnHandler(sf.SP, OBJECTREFToObject(throwable));
-#endif // _TARGET_ARM_
+#endif // !USE_CALLER_SP_IN_FUNCLET
switch(funcletType)
{
diff --git a/src/vm/exceptionhandling.h b/src/vm/exceptionhandling.h
index 71bcab749f..4dba095ff5 100644
--- a/src/vm/exceptionhandling.h
+++ b/src/vm/exceptionhandling.h
@@ -404,6 +404,7 @@ private:
EE_ILEXCEPTION_CLAUSE* pEHClause,
MethodDesc* pMD,
EHFuncletType funcletType
+ X86_ARG(PT_CONTEXT pContextRecord)
ARM_ARG(PT_CONTEXT pContextRecord)
ARM64_ARG(PT_CONTEXT pContextRecord)
);
diff --git a/src/vm/i386/ehhelpers.S b/src/vm/i386/ehhelpers.S
new file mode 100644
index 0000000000..9f959b1bbd
--- /dev/null
+++ b/src/vm/i386/ehhelpers.S
@@ -0,0 +1,98 @@
+// 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.
+
+.intel_syntax noprefix
+#include "unixasmmacros.inc"
+#include "asmconstants.h"
+
+// DWORD_PTR STDCALL CallEHFunclet(Object *pThrowable, UINT_PTR pFuncletToInvoke, UINT_PTR *pFirstNonVolReg, UINT_PTR *pFuncletCallerSP);
+// ESP based frame
+NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler
+
+ ESP_PROLOG_BEG
+ PROLOG_PUSH ebp
+ mov ebp, esp
+ PROLOG_PUSH ebx
+ PROLOG_PUSH esi
+ PROLOG_PUSH edi
+ ESP_PROLOG_END
+
+ // On entry:
+ //
+ // [ebp+ 8] = throwable
+ // [ebp+12] = PC to invoke
+ // [ebp+16] = address of EDI register in CONTEXT record // used to restore the non-volatile registers of CrawlFrame
+ // [ebp+20] = address of the location where the SP of funclet's caller (i.e. this helper) should be saved.
+ //
+
+ // Save the SP of this function
+ mov eax, [ebp + 20]
+ mov [eax], esp
+ // Save the funclet PC for later call
+ mov edx, [ebp + 12]
+ // Pass throwable object to funclet
+ mov eax, [ebp + 8]
+ // Restore non-volatiles registers
+ mov ecx, [ebp + 16]
+ mov edi, [ecx]
+ mov esi, [ecx + 4]
+ mov ebx, [ecx + 8]
+ mov ebp, [ecx + 24]
+ // Invoke the funclet
+ call edx
+
+ ESP_EPILOG_BEG
+ EPILOG_POP edi
+ EPILOG_POP esi
+ EPILOG_POP ebx
+ EPILOG_POP ebp
+ ESP_EPILOG_END
+
+ ret 16
+
+NESTED_END CallEHFunclet, _TEXT
+
+// DWORD_PTR STDCALL CallEHFilterFunclet(Object *pThrowable, TADDR CallerSP, UINT_PTR pFuncletToInvoke, UINT_PTR *pFuncletCallerSP);
+// ESP based frame
+NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler
+
+ ESP_PROLOG_BEG
+ PROLOG_PUSH ebp
+ mov ebp, esp
+ PROLOG_PUSH ebx
+ PROLOG_PUSH esi
+ PROLOG_PUSH edi
+ ESP_PROLOG_END
+
+ // On entry:
+ //
+ // [ebp+ 8] = throwable
+ // [ebp+12] = FP to restore
+ // [ebp+16] = PC to invoke
+ // [ebp+20] = address of the location where the SP of funclet's caller (i.e. this helper) should be saved.
+ //
+
+ // Save the SP of this function
+ mov eax, [ebp + 20]
+ mov [eax], esp
+ // Save the funclet PC for later call
+ mov edx, [ebp + 16]
+ // Pass throwable object to funclet
+ mov eax, [ebp + 8]
+ // Restore FP
+ mov ebp, [ebp + 12]
+ // Invoke the funclet
+ call edx
+
+ ESP_EPILOG_BEG
+ EPILOG_POP edi
+ EPILOG_POP esi
+ EPILOG_POP ebx
+ EPILOG_POP ebp
+ ESP_EPILOG_END
+
+ ret 16
+
+NESTED_END CallEHFunclet, _TEXT
+