summaryrefslogtreecommitdiff
path: root/src/pal/src
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/src')
-rw-r--r--src/pal/src/CMakeLists.txt40
-rw-r--r--src/pal/src/arch/amd64/callsignalhandlerwrapper.S31
-rw-r--r--src/pal/src/arch/amd64/signalhandlerhelper.cpp70
-rw-r--r--src/pal/src/arch/arm/callsignalhandlerwrapper.S32
-rw-r--r--src/pal/src/arch/arm/signalhandlerhelper.cpp70
-rw-r--r--src/pal/src/arch/arm64/callsignalhandlerwrapper.S30
-rw-r--r--src/pal/src/arch/arm64/signalhandlerhelper.cpp69
-rw-r--r--src/pal/src/arch/i386/callsignalhandlerwrapper.S47
-rw-r--r--src/pal/src/arch/i386/signalhandlerhelper.cpp77
-rw-r--r--src/pal/src/exception/seh.cpp2
-rw-r--r--src/pal/src/exception/signal.cpp165
-rw-r--r--src/pal/src/exception/signal.hpp52
-rw-r--r--src/pal/src/include/pal/context.h15
-rw-r--r--src/pal/src/include/pal/signal.hpp141
-rw-r--r--src/pal/src/init/sxs.cpp15
-rw-r--r--src/pal/src/thread/context.cpp29
-rw-r--r--src/pal/src/thread/thread.cpp13
17 files changed, 808 insertions, 90 deletions
diff --git a/src/pal/src/CMakeLists.txt b/src/pal/src/CMakeLists.txt
index 16c9d8bd6f..dae0f3fe3f 100644
--- a/src/pal/src/CMakeLists.txt
+++ b/src/pal/src/CMakeLists.txt
@@ -101,35 +101,19 @@ set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -Wl,--no
add_compile_options(-fPIC)
-if(PAL_CMAKE_PLATFORM_ARCH_AMD64)
- set(ARCH_SOURCES
- arch/amd64/context2.S
- arch/amd64/debugbreak.S
- arch/amd64/exceptionhelper.S
- arch/amd64/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
- )
-elseif(PAL_CMAKE_PLATFORM_ARCH_I386)
- set(ARCH_SOURCES
- arch/i386/context2.S
- arch/i386/debugbreak.S
- arch/i386/exceptionhelper.S
- arch/i386/processor.cpp
+set(ARCH_SOURCES
+ arch/${ARCH_SOURCES_DIR}/context2.S
+ arch/${ARCH_SOURCES_DIR}/debugbreak.S
+ arch/${ARCH_SOURCES_DIR}/exceptionhelper.S
+ arch/${ARCH_SOURCES_DIR}/processor.cpp
+)
+
+if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
+ list(APPEND PLATFORM_SOURCES
+ arch/${ARCH_SOURCES_DIR}/callsignalhandlerwrapper.S
+ arch/${ARCH_SOURCES_DIR}/signalhandlerhelper.cpp
)
-endif()
+endif(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
if(PAL_CMAKE_PLATFORM_ARCH_ARM)
set_source_files_properties(exception/seh.cpp PROPERTIES COMPILE_FLAGS -Wno-error=inline-asm)
diff --git a/src/pal/src/arch/amd64/callsignalhandlerwrapper.S b/src/pal/src/arch/amd64/callsignalhandlerwrapper.S
new file mode 100644
index 0000000000..8260591c30
--- /dev/null
+++ b/src/pal/src/arch/amd64/callsignalhandlerwrapper.S
@@ -0,0 +1,31 @@
+// 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"
+
+.macro CALL_SIGNAL_HANDLER_WRAPPER Alignment
+
+.globl C_FUNC(SignalHandlerWorkerReturnOffset\Alignment)
+C_FUNC(SignalHandlerWorkerReturnOffset\Alignment):
+ .int LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment)-C_FUNC(CallSignalHandlerWrapper\Alignment)
+
+// This function is never called, only a fake stack frame will be setup to have a return
+// address set to SignalHandlerWorkerReturn during SIGSEGV handling.
+// It enables the unwinder to unwind stack from the handling code to the actual failure site.
+NESTED_ENTRY CallSignalHandlerWrapper\Alignment, _TEXT, NoHandler
+ .cfi_def_cfa_offset (128 + 8 + \Alignment) // red zone + return address + alignment
+ .cfi_offset rip, -(128 + 8 + \Alignment)
+ push_nonvol_reg rbp
+ call EXTERNAL_C_FUNC(signal_handler_worker)
+LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment):
+ pop rbp
+ ret
+NESTED_END CallSignalHandlerWrapper\Alignment, _TEXT
+
+.endm
+
+CALL_SIGNAL_HANDLER_WRAPPER 0
+CALL_SIGNAL_HANDLER_WRAPPER 8
diff --git a/src/pal/src/arch/amd64/signalhandlerhelper.cpp b/src/pal/src/arch/amd64/signalhandlerhelper.cpp
new file mode 100644
index 0000000000..5e37583a9f
--- /dev/null
+++ b/src/pal/src/arch/amd64/signalhandlerhelper.cpp
@@ -0,0 +1,70 @@
+// 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 "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do this first
+
+#include "pal/palinternal.h"
+#include "pal/context.h"
+#include "pal/signal.hpp"
+#include "pal/utils.h"
+#include <sys/ucontext.h>
+
+/*++
+Function :
+ signal_handler_worker
+
+ Handles signal on the original stack where the signal occured.
+ Invoked via setcontext.
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+ returnPoint - context to which the function returns if the common_signal_handler returns
+
+ (no return value)
+--*/
+void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint)
+{
+ ucontext_t *ucontext = (ucontext_t *)context;
+ size_t faultSp = (size_t)MCREG_Rsp(ucontext->uc_mcontext);
+
+ _ASSERTE(IS_ALIGNED(faultSp, 8));
+
+ size_t fakeFrameReturnAddress;
+
+ if (IS_ALIGNED(faultSp, 16))
+ {
+ fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset0 + (size_t)CallSignalHandlerWrapper0;
+ }
+ else
+ {
+ fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset8 + (size_t)CallSignalHandlerWrapper8;
+ }
+
+ // preserve 128 bytes long red zone and align stack pointer
+ size_t* sp = (size_t*)ALIGN_DOWN(faultSp - 128, 16);
+
+ // Build fake stack frame to enable the stack unwinder to unwind from signal_handler_worker to the faulting instruction
+ *--sp = (size_t)MCREG_Rip(ucontext->uc_mcontext);
+ *--sp = (size_t)MCREG_Rbp(ucontext->uc_mcontext);
+ size_t fp = (size_t)sp;
+ *--sp = fakeFrameReturnAddress;
+
+ // Switch the current context to the signal_handler_worker and the original stack
+ ucontext_t ucontext2;
+ getcontext(&ucontext2);
+
+ // We don't care about the other registers state since the stack unwinding restores
+ // them for the target frame directly from the signal context.
+ MCREG_Rsp(ucontext2.uc_mcontext) = (size_t)sp;
+ MCREG_Rbx(ucontext2.uc_mcontext) = (size_t)faultSp;
+ MCREG_Rbp(ucontext2.uc_mcontext) = (size_t)fp;
+ MCREG_Rip(ucontext2.uc_mcontext) = (size_t)signal_handler_worker;
+ MCREG_Rdi(ucontext2.uc_mcontext) = code;
+ MCREG_Rsi(ucontext2.uc_mcontext) = (size_t)siginfo;
+ MCREG_Rdx(ucontext2.uc_mcontext) = (size_t)context;
+ MCREG_Rcx(ucontext2.uc_mcontext) = (size_t)returnPoint;
+
+ setcontext(&ucontext2);
+}
diff --git a/src/pal/src/arch/arm/callsignalhandlerwrapper.S b/src/pal/src/arch/arm/callsignalhandlerwrapper.S
new file mode 100644
index 0000000000..266e4fdfe9
--- /dev/null
+++ b/src/pal/src/arch/arm/callsignalhandlerwrapper.S
@@ -0,0 +1,32 @@
+// 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"
+#include "asmconstants.h"
+
+.syntax unified
+.thumb
+
+.macro CALL_SIGNAL_HANDLER_WRAPPER Alignment
+
+.globl C_FUNC(SignalHandlerWorkerReturnOffset\Alignment)
+C_FUNC(SignalHandlerWorkerReturnOffset\Alignment):
+ .int LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment)-C_FUNC(CallSignalHandlerWrapper\Alignment)
+
+// This function is never called, only a fake stack frame will be setup to have a return
+// address set to SignalHandlerWorkerReturn during SIGSEGV handling.
+// It enables the unwinder to unwind stack from the handling code to the actual failure site.
+NESTED_ENTRY CallSignalHandlerWrapper\Alignment, _TEXT, NoHandler
+ sub sp, sp, #(8 + \Alignment) // red zone + alignment
+ stmfd sp!, {r7, lr}
+ bl EXTERNAL_C_FUNC(signal_handler_worker)
+LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment):
+ ldmfd sp!, {r7, lr}
+ bx lr
+NESTED_END CallSignalHandlerWrapper\Alignment, _TEXT
+
+.endm
+
+CALL_SIGNAL_HANDLER_WRAPPER 0
+CALL_SIGNAL_HANDLER_WRAPPER 4
diff --git a/src/pal/src/arch/arm/signalhandlerhelper.cpp b/src/pal/src/arch/arm/signalhandlerhelper.cpp
new file mode 100644
index 0000000000..beda0b441e
--- /dev/null
+++ b/src/pal/src/arch/arm/signalhandlerhelper.cpp
@@ -0,0 +1,70 @@
+// 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 "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do this first
+
+#include "pal/palinternal.h"
+#include "pal/context.h"
+#include "pal/signal.hpp"
+#include "pal/utils.h"
+#include <sys/ucontext.h>
+
+/*++
+Function :
+ signal_handler_worker
+
+ Handles signal on the original stack where the signal occured.
+ Invoked via setcontext.
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+ returnPoint - context to which the function returns if the common_signal_handler returns
+
+ (no return value)
+--*/
+void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint)
+{
+ ucontext_t *ucontext = (ucontext_t *)context;
+ size_t faultSp = (size_t)MCREG_Sp(ucontext->uc_mcontext);
+
+ _ASSERTE(IS_ALIGNED(faultSp, 4));
+
+ size_t fakeFrameReturnAddress;
+
+ if (IS_ALIGNED(faultSp, 8))
+ {
+ fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset0 + (size_t)CallSignalHandlerWrapper0;
+ }
+ else
+ {
+ fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset4 + (size_t)CallSignalHandlerWrapper4;
+ }
+
+ // preserve 8 bytes long red zone and align stack pointer
+ size_t* sp = (size_t*)ALIGN_DOWN(faultSp - 8, 8);
+
+ // Build fake stack frame to enable the stack unwinder to unwind from signal_handler_worker to the faulting instruction
+ // pushed LR
+ *--sp = (size_t)MCREG_Pc(ucontext->uc_mcontext);
+ // pushed frame pointer
+ *--sp = (size_t)MCREG_R7(ucontext->uc_mcontext);
+
+ // Switch the current context to the signal_handler_worker and the original stack
+ ucontext_t ucontext2;
+ getcontext(&ucontext2);
+
+ // We don't care about the other registers state since the stack unwinding restores
+ // them for the target frame directly from the signal context.
+ MCREG_Sp(ucontext2.uc_mcontext) = (size_t)sp;
+ MCREG_R7(ucontext2.uc_mcontext) = (size_t)sp; // Fp and Sp are the same
+ MCREG_Lr(ucontext2.uc_mcontext) = fakeFrameReturnAddress;
+ MCREG_Pc(ucontext2.uc_mcontext) = (size_t)signal_handler_worker;
+ MCREG_R0(ucontext2.uc_mcontext) = code;
+ MCREG_R1(ucontext2.uc_mcontext) = (size_t)siginfo;
+ MCREG_R2(ucontext2.uc_mcontext) = (size_t)context;
+ MCREG_R3(ucontext2.uc_mcontext) = (size_t)returnPoint;
+
+ setcontext(&ucontext2);
+}
diff --git a/src/pal/src/arch/arm64/callsignalhandlerwrapper.S b/src/pal/src/arch/arm64/callsignalhandlerwrapper.S
new file mode 100644
index 0000000000..6bff23b7be
--- /dev/null
+++ b/src/pal/src/arch/arm64/callsignalhandlerwrapper.S
@@ -0,0 +1,30 @@
+// 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"
+#include "asmconstants.h"
+
+.macro CALL_SIGNAL_HANDLER_WRAPPER Alignment
+
+.globl C_FUNC(SignalHandlerWorkerReturnOffset\Alignment)
+C_FUNC(SignalHandlerWorkerReturnOffset\Alignment):
+ .int LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment)-C_FUNC(CallSignalHandlerWrapper\Alignment)
+
+// This function is never called, only a fake stack frame will be setup to have a return
+// address set to SignalHandlerWorkerReturn during SIGSEGV handling.
+// It enables the unwinder to unwind stack from the handling code to the actual failure site.
+NESTED_ENTRY CallSignalHandlerWrapper\Alignment, _TEXT, NoHandler
+ PROLOG_STACK_ALLOC (128 + 8 + 8 + \Alignment) // red zone + fp + lr + alignment
+ PROLOG_SAVE_REG_PAIR fp, lr, 0
+ bl EXTERNAL_C_FUNC(signal_handler_worker)
+LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment):
+ EPILOG_RESTORE_REG_PAIR fp, lr, 0
+ EPILOG_STACK_FREE (128 + 8 + 8 + \Alignment)
+ ret
+NESTED_END CallSignalHandlerWrapper\Alignment, _TEXT
+
+.endm
+
+CALL_SIGNAL_HANDLER_WRAPPER 0
+CALL_SIGNAL_HANDLER_WRAPPER 8
diff --git a/src/pal/src/arch/arm64/signalhandlerhelper.cpp b/src/pal/src/arch/arm64/signalhandlerhelper.cpp
new file mode 100644
index 0000000000..3891b9ea7f
--- /dev/null
+++ b/src/pal/src/arch/arm64/signalhandlerhelper.cpp
@@ -0,0 +1,69 @@
+// 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 "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do this first
+
+#include "pal/palinternal.h"
+#include "pal/context.h"
+#include "pal/signal.hpp"
+#include "pal/utils.h"
+#include <sys/ucontext.h>
+
+/*++
+Function :
+ signal_handler_worker
+
+ Handles signal on the original stack where the signal occured.
+ Invoked via setcontext.
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+ returnPoint - context to which the function returns if the common_signal_handler returns
+
+ (no return value)
+--*/
+void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint)
+{
+ ucontext_t *ucontext = (ucontext_t *)context;
+ size_t faultSp = (size_t)MCREG_Sp(ucontext->uc_mcontext);
+ _ASSERTE(IS_ALIGNED(faultSp, 8));
+
+ size_t fakeFrameReturnAddress;
+
+ if (IS_ALIGNED(faultSp, 16))
+ {
+ fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset0 + (size_t)CallSignalHandlerWrapper0;
+ }
+ else
+ {
+ fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset8 + (size_t)CallSignalHandlerWrapper8;
+ }
+
+ // preserve 128 bytes long red zone and align stack pointer
+ size_t* sp = (size_t*)ALIGN_DOWN(faultSp - 128, 16);
+
+ // Build fake stack frame to enable the stack unwinder to unwind from signal_handler_worker to the faulting instruction
+ // pushed LR
+ *--sp = (size_t)MCREG_Pc(ucontext->uc_mcontext);
+ // pushed frame pointer
+ *--sp = (size_t)MCREG_Fp(ucontext->uc_mcontext);
+
+ // Switch the current context to the signal_handler_worker and the original stack
+ ucontext_t ucontext2;
+ getcontext(&ucontext2);
+
+ // We don't care about the other registers state since the stack unwinding restores
+ // them for the target frame directly from the signal context.
+ MCREG_Sp(ucontext2.uc_mcontext) = (size_t)sp;
+ MCREG_Fp(ucontext2.uc_mcontext) = (size_t)sp; // Fp and Sp are the same
+ MCREG_Lr(ucontext2.uc_mcontext) = fakeFrameReturnAddress;
+ MCREG_Pc(ucontext2.uc_mcontext) = (size_t)signal_handler_worker;
+ MCREG_X0(ucontext2.uc_mcontext) = code;
+ MCREG_X1(ucontext2.uc_mcontext) = (size_t)siginfo;
+ MCREG_X2(ucontext2.uc_mcontext) = (size_t)context;
+ MCREG_X3(ucontext2.uc_mcontext) = (size_t)returnPoint;
+
+ setcontext(&ucontext2);
+}
diff --git a/src/pal/src/arch/i386/callsignalhandlerwrapper.S b/src/pal/src/arch/i386/callsignalhandlerwrapper.S
new file mode 100644
index 0000000000..26f06d9886
--- /dev/null
+++ b/src/pal/src/arch/i386/callsignalhandlerwrapper.S
@@ -0,0 +1,47 @@
+// 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"
+
+.macro CALL_SIGNAL_HANDLER_WRAPPER Alignment
+
+.globl C_FUNC(SignalHandlerWorkerReturnOffset\Alignment)
+C_FUNC(SignalHandlerWorkerReturnOffset\Alignment):
+ .int LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment)-C_FUNC(CallSignalHandlerWrapper\Alignment)
+
+// This function is never called, only a fake stack frame will be setup to have a return
+// address set to SignalHandlerWorkerReturn during SIGSEGV handling.
+// It enables the unwinder to unwind stack from the handling code to the actual failure site.
+NESTED_ENTRY CallSignalHandlerWrapper\Alignment, _TEXT, NoHandler
+
+ .cfi_def_cfa_offset (4 + \Alignment) // return address + stack alignment
+ .cfi_offset eip, -(4 + \Alignment)
+ push ebp
+ .cfi_adjust_cfa_offset 4
+ .cfi_rel_offset ebp, 0
+ mov ebp, esp
+ .cfi_def_cfa_register ebp
+ // Align stack
+ sub esp, 8
+ // Simulate arguments pushing
+ push eax
+ push eax
+ push eax
+ push eax
+ call EXTERNAL_C_FUNC(signal_handler_worker)
+LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment):
+ add esp, 4 * 4 + 8
+ pop ebp
+ ret
+
+NESTED_END CallSignalHandlerWrapper\Alignment, _TEXT
+
+.endm
+
+CALL_SIGNAL_HANDLER_WRAPPER 0
+CALL_SIGNAL_HANDLER_WRAPPER 4
+CALL_SIGNAL_HANDLER_WRAPPER 8
+CALL_SIGNAL_HANDLER_WRAPPER 12
diff --git a/src/pal/src/arch/i386/signalhandlerhelper.cpp b/src/pal/src/arch/i386/signalhandlerhelper.cpp
new file mode 100644
index 0000000000..5e7333ad3d
--- /dev/null
+++ b/src/pal/src/arch/i386/signalhandlerhelper.cpp
@@ -0,0 +1,77 @@
+// 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 "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do this first
+
+#include "pal/palinternal.h"
+#include "pal/context.h"
+#include "pal/signal.hpp"
+#include "pal/utils.h"
+#include <sys/ucontext.h>
+
+/*++
+Function :
+ signal_handler_worker
+
+ Handles signal on the original stack where the signal occured.
+ Invoked via setcontext.
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+ returnPoint - context to which the function returns if the common_signal_handler returns
+
+ (no return value)
+--*/
+void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint)
+{
+ ucontext_t *ucontext = (ucontext_t *)context;
+ size_t faultSp = (size_t)MCREG_Esp(ucontext->uc_mcontext);
+
+ _ASSERTE(IS_ALIGNED(faultSp, 4));
+
+ size_t fakeFrameReturnAddress;
+
+ switch (faultSp & 0xc)
+ {
+ case 0x0:
+ fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset0 + (size_t)CallSignalHandlerWrapper0;
+ break;
+ case 0x4:
+ fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset4 + (size_t)CallSignalHandlerWrapper4;
+ break;
+ case 0x8:
+ fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset8 + (size_t)CallSignalHandlerWrapper8;
+ break;
+ case 0xc:
+ fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset12 + (size_t)CallSignalHandlerWrapper12;
+ break;
+ }
+
+ size_t* sp = (size_t*)ALIGN_DOWN(faultSp, 16);
+
+ // Build fake stack frame to enable the stack unwinder to unwind from signal_handler_worker to the faulting instruction
+ *--sp = (size_t)MCREG_Eip(ucontext->uc_mcontext);
+ *--sp = (size_t)MCREG_Ebp(ucontext->uc_mcontext);
+ size_t fp = (size_t)sp;
+ // Align stack
+ sp -= 2;
+ *--sp = (size_t)returnPoint;
+ *--sp = (size_t)context;
+ *--sp = (size_t)siginfo;
+ *--sp = code;
+ *--sp = fakeFrameReturnAddress;
+
+ // Switch the current context to the signal_handler_worker and the original stack
+ ucontext_t ucontext2;
+ getcontext(&ucontext2);
+
+ // We don't care about the other registers state since the stack unwinding restores
+ // them for the target frame directly from the signal context.
+ MCREG_Esp(ucontext2.uc_mcontext) = (size_t)sp;
+ MCREG_Ebp(ucontext2.uc_mcontext) = (size_t)fp;
+ MCREG_Eip(ucontext2.uc_mcontext) = (size_t)signal_handler_worker;
+
+ setcontext(&ucontext2);
+}
diff --git a/src/pal/src/exception/seh.cpp b/src/pal/src/exception/seh.cpp
index ad09e02884..7682e9e6e6 100644
--- a/src/pal/src/exception/seh.cpp
+++ b/src/pal/src/exception/seh.cpp
@@ -27,7 +27,7 @@ Abstract:
#include "pal/init.h"
#include "pal/process.h"
#include "pal/malloc.hpp"
-#include "signal.hpp"
+#include "pal/signal.hpp"
#if HAVE_MACH_EXCEPTIONS
#include "machexception.h"
diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp
index 26e2a012c5..d442afaf4c 100644
--- a/src/pal/src/exception/signal.cpp
+++ b/src/pal/src/exception/signal.cpp
@@ -27,12 +27,15 @@ SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do
#include "pal/threadinfo.hpp"
#include "pal/threadsusp.hpp"
#include "pal/seh.hpp"
+#include "pal/signal.hpp"
#include "pal/palinternal.h"
#if !HAVE_MACH_EXCEPTIONS
#include "pal/init.h"
#include "pal/process.h"
#include "pal/debug.h"
+#include "pal/virtual.h"
+#include "pal/utils.h"
#include <signal.h>
#include <errno.h>
@@ -40,6 +43,7 @@ SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do
#include <sys/ucontext.h>
#include <sys/utsname.h>
#include <unistd.h>
+#include <sys/mman.h>
#include "pal/context.h"
@@ -63,6 +67,13 @@ typedef void *siginfo_t;
#endif /* !HAVE_SIGINFO_T */
typedef void (*SIGFUNC)(int, siginfo_t *, void *);
+// Return context and status for the signal_handler_worker.
+struct SignalHandlerWorkerReturnPoint
+{
+ bool returnFromHandler;
+ ucontext_t context;
+};
+
/* internal function declarations *********************************************/
static void sigill_handler(int code, siginfo_t *siginfo, void *context);
@@ -80,7 +91,7 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext
static void inject_activation_handler(int code, siginfo_t *siginfo, void *context);
#endif
-static void handle_signal(int signal_id, SIGFUNC sigfunc, struct sigaction *previousAction);
+static void handle_signal(int signal_id, SIGFUNC sigfunc, struct sigaction *previousAction, int additionalFlags = 0);
static void restore_signal(int signal_id, struct sigaction *previousAction);
/* internal data declarations *********************************************/
@@ -108,6 +119,88 @@ int g_common_signal_handler_context_locvar_offset = 0;
/*++
Function :
+ EnsureSignalAlternateStack
+
+ Ensure that alternate stack for signal handling is allocated for the current thread
+
+Parameters :
+ None
+
+Return :
+ TRUE in case of a success, FALSE otherwise
+--*/
+BOOL EnsureSignalAlternateStack()
+{
+ stack_t oss;
+
+ // Query the current alternate signal stack
+ int st = sigaltstack(NULL, &oss);
+
+ if ((st == 0) && (oss.ss_flags == SS_DISABLE))
+ {
+ // There is no alternate stack for SIGSEGV handling installed yet so allocate one
+
+ // We include the size of the SignalHandlerWorkerReturnPoint in the alternate stack size since the
+ // context contained in it is large and the SIGSTKSZ was not sufficient on ARM64 during testing.
+ int altStackSize = SIGSTKSZ + ALIGN_UP(sizeof(SignalHandlerWorkerReturnPoint), 16) + VIRTUAL_PAGE_SIZE;
+ void* altStack;
+ int st = posix_memalign(&altStack, VIRTUAL_PAGE_SIZE, altStackSize);
+ if (st == 0)
+ {
+ // create a guard page for the alternate stack
+ st = mprotect(altStack, VIRTUAL_PAGE_SIZE, PROT_NONE);
+ if (st == 0)
+ {
+ stack_t ss;
+ ss.ss_sp = (char*)altStack;
+ ss.ss_size = altStackSize;
+ ss.ss_flags = 0;
+ st = sigaltstack(&ss, NULL);
+ if (st != 0)
+ {
+ // Installation of the alternate stack failed, so revert the guard page protection
+ int st2 = mprotect(altStack, VIRTUAL_PAGE_SIZE, PROT_READ | PROT_WRITE);
+ _ASSERTE(st2 == 0);
+ }
+ }
+
+ if (st != 0)
+ {
+ free(altStack);
+ }
+ }
+ }
+
+ return (st == 0);
+}
+
+/*++
+Function :
+ FreeSignalAlternateStack
+
+ Free alternate stack for signal handling
+
+Parameters :
+ None
+
+Return :
+ None
+--*/
+void FreeSignalAlternateStack()
+{
+ stack_t ss, oss;
+ ss.ss_flags = SS_DISABLE;
+ int st = sigaltstack(&ss, &oss);
+ if ((st == 0) && (oss.ss_flags != SS_DISABLE))
+ {
+ int st = mprotect(oss.ss_sp, VIRTUAL_PAGE_SIZE, PROT_READ | PROT_WRITE);
+ _ASSERTE(st == 0);
+ free(oss.ss_sp);
+ }
+}
+
+/*++
+Function :
SEHInitializeSignals
Set up signal handlers to catch signals and translate them to exceptions
@@ -139,10 +232,16 @@ BOOL SEHInitializeSignals(DWORD flags)
handle_signal(SIGTRAP, sigtrap_handler, &g_previous_sigtrap);
handle_signal(SIGFPE, sigfpe_handler, &g_previous_sigfpe);
handle_signal(SIGBUS, sigbus_handler, &g_previous_sigbus);
- handle_signal(SIGSEGV, sigsegv_handler, &g_previous_sigsegv);
+ // SIGSEGV handler runs on a separate stack so that we can handle stack overflow
+ handle_signal(SIGSEGV, sigsegv_handler, &g_previous_sigsegv, SA_ONSTACK);
handle_signal(SIGINT, sigint_handler, &g_previous_sigint);
handle_signal(SIGQUIT, sigquit_handler, &g_previous_sigquit);
+ if (!EnsureSignalAlternateStack())
+ {
+ return FALSE;
+ }
+
if (flags & PAL_INITIALIZE_REGISTER_SIGTERM_HANDLER)
{
handle_signal(SIGTERM, sigterm_handler, &g_previous_sigterm);
@@ -276,6 +375,28 @@ static void sigfpe_handler(int code, siginfo_t *siginfo, void *context)
/*++
Function :
+ signal_handler_worker
+
+ Handles signal on the original stack where the signal occured.
+ Invoked via setcontext.
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+ returnPoint - context to which the function returns if the common_signal_handler returns
+
+ (no return value)
+--*/
+extern "C" void signal_handler_worker(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint)
+{
+ // TODO: First variable parameter says whether a read (0) or write (non-0) caused the
+ // fault. We must disassemble the instruction at record.ExceptionAddress
+ // to correctly fill in this value.
+ returnPoint->returnFromHandler = common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr);
+ setcontext(&returnPoint->context);
+}
+
+/*++
+Function :
sigsegv_handler
handle SIGSEGV signal (EXCEPTION_ACCESS_VIOLATION, others)
@@ -289,10 +410,38 @@ static void sigsegv_handler(int code, siginfo_t *siginfo, void *context)
{
if (PALIsInitialized())
{
- // TODO: First variable parameter says whether a read (0) or write (non-0) caused the
- // fault. We must disassemble the instruction at record.ExceptionAddress
- // to correctly fill in this value.
- if (common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr))
+ // First check if we have a stack overflow
+ size_t sp = (size_t)GetNativeContextSP((native_context_t *)context);
+ size_t failureAddress = (size_t)siginfo->si_addr;
+
+ // If the failure address is at most one page above or below the stack pointer,
+ // we have a stack overflow.
+ if ((failureAddress - (sp - VIRTUAL_PAGE_SIZE)) < 2 * VIRTUAL_PAGE_SIZE)
+ {
+ (void)write(STDERR_FILENO, StackOverflowMessage, sizeof(StackOverflowMessage) - 1);
+ PROCAbort();
+ }
+
+ // Now that we know the SIGSEGV didn't happen due to a stack overflow, execute the common
+ // hardware signal handler on the original stack.
+
+ // Establish a return point in case the common_signal_handler returns
+
+ volatile bool contextInitialization = true;
+
+ SignalHandlerWorkerReturnPoint returnPoint;
+ getcontext(&returnPoint.context);
+
+ // When the signal handler worker completes, it uses setcontext to return to this point
+
+ if (contextInitialization)
+ {
+ contextInitialization = false;
+ ExecuteHandlerOnOriginalStack(code, siginfo, context, &returnPoint);
+ _ASSERTE(FALSE); // The ExecuteHandlerOnOriginalStack should never return
+ }
+
+ if (returnPoint.returnFromHandler)
{
return;
}
@@ -666,11 +815,11 @@ Parameters :
note : if sigfunc is NULL, the default signal handler is restored
--*/
-void handle_signal(int signal_id, SIGFUNC sigfunc, struct sigaction *previousAction)
+void handle_signal(int signal_id, SIGFUNC sigfunc, struct sigaction *previousAction, int additionalFlags)
{
struct sigaction newAction;
- newAction.sa_flags = SA_RESTART;
+ newAction.sa_flags = SA_RESTART | additionalFlags;
#if HAVE_SIGINFO_T
newAction.sa_handler = NULL;
newAction.sa_sigaction = sigfunc;
diff --git a/src/pal/src/exception/signal.hpp b/src/pal/src/exception/signal.hpp
deleted file mode 100644
index cd019e676b..0000000000
--- a/src/pal/src/exception/signal.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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.
-
-/*++
-
-
-
-Module Name:
-
- exception/signal.hpp
-
-Abstract:
- Private signal handling utilities for SEH
-
-
-
---*/
-
-#ifndef _PAL_SIGNAL_HPP_
-#define _PAL_SIGNAL_HPP_
-
-#if !HAVE_MACH_EXCEPTIONS
-
-/*++
-Function :
- SEHInitializeSignals
-
- Set-up signal handlers to catch signals and translate them to exceptions
-
-Parameters :
- flags: PAL initialization flags
-
-Return :
- TRUE in case of a success, FALSE otherwise
---*/
-BOOL SEHInitializeSignals(DWORD flags);
-
-/*++
-Function :
- SEHCleanupSignals
-
- Restore default signal handlers
-
- (no parameters, no return value)
---*/
-void SEHCleanupSignals();
-
-#endif // !HAVE_MACH_EXCEPTIONS
-
-#endif /* _PAL_SIGNAL_HPP_ */
-
diff --git a/src/pal/src/include/pal/context.h b/src/pal/src/include/pal/context.h
index 08fa05da7d..db6d69579a 100644
--- a/src/pal/src/include/pal/context.h
+++ b/src/pal/src/include/pal/context.h
@@ -639,6 +639,21 @@ LPVOID GetNativeContextPC(const native_context_t *context);
/*++
Function :
+ GetNativeContextSP
+
+ Returns the stack pointer from the native context.
+
+Parameters :
+ const native_context_t *native : native context
+
+Return value :
+ The stack pointer from the native context.
+
+--*/
+LPVOID GetNativeContextSP(const native_context_t *context);
+
+/*++
+Function :
CONTEXTGetExceptionCodeForSignal
Translates signal and context information to a Win32 exception code.
diff --git a/src/pal/src/include/pal/signal.hpp b/src/pal/src/include/pal/signal.hpp
new file mode 100644
index 0000000000..f9afd49b33
--- /dev/null
+++ b/src/pal/src/include/pal/signal.hpp
@@ -0,0 +1,141 @@
+// 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.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/signal.hpp
+
+Abstract:
+ Private signal handling utilities for SEH
+
+
+
+--*/
+
+#ifndef _PAL_SIGNAL_HPP_
+#define _PAL_SIGNAL_HPP_
+
+#if !HAVE_MACH_EXCEPTIONS
+
+struct SignalHandlerWorkerReturnPoint;
+
+/*++
+Function :
+ CallSignalHandlerWrapperX
+
+ These functions are never called, only a fake stack frame will be setup to have a return
+ address set to SignalHandlerWorkerReturnX during SIGSEGV handling.
+ It enables the unwinder to unwind stack from the handling code to the actual failure site.
+
+ There are four variants of this function based on what stack alignment needs to be done
+ to ensure properly aligned stack pointer at the call site of the signal_handler_worker.
+
+Parameters :
+ none
+
+ (no return value)
+--*/
+extern "C" void CallSignalHandlerWrapper0();
+extern "C" void CallSignalHandlerWrapper4();
+extern "C" void CallSignalHandlerWrapper8();
+extern "C" void CallSignalHandlerWrapper12();
+
+// Offset of the return address from the signal_handler_worker in the CallSignalHandlerWrapperX
+// relative to the start of the function.
+// There are four offsets matching the stack alignments as described in the function header above.
+extern "C" int SignalHandlerWorkerReturnOffset0;
+extern "C" int SignalHandlerWorkerReturnOffset4;
+extern "C" int SignalHandlerWorkerReturnOffset8;
+extern "C" int SignalHandlerWorkerReturnOffset12;
+
+/*++
+Function :
+ signal_handler_worker
+
+ Handles signal on the original stack where the signal occured.
+ Invoked via setcontext.
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+ returnPoint - context to which the function returns if the common_signal_handler returns
+
+ (no return value)
+--*/
+extern "C" void signal_handler_worker(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint);
+
+/*++
+Function :
+ ExecuteHandlerOnOriginalStack
+
+ Executes signal_handler_worker on the original stack where the signal occured.
+ It installs fake stack frame to enable stack unwinding to the signal source location.
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+ returnPoint - context to which the function returns if the common_signal_handler returns
+
+ (no return value)
+--*/
+void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint);
+
+/*++
+Function :
+ EnsureSignalAlternateStack
+
+ Ensure that alternate stack for signal handling is allocated for the current thread
+
+Parameters :
+ None
+
+Return :
+ TRUE in case of a success, FALSE otherwise
+--*/
+BOOL EnsureSignalAlternateStack();
+
+/*++
+Function :
+ FreeSignalAlternateStack
+
+ Free alternate stack for signal handling
+
+Parameters :
+ None
+
+Return :
+ None
+--*/
+void FreeSignalAlternateStack();
+
+/*++
+Function :
+ SEHInitializeSignals
+
+ Set-up signal handlers to catch signals and translate them to exceptions
+
+Parameters :
+ flags: PAL initialization flags
+
+Return :
+ TRUE in case of a success, FALSE otherwise
+--*/
+BOOL SEHInitializeSignals(DWORD flags);
+
+/*++
+Function :
+ SEHCleanupSignals
+
+ Restore default signal handlers
+
+ (no parameters, no return value)
+--*/
+void SEHCleanupSignals();
+
+#endif // !HAVE_MACH_EXCEPTIONS
+
+#endif /* _PAL_SIGNAL_HPP_ */
+
diff --git a/src/pal/src/init/sxs.cpp b/src/pal/src/init/sxs.cpp
index 225f91684b..3f323c6a6e 100644
--- a/src/pal/src/init/sxs.cpp
+++ b/src/pal/src/init/sxs.cpp
@@ -16,6 +16,7 @@
#include "pal/module.h"
#include "pal/process.h"
#include "pal/seh.hpp"
+#include "pal/signal.hpp"
using namespace CorUnix;
@@ -106,8 +107,20 @@ PAL_ERROR
AllocatePalThread(CPalThread **ppThread)
{
CPalThread *pThread = NULL;
+ PAL_ERROR palError;
- PAL_ERROR palError = CreateThreadData(&pThread);
+#if !HAVE_MACH_EXCEPTIONS
+ // Ensure alternate stack for SIGSEGV handling. Our SIGSEGV handler is set to
+ // run on an alternate stack and the stack needs to be allocated per thread.
+ if (!EnsureSignalAlternateStack())
+ {
+ ERROR("Cannot allocate alternate stack for SIGSEGV handler!\n");
+ palError = ERROR_NOT_ENOUGH_MEMORY;
+ goto exit;
+ }
+#endif // !HAVE_MACH_EXCEPTIONS
+
+ palError = CreateThreadData(&pThread);
if (NO_ERROR != palError)
{
goto exit;
diff --git a/src/pal/src/thread/context.cpp b/src/pal/src/thread/context.cpp
index 98867c9554..04a6fe5aaf 100644
--- a/src/pal/src/thread/context.cpp
+++ b/src/pal/src/thread/context.cpp
@@ -617,6 +617,35 @@ LPVOID GetNativeContextPC(const native_context_t *context)
/*++
Function :
+ GetNativeContextSP
+
+ Returns the stack pointer from the native context.
+
+Parameters :
+ const native_context_t *native : native context
+
+Return value :
+ The stack pointer from the native context.
+
+--*/
+LPVOID GetNativeContextSP(const native_context_t *context)
+{
+#ifdef _AMD64_
+ return (LPVOID)MCREG_Rsp(context->uc_mcontext);
+#elif defined(_X86_)
+ return (LPVOID) MCREG_Esp(context->uc_mcontext);
+#elif defined(_ARM_)
+ return (LPVOID) MCREG_Sp(context->uc_mcontext);
+#elif defined(_ARM64_)
+ return (LPVOID) MCREG_Sp(context->uc_mcontext);
+#else
+# error implement me for this architecture
+#endif
+}
+
+
+/*++
+Function :
CONTEXTGetExceptionCodeForSignal
Translates signal and context information to a Win32 exception code.
diff --git a/src/pal/src/thread/thread.cpp b/src/pal/src/thread/thread.cpp
index 53283320c5..df42ebcc96 100644
--- a/src/pal/src/thread/thread.cpp
+++ b/src/pal/src/thread/thread.cpp
@@ -28,6 +28,7 @@ SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do
#include "pal/handlemgr.hpp"
#include "pal/cs.hpp"
#include "pal/seh.hpp"
+#include "pal/signal.hpp"
#include "procprivate.hpp"
#include "pal/process.h"
@@ -177,6 +178,10 @@ static void InternalEndCurrentThreadWrapper(void *arg)
// in InternalEndCurrentThread.
InternalEndCurrentThread(pThread);
pthread_setspecific(thObjKey, NULL);
+
+#if !HAVE_MACH_EXCEPTIONS
+ FreeSignalAlternateStack();
+#endif // !HAVE_MACH_EXCEPTIONS
}
/*++
@@ -1659,6 +1664,14 @@ CPalThread::ThreadEntry(
goto fail;
}
+#if !HAVE_MACH_EXCEPTIONS
+ if (!EnsureSignalAlternateStack())
+ {
+ ASSERT("Cannot allocate alternate stack for SIGSEGV!\n");
+ goto fail;
+ }
+#endif // !HAVE_MACH_EXCEPTIONS
+
#if defined(FEATURE_PAL_SXS) && defined(_DEBUG)
// We cannot assert yet, as we haven't set in this thread into the TLS, and so __ASSERT_ENTER
// will fail if the assert fails and we'll crash.