From: Jonghyun Park Date: Wed, 18 Jan 2017 09:45:09 +0000 (+0900) Subject: [x86/Linux] Port RtlVirtualUnwind (dotnet/coreclr#8911) X-Git-Tag: submit/tizen/20210909.063632~11030^2~8424 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=325be29a9bc4cf6c364cae0f341e3a53f3768965;p=platform%2Fupstream%2Fdotnet%2Fruntime.git [x86/Linux] Port RtlVirtualUnwind (dotnet/coreclr#8911) * [x86/Linux] (Partially) port RtlVirtualUnwind * Rewrite x86 Unwinder using UnwindStackFrame * Extract UnwindStackFrame from EECodeManager * Port 'InlinedCallFrame::UpdateRegDisplay' Commit migrated from https://github.com/dotnet/coreclr/commit/009697370b7dd9a155601842360633d2fd2ba286 --- diff --git a/src/coreclr/src/inc/eetwain.h b/src/coreclr/src/inc/eetwain.h index 55cc905..1d195d7 100644 --- a/src/coreclr/src/inc/eetwain.h +++ b/src/coreclr/src/inc/eetwain.h @@ -613,6 +613,13 @@ HRESULT FixContextForEnC(PCONTEXT pCtx, }; +#ifdef _TARGET_X86_ +bool UnwindStackFrame(PREGDISPLAY pContext, + EECodeInfo *pCodeInfo, + unsigned flags, + CodeManState *pState, + StackwalkCacheUnwindInfo *pUnwindInfo); +#endif /***************************************************************************** ToDo: Do we want to include JIT/IL/target.h? diff --git a/src/coreclr/src/inc/regdisp.h b/src/coreclr/src/inc/regdisp.h index 8114346..6806000 100644 --- a/src/coreclr/src/inc/regdisp.h +++ b/src/coreclr/src/inc/regdisp.h @@ -489,7 +489,9 @@ inline void CopyRegDisplay(const PREGDISPLAY pInRD, PREGDISPLAY pOutRD, T_CONTEX T_CONTEXT* pOutCallerCtx = NULL; -#ifdef _TARGET_X86_ +#ifndef WIN64EXCEPTIONS + +#if defined(_TARGET_X86_) if (pInRD->pEdi != NULL) {pOutCtx->Edi = *pInRD->pEdi;} else {pInRD->pEdi = NULL;} if (pInRD->pEsi != NULL) {pOutCtx->Esi = *pInRD->pEsi;} else {pInRD->pEsi = NULL;} if (pInRD->pEbx != NULL) {pOutCtx->Ebx = *pInRD->pEbx;} else {pInRD->pEbx = NULL;} @@ -499,13 +501,19 @@ inline void CopyRegDisplay(const PREGDISPLAY pInRD, PREGDISPLAY pOutRD, T_CONTEX if (pInRD->pEdx != NULL) {pOutCtx->Edx = *pInRD->pEdx;} else {pInRD->pEdx = NULL;} pOutCtx->Esp = pInRD->Esp; pOutCtx->Eip = pInRD->ControlPC; -#else +#else // _TARGET_X86_ + PORTABILITY_ASSERT("CopyRegDisplay"); +#endif // _TARGET_???_ + +#else // WIN64EXCEPTIONS + *pOutCtx = *(pInRD->pCurrentContext); if (pInRD->IsCallerContextValid) { pOutCallerCtx = pInRD->pCallerContext; } -#endif + +#endif // WIN64EXCEPTIONS if (pOutRD) FillRegDisplay(pOutRD, pOutCtx, pOutCallerCtx); @@ -572,6 +580,8 @@ inline void UpdateContextFromRegDisp(PREGDISPLAY pRegDisp, PT_CONTEXT pContext) { _ASSERTE((pRegDisp != NULL) && (pContext != NULL)); +#ifndef WIN64EXCEPTIONS + #if defined(_TARGET_X86_) pContext->ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL); pContext->Edi = *pRegDisp->pEdi; @@ -583,9 +593,15 @@ inline void UpdateContextFromRegDisp(PREGDISPLAY pRegDisp, PT_CONTEXT pContext) pContext->Edx = *pRegDisp->pEdx; pContext->Esp = pRegDisp->Esp; pContext->Eip = pRegDisp->ControlPC; -#else +#else // _TARGET_X86_ + PORTABILITY_ASSERT("UpdateContextFromRegDisp"); +#endif // _TARGET_???_ + +#else // WIN64EXCEPTIONS + *pContext = *pRegDisp->pCurrentContext; -#endif + +#endif // WIN64EXCEPTIONS } diff --git a/src/coreclr/src/unwinder/CMakeLists.txt b/src/coreclr/src/unwinder/CMakeLists.txt index bf1cfa2..5cd7bae 100644 --- a/src/coreclr/src/unwinder/CMakeLists.txt +++ b/src/coreclr/src/unwinder/CMakeLists.txt @@ -12,12 +12,10 @@ set(UNWINDER_SOURCES ) # Include platform specific unwinder for applicable (native and cross-target) builds. -if(NOT DEFINED CLR_CMAKE_TARGET_ARCH_I386) - include_directories(${ARCH_SOURCES_DIR}) - list(APPEND UNWINDER_SOURCES - ${ARCH_SOURCES_DIR}/unwinder_${ARCH_SOURCES_DIR}.cpp - ) -endif() +include_directories(${ARCH_SOURCES_DIR}) +list(APPEND UNWINDER_SOURCES + ${ARCH_SOURCES_DIR}/unwinder_${ARCH_SOURCES_DIR}.cpp +) convert_to_absolute_path(UNWINDER_SOURCES ${UNWINDER_SOURCES}) diff --git a/src/coreclr/src/unwinder/i386/unwinder_i386.cpp b/src/coreclr/src/unwinder/i386/unwinder_i386.cpp new file mode 100644 index 0000000..8d2ea53 --- /dev/null +++ b/src/coreclr/src/unwinder/i386/unwinder_i386.cpp @@ -0,0 +1,205 @@ +// 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 "stdafx.h" +#include "unwinder_i386.h" + +#ifdef WIN64EXCEPTIONS +/*++ + +Routine Description: + + This function virtually unwinds the specified function by executing its + prologue code backward or its epilogue code forward. + + If a context pointers record is specified, then the address where each + nonvolatile registers is restored from is recorded in the appropriate + element of the context pointers record. + +Arguments: + + HandlerType - Supplies the handler type expected for the virtual unwind. + This may be either an exception or an unwind handler. A flag may + optionally be supplied to avoid epilogue detection if it is known + the specified control PC is not located inside a function epilogue. + + ImageBase - Supplies the base address of the image that contains the + function being unwound. + + ControlPc - Supplies the address where control left the specified + function. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. + + ContextRecord - Supplies the address of a context record. + + + HandlerData - Supplies a pointer to a variable that receives a pointer + the the language handler data. + + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. + + ContextPointers - Supplies an optional pointer to a context pointers + record. + + HandlerRoutine - Supplies an optional pointer to a variable that receives + the handler routine address. If control did not leave the specified + function in either the prologue or an epilogue and a handler of the + proper type is associated with the function, then the address of the + language specific exception handler is returned. Otherwise, NULL is + returned. +--*/ +HRESULT +OOPStackUnwinderX86::VirtualUnwind( + __in DWORD HandlerType, + __in DWORD ImageBase, + __in DWORD ControlPc, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PCONTEXT ContextRecord, + __out PVOID *HandlerData, + __out PDWORD EstablisherFrame, + __inout_opt PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, + __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine + ) +{ + *EstablisherFrame = ContextRecord->Esp; + if (HandlerRoutine != NULL) + { + *HandlerRoutine = NULL; + } + + REGDISPLAY rd; + + if (ContextPointers != NULL) + { +#define CALLEE_SAVED_REGISTER(reg) rd.p##reg = ContextPointers->reg; + ENUM_CALLEE_SAVED_REGISTERS(); +#undef CALLEE_SAVED_REGISTER + } + else + { +#define CALLEE_SAVED_REGISTER(reg) rd.p##reg = NULL; + ENUM_CALLEE_SAVED_REGISTERS(); +#undef CALLEE_SAVED_REGISTER + } + + if (rd.pEbp == NULL) + { + rd.pEbp = &(ContextRecord->Ebp); + } + rd.Esp = ContextRecord->Esp; + rd.ControlPC = (PCODE)(ContextRecord->Eip); + rd.PCTAddr = (UINT_PTR)&(ContextRecord->Eip); + + CodeManState codeManState; + codeManState.dwIsSet = 0; + + EECodeInfo codeInfo; + codeInfo.Init((PCODE) ControlPc); + + if (!UnwindStackFrame(&rd, &codeInfo, UpdateAllRegs, &codeManState, NULL)) + { + return HRESULT_FROM_WIN32(ERROR_READ_FAULT); + } + +#define CALLEE_SAVED_REGISTER(reg) if (rd.p##reg != NULL) { ContextRecord->reg = *rd.p##reg; } + ENUM_CALLEE_SAVED_REGISTERS(); +#undef CALLEE_SAVED_REGISTER + + if (ContextPointers != NULL) + { +#define CALLEE_SAVED_REGISTER(reg) if (rd.p##reg != &(ContextRecord->reg)) { ContextPointers->reg = rd.p##reg; } + ENUM_CALLEE_SAVED_REGISTERS(); +#undef CALLEE_SAVED_REGISTER + } + + ContextRecord->Esp = rd.Esp; + ContextRecord->Eip = rd.ControlPC; + ContextRecord->Ebp = *rd.pEbp; + + return S_OK; +} + +//--------------------------------------------------------------------------------------- +// +// This function behaves like the RtlVirtualUnwind in Windows. +// It virtually unwinds the specified function by executing its +// prologue code backward or its epilogue code forward. +// +// If a context pointers record is specified, then the address where each +// nonvolatile registers is restored from is recorded in the appropriate +// element of the context pointers record. +// +// Arguments: +// +// HandlerType - Supplies the handler type expected for the virtual unwind. +// This may be either an exception or an unwind handler. A flag may +// optionally be supplied to avoid epilogue detection if it is known +// the specified control PC is not located inside a function epilogue. +// +// ImageBase - Supplies the base address of the image that contains the +// function being unwound. +// +// ControlPc - Supplies the address where control left the specified +// function. +// +// FunctionEntry - Supplies the address of the function table entry for the +// specified function. +// +// ContextRecord - Supplies the address of a context record. +// +// HandlerData - Supplies a pointer to a variable that receives a pointer +// the the language handler data. +// +// EstablisherFrame - Supplies a pointer to a variable that receives the +// the establisher frame pointer value. +// +// ContextPointers - Supplies an optional pointer to a context pointers +// record. +// +// Return value: +// +// The handler routine address. If control did not leave the specified +// function in either the prologue or an epilogue and a handler of the +// proper type is associated with the function, then the address of the +// language specific exception handler is returned. Otherwise, NULL is +// returned. +// +EXTERN_C +NTSYSAPI +PEXCEPTION_ROUTINE +NTAPI +RtlVirtualUnwind ( + __in DWORD HandlerType, + __in DWORD ImageBase, + __in DWORD ControlPc, + __in PRUNTIME_FUNCTION FunctionEntry, + __inout PT_CONTEXT ContextRecord, + __out PVOID *HandlerData, + __out PDWORD EstablisherFrame, + __inout_opt PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers + ) +{ + PEXCEPTION_ROUTINE handlerRoutine; + + HRESULT res = OOPStackUnwinderX86::VirtualUnwind( + HandlerType, + ImageBase, + ControlPc, + (_PIMAGE_RUNTIME_FUNCTION_ENTRY)FunctionEntry, + ContextRecord, + HandlerData, + EstablisherFrame, + ContextPointers, + &handlerRoutine); + + _ASSERTE(SUCCEEDED(res)); + + return handlerRoutine; +} +#endif // WIN64EXCEPTIONS diff --git a/src/coreclr/src/unwinder/i386/unwinder_i386.h b/src/coreclr/src/unwinder/i386/unwinder_i386.h new file mode 100644 index 0000000..bed30bf --- /dev/null +++ b/src/coreclr/src/unwinder/i386/unwinder_i386.h @@ -0,0 +1,33 @@ +// 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. + +// + +#ifndef __unwinder_i386_h__ +#define __unwinder_i386_h__ + +#include "unwinder.h" + +#ifdef WIN64EXCEPTIONS +//--------------------------------------------------------------------------------------- +// +// See the comment for the base class code:OOPStackUnwinder. +// + +class OOPStackUnwinderX86 : public OOPStackUnwinder +{ +public: + static HRESULT VirtualUnwind(__in DWORD HandlerType, + __in DWORD ImageBase, + __in DWORD ControlPc, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PCONTEXT ContextRecord, + __out PVOID *HandlerData, + __out PDWORD EstablisherFrame, + __inout_opt PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, + __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine); +}; +#endif // WIN64EXCEPTIONS + +#endif // __unwinder_i386_h__ diff --git a/src/coreclr/src/vm/eetwain.cpp b/src/coreclr/src/vm/eetwain.cpp index 16b9de4..81a5a0a 100644 --- a/src/coreclr/src/vm/eetwain.cpp +++ b/src/coreclr/src/vm/eetwain.cpp @@ -3884,21 +3884,11 @@ bool UnwindEbpDoubleAlignFrame( return true; } -/***************************************************************************** - * - * Unwind the current stack frame, i.e. update the virtual register - * set in pContext. This will be similar to the state after the function - * returns back to caller (IP points to after the call, Frame and Stack - * pointer has been reset, callee-saved registers restored (if UpdateAllRegs), - * callee-unsaved registers are trashed. - * Returns success of operation. - */ - -bool EECodeManager::UnwindStackFrame(PREGDISPLAY pContext, - EECodeInfo *pCodeInfo, - unsigned flags, - CodeManState *pState, - StackwalkCacheUnwindInfo *pUnwindInfo /* out-only, perf improvement */) +bool UnwindStackFrame(PREGDISPLAY pContext, + EECodeInfo *pCodeInfo, + unsigned flags, + CodeManState *pState, + StackwalkCacheUnwindInfo *pUnwindInfo /* out-only, perf improvement */) { CONTRACTL { NOTHROW; @@ -3991,6 +3981,29 @@ bool EECodeManager::UnwindStackFrame(PREGDISPLAY pContext, return true; } +#endif // _TARGET_X86_ + +#if defined(_TARGET_X86_) && !defined(WIN64EXCEPTIONS) + +/***************************************************************************** + * + * Unwind the current stack frame, i.e. update the virtual register + * set in pContext. This will be similar to the state after the function + * returns back to caller (IP points to after the call, Frame and Stack + * pointer has been reset, callee-saved registers restored (if UpdateAllRegs), + * callee-unsaved registers are trashed. + * Returns success of operation. + */ + +bool EECodeManager::UnwindStackFrame(PREGDISPLAY pContext, + EECodeInfo *pCodeInfo, + unsigned flags, + CodeManState *pState, + StackwalkCacheUnwindInfo *pUnwindInfo /* out-only, perf improvement */) +{ + return ::UnwindStackFrame(pContext, pCodeInfo, flags, pState, pUnwindInfo); +} + /*****************************************************************************/ #elif !defined(CROSSGEN_COMPILE) // _TARGET_X86_ - UnwindStackFrame /*****************************************************************************/ diff --git a/src/coreclr/src/vm/i386/cgenx86.cpp b/src/coreclr/src/vm/i386/cgenx86.cpp index 55f8459..477ef20 100644 --- a/src/coreclr/src/vm/i386/cgenx86.cpp +++ b/src/coreclr/src/vm/i386/cgenx86.cpp @@ -590,6 +590,26 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) /* Now we need to pop off the outgoing arguments */ pRD->Esp = (DWORD) dac_cast(m_pCallSiteSP) + stackArgSize; + +#ifdef WIN64EXCEPTIONS + pRD->IsCallerContextValid = FALSE; + pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. + + pRD->pCurrentContext->Eip = pRD->ControlPC; + pRD->pCurrentContext->Esp = pRD->Esp; + pRD->pCurrentContext->Ebp = *pRD->pEbp; + +#define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = NULL; + ENUM_CALLEE_SAVED_REGISTERS(); +#undef CALLEE_SAVED_REGISTER + + pRD->pCurrentContextPointers->Ebp = pRD->pEbp; + + SyncRegDisplayToCurrentContext(pRD); +#endif + + LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK InlinedCallFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->Esp)); + RETURN; } diff --git a/src/coreclr/src/vm/i386/unixstubs.cpp b/src/coreclr/src/vm/i386/unixstubs.cpp index 4025c26..cb08f27 100644 --- a/src/coreclr/src/vm/i386/unixstubs.cpp +++ b/src/coreclr/src/vm/i386/unixstubs.cpp @@ -103,22 +103,3 @@ RtlpGetFunctionEndAddress ( PORTABILITY_ASSERT("RtlpGetFunctionEndAddress"); return 0; } - -EXTERN_C -NTSYSAPI -PEXCEPTION_ROUTINE -NTAPI -RtlVirtualUnwind ( - __in DWORD HandlerType, - __in DWORD ImageBase, - __in DWORD ControlPc, - __in PRUNTIME_FUNCTION FunctionEntry, - __inout PT_CONTEXT ContextRecord, - __out PVOID *HandlerData, - __out PDWORD EstablisherFrame, - __inout_opt PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers - ) -{ - PORTABILITY_ASSERT("RtlVirtualUnwind"); - return NULL; -} diff --git a/src/coreclr/src/vm/stackwalk.cpp b/src/coreclr/src/vm/stackwalk.cpp index 44e5f99..3e74dfd 100644 --- a/src/coreclr/src/vm/stackwalk.cpp +++ b/src/coreclr/src/vm/stackwalk.cpp @@ -561,9 +561,7 @@ UINT_PTR Thread::VirtualUnwindCallFrame(PREGDISPLAY pRD, EECodeInfo* pCodeInfo / } else { - PT_KNONVOLATILE_CONTEXT_POINTERS pCurrentContextPointers = NULL; - NOT_X86(pCurrentContextPointers = pRD->pCurrentContextPointers); - VirtualUnwindCallFrame(pRD->pCurrentContext, pCurrentContextPointers, pCodeInfo); + VirtualUnwindCallFrame(pRD->pCurrentContext, pRD->pCurrentContextPointers, pCodeInfo); } SyncRegDisplayToCurrentContext(pRD);