[x86/Linux] Port RtlVirtualUnwind (dotnet/coreclr#8911)
authorJonghyun Park <parjong@gmail.com>
Wed, 18 Jan 2017 09:45:09 +0000 (18:45 +0900)
committerJan Vorlicek <janvorli@microsoft.com>
Wed, 18 Jan 2017 09:45:09 +0000 (10:45 +0100)
* [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

src/coreclr/src/inc/eetwain.h
src/coreclr/src/inc/regdisp.h
src/coreclr/src/unwinder/CMakeLists.txt
src/coreclr/src/unwinder/i386/unwinder_i386.cpp [new file with mode: 0644]
src/coreclr/src/unwinder/i386/unwinder_i386.h [new file with mode: 0644]
src/coreclr/src/vm/eetwain.cpp
src/coreclr/src/vm/i386/cgenx86.cpp
src/coreclr/src/vm/i386/unixstubs.cpp
src/coreclr/src/vm/stackwalk.cpp

index 55cc905..1d195d7 100644 (file)
@@ -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>ToDo: Do we want to include JIT/IL/target.h? </TODO>
index 8114346..6806000 100644 (file)
@@ -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
 }
 
 
index bf1cfa2..5cd7bae 100644 (file)
@@ -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 (file)
index 0000000..8d2ea53
--- /dev/null
@@ -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 (file)
index 0000000..bed30bf
--- /dev/null
@@ -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__
index 16b9de4..81a5a0a 100644 (file)
@@ -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
 /*****************************************************************************/
index 55f8459..477ef20 100644 (file)
@@ -590,6 +590,26 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
 
     /* Now we need to pop off the outgoing arguments */
     pRD->Esp  = (DWORD) dac_cast<TADDR>(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;
 }
 
index 4025c26..cb08f27 100644 (file)
@@ -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;
-}
index 44e5f99..3e74dfd 100644 (file)
@@ -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);