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
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
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()
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
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
if (CatchHardwareExceptionHolder::IsEnabled())
{
- throw exception;
+ PAL_ThrowExceptionFromContext(&exception.ContextRecord, &exception);
}
}
extern "C" int DllTest1();
extern "C" int DllTest2();
-#if !defined(__FreeBSD__) && !defined(__NetBSD__)
-
bool bSignal = false;
bool bCatch = false;
bool bHandler = false;
_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;
printf("ERROR: code was executed after the access violation.\n");
return FAIL;
-#endif
- return PASS;
}
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
-
DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_ObjectArg2, _ObjObj
#endif
-
-LEAF_ENTRY StartUnwindingNativeFrames, _TEXT
- EMIT_BREAKPOINT
-LEAF_END StartUnwindingNativeFrames, _TEXT
-
// 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();
}
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