diff options
author | Hanjoung Lee <waterets@gmail.com> | 2017-02-24 16:02:21 +0900 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2017-02-23 23:02:21 -0800 |
commit | 77f2ad4c07b24fcfe1298dd7cd260876ef46e6c3 (patch) | |
tree | ca3ae57361dfc130e729df51bee8e13b8649d873 /src/vm | |
parent | 687e0fbfe1b753653968308d1cd17c079c5a87fc (diff) | |
download | coreclr-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.txt | 1 | ||||
-rw-r--r-- | src/vm/eetwain.cpp | 31 | ||||
-rw-r--r-- | src/vm/exceptionhandling.cpp | 83 | ||||
-rw-r--r-- | src/vm/exceptionhandling.h | 1 | ||||
-rw-r--r-- | src/vm/i386/ehhelpers.S | 98 |
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 + |