Fix hardware exception handling in native code on BSD
authorJan Vorlicek <janvorli@microsoft.com>
Mon, 11 Apr 2016 12:21:01 +0000 (14:21 +0200)
committerJan Vorlicek <janvorli@microsoft.com>
Tue, 12 Apr 2016 11:56:54 +0000 (13:56 +0200)
There is a problem with hardware exception handling when the exception
happens in native code and it is under the HardwareExceptionHolder on
FreeBSD and NetBSD. The problem was that the C++ unwinder was unable
to cross the signal trampoline.
This change fixes the problem by throwing the PAL_SEHException from
the context of the hardware exception itself rather than from the
signal handler.
Since it uses the same code as we were using for StartUnwindingNativeFrames
in CoreCLR before, I have moved that stuff to PAL and let both CoreCLR and
the signal handlers use it too.
At the same time, I have reenabled the paltest_pal_sxs_test1 for FreeBSD and
NetBSD, since it now works.

Commit migrated from https://github.com/dotnet/coreclr/commit/9475224327e45faa56e6ecbfcc5fa503656d5c20

src/coreclr/src/pal/inc/pal.h
src/coreclr/src/pal/src/CMakeLists.txt
src/coreclr/src/pal/src/arch/arm/exceptionhelper.S [new file with mode: 0644]
src/coreclr/src/pal/src/arch/arm64/exceptionhelper.S [new file with mode: 0644]
src/coreclr/src/pal/src/arch/i386/exceptionhelper.S [new file with mode: 0644]
src/coreclr/src/pal/src/exception/seh.cpp
src/coreclr/src/pal/tests/palsuite/exception_handling/pal_sxs/test1/exceptionsxs.cpp
src/coreclr/src/vm/amd64/unixasmhelpers.S
src/coreclr/src/vm/arm64/asmhelpers.S
src/coreclr/src/vm/exceptionhandling.cpp

index 648960c..590cb64 100644 (file)
@@ -6501,6 +6501,13 @@ PALAPI
 PAL_SetGetGcMarkerExceptionCode(
     IN PGET_GCMARKER_EXCEPTION_CODE getGcMarkerExceptionCode);
 
+PALIMPORT
+VOID
+PALAPI
+PAL_ThrowExceptionFromContext(
+    IN CONTEXT* context,
+    IN PAL_SEHException* ex);
+
 //
 // This holder is used to indicate that a hardware
 // exception should be raised as a C++ exception
index 2034812..04e5bca 100644 (file)
@@ -72,18 +72,21 @@ if(PAL_CMAKE_PLATFORM_ARCH_AMD64)
   set(ARCH_SOURCES
     arch/i386/context2.S
     arch/i386/debugbreak.S
+    arch/i386/exceptionhelper.S
     arch/i386/processor.cpp
   )
 elseif(PAL_CMAKE_PLATFORM_ARCH_ARM)
   set(ARCH_SOURCES
     arch/arm/context2.S
     arch/arm/debugbreak.S
+    arch/arm/exceptionhelper.S
     arch/arm/processor.cpp
   )
 elseif(PAL_CMAKE_PLATFORM_ARCH_ARM64)
   set(ARCH_SOURCES
     arch/arm64/context2.S
     arch/arm64/debugbreak.S
+    arch/arm64/exceptionhelper.S
     arch/arm64/processor.cpp
   )
 endif()
diff --git a/src/coreclr/src/pal/src/arch/arm/exceptionhelper.S b/src/coreclr/src/pal/src/arch/arm/exceptionhelper.S
new file mode 100644 (file)
index 0000000..18df3f7
--- /dev/null
@@ -0,0 +1,13 @@
+// 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 "unixasmmacros.inc"
+
+.syntax unified
+.thumb
+
+LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT
+       // TODO: Implement
+       bx lr
+LEAF_END ThrowExceptionFromContextInternal, _TEXT
diff --git a/src/coreclr/src/pal/src/arch/arm64/exceptionhelper.S b/src/coreclr/src/pal/src/arch/arm64/exceptionhelper.S
new file mode 100644 (file)
index 0000000..4fdcfc5
--- /dev/null
@@ -0,0 +1,9 @@
+// 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 "unixasmmacros.inc"
+
+LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT
+    EMIT_BREAKPOINT
+LEAF_END ThrowExceptionFromContextInternal, _TEXT
diff --git a/src/coreclr/src/pal/src/arch/i386/exceptionhelper.S b/src/coreclr/src/pal/src/arch/i386/exceptionhelper.S
new file mode 100644 (file)
index 0000000..b7b34ac
--- /dev/null
@@ -0,0 +1,42 @@
+// 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"
+
+//////////////////////////////////////////////////////////////////////////
+//
+// This function creates a stack frame right below the target frame, restores all callee 
+// saved registers from the passed in context, sets the RSP to that frame and sets the 
+// return address to the target frame's RIP.
+// Then it uses the ThrowExceptionHelper to throw the passed in exception from that context.
+// EXTERN_C void ThrowExceptionFromContextInternal(CONTEXT* context, PAL_SEHException* ex);
+LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT
+        // Save the RBP to the stack so that the unwind can work at the instruction after
+        // loading the RBP from the context, but before loading the RSP from the context.
+        push_nonvol_reg rbp
+        mov     r12, [rdi + CONTEXT_R12]
+        mov     r13, [rdi + CONTEXT_R13]
+        mov     r14, [rdi + CONTEXT_R14]
+        mov     r15, [rdi + CONTEXT_R15]
+        mov     rbx, [rdi + CONTEXT_Rbx]
+        mov     rbp, [rdi + CONTEXT_Rbp]
+        mov     rsp, [rdi + CONTEXT_Rsp]
+        // The RSP was set to the target frame's value, so the current function's
+        // CFA is now right at the RSP.
+        .cfi_def_cfa_offset 0
+
+        // Indicate that now that we have moved the RSP to the target address, 
+        // the RBP is no longer saved in the current stack frame. 
+        .cfi_restore rbp
+
+        mov     rax, [rdi + CONTEXT_Rip]
+
+        // Store return address to the stack
+        push_register rax
+        // The PAL_SEHException pointer
+        mov     rdi, rsi 
+        jmp     EXTERNAL_C_FUNC(ThrowExceptionHelper)
+LEAF_END ThrowExceptionFromContextInternal, _TEXT
index 222ea65..4cc30c2 100644 (file)
@@ -149,6 +149,48 @@ PAL_SetGetGcMarkerExceptionCode(
     g_getGcMarkerExceptionCode = getGcMarkerExceptionCode;
 }
 
+EXTERN_C void ThrowExceptionFromContextInternal(CONTEXT* context, PAL_SEHException* ex);
+
+/*++
+Function:
+    PAL_ThrowExceptionFromContext
+
+    This function creates a stack frame right below the target frame, restores all callee
+    saved registers from the passed in context, sets the RSP to that frame and sets the
+    return address to the target frame's RIP.
+    Then it uses the ThrowExceptionHelper to throw the passed in exception from that context.
+
+Parameters:
+    CONTEXT* context - context from which the exception will be thrown
+    PAL_SEHException* ex - the exception to throw.
+--*/
+VOID
+PALAPI 
+PAL_ThrowExceptionFromContext(CONTEXT* context, PAL_SEHException* ex)
+{
+    // We need to make a copy of the exception off stack, since the "ex" is located in one of the stack
+    // frames that will become obsolete by the ThrowExceptionFromContextInternal and the ThrowExceptionHelper
+    // could overwrite the "ex" object by stack e.g. when allocating the low level exception object for "throw".
+    static __thread BYTE threadLocalExceptionStorage[sizeof(PAL_SEHException)];
+    ThrowExceptionFromContextInternal(context, new (threadLocalExceptionStorage) PAL_SEHException(*ex));
+}
+
+/*++
+Function:
+    ThrowExceptionHelper
+
+    Helper function to throw the passed in exception.
+    It is called from the assembler function ThrowExceptionFromContextInternal
+
+Parameters:
+    PAL_SEHException* ex - the exception to throw.
+--*/
+extern "C"
+void ThrowExceptionHelper(PAL_SEHException* ex)
+{
+    throw *ex;
+}
+
 /*++
 Function:
     SEHProcessException
@@ -175,7 +217,7 @@ SEHProcessException(PEXCEPTION_POINTERS pointers)
 
         if (CatchHardwareExceptionHolder::IsEnabled())
         {
-            throw exception;
+            PAL_ThrowExceptionFromContext(&exception.ContextRecord, &exception);
         }
     }
 
index 2d9d0b5..97a963c 100644 (file)
@@ -31,8 +31,6 @@ extern "C" int InitializeDllTest2();
 extern "C" int DllTest1();
 extern "C" int DllTest2();
 
-#if !defined(__FreeBSD__) && !defined(__NetBSD__)
-
 bool bSignal = false;
 bool bCatch = false;
 bool bHandler = false;
@@ -69,11 +67,8 @@ void sigsegv_handler(int code, siginfo_t *siginfo, void *context)
     _exit(PASS);
 }
 
-#endif
-
 int main(int argc, char *argv[])
 {
-#if !defined(__FreeBSD__) && !defined(__NetBSD__)
     struct sigaction newAction;
     struct sigaction oldAction;
     newAction.sa_flags = SA_SIGINFO | SA_RESTART;
@@ -119,6 +114,4 @@ int main(int argc, char *argv[])
 
     printf("ERROR: code was executed after the access violation.\n");
     return FAIL;
-#endif
-    return PASS;
 }
index 150b502..7db8e52 100644 (file)
@@ -333,43 +333,3 @@ NullObject:
         jmp     C_FUNC(JIT_InternalThrow)
                                                                                       
 LEAF_END SinglecastDelegateInvokeStub, _TEXT
-
-//////////////////////////////////////////////////////////////////////////
-//
-// This function initiates unwinding of native frames during the unwinding of a managed 
-// exception. The managed exception can be propagated over several managed / native ranges 
-// until it is finally handled by a managed handler or leaves the stack unhandled and
-// aborts the current process.
-// It creates a stack frame right below the target frame, restores all callee saved registers
-// from the passed in context and finally sets the RSP to that frame and sets the return
-// address to the target frame's RIP.
-//
-// EXTERN_C void StartUnwindingNativeFrames(CONTEXT* context, PAL_SEHException* ex);
-LEAF_ENTRY StartUnwindingNativeFrames, _TEXT
-        // Save the RBP to the stack so that the unwind can work at the instruction after
-        // loading the RBP from the context, but before loading the RSP from the context.
-        push_nonvol_reg rbp
-        mov     r12, [rdi + OFFSETOF__CONTEXT__R12]
-        mov     r13, [rdi + OFFSETOF__CONTEXT__R13]
-        mov     r14, [rdi + OFFSETOF__CONTEXT__R14]
-        mov     r15, [rdi + OFFSETOF__CONTEXT__R15]
-        mov     rbx, [rdi + OFFSETOF__CONTEXT__Rbx]
-        mov     rbp, [rdi + OFFSETOF__CONTEXT__Rbp]
-        mov     rsp, [rdi + OFFSETOF__CONTEXT__Rsp]
-        // The RSP was set to the target frame's value, so the current function's
-        // CFA is now right at the RSP.
-        .cfi_def_cfa_offset 0
-
-        // Indicate that now that we have moved the RSP to the target address, 
-        // the RBP is no longer saved in the current stack frame. 
-        .cfi_restore rbp
-
-        mov     rax, [rdi + OFFSETOF__CONTEXT__Rip]
-
-        // Store return address to the stack
-        push_register rax
-        // The PAL_SEHException pointer
-        mov     rdi, rsi 
-        jmp     EXTERNAL_C_FUNC(ThrowExceptionHelper)
-LEAF_END StartUnwindingNativeFrames, _TEXT
-
index 8cc6150..cccc870 100644 (file)
@@ -835,8 +835,3 @@ DynamicHelper DynamicHelperFrameFlags_ObjectArg, _Obj
 DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_ObjectArg2, _ObjObj
 
 #endif
-
-LEAF_ENTRY StartUnwindingNativeFrames, _TEXT
-    EMIT_BREAKPOINT
-LEAF_END StartUnwindingNativeFrames, _TEXT
-
index 14ba4e4..c3c5db2 100644 (file)
@@ -4467,11 +4467,7 @@ VOID UnwindManagedExceptionPass2(PAL_SEHException& ex, CONTEXT* unwindStartConte
 
             // Now we need to unwind the native frames until we reach managed frames again or the exception is
             // handled in the native code.
-            // We need to make a copy of the exception off stack, since the "ex" is located in one of the stack
-            // frames that will become obsolete by the StartUnwindingNativeFrames and the ThrowExceptionHelper 
-            // could overwrite the "ex" object by stack e.g. when allocating the low level exception object for "throw".
-            static __thread BYTE threadLocalExceptionStorage[sizeof(PAL_SEHException)];
-            StartUnwindingNativeFrames(currentFrameContext, new (threadLocalExceptionStorage) PAL_SEHException(ex));
+            PAL_ThrowExceptionFromContext(currentFrameContext, &ex);
             UNREACHABLE();
         }
 
@@ -4637,20 +4633,6 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
     EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
 }
 
-//---------------------------------------------------------------------------------------
-//
-// Helper function to throw the passed in exception.
-// It is called from the assembler function StartUnwindingNativeFrames 
-// Arguments:
-//
-//      ex - the exception to throw.
-//
-extern "C"
-void ThrowExceptionHelper(PAL_SEHException* ex)
-{
-    throw *ex;
-}
-
 VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex)
 {
     do