diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2017-04-13 14:17:19 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2017-04-13 14:17:19 +0900 |
commit | a56e30c8d33048216567753d9d3fefc2152af8ac (patch) | |
tree | 7e5d979695fc4a431740982eb1cfecc2898b23a5 /src/pal/src | |
parent | 4b11dc566a5bbfa1378d6266525c281b028abcc8 (diff) | |
download | coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.tar.gz coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.tar.bz2 coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.zip |
Imported Upstream version 2.0.0.11353upstream/2.0.0.11353
Diffstat (limited to 'src/pal/src')
43 files changed, 1659 insertions, 1148 deletions
diff --git a/src/pal/src/CMakeLists.txt b/src/pal/src/CMakeLists.txt index 16c9d8bd6f..5314cdf86b 100644 --- a/src/pal/src/CMakeLists.txt +++ b/src/pal/src/CMakeLists.txt @@ -45,6 +45,9 @@ else() elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l) set(PAL_CMAKE_PLATFORM_ARCH_ARM 1) add_definitions(-D_ARM_) + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL arm) + set(PAL_CMAKE_PLATFORM_ARCH_ARM 1) + add_definitions(-D_ARM_) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) set(PAL_CMAKE_PLATFORM_ARCH_ARM64 1) add_definitions(-D_ARM64_) @@ -77,13 +80,17 @@ add_definitions(-D_FILE_OFFSET_BITS=64) if(PAL_CMAKE_PLATFORM_ARCH_AMD64) add_definitions(-DBIT64=1) add_definitions(-D_WIN64=1) + set(PAL_ARCH_SOURCES_DIR amd64) elseif(PAL_CMAKE_PLATFORM_ARCH_ARM) add_definitions(-DBIT32=1) + set(PAL_ARCH_SOURCES_DIR arm) elseif(PAL_CMAKE_PLATFORM_ARCH_ARM64) add_definitions(-DBIT64=1) add_definitions(-D_WIN64=1) + set(PAL_ARCH_SOURCES_DIR arm64) elseif(PAL_CMAKE_PLATFORM_ARCH_I386) add_definitions(-DBIT32=1) + set(PAL_ARCH_SOURCES_DIR i386) endif() if(CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CLR_CMAKE_PLATFORM_ALPINE_LINUX) @@ -101,35 +108,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/${PAL_ARCH_SOURCES_DIR}/context2.S + arch/${PAL_ARCH_SOURCES_DIR}/debugbreak.S + arch/${PAL_ARCH_SOURCES_DIR}/exceptionhelper.S + arch/${PAL_ARCH_SOURCES_DIR}/processor.cpp +) + +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + list(APPEND PLATFORM_SOURCES + arch/${PAL_ARCH_SOURCES_DIR}/callsignalhandlerwrapper.S + arch/${PAL_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) @@ -177,6 +168,7 @@ set(SOURCES map/virtual.cpp memory/heap.cpp memory/local.cpp + misc/cgroup.cpp misc/dbgmsg.cpp misc/environ.cpp misc/error.cpp @@ -189,7 +181,6 @@ set(SOURCES misc/sysinfo.cpp misc/time.cpp misc/utils.cpp - misc/version.cpp objmgr/palobjbase.cpp objmgr/shmobject.cpp objmgr/shmobjectmanager.cpp @@ -303,21 +294,26 @@ if(CMAKE_SYSTEM_NAME STREQUAL Linux) endif() if(CLR_CMAKE_PLATFORM_ANDROID) + find_library(LZMA NAMES lzma) + + if(LZMA STREQUAL LZMA-NOTFOUND) + message(FATAL_ERROR "Cannot find liblzma.") + endif(LZMA STREQUAL LZMA-NOTFOUND) + target_link_libraries(coreclrpal gnustl_shared android-support - android-glob) + android-glob + ${LZMA}) endif() - if(NOT CLR_CMAKE_PLATFORM_ANDROID) - find_library(UNWIND NAMES unwind) + find_library(UNWIND NAMES unwind) - if(UNWIND STREQUAL UNWIND-NOTFOUND) - message(FATAL_ERROR "Cannot find libunwind. Try installing libunwind8-dev and libunwind8.") - endif(UNWIND STREQUAL UNWIND-NOTFOUND) + if(UNWIND STREQUAL UNWIND-NOTFOUND) + message(FATAL_ERROR "Cannot find libunwind. Try installing libunwind8-dev and libunwind8.") + endif(UNWIND STREQUAL UNWIND-NOTFOUND) - target_link_libraries(coreclrpal ${UNWIND}) - endif() + target_link_libraries(coreclrpal ${UNWIND}) if(CLR_MAKE_PLATFORM_ANDROID) find_library(ANDROID_SUPPORT NAMES android-support) 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/optimizedtls.cpp b/src/pal/src/arch/amd64/optimizedtls.cpp deleted file mode 100644 index cd89db6b0a..0000000000 --- a/src/pal/src/arch/amd64/optimizedtls.cpp +++ /dev/null @@ -1,237 +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: - - optimizedtls.cpp - -Abstract: - - Implementation of platform-specific Thread local storage functions. - - - ---*/ - -#include "pal/thread.hpp" -#include "pal/malloc.hpp" - -#include <pthread.h> - -#include "pal/dbgmsg.h" -#include "pal/misc.h" -#include "pal/debug.h" - -#include <stddef.h> - -using namespace CorUnix; - -SET_DEFAULT_DEBUG_CHANNEL(THREAD); - -#if defined(USE_OPTIMIZEDTLSGETTER) - -#define PAL_safe_offsetof(s,m) ((size_t)((ptrdiff_t)&(char&)(((s *)64)->m))-64) - -/*++ -Function: - CorUnix::TLSMakeOptimizedGetter - - Creates a platform-optimized version of TlsGetValue compiled - for a particular index. - - Generates the hot part of CorUnix::InternalGetCurrentThread - as a chunk of highly optimized machine-specific code at runtime. - - Check the difference between CorUnix::InternalGetCurrentThread and - CorUnix::InternalGetCurrentThreadSlow to see the C/C++ code that matches - the code generated by this function. ---*/ -PAL_POPTIMIZEDTLSGETTER -CorUnix::TLSMakeOptimizedGetter( - IN CPalThread* pThread, - IN DWORD dwTlsIndex) -{ -#ifdef BIT64 -#pragma unused(pThread, dwTlsIndex) - ERROR("TLSMakeOptimizedGetter not rewritten for amd64 yet."); - return NULL; -#else - PAL_POPTIMIZEDTLSGETTER Ret = NULL; - BYTE* p; - int i = 0; - -#ifdef __APPLE__ -#define TLS_OPTIMIZED_GETTER_SIZE 118 -#else -#define TLS_OPTIMIZED_GETTER_SIZE 115 -#endif - - p = (BYTE*)InternalMalloc(pThread, TLS_OPTIMIZED_GETTER_SIZE * sizeof(BYTE)); - - if (p == NULL) - { - return Ret; - } - - // Need to preserve %ecx, %edx, and %esi registers as specified in - // GetThreadGeneric(void) in vm/amd64/asmhelpers.s - p[i++] = 0x51; // push %ecx - p[i++] = 0x52; // push %edx - p[i++] = 0x89; // mov %esp,%eax // %eax = sp; - p[i++] = 0xe0; - p[i++] = 0xc1; // shr $0x11,%eax // sp >> 17; - p[i++] = 0xe8; - p[i++] = 0x11; - p[i++] = 0x89; // mov %eax,%edx // key = sp >> 17; - p[i++] = 0xc2; - p[i++] = 0xc1; // sar $0x7,%edx // key >> 7; - p[i++] = 0xfa; - p[i++] = 0x07; - p[i++] = 0x29; // sub %edx,%eax // key -= key >> 7; - p[i++] = 0xd0; - p[i++] = 0x89; // mov %eax,%edx - p[i++] = 0xc2; - p[i++] = 0xc1; // sar $0x5,%edx // key >> 5; - p[i++] = 0xfa; - p[i++] = 0x05; - p[i++] = 0x29; // sub %edx,%eax // key -= key >> 5; - p[i++] = 0xd0; - p[i++] = 0x89; // mov %eax,%edx - p[i++] = 0xc2; - p[i++] = 0xc1; // sar $0x3,%edx // key >> 3; - p[i++] = 0xfa; - p[i++] = 0x03; - p[i++] = 0x29; // sub %edx,%eax // key -= key >> 3; - p[i++] = 0xd0; - p[i++] = 0x25; // and $0xff,%eax // key &= 0xFF; - p[i++] = 0xff; - p[i++] = 0x00; - p[i++] = 0x00; - p[i++] = 0x00; - p[i++] = 0x8b; // mov (flush_counter),%ecx // %ecx = counter = flush_counter; - p[i++] = 0x0d; - *((DWORD*) &p[i]) = (DWORD)&flush_counter; - i += sizeof(DWORD); - p[i++] = 0x8b; // mov (thread_hints,%eax,4),%eax // %edx = pThread = thread_hints[key]; - p[i++] = 0x14; - p[i++] = 0x85; - *((DWORD*) &p[i]) = (DWORD)&thread_hints; - i += sizeof(DWORD); - p[i++] = 0x39; // cmp %esp,offsetof(CPalThread,tlsInfo)+offsetof(CThreadTLSInfo,minStack)(%edx) - // if ((size_t)pThread->tlsInfo.minStack <= sp) - p[i++] = 0xa2; - *((DWORD*) &p[i]) = (DWORD)(PAL_safe_offsetof(CPalThread,tlsInfo)+PAL_safe_offsetof(CThreadTLSInfo,minStack)); - i += sizeof(DWORD); - p[i++] = 0x77; // ja CallInternalGetCurrentThreadSlow: - p[i++] = 0x19; - p[i++] = 0x3b; // cmp offsetof(CPalThread,tlsInfo)+offsetof(CThreadTLSInfo,maxStack)(%edx),%esp - // if (sp < (size_t)pThread->tlsInfo.maxStack) - p[i++] = 0xa2; - *((DWORD*) &p[i]) = (DWORD)(PAL_safe_offsetof(CPalThread,tlsInfo)+PAL_safe_offsetof(CThreadTLSInfo,maxStack)); - i += sizeof(DWORD); - p[i++] = 0x73; // jae CallInternalGetCurrentThreadSlow: - p[i++] = 0x11; - p[i++] = 0x39; // cmp (flush_counter),%ecx // if (counter == flush_counter) - p[i++] = 0x0d; - *((DWORD*) &p[i]) = (DWORD)&flush_counter; - i += sizeof(DWORD); - p[i++] = 0x75; // jne CallInternalGetCurrentThreadSlow: - p[i++] = 0x09; - if (dwTlsIndex != THREAD_OBJECT_TLS_INDEX) - { - p[i++] = 0x8b; // mov offsetof(pThread->tlsSlots[dwTlsIndex])(%edx),%eax // %eax = pThread->tlsSlots[dwTlsIndex]; - p[i++] = 0x82; - *((DWORD*) &p[i]) = (DWORD)(PAL_safe_offsetof(CPalThread,tlsInfo)+PAL_safe_offsetof(CThreadTLSInfo,tlsSlots[dwTlsIndex])); - i += sizeof(DWORD); - } - else - { - p[i++] = 0x89; // mov %edx,%eax // %eax = pThread; - p[i++] = 0xd0; - p[i++] = 0x90; // nop - p[i++] = 0x90; // nop - p[i++] = 0x90; // nop - p[i++] = 0x90; // nop - } - p[i++] = 0x5a; // pop %edx - p[i++] = 0x59; // pop %ecx - p[i++] = 0xc3; // ret - // CallInternalGetCurrentThreadSlow: - p[i++] = 0x5a; // pop %edx - p[i++] = 0x59; // pop %ecx - p[i++] = 0x8d; // lea (thread_hints,%eax,4),%eax // %eax = &thread_hints[key]; - p[i++] = 0x04; - p[i++] = 0x85; - *((DWORD*) &p[i]) = (DWORD)&thread_hints; - i += sizeof(DWORD); - p[i++] = 0x55; // push %ebp - p[i++] = 0x89; // mov %esp,%ebp - p[i++] = 0xe5; - p[i++] = 0x51; // push %ecx - p[i++] = 0x89; // mov %esp,%ecx // this is the reference esp - need to match the reference esp used in the fast path. - p[i++] = 0xe1; - p[i++] = 0x52; // push %edx -#ifdef __APPLE__ - // establish 16-byte stack alignment - p[i++] = 0x83; // subl $8,%esp - p[i++] = 0xec; - p[i++] = 0x08; -#endif - p[i++] = 0x50; // push %eax // store &thread_hints[key] on stack as 2nd argument; - p[i++] = 0x51; // push %ecx // reference esp - The 1st argument for call to InternalGetCurrentThreadSlow. - p[i++] = 0xe8; // call InternalGetCurrentThreadSlow - *((DWORD*) &p[i]) = (DWORD)&InternalGetCurrentThreadSlow - (DWORD)(&p[i+sizeof(DWORD)]); - i += sizeof(DWORD); -#ifdef __APPLE__ - p[i++] = 0x83; // addl $16,%esp - p[i++] = 0xc4; - p[i++] = 0x10; -#else - p[i++] = 0x83; // addl $8,%esp - p[i++] = 0xc4; - p[i++] = 0x08; -#endif - if (dwTlsIndex != THREAD_OBJECT_TLS_INDEX) - { - p[i++] = 0x8b; // mov offsetof(pThread->tlsSlots[dwTlsIndex])(%eax),%eax // %eax = pThread->tlsSlots[dwTlsIndex]; - p[i++] = 0x80; - *((DWORD*) &p[i]) = (DWORD)(PAL_safe_offsetof(CPalThread,tlsInfo)+PAL_safe_offsetof(CThreadTLSInfo,tlsSlots[dwTlsIndex])); - i += sizeof(DWORD); - } - p[i++] = 0x5a; // pop %edx - p[i++] = 0x59; // pop %ecx - p[i++] = 0xc9; // leave - p[i++] = 0xc3; // ret - - if (i > TLS_OPTIMIZED_GETTER_SIZE) - { - ASSERT("Invalid TLS_OPTIMIZED_GETTER_SIZE %d\n", i); - } - - DBG_FlushInstructionCache(p, TLS_OPTIMIZED_GETTER_SIZE * sizeof(BYTE)); - - Ret = (PAL_POPTIMIZEDTLSGETTER)p; - - return Ret; -#endif // BIT64 else -} - -/*++ -Function: - TLSFreeOptimizedGetter - - Frees a function created by MakeOptimizedTlsGetter(). ---*/ -VOID -CorUnix::TLSFreeOptimizedGetter( - IN PAL_POPTIMIZEDTLSGETTER pOptimizedTlsGetter) -{ - InternalFree(InternalGetCurrentThread(), (void *)pOptimizedTlsGetter); -} - -#endif // USE_OPTIMIZEDTLSGETTER diff --git a/src/pal/src/arch/amd64/signalhandlerhelper.cpp b/src/pal/src/arch/amd64/signalhandlerhelper.cpp new file mode 100644 index 0000000000..8789f5a622 --- /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 + CONTEXT context2; + RtlCaptureContext(&context2); + + // We don't care about the other registers state since the stack unwinding restores + // them for the target frame directly from the signal context. + context2.Rsp = (size_t)sp; + context2.Rbx = (size_t)faultSp; + context2.Rbp = (size_t)fp; + context2.Rip = (size_t)signal_handler_worker; + context2.Rdi = code; + context2.Rsi = (size_t)siginfo; + context2.Rdx = (size_t)context; + context2.Rcx = (size_t)returnPoint; + + RtlRestoreContext(&context2, NULL); +} 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..e1ad460905 --- /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 + CONTEXT context2; + RtlCaptureContext(&context2); + + // We don't care about the other registers state since the stack unwinding restores + // them for the target frame directly from the signal context. + context2.Sp = (size_t)sp; + context2.R7 = (size_t)sp; // Fp and Sp are the same + context2.Lr = fakeFrameReturnAddress; + context2.Pc = (size_t)signal_handler_worker; + context2.R0 = code; + context2.R1 = (size_t)siginfo; + context2.R2 = (size_t)context; + context2.R3 = (size_t)returnPoint; + + RtlRestoreContext(&context2, NULL); +} diff --git a/src/pal/src/arch/arm64/asmconstants.h b/src/pal/src/arch/arm64/asmconstants.h new file mode 100644 index 0000000000..b2bf74461f --- /dev/null +++ b/src/pal/src/arch/arm64/asmconstants.h @@ -0,0 +1,95 @@ +// 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 __PAL_ARM64_ASMCONSTANTS_H__ +#define __PAL_ARM64_ASMCONSTANTS_H__ + +#define CONTEXT_ARM64 0x00400000L + +#define CONTEXT_CONTROL_BIT (0) +#define CONTEXT_INTEGER_BIT (1) +#define CONTEXT_FLOATING_POINT_BIT (2) +#define CONTEXT_DEBUG_REGISTERS_BIT (3) + +#define CONTEXT_CONTROL (CONTEXT_ARM64 | (1L << CONTEXT_CONTROL_BIT)) +#define CONTEXT_INTEGER (CONTEXT_ARM64 | (1 << CONTEXT_INTEGER_BIT)) +#define CONTEXT_FLOATING_POINT (CONTEXT_ARM64 | (1 << CONTEXT_FLOATING_POINT_BIT)) +#define CONTEXT_DEBUG_REGISTERS (CONTEXT_ARM64 | (1 << CONTEXT_DEBUG_REGISTERS_BIT)) + +#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) + + +#define CONTEXT_ContextFlags 0 +#define CONTEXT_Cpsr CONTEXT_ContextFlags+4 +#define CONTEXT_X0 CONTEXT_Cpsr+4 +#define CONTEXT_X1 CONTEXT_X0+8 +#define CONTEXT_X2 CONTEXT_X1+8 +#define CONTEXT_X3 CONTEXT_X2+8 +#define CONTEXT_X4 CONTEXT_X3+8 +#define CONTEXT_X5 CONTEXT_X4+8 +#define CONTEXT_X6 CONTEXT_X5+8 +#define CONTEXT_X7 CONTEXT_X6+8 +#define CONTEXT_X8 CONTEXT_X7+8 +#define CONTEXT_X9 CONTEXT_X8+8 +#define CONTEXT_X10 CONTEXT_X9+8 +#define CONTEXT_X11 CONTEXT_X10+8 +#define CONTEXT_X12 CONTEXT_X11+8 +#define CONTEXT_X13 CONTEXT_X12+8 +#define CONTEXT_X14 CONTEXT_X13+8 +#define CONTEXT_X15 CONTEXT_X14+8 +#define CONTEXT_X16 CONTEXT_X15+8 +#define CONTEXT_X17 CONTEXT_X16+8 +#define CONTEXT_X18 CONTEXT_X17+8 +#define CONTEXT_X19 CONTEXT_X18+8 +#define CONTEXT_X20 CONTEXT_X19+8 +#define CONTEXT_X21 CONTEXT_X20+8 +#define CONTEXT_X22 CONTEXT_X21+8 +#define CONTEXT_X23 CONTEXT_X22+8 +#define CONTEXT_X24 CONTEXT_X23+8 +#define CONTEXT_X25 CONTEXT_X24+8 +#define CONTEXT_X26 CONTEXT_X25+8 +#define CONTEXT_X27 CONTEXT_X26+8 +#define CONTEXT_X28 CONTEXT_X27+8 +#define CONTEXT_Fp CONTEXT_X28+8 +#define CONTEXT_Lr CONTEXT_Fp+8 +#define CONTEXT_Sp CONTEXT_Lr+8 +#define CONTEXT_Pc CONTEXT_Sp+8 +#define CONTEXT_NEON_OFFSET CONTEXT_Pc+8 +#define CONTEXT_V0 0 +#define CONTEXT_V1 CONTEXT_V0+16 +#define CONTEXT_V2 CONTEXT_V1+16 +#define CONTEXT_V3 CONTEXT_V2+16 +#define CONTEXT_V4 CONTEXT_V3+16 +#define CONTEXT_V5 CONTEXT_V4+16 +#define CONTEXT_V6 CONTEXT_V5+16 +#define CONTEXT_V7 CONTEXT_V6+16 +#define CONTEXT_V8 CONTEXT_V7+16 +#define CONTEXT_V9 CONTEXT_V8+16 +#define CONTEXT_V10 CONTEXT_V9+16 +#define CONTEXT_V11 CONTEXT_V10+16 +#define CONTEXT_V12 CONTEXT_V11+16 +#define CONTEXT_V13 CONTEXT_V12+16 +#define CONTEXT_V14 CONTEXT_V13+16 +#define CONTEXT_V15 CONTEXT_V14+16 +#define CONTEXT_V16 CONTEXT_V15+16 +#define CONTEXT_V17 CONTEXT_V16+16 +#define CONTEXT_V18 CONTEXT_V17+16 +#define CONTEXT_V19 CONTEXT_V18+16 +#define CONTEXT_V20 CONTEXT_V19+16 +#define CONTEXT_V21 CONTEXT_V20+16 +#define CONTEXT_V22 CONTEXT_V21+16 +#define CONTEXT_V23 CONTEXT_V22+16 +#define CONTEXT_V24 CONTEXT_V23+16 +#define CONTEXT_V25 CONTEXT_V24+16 +#define CONTEXT_V26 CONTEXT_V25+16 +#define CONTEXT_V27 CONTEXT_V26+16 +#define CONTEXT_V28 CONTEXT_V27+16 +#define CONTEXT_V29 CONTEXT_V28+16 +#define CONTEXT_V30 CONTEXT_V29+16 +#define CONTEXT_V31 CONTEXT_V30+16 +#define CONTEXT_FLOAT_CONTROL_OFFSET CONTEXT_V31 +#define CONTEXT_Fpcr 0 +#define CONTEXT_Fpsr CONTEXT_Fpcr+4 + +#endif diff --git a/src/pal/src/arch/arm64/callsignalhandlerwrapper.S b/src/pal/src/arch/arm64/callsignalhandlerwrapper.S new file mode 100644 index 0000000000..90fb602479 --- /dev/null +++ b/src/pal/src/arch/arm64/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" + +.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 +__StackAllocationSize = (128 + 8 + 8 + \Alignment) // red zone + fp + lr + alignment + PROLOG_STACK_ALLOC __StackAllocationSize + .cfi_adjust_cfa_offset __StackAllocationSize + 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 __StackAllocationSize + 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/context2.S b/src/pal/src/arch/arm64/context2.S index a64e62c94d..e62a9ac4d9 100644 --- a/src/pal/src/arch/arm64/context2.S +++ b/src/pal/src/arch/arm64/context2.S @@ -8,87 +8,7 @@ // #include "unixasmmacros.inc" - -#define CONTEXT_ARM64 0x00400000L - -#define CONTEXT_CONTROL (CONTEXT_ARM64 | 0x1L) -#define CONTEXT_INTEGER (CONTEXT_ARM64 | 0x2L) -#define CONTEXT_FLOATING_POINT (CONTEXT_ARM64 | 0x4L) -#define CONTEXT_DEBUG_REGISTERS (CONTEXT_ARM64 | 0x8L) - -#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) - -#define CONTEXT_ContextFlags 0 -#define CONTEXT_Cpsr CONTEXT_ContextFlags+4 -#define CONTEXT_X0 CONTEXT_Cpsr+4 -#define CONTEXT_X1 CONTEXT_X0+8 -#define CONTEXT_X2 CONTEXT_X1+8 -#define CONTEXT_X3 CONTEXT_X2+8 -#define CONTEXT_X4 CONTEXT_X3+8 -#define CONTEXT_X5 CONTEXT_X4+8 -#define CONTEXT_X6 CONTEXT_X5+8 -#define CONTEXT_X7 CONTEXT_X6+8 -#define CONTEXT_X8 CONTEXT_X7+8 -#define CONTEXT_X9 CONTEXT_X8+8 -#define CONTEXT_X10 CONTEXT_X9+8 -#define CONTEXT_X11 CONTEXT_X10+8 -#define CONTEXT_X12 CONTEXT_X11+8 -#define CONTEXT_X13 CONTEXT_X12+8 -#define CONTEXT_X14 CONTEXT_X13+8 -#define CONTEXT_X15 CONTEXT_X14+8 -#define CONTEXT_X16 CONTEXT_X15+8 -#define CONTEXT_X17 CONTEXT_X16+8 -#define CONTEXT_X18 CONTEXT_X17+8 -#define CONTEXT_X19 CONTEXT_X18+8 -#define CONTEXT_X20 CONTEXT_X19+8 -#define CONTEXT_X21 CONTEXT_X20+8 -#define CONTEXT_X22 CONTEXT_X21+8 -#define CONTEXT_X23 CONTEXT_X22+8 -#define CONTEXT_X24 CONTEXT_X23+8 -#define CONTEXT_X25 CONTEXT_X24+8 -#define CONTEXT_X26 CONTEXT_X25+8 -#define CONTEXT_X27 CONTEXT_X26+8 -#define CONTEXT_X28 CONTEXT_X27+8 -#define CONTEXT_Fp CONTEXT_X28+8 -#define CONTEXT_Lr CONTEXT_Fp+8 -#define CONTEXT_Sp CONTEXT_Lr+8 -#define CONTEXT_Pc CONTEXT_Sp+8 -#define CONTEXT_NEON_OFFSET CONTEXT_Pc+8 -#define CONTEXT_V0 0 -#define CONTEXT_V1 CONTEXT_V0+16 -#define CONTEXT_V2 CONTEXT_V1+16 -#define CONTEXT_V3 CONTEXT_V2+16 -#define CONTEXT_V4 CONTEXT_V3+16 -#define CONTEXT_V5 CONTEXT_V4+16 -#define CONTEXT_V6 CONTEXT_V5+16 -#define CONTEXT_V7 CONTEXT_V6+16 -#define CONTEXT_V8 CONTEXT_V7+16 -#define CONTEXT_V9 CONTEXT_V8+16 -#define CONTEXT_V10 CONTEXT_V9+16 -#define CONTEXT_V11 CONTEXT_V10+16 -#define CONTEXT_V12 CONTEXT_V11+16 -#define CONTEXT_V13 CONTEXT_V12+16 -#define CONTEXT_V14 CONTEXT_V13+16 -#define CONTEXT_V15 CONTEXT_V14+16 -#define CONTEXT_V16 CONTEXT_V15+16 -#define CONTEXT_V17 CONTEXT_V16+16 -#define CONTEXT_V18 CONTEXT_V17+16 -#define CONTEXT_V19 CONTEXT_V18+16 -#define CONTEXT_V20 CONTEXT_V19+16 -#define CONTEXT_V21 CONTEXT_V20+16 -#define CONTEXT_V22 CONTEXT_V21+16 -#define CONTEXT_V23 CONTEXT_V22+16 -#define CONTEXT_V24 CONTEXT_V23+16 -#define CONTEXT_V25 CONTEXT_V24+16 -#define CONTEXT_V26 CONTEXT_V25+16 -#define CONTEXT_V27 CONTEXT_V26+16 -#define CONTEXT_V28 CONTEXT_V27+16 -#define CONTEXT_V29 CONTEXT_V28+16 -#define CONTEXT_V30 CONTEXT_V29+16 -#define CONTEXT_V31 CONTEXT_V30+16 -#define CONTEXT_FLOAT_CONTROL_OFFSET CONTEXT_V31 -#define CONTEXT_Fpcr 0 -#define CONTEXT_Fpsr CONTEXT_Fpcr+4 +#include "asmconstants.h" // Incoming: // x0: Context* @@ -115,10 +35,8 @@ LEAF_ENTRY CONTEXT_CaptureContext, _TEXT ldr x2, [sp, 24] str w2, [x0, CONTEXT_Cpsr] stp fp, lr, [x0, CONTEXT_Fp] - add sp, sp, #32 - mov x2, sp + add x2, sp, #32 stp x2, lr, [x0, CONTEXT_Sp] - sub sp, sp, #32 LOCAL_LABEL(Done_CONTEXT_CONTROL): // we dont clobber x1 in the CONTEXT_CONTROL case @@ -224,14 +142,8 @@ LEAF_ENTRY RtlRestoreContext, _TEXT // since we potentially clobber x0 below, we'll bank it in x16 mov x16, x0 - ldr w2, [x16, CONTEXT_ContextFlags] - // clangs assembler doesn't seem to support the mov Wx, imm32 yet - movz w3, #0x40, lsl #16 - movk w3, #0x4 - mov w4, w3 - and w3, w2, w3 - cmp w3, w4 - b.ne LOCAL_LABEL(No_Restore_CONTEXT_FLOATING_POINT) + ldr w17, [x16, CONTEXT_ContextFlags] + tbz w17, #CONTEXT_FLOATING_POINT_BIT, LOCAL_LABEL(No_Restore_CONTEXT_FLOATING_POINT) add x16, x16, CONTEXT_NEON_OFFSET ldp q0, q1, [x16, CONTEXT_V0] @@ -256,12 +168,7 @@ LEAF_ENTRY RtlRestoreContext, _TEXT sub x16, x16, CONTEXT_NEON_OFFSET LOCAL_LABEL(No_Restore_CONTEXT_FLOATING_POINT): - movz w2, #0x40, lsl #16 - movk w2, #0x2 - mov w3, w2 - and w2, w1, w2 - cmp w2, w3 - b.ne LOCAL_LABEL(No_Restore_CONTEXT_INTEGER) + tbz w17, #CONTEXT_INTEGER_BIT, LOCAL_LABEL(No_Restore_CONTEXT_INTEGER) ldp x0, x1, [x16, CONTEXT_X0] ldp x2, x3, [x16, CONTEXT_X2] @@ -279,12 +186,7 @@ LOCAL_LABEL(No_Restore_CONTEXT_FLOATING_POINT): ldr x28, [x16, CONTEXT_X28] LOCAL_LABEL(No_Restore_CONTEXT_INTEGER): - movz w2, #0x40, lsl #16 - movk w2, #0x2 - mov w3, w2 - and w2, w1, w2 - cmp w2, w3 - b.ne LOCAL_LABEL(No_Restore_CONTEXT_CONTROL) + tbz w17, #CONTEXT_CONTROL_BIT, LOCAL_LABEL(No_Restore_CONTEXT_CONTROL) ldr w17, [x16, CONTEXT_Cpsr] msr nzcv, x17 @@ -293,8 +195,8 @@ LOCAL_LABEL(No_Restore_CONTEXT_INTEGER): mov sp, x17 ldr x17, [x16, CONTEXT_Pc] br x17 - + LOCAL_LABEL(No_Restore_CONTEXT_CONTROL): - ret + ret LEAF_END RtlRestoreContext, _TEXT diff --git a/src/pal/src/arch/arm64/exceptionhelper.S b/src/pal/src/arch/arm64/exceptionhelper.S index 4fdcfc5eb1..480846eb61 100644 --- a/src/pal/src/arch/arm64/exceptionhelper.S +++ b/src/pal/src/arch/arm64/exceptionhelper.S @@ -3,7 +3,30 @@ // See the LICENSE file in the project root for more information. #include "unixasmmacros.inc" +#include "asmconstants.h" +////////////////////////////////////////////////////////////////////////// +// +// This function creates a stack frame right below the target frame, restores all callee +// saved registers, SP, and LR from the passed in context. +// 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 - EMIT_BREAKPOINT + // Save the FP & LR to the stack so that the unwind can work at the instruction after + // loading the FP from the context, but before loading the SP from the context. + stp fp, lr, [sp, -16]! + + ldp x19,x20, [x0, #(CONTEXT_X19)] + ldp x21,x22, [x0, #(CONTEXT_X21)] + ldp x23,x24, [x0, #(CONTEXT_X23)] + ldp x24,x25, [x0, #(CONTEXT_X24)] + ldp x26,x27, [x0, #(CONTEXT_X26)] + ldp x28,fp, [x0, #(CONTEXT_X28)] + ldr lr, [x0, #(CONTEXT_Pc)] + ldr x2, [x0, #(CONTEXT_Sp)] + mov sp, x2 + + // The PAL_SEHException pointer + mov x0, x1 + b EXTERNAL_C_FUNC(ThrowExceptionHelper) LEAF_END ThrowExceptionFromContextInternal, _TEXT diff --git a/src/pal/src/arch/arm64/signalhandlerhelper.cpp b/src/pal/src/arch/arm64/signalhandlerhelper.cpp new file mode 100644 index 0000000000..c35c629ab3 --- /dev/null +++ b/src/pal/src/arch/arm64/signalhandlerhelper.cpp @@ -0,0 +1,67 @@ +// 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 + CONTEXT context2; + RtlCaptureContext(&context2); + + context2.Sp = (size_t)sp; + context2.Fp = (size_t)sp; + context2.Lr = fakeFrameReturnAddress; + context2.Pc = (size_t)signal_handler_worker; + context2.X0 = code; + context2.X1 = (size_t)siginfo; + context2.X2 = (size_t)context; + context2.X3 = (size_t)returnPoint; + + RtlRestoreContext(&context2, NULL); +} diff --git a/src/pal/src/arch/i386/asmconstants.h b/src/pal/src/arch/i386/asmconstants.h index ff763ef16b..d947cb8bcd 100644 --- a/src/pal/src/arch/i386/asmconstants.h +++ b/src/pal/src/arch/i386/asmconstants.h @@ -28,3 +28,4 @@ #define CONTEXT_Xmm5 CONTEXT_Xmm4+16 #define CONTEXT_Xmm6 CONTEXT_Xmm5+16 #define CONTEXT_Xmm7 CONTEXT_Xmm6+16 +#define CONTEXT_ResumeEsp CONTEXT_ExtendedRegisters+512 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/context2.S b/src/pal/src/arch/i386/context2.S index 11aba5e647..6c31b074cc 100644 --- a/src/pal/src/arch/i386/context2.S +++ b/src/pal/src/arch/i386/context2.S @@ -42,6 +42,7 @@ LEAF_ENTRY CONTEXT_CaptureContext, _TEXT mov [eax + CONTEXT_Ebp], ebp lea ebx, [esp + 12] mov [eax + CONTEXT_Esp], ebx + mov [eax + CONTEXT_ResumeEsp], ebx mov ebx, [esp + 8] mov [eax + CONTEXT_Eip], ebx @@ -82,7 +83,7 @@ LOCAL_LABEL(Done_CONTEXT_EXTENDED_REGISTERS): // Restore pop ebx pop eax - ret 4 + ret LEAF_END CONTEXT_CaptureContext, _TEXT LEAF_ENTRY RtlCaptureContext, _TEXT @@ -114,7 +115,7 @@ LOCAL_LABEL(Done_Restore_CONTEXT_FLOATING_POINT): LOCAL_LABEL(Done_Restore_CONTEXT_EXTENDED_REGISTERS): // Restore Stack - mov esp, [eax + CONTEXT_Esp] + mov esp, [eax + CONTEXT_ResumeEsp] // Create a minimal frame push DWORD PTR [eax + CONTEXT_Eip] diff --git a/src/pal/src/arch/i386/exceptionhelper.S b/src/pal/src/arch/i386/exceptionhelper.S index 2061be26f8..bf44124479 100644 --- a/src/pal/src/arch/i386/exceptionhelper.S +++ b/src/pal/src/arch/i386/exceptionhelper.S @@ -19,11 +19,14 @@ LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT push ebp - mov eax, [esp + 12] // ebx: PAL_SEHException * - mov ebx, [esp + 8] // eax: CONTEXT * + mov ecx, [esp + 12] // ecx: PAL_SEHException * (first argument for ThrowExceptionHelper) + mov eax, [esp + 8] // ebx: CONTEXT * - mov ebp, [ebx + CONTEXT_Ebp] - mov esp, [ebx + CONTEXT_Esp] + mov ebp, [eax + CONTEXT_Ebp] + mov esp, [eax + CONTEXT_ResumeEsp] + mov ebx, [eax + CONTEXT_Ebx] + mov esi, [eax + CONTEXT_Esi] + mov edi, [eax + CONTEXT_Edi] // The ESP is re-initialized as the target frame's value, so the current function's // CFA is now right at the ESP. @@ -33,11 +36,9 @@ LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT // the EBP is no longer saved in the current stack frame. .cfi_restore ebp - // Store PAL_SEHException as the first argument - push eax - // Store return address to the stack - mov ebx, [ebx + CONTEXT_Eip] - push ebx + mov eax, [eax + CONTEXT_Eip] + push eax jmp EXTERNAL_C_FUNC(ThrowExceptionHelper) + LEAF_END ThrowExceptionFromContextInternal, _TEXT diff --git a/src/pal/src/arch/i386/signalhandlerhelper.cpp b/src/pal/src/arch/i386/signalhandlerhelper.cpp new file mode 100644 index 0000000000..3369abe093 --- /dev/null +++ b/src/pal/src/arch/i386/signalhandlerhelper.cpp @@ -0,0 +1,78 @@ +// 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 + CONTEXT context2; + RtlCaptureContext(&context2); + + // We don't care about the other registers state since the stack unwinding restores + // them for the target frame directly from the signal context. + context2.Esp = (size_t)sp; + context2.ResumeEsp = (size_t)sp; + context2.Ebp = (size_t)fp; + context2.Eip = (size_t)signal_handler_worker; + + RtlRestoreContext(&context2, NULL); +} diff --git a/src/pal/src/config.h.in b/src/pal/src/config.h.in index 77d7bfaf5a..ab5fa0341d 100644 --- a/src/pal/src/config.h.in +++ b/src/pal/src/config.h.in @@ -40,6 +40,7 @@ #cmakedefine01 HAVE_UTIMES #cmakedefine01 HAVE_SYSCTL #cmakedefine01 HAVE_SYSCONF +#cmakedefine01 HAVE_SYSINFO #cmakedefine01 HAVE_LOCALTIME_R #cmakedefine01 HAVE_GMTIME_R #cmakedefine01 HAVE_TIMEGM @@ -57,6 +58,8 @@ #cmakedefine01 HAVE_TTRACE #cmakedefine HAVE_UNW_GET_SAVE_LOC #cmakedefine HAVE_UNW_GET_ACCESSORS +#cmakedefine01 HAVE_XSWDEV +#cmakedefine01 HAVE_XSW_USAGE #cmakedefine01 HAVE_STAT_TIMESPEC #cmakedefine01 HAVE_STAT_NSEC diff --git a/src/pal/src/configure.cmake b/src/pal/src/configure.cmake index 4f2bc5739b..4d78f54423 100644 --- a/src/pal/src/configure.cmake +++ b/src/pal/src/configure.cmake @@ -78,6 +78,7 @@ check_function_exists(fsync HAVE_FSYNC) check_function_exists(futimes HAVE_FUTIMES) check_function_exists(utimes HAVE_UTIMES) check_function_exists(sysctl HAVE_SYSCTL) +check_function_exists(sysinfo HAVE_SYSINFO) check_function_exists(sysconf HAVE_SYSCONF) check_function_exists(localtime_r HAVE_LOCALTIME_R) check_function_exists(gmtime_r HAVE_GMTIME_R) @@ -122,6 +123,7 @@ check_struct_has_member ("struct stat" st_atimensec "sys/types.h;sys/stat.h" HAV check_struct_has_member ("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF) check_struct_has_member ("ucontext_t" uc_mcontext.gregs[0] ucontext.h HAVE_GREGSET_T) check_struct_has_member ("ucontext_t" uc_mcontext.__gregs[0] ucontext.h HAVE___GREGSET_T) +check_struct_has_member ("struct sysinfo" mem_unit "sys/sysinfo.h" HAVE_SYSINFO_WITH_MEM_UNIT) set(CMAKE_EXTRA_INCLUDE_FILES machine/reg.h) check_type_size("struct reg" BSD_REGS_T) @@ -728,9 +730,9 @@ check_cxx_source_runs(" int main(void) { double infinity = 1.0 / 0.0; if (pow(1.0, infinity) != 1.0 || pow(1.0, -infinity) != 1.0) { - exit(1) + exit(1); } - if (!isnan(pow(-1.0, infinity)) || !isnan(pow(-1.0, -infinity))) { + if (pow(-1.0, infinity) != 1.0 || pow(-1.0, -infinity) != 1.0) { exit(1); } if (pow(0.0, infinity) != 0.0) { @@ -742,7 +744,7 @@ int main(void) { if (pow(-1.1, infinity) != infinity || pow(1.1, infinity) != infinity) { exit(1); } - if (pow(-1.1, -infinity) != 0.0 || pow(1.1, infinity) != 0.0) { + if (pow(-1.1, -infinity) != 0.0 || pow(1.1, -infinity) != 0.0) { exit(1); } if (pow(-0.0, -1) != -infinity) { @@ -982,6 +984,29 @@ int main(int argc, char **argv) return 0; }" UNWIND_CONTEXT_IS_UCONTEXT_T) +check_cxx_source_compiles(" +#include <sys/param.h> +#include <sys/sysctl.h> +#include <vm/vm_param.h> + +int main(int argc, char **argv) +{ + struct xswdev xsw; + + return 0; +}" HAVE_XSWDEV) + +check_cxx_source_compiles(" +#include <sys/param.h> +#include <sys/sysctl.h> + +int main(int argc, char **argv) +{ + struct xsw_usage xsu; + + return 0; +}" HAVE_XSW_USAGE) + set(CMAKE_REQUIRED_LIBRARIES pthread) check_cxx_source_compiles(" #include <errno.h> diff --git a/src/pal/src/cruntime/math.cpp b/src/pal/src/cruntime/math.cpp index d53dbe7982..7b5175a526 100644 --- a/src/pal/src/cruntime/math.cpp +++ b/src/pal/src/cruntime/math.cpp @@ -343,7 +343,7 @@ PALIMPORT double __cdecl PAL_pow(double x, double y) } else if (x == -1.0) { - ret = PAL_NAN_DBL; // NaN + ret = 1.0; } else if ((x > -1.0) && (x < 1.0)) { @@ -362,7 +362,7 @@ PALIMPORT double __cdecl PAL_pow(double x, double y) } else if (x == -1.0) { - ret = PAL_NAN_DBL; // NaN + ret = 1.0; } else if ((x > -1.0) && (x < 1.0)) { @@ -384,17 +384,7 @@ PALIMPORT double __cdecl PAL_pow(double x, double y) else #endif // !HAVE_COMPATIBLE_POW - if ((y == 0.0) && isnan(x)) - { - // Windows returns NaN for pow(NaN, 0), but POSIX specifies - // a return value of 1 for that case. We need to return - // the same result as Windows. - ret = PAL_NAN_DBL; - } - else - { - ret = pow(x, y); - } + ret = pow(x, y); #if !HAVE_VALID_NEGATIVE_INF_POW if ((ret == PAL_POSINF_DBL) && (x < 0) && isfinite(x) && (ceil(y / 2) != floor(y / 2))) @@ -706,7 +696,7 @@ PALIMPORT float __cdecl PAL_powf(float x, float y) } else if (x == -1.0f) { - ret = PAL_NAN_FLT; // NaN + ret = 1.0f; } else if ((x > -1.0f) && (x < 1.0f)) { @@ -725,7 +715,7 @@ PALIMPORT float __cdecl PAL_powf(float x, float y) } else if (x == -1.0f) { - ret = PAL_NAN_FLT; // NaN + ret = 1.0f; } else if ((x > -1.0f) && (x < 1.0f)) { @@ -747,18 +737,8 @@ PALIMPORT float __cdecl PAL_powf(float x, float y) else #endif // !HAVE_COMPATIBLE_POW - if ((y == 0.0f) && isnan(x)) - { - // Windows returns NaN for powf(NaN, 0), but POSIX specifies - // a return value of 1 for that case. We need to return - // the same result as Windows. - ret = PAL_NAN_FLT; - } - else - { - ret = powf(x, y); - } - + ret = powf(x, y); + #if !HAVE_VALID_NEGATIVE_INF_POW if ((ret == PAL_POSINF_FLT) && (x < 0) && isfinite(x) && (ceilf(y / 2) != floorf(y / 2))) { diff --git a/src/pal/src/cruntime/misctls.cpp b/src/pal/src/cruntime/misctls.cpp index e46582ec17..2df32fe115 100644 --- a/src/pal/src/cruntime/misctls.cpp +++ b/src/pal/src/cruntime/misctls.cpp @@ -128,6 +128,11 @@ done: return retval; } +UINT GetExponent(double d) +{ + return (*((UINT*)&d + 1) >> 20) & 0x000007ff; +} + /** Function: @@ -150,250 +155,143 @@ NOTES: char * __cdecl _ecvt( double value, int count, int * dec, int * sign ) { - CONST CHAR * FORMAT_STRING = "%.348e"; - CHAR TempBuffer[ ECVT_MAX_BUFFER_SIZE ]; - CPalThread *pThread = NULL; - LPSTR lpReturnBuffer = NULL; - LPSTR lpStartOfReturnBuffer = NULL; - LPSTR lpTempBuffer = NULL; - LPSTR lpEndOfTempBuffer = NULL; - INT nTempBufferLength = 0; - CHAR ExponentBuffer[ 6 ]; - INT nExponentValue = 0; - INT LoopIndex = 0; - PERF_ENTRY(_ecvt); ENTRY( "_ecvt( value=%.30g, count=%d, dec=%p, sign=%p )\n", value, count, dec, sign ); + + _ASSERTE(dec != nullptr && sign != nullptr); + CPalThread *pThread = InternalGetCurrentThread(); + LPSTR lpStartOfReturnBuffer = pThread->crtInfo.ECVTBuffer; - /* Get the per-thread buffer from the thread structure. */ - pThread = InternalGetCurrentThread(); - - lpStartOfReturnBuffer = lpReturnBuffer = pThread->crtInfo.ECVTBuffer; - - /* Sanity checks */ - if ( !dec || !sign ) - { - ERROR( "dec and sign have to be valid pointers.\n" ); - *lpReturnBuffer = '\0'; - goto done; - } - else + if (count > ECVT_MAX_COUNT_SIZE) { - *dec = *sign = 0; + count = ECVT_MAX_COUNT_SIZE; } - if ( value < 0.0 ) - { - *sign = 1; - } + // the caller of _ecvt should already checked the Infinity and NAN values + _ASSERTE(GetExponent(value) != 0x7ff); - if ( count > ECVT_MAX_COUNT_SIZE ) + CHAR TempBuffer[ECVT_MAX_BUFFER_SIZE]; + + *dec = *sign = 0; + + if (value < 0.0) { - count = ECVT_MAX_COUNT_SIZE; + *sign = 1; } - - /* Get the string to work with. */ - sprintf_s( TempBuffer, sizeof(TempBuffer), FORMAT_STRING, value ); - - /* Check to see if value was a valid number. */ - if ( strcmp( "NaN", TempBuffer ) == 0 || strcmp( "-NaN", TempBuffer ) == 0 ) + { - TRACE( "value was not a number!\n" ); - if (strcpy_s( lpStartOfReturnBuffer, ECVT_MAX_BUFFER_SIZE, "1#QNAN0" ) != SAFECRT_SUCCESS) + // we have issue #10290 tracking fixing the sign of 0.0 across the platforms + if (value == 0.0) { - ERROR( "strcpy_s failed!\n" ); - *lpStartOfReturnBuffer = '\0'; + for (int j = 0; j < count; j++) + { + lpStartOfReturnBuffer[j] = '0'; + } + lpStartOfReturnBuffer[count] = '\0'; goto done; + } + + int tempBufferLength = snprintf(TempBuffer, ECVT_MAX_BUFFER_SIZE, "%.40e", value); + _ASSERTE(tempBufferLength > 0 && ECVT_MAX_BUFFER_SIZE > tempBufferLength); + + // + // Calculate the exponent value + // + + int exponentIndex = strrchr(TempBuffer, 'e') - TempBuffer; + _ASSERTE(exponentIndex > 0 && (exponentIndex < tempBufferLength - 1)); + + int i = exponentIndex + 1; + int exponentSign = 1; + if (TempBuffer[i] == '-') + { + exponentSign = -1; + i++; } - - *dec = 1; - goto done; - } - - /* Check to see if it is infinite. */ - if ( strcmp( "Inf", TempBuffer ) == 0 || strcmp( "-Inf", TempBuffer ) == 0 ) - { - TRACE( "value is infinite!\n" ); - if (strcpy_s( lpStartOfReturnBuffer, ECVT_MAX_BUFFER_SIZE, "1#INF00" ) != SAFECRT_SUCCESS) + else if (TempBuffer[i] == '+') { - ERROR( "strcpy_s failed!\n" ); - *lpStartOfReturnBuffer = '\0'; - goto done; + i++; } - *dec = 1; - if ( *TempBuffer == '-' ) + int exponentValue = 0; + while (i < tempBufferLength) { - *sign = 1; + _ASSERTE(TempBuffer[i] >= '0' && TempBuffer[i] <= '9'); + exponentValue = exponentValue * 10 + ((BYTE) TempBuffer[i] - (BYTE) '0'); + i++; } - goto done; - } - - nTempBufferLength = strlen( TempBuffer ); - lpEndOfTempBuffer = &(TempBuffer[ nTempBufferLength ]); - - /* Extract the exponent, and convert it to integer. */ - while ( *lpEndOfTempBuffer != 'e' && nTempBufferLength > 0 ) - { - nTempBufferLength--; - lpEndOfTempBuffer--; - } - - ExponentBuffer[ 0 ] = '\0'; - if (strncat_s( ExponentBuffer, sizeof(ExponentBuffer), lpEndOfTempBuffer + 1, 5 ) != SAFECRT_SUCCESS) - { - ERROR( "strncat_s failed!\n" ); - *lpStartOfReturnBuffer = '\0'; - goto done; - } - - nExponentValue = atoi( ExponentBuffer ); + exponentValue *= exponentSign; + + // + // Determine decimal location. + // - /* End the string at the 'e' */ - *lpEndOfTempBuffer = '\0'; - nTempBufferLength--; + if (exponentValue == 0) + { + *dec = 1; + } + else + { + *dec = exponentValue + 1; + } + + // + // Copy the string from the temp buffer upto precision characters, removing the sign, and decimal as required. + // + + i = 0; + int mantissaIndex = 0; + while (i < count && mantissaIndex < exponentIndex) + { + if (TempBuffer[mantissaIndex] >= '0' && TempBuffer[mantissaIndex] <= '9') + { + lpStartOfReturnBuffer[i] = TempBuffer[mantissaIndex]; + i++; + } + mantissaIndex++; + } - /* Determine decimal location. */ - if ( nExponentValue == 0 ) - { - *dec = 1; - } - else - { - *dec = nExponentValue + 1; - } + while (i < count) + { + lpStartOfReturnBuffer[i] = '0'; // append zeros as needed + i++; + } - if ( value == 0.0 ) - { - *dec = 0; - } - /* Copy the string from the temp buffer upto count characters, - removing the sign, and decimal as required. */ - lpTempBuffer = TempBuffer; - *lpReturnBuffer = '0'; - lpReturnBuffer++; + lpStartOfReturnBuffer[i] = '\0'; + + // + // Round if needed + // - while ( LoopIndex < ECVT_MAX_COUNT_SIZE ) - { - if ( isdigit(*lpTempBuffer) ) + if (mantissaIndex >= exponentIndex || TempBuffer[mantissaIndex] < '5') { - *lpReturnBuffer = *lpTempBuffer; - LoopIndex++; - lpReturnBuffer++; + goto done; } - lpTempBuffer++; - if ( LoopIndex == count + 1 ) + i = count - 1; + while (lpStartOfReturnBuffer[i] == '9' && i > 0) { - break; + lpStartOfReturnBuffer[i] = '0'; + i--; } - } - *lpReturnBuffer = '\0'; - - /* Round if needed. If count is less then 0 - then windows does not round for some reason.*/ - nTempBufferLength = strlen( lpStartOfReturnBuffer ) - 1; - - /* Add one for the preceeding zero. */ - lpReturnBuffer = ( lpStartOfReturnBuffer + 1 ); - - if ( nTempBufferLength >= count && count >= 0 ) - { - /* Determine whether I need to round up. */ - if ( *(lpReturnBuffer + count) >= '5' ) + if (i == 0 && lpStartOfReturnBuffer[i] == '9') { - CHAR cNumberToBeRounded; - if ( count != 0 ) - { - cNumberToBeRounded = *(lpReturnBuffer + count - 1); - } - else - { - cNumberToBeRounded = *lpReturnBuffer; - } - - if ( cNumberToBeRounded < '9' ) - { - if ( count > 0 ) - { - /* Add one to the character. */ - (*(lpReturnBuffer + count - 1))++; - } - else - { - if ( cNumberToBeRounded >= '5' ) - { - (*dec)++; - } - } - } - else - { - LPSTR lpRounding = NULL; - - if ( count > 0 ) - { - lpRounding = lpReturnBuffer + count - 1; - } - else - { - lpRounding = lpReturnBuffer + count; - } - - while ( cNumberToBeRounded == '9' ) - { - cNumberToBeRounded = *lpRounding; - - if ( cNumberToBeRounded == '9' ) - { - *lpRounding = '0'; - lpRounding--; - } - } - - if ( lpRounding == lpStartOfReturnBuffer ) - { - /* Overflow. number is a whole number now. */ - *lpRounding = '1'; - memset( ++lpRounding, '0', count); - - /* The decimal has moved. */ - (*dec)++; - } - else - { - *lpRounding = ++cNumberToBeRounded; - } - } + lpStartOfReturnBuffer[i] = '1'; + (*dec)++; } else { - /* Get rid of the preceding 0 */ - lpStartOfReturnBuffer++; - } - } - - if ( *lpStartOfReturnBuffer == '0' ) - { - lpStartOfReturnBuffer++; - } - - if ( count >= 0 ) - { - *(lpStartOfReturnBuffer + count) = '\0'; - } - else - { - *lpStartOfReturnBuffer = '\0'; + lpStartOfReturnBuffer[i]++; + } } done: LOGEXIT( "_ecvt returning %p (%s)\n", lpStartOfReturnBuffer , lpStartOfReturnBuffer ); PERF_EXIT(_ecvt); - + return lpStartOfReturnBuffer; } diff --git a/src/pal/src/exception/seh-unwind.cpp b/src/pal/src/exception/seh-unwind.cpp index e3fa09f7c8..1f20ee0cad 100644 --- a/src/pal/src/exception/seh-unwind.cpp +++ b/src/pal/src/exception/seh-unwind.cpp @@ -155,6 +155,7 @@ static void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext) #elif defined(_X86_) unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Eip); unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->Esp); + unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &winContext->ResumeEsp); unw_get_reg(cursor, UNW_X86_EBP, (unw_word_t *) &winContext->Ebp); unw_get_reg(cursor, UNW_X86_EBX, (unw_word_t *) &winContext->Ebx); unw_get_reg(cursor, UNW_X86_ESI, (unw_word_t *) &winContext->Esi); @@ -243,6 +244,7 @@ static void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, GetContextPointer(cursor, unwContext, UNW_AARCH64_X26, &contextPointers->X26); GetContextPointer(cursor, unwContext, UNW_AARCH64_X27, &contextPointers->X27); GetContextPointer(cursor, unwContext, UNW_AARCH64_X28, &contextPointers->X28); + GetContextPointer(cursor, unwContext, UNW_AARCH64_X29, &contextPointers->Fp); #else #error unsupported architecture #endif diff --git a/src/pal/src/exception/seh.cpp b/src/pal/src/exception/seh.cpp index ad09e02884..2d1c18218a 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" @@ -111,14 +111,12 @@ Return value : BOOL SEHInitialize (CPalThread *pthrCurrent, DWORD flags) { -#if !HAVE_MACH_EXCEPTIONS if (!SEHInitializeSignals(flags)) { ERROR("SEHInitializeSignals failed!\n"); SEHCleanup(); return FALSE; } -#endif return TRUE; } @@ -142,9 +140,8 @@ SEHCleanup() #if HAVE_MACH_EXCEPTIONS SEHCleanupExceptionPort(); -#else - SEHCleanupSignals(); #endif + SEHCleanupSignals(); } /*++ @@ -226,7 +223,11 @@ Parameters: PAL_SEHException* ex - the exception to throw. --*/ extern "C" +#ifdef _X86_ +void __fastcall ThrowExceptionHelper(PAL_SEHException* ex) +#else // _X86_ void ThrowExceptionHelper(PAL_SEHException* ex) +#endif // !_X86_ { throw std::move(*ex); } diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp index 26e2a012c5..57ae62ea21 100644 --- a/src/pal/src/exception/signal.cpp +++ b/src/pal/src/exception/signal.cpp @@ -27,24 +27,28 @@ 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" + +#include <errno.h> +#include <signal.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> #include <string.h> #include <sys/ucontext.h> #include <sys/utsname.h> #include <unistd.h> +#include <sys/mman.h> #include "pal/context.h" -using namespace CorUnix; - #ifdef SIGRTMIN #define INJECT_ACTIVATION_SIGNAL SIGRTMIN #endif @@ -52,6 +56,9 @@ using namespace CorUnix; #if !defined(INJECT_ACTIVATION_SIGNAL) && defined(FEATURE_HIJACK) #error FEATURE_HIJACK requires INJECT_ACTIVATION_SIGNAL to be defined #endif +#endif // !HAVE_MACH_EXCEPTIONS + +using namespace CorUnix; /* local type definitions *****************************************************/ @@ -63,8 +70,19 @@ typedef void *siginfo_t; #endif /* !HAVE_SIGINFO_T */ typedef void (*SIGFUNC)(int, siginfo_t *, void *); +#if !HAVE_MACH_EXCEPTIONS +// Return context and status for the signal_handler_worker. +struct SignalHandlerWorkerReturnPoint +{ + bool returnFromHandler; + CONTEXT context; +}; +#endif // !HAVE_MACH_EXCEPTIONS + /* internal function declarations *********************************************/ +static void sigterm_handler(int code, siginfo_t *siginfo, void *context); +#if !HAVE_MACH_EXCEPTIONS static void sigill_handler(int code, siginfo_t *siginfo, void *context); static void sigfpe_handler(int code, siginfo_t *siginfo, void *context); static void sigsegv_handler(int code, siginfo_t *siginfo, void *context); @@ -72,19 +90,23 @@ static void sigtrap_handler(int code, siginfo_t *siginfo, void *context); static void sigbus_handler(int code, siginfo_t *siginfo, void *context); static void sigint_handler(int code, siginfo_t *siginfo, void *context); static void sigquit_handler(int code, siginfo_t *siginfo, void *context); -static void sigterm_handler(int code, siginfo_t *siginfo, void *context); static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...); #ifdef INJECT_ACTIVATION_SIGNAL static void inject_activation_handler(int code, siginfo_t *siginfo, void *context); #endif +#endif // !HAVE_MACH_EXCEPTIONS -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 *********************************************/ +static bool registered_sigterm_handler = false; + +struct sigaction g_previous_sigterm; +#if !HAVE_MACH_EXCEPTIONS struct sigaction g_previous_sigill; struct sigaction g_previous_sigtrap; struct sigaction g_previous_sigfpe; @@ -92,9 +114,6 @@ struct sigaction g_previous_sigbus; struct sigaction g_previous_sigsegv; struct sigaction g_previous_sigint; struct sigaction g_previous_sigquit; -struct sigaction g_previous_sigterm; - -static bool registered_sigterm_handler = false; #ifdef INJECT_ACTIVATION_SIGNAL struct sigaction g_previous_activation; @@ -103,9 +122,94 @@ struct sigaction g_previous_activation; // Offset of the local variable containing native context in the common_signal_handler function. // This offset is relative to the frame pointer. int g_common_signal_handler_context_locvar_offset = 0; +#endif // !HAVE_MACH_EXCEPTIONS /* public function definitions ************************************************/ +#if !HAVE_MACH_EXCEPTIONS +/*++ +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); + } +} +#endif // !HAVE_MACH_EXCEPTIONS + /*++ Function : SEHInitializeSignals @@ -122,6 +226,7 @@ BOOL SEHInitializeSignals(DWORD flags) { TRACE("Initializing signal handlers\n"); +#if !HAVE_MACH_EXCEPTIONS /* we call handle_signal for every possible signal, even if we don't provide a signal handler. @@ -139,16 +244,24 @@ 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; + } +#endif // !HAVE_MACH_EXCEPTIONS + if (flags & PAL_INITIALIZE_REGISTER_SIGTERM_HANDLER) { handle_signal(SIGTERM, sigterm_handler, &g_previous_sigterm); registered_sigterm_handler = true; } +#if !HAVE_MACH_EXCEPTIONS #ifdef INJECT_ACTIVATION_SIGNAL handle_signal(INJECT_ACTIVATION_SIGNAL, inject_activation_handler, &g_previous_activation); #endif @@ -162,6 +275,7 @@ BOOL SEHInitializeSignals(DWORD flags) issued a SIGPIPE will, instead, report an error and set errno to EPIPE. */ signal(SIGPIPE, SIG_IGN); +#endif // !HAVE_MACH_EXCEPTIONS return TRUE; } @@ -186,6 +300,7 @@ void SEHCleanupSignals() { TRACE("Restoring default signal handlers\n"); +#if !HAVE_MACH_EXCEPTIONS restore_signal(SIGILL, &g_previous_sigill); restore_signal(SIGTRAP, &g_previous_sigtrap); restore_signal(SIGFPE, &g_previous_sigfpe); @@ -193,19 +308,23 @@ void SEHCleanupSignals() restore_signal(SIGSEGV, &g_previous_sigsegv); restore_signal(SIGINT, &g_previous_sigint); restore_signal(SIGQUIT, &g_previous_sigquit); +#endif // !HAVE_MACH_EXCEPTIONS if (registered_sigterm_handler) { restore_signal(SIGTERM, &g_previous_sigterm); } +#if !HAVE_MACH_EXCEPTIONS #ifdef INJECT_ACTIVATION_SIGNAL restore_signal(INJECT_ACTIVATION_SIGNAL, &g_previous_activation); #endif +#endif // !HAVE_MACH_EXCEPTIONS } /* internal function definitions **********************************************/ +#if !HAVE_MACH_EXCEPTIONS /*++ Function : sigill_handler @@ -276,6 +395,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); + RtlRestoreContext(&returnPoint->context, NULL); +} + +/*++ +Function : sigsegv_handler handle SIGSEGV signal (EXCEPTION_ACCESS_VIOLATION, others) @@ -289,10 +430,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; + RtlCaptureContext(&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; } @@ -422,6 +591,7 @@ static void sigquit_handler(int code, siginfo_t *siginfo, void *context) restore_signal(code, &g_previous_sigquit); kill(gPID, code); } +#endif // !HAVE_MACH_EXCEPTIONS /*++ Function : @@ -452,6 +622,7 @@ static void sigterm_handler(int code, siginfo_t *siginfo, void *context) } } +#if !HAVE_MACH_EXCEPTIONS #ifdef INJECT_ACTIVATION_SIGNAL /*++ Function : @@ -650,6 +821,7 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext return false; } +#endif // !HAVE_MACH_EXCEPTIONS /*++ Function : @@ -666,11 +838,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; @@ -707,5 +879,3 @@ void restore_signal(int signal_id, struct sigaction *previousAction) errno, strerror(errno)); } } - -#endif // !HAVE_MACH_EXCEPTIONS 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/file/filetime.cpp b/src/pal/src/file/filetime.cpp index a8666b0dff..ca36e04d9d 100644 --- a/src/pal/src/file/filetime.cpp +++ b/src/pal/src/file/filetime.cpp @@ -81,8 +81,8 @@ SET_DEFAULT_DEBUG_CHANNEL(FILE); Both epochs are Gregorian. 1970 - 1601 = 369. Assuming a leap year every four years, 369 / 4 = 92. However, 1700, 1800, and 1900 were NOT leap years, so 89 leap years, 280 non-leap years. - 89 * 366 + 280 * 365 = 134744 days between epochs. Of course - 60 * 60 * 24 = 86400 seconds per day, so 134744 * 86400 = + 89 * 366 + 280 * 365 = 134774 days between epochs. Of course + 60 * 60 * 24 = 86400 seconds per day, so 134774 * 86400 = 11644473600 = SECS_BETWEEN_1601_AND_1970_EPOCHS. To 2001: @@ -504,23 +504,29 @@ PALAPI GetSystemTimeAsFileTime( OUT LPFILETIME lpSystemTimeAsFileTime) { - struct timeval Time; - PERF_ENTRY(GetSystemTimeAsFileTime); ENTRY("GetSystemTimeAsFileTime(lpSystemTimeAsFileTime=%p)\n", lpSystemTimeAsFileTime); - if ( gettimeofday( &Time, NULL ) != 0 ) +#if HAVE_WORKING_CLOCK_GETTIME + struct timespec Time; + if (clock_gettime(CLOCK_REALTIME, &Time) == 0) { - ASSERT("gettimeofday() failed"); - /* no way to indicate failure, so set time to zero */ - *lpSystemTimeAsFileTime = FILEUnixTimeToFileTime( 0, 0 ); + *lpSystemTimeAsFileTime = FILEUnixTimeToFileTime( Time.tv_sec, Time.tv_nsec ); } - else +#else + struct timeval Time; + if (gettimeofday(&Time, NULL) == 0) { /* use (tv_usec * 1000) because 2nd arg is in nanoseconds */ - *lpSystemTimeAsFileTime = FILEUnixTimeToFileTime( Time.tv_sec, - Time.tv_usec * 1000 ); + *lpSystemTimeAsFileTime = FILEUnixTimeToFileTime( Time.tv_sec, Time.tv_usec * 1000); + } +#endif + else + { + /* no way to indicate failure, so set time to zero */ + ASSERT("clock_gettime or gettimeofday failed"); + *lpSystemTimeAsFileTime = FILEUnixTimeToFileTime( 0, 0 ); } LOGEXIT("GetSystemTimeAsFileTime returns.\n"); diff --git a/src/pal/src/include/pal/context.h b/src/pal/src/include/pal/context.h index 6857c130ee..db6d69579a 100644 --- a/src/pal/src/include/pal/context.h +++ b/src/pal/src/include/pal/context.h @@ -248,8 +248,7 @@ inline void *FPREG_Xstate_Ymmh(const ucontext_t *uc) #define MCREG_Sp(mc) ((mc).sp) #define MCREG_Pc(mc) ((mc).pc) -#define MCREG_PState(mc) ((mc).pstate) -#define MCREG_Cpsr(mc) ((mc).cpsr) +#define MCREG_Cpsr(mc) ((mc).pstate) #else // For FreeBSD, as found in x86/ucontext.h #define MCREG_Rbp(mc) ((mc).mc_rbp) @@ -640,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/module.h b/src/pal/src/include/pal/module.h index 95fa605c21..72df268d3c 100644 --- a/src/pal/src/include/pal/module.h +++ b/src/pal/src/include/pal/module.h @@ -25,7 +25,7 @@ extern "C" { #endif // __cplusplus -typedef BOOL (__stdcall *PDLLMAIN)(HINSTANCE, DWORD, LPVOID); /* entry point of module */ +typedef BOOL (PALAPI *PDLLMAIN)(HINSTANCE, DWORD, LPVOID); /* entry point of module */ typedef HINSTANCE (PALAPI *PREGISTER_MODULE)(LPCSTR); /* used to create the HINSTANCE for above DLLMain entry point */ typedef VOID (PALAPI *PUNREGISTER_MODULE)(HINSTANCE); /* used to cleanup the HINSTANCE for above DLLMain entry point */ diff --git a/src/pal/src/include/pal/signal.hpp b/src/pal/src/include/pal/signal.hpp new file mode 100644 index 0000000000..dfe21f10fb --- /dev/null +++ b/src/pal/src/include/pal/signal.hpp @@ -0,0 +1,140 @@ +// 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(); + +#endif // !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 /* _PAL_SIGNAL_HPP_ */ diff --git a/src/pal/src/init/pal.cpp b/src/pal/src/init/pal.cpp index e6db7dca2e..fa94922325 100644 --- a/src/pal/src/init/pal.cpp +++ b/src/pal/src/init/pal.cpp @@ -105,7 +105,7 @@ static PCRITICAL_SECTION init_critsec = NULL; static int Initialize(int argc, const char *const argv[], DWORD flags); static BOOL INIT_IncreaseDescriptorLimit(void); static LPWSTR INIT_FormatCommandLine (int argc, const char * const *argv); -static LPWSTR INIT_FindEXEPath(LPCSTR exe_name); +static LPWSTR INIT_ConvertEXEPath(LPCSTR exe_name); #ifdef _DEBUG extern void PROCDumpThreadList(void); @@ -406,7 +406,7 @@ Initialize( } /* find out the application's full path */ - exe_path = INIT_FindEXEPath(argv[0]); + exe_path = INIT_ConvertEXEPath(argv[0]); if (NULL == exe_path) { ERROR("Unable to find exe path\n"); @@ -1130,14 +1130,13 @@ static LPWSTR INIT_FormatCommandLine (int argc, const char * const *argv) /*++ Function: - INIT_FindEXEPath + INIT_ConvertEXEPath Abstract: - Determine the full, canonical path of the current executable by searching - $PATH. + Check whether the executable path is valid, and convert its type (LPCSTR -> LPWSTR) Parameters: - LPCSTR exe_name : file to search for + LPCSTR exe_name : full path of the current executable Return: pointer to buffer containing the full path. This buffer must be released @@ -1145,299 +1144,33 @@ Return: Notes : this function assumes that "exe_name" is in Unix style (no \) - -Notes 2: - This doesn't handle the case of directories with the desired name - (and directories are usually executable...) --*/ -static LPWSTR INIT_FindEXEPath(LPCSTR exe_name) +static LPWSTR INIT_ConvertEXEPath(LPCSTR exe_path) { -#ifndef __APPLE__ PathCharString real_path; - LPSTR env_path; - LPSTR path_ptr; - LPSTR cur_dir; - INT exe_name_length; - BOOL need_slash; LPWSTR return_value; INT return_size; struct stat theStats; - /* if a path is specified, only search there */ - if (strchr(exe_name, '/')) - { - if ( -1 == stat( exe_name, &theStats ) ) - { - ERROR( "The file does not exist\n" ); - return NULL; - } - - if ( UTIL_IsExecuteBitsSet( &theStats ) ) - { - if (!CorUnix::RealPathHelper(exe_name, real_path)) - { - ERROR("realpath() failed!\n"); - return NULL; - } - - return_size=MultiByteToWideChar(CP_ACP,0,real_path,-1,NULL,0); - if ( 0 == return_size ) - { - ASSERT("MultiByteToWideChar failure\n"); - return NULL; - } - - return_value = reinterpret_cast<LPWSTR>(InternalMalloc((return_size*sizeof(WCHAR)))); - if ( NULL == return_value ) - { - ERROR("Not enough memory to create full path\n"); - return NULL; - } - else - { - if (!MultiByteToWideChar(CP_ACP, 0, real_path, -1, - return_value, return_size)) - { - ASSERT("MultiByteToWideChar failure\n"); - free(return_value); - return_value = NULL; - } - else - { - TRACE("full path to executable is %s\n", real_path.GetString()); - } - } - return return_value; - } - } - - /* no path was specified : search $PATH */ - - env_path = EnvironGetenv("PATH"); - if (!env_path || *env_path=='\0') - { - WARN("$PATH isn't set.\n"); - if (env_path != NULL) - { - free(env_path); - } - goto last_resort; - } - - exe_name_length=strlen(exe_name); - - cur_dir=env_path; - - while (cur_dir) + if (!strchr(exe_path, '/')) { - LPSTR full_path; - struct stat theStats; - - /* skip all leading ':' */ - while (*cur_dir==':') - { - cur_dir++; - } - if (*cur_dir=='\0') - { - break; - } - - /* cut string at next ':' */ - path_ptr = strchr(cur_dir, ':'); - if (path_ptr) - { - /* check if we need to add a '/' between the path and filename */ - need_slash=(*(path_ptr-1))!='/'; - - /* NULL_terminate path element */ - *path_ptr++='\0'; - } - else - { - /* check if we need to add a '/' between the path and filename */ - need_slash=(cur_dir[strlen(cur_dir)-1])!='/'; - } - - TRACE("looking for %s in %s\n", exe_name, cur_dir); - - /* build tentative full file name */ - int iLength = (strlen(cur_dir)+exe_name_length+2); - full_path = reinterpret_cast<LPSTR>(InternalMalloc(iLength)); - if (!full_path) - { - ERROR("Not enough memory!\n"); - break; - } - - if (strcpy_s(full_path, iLength, cur_dir) != SAFECRT_SUCCESS) - { - ERROR("strcpy_s failed!\n"); - free(full_path); - free(env_path); - return NULL; - } - - if (need_slash) - { - if (strcat_s(full_path, iLength, "/") != SAFECRT_SUCCESS) - { - ERROR("strcat_s failed!\n"); - free(full_path); - free(env_path); - return NULL; - } - } - - if (strcat_s(full_path, iLength, exe_name) != SAFECRT_SUCCESS) - { - ERROR("strcat_s failed!\n"); - free(full_path); - free(env_path); - return NULL; - } - - /* see if file exists AND is executable */ - if ( -1 != stat( full_path, &theStats ) ) - { - if( UTIL_IsExecuteBitsSet( &theStats ) ) - { - /* generate canonical path */ - if (!CorUnix::RealPathHelper(full_path, real_path)) - { - ERROR("realpath() failed!\n"); - free(full_path); - free(env_path); - return NULL; - } - free(full_path); - - return_size = MultiByteToWideChar(CP_ACP,0,real_path,-1,NULL,0); - if ( 0 == return_size ) - { - ASSERT("MultiByteToWideChar failure\n"); - free(env_path); - return NULL; - } - - return_value = reinterpret_cast<LPWSTR>(InternalMalloc((return_size*sizeof(WCHAR)))); - if ( NULL == return_value ) - { - ERROR("Not enough memory to create full path\n"); - free(env_path); - return NULL; - } - - if (!MultiByteToWideChar(CP_ACP, 0, real_path, -1, return_value, - return_size)) - { - ASSERT("MultiByteToWideChar failure\n"); - free(return_value); - return_value = NULL; - } - else - { - TRACE("found %s in %s; real path is %s\n", exe_name, - cur_dir,real_path.GetString()); - } - - free(env_path); - return return_value; - } - } - - /* file doesn't exist : keep searching */ - free(full_path); - - /* path_ptr is NULL if there's no ':' after this directory */ - cur_dir=path_ptr; - } - - free(env_path); - TRACE("No %s found in $PATH (%s)\n", exe_name, EnvironGetenv("PATH", FALSE)); - -last_resort: - /* last resort : see if the executable is in the current directory. This is - possible if it comes from a exec*() call. */ - if (0 == stat(exe_name,&theStats)) - { - if ( UTIL_IsExecuteBitsSet( &theStats ) ) - { - if (!CorUnix::RealPathHelper(exe_name, real_path)) - { - ERROR("realpath() failed!\n"); - return NULL; - } - - return_size = MultiByteToWideChar(CP_ACP,0,real_path,-1,NULL,0); - if (0 == return_size) - { - ASSERT("MultiByteToWideChar failure\n"); - return NULL; - } - - return_value = reinterpret_cast<LPWSTR>(InternalMalloc((return_size*sizeof(WCHAR)))); - if (NULL == return_value) - { - ERROR("Not enough memory to create full path\n"); - return NULL; - } - else - { - if (!MultiByteToWideChar(CP_ACP, 0, real_path, -1, - return_value, return_size)) - { - ASSERT("MultiByteToWideChar failure\n"); - free(return_value); - return_value = NULL; - } - else - { - TRACE("full path to executable is %s\n", real_path.GetString()); - } - } - - return return_value; - } - else - { - ERROR("found %s in current directory, but it isn't executable!\n", - exe_name); - } - } - else - { - TRACE("last resort failed : executable %s is not in the current " - "directory\n",exe_name); + ERROR( "The exe path is not fully specified\n" ); + return NULL; } - ERROR("executable %s not found anywhere!\n", exe_name); - return NULL; -#else // !__APPLE__ - // On the Mac we can just directly ask the OS for the executable path. - - LPWSTR return_value; - INT return_size; - - PathCharString exec_pathPS; - LPSTR exec_path = exec_pathPS.OpenStringBuffer(MAX_PATH); - uint32_t bufsize = exec_pathPS.GetCount(); - - if (-1 == _NSGetExecutablePath(exec_path, &bufsize)) + if (-1 == stat(exe_path, &theStats)) { - exec_pathPS.CloseBuffer(exec_pathPS.GetCount()); - exec_path = exec_pathPS.OpenStringBuffer(bufsize); + ERROR( "The file does not exist\n" ); + return NULL; } - if (_NSGetExecutablePath(exec_path, &bufsize)) + if (!CorUnix::RealPathHelper(exe_path, real_path)) { - ASSERT("_NSGetExecutablePath failure\n"); + ERROR("realpath() failed!\n"); return NULL; } - exec_pathPS.CloseBuffer(bufsize); - - return_size = MultiByteToWideChar(CP_ACP,0,exec_path,-1,NULL,0); + return_size = MultiByteToWideChar(CP_ACP, 0, real_path, -1, NULL, 0); if (0 == return_size) { ASSERT("MultiByteToWideChar failure\n"); @@ -1452,7 +1185,7 @@ last_resort: } else { - if (!MultiByteToWideChar(CP_ACP, 0, exec_path, -1, + if (!MultiByteToWideChar(CP_ACP, 0, real_path, -1, return_value, return_size)) { ASSERT("MultiByteToWideChar failure\n"); @@ -1461,10 +1194,9 @@ last_resort: } else { - TRACE("full path to executable is %s\n", exec_path); + TRACE("full path to executable is %s\n", real_path.GetString()); } } return return_value; -#endif // !__APPLE__ } 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/loader/module.cpp b/src/pal/src/loader/module.cpp index 63a65ffb61..bbe8b9ddcc 100644 --- a/src/pal/src/loader/module.cpp +++ b/src/pal/src/loader/module.cpp @@ -280,6 +280,16 @@ GetProcAddress( module = (MODSTRUCT *) hModule; + /* try to assert on attempt to locate symbol by ordinal */ + /* this can't be an exact test for HIWORD((DWORD)lpProcName) == 0 + because of the address range reserved for ordinals contain can + be a valid string address on non-Windows systems + */ + if ((DWORD_PTR)lpProcName < VIRTUAL_PAGE_SIZE) + { + ASSERT("Attempt to locate symbol by ordinal?!\n"); + } + /* parameter validation */ if ((lpProcName == nullptr) || (*lpProcName == '\0')) @@ -295,16 +305,6 @@ GetProcAddress( SetLastError(ERROR_INVALID_HANDLE); goto done; } - - /* try to assert on attempt to locate symbol by ordinal */ - /* this can't be an exact test for HIWORD((DWORD)lpProcName) == 0 - because of the address range reserved for ordinals contain can - be a valid string address on non-Windows systems - */ - if ((DWORD_PTR)lpProcName < VIRTUAL_PAGE_SIZE) - { - ASSERT("Attempt to locate symbol by ordinal?!\n"); - } // Get the symbol's address. diff --git a/src/pal/src/locale/unicode.cpp b/src/pal/src/locale/unicode.cpp index 3c119744b0..69214735d1 100644 --- a/src/pal/src/locale/unicode.cpp +++ b/src/pal/src/locale/unicode.cpp @@ -42,7 +42,7 @@ Revision History: #endif // __APPLE__ #include <errno.h> #if HAVE_COREFOUNDATION -#include <corefoundation/corefoundation.h> +#include <CoreFoundation/CoreFoundation.h> #endif // HAVE_COREFOUNDATION #include <debugmacrosext.h> diff --git a/src/pal/src/misc/cgroup.cpp b/src/pal/src/misc/cgroup.cpp new file mode 100644 index 0000000000..40178032e3 --- /dev/null +++ b/src/pal/src/misc/cgroup.cpp @@ -0,0 +1,335 @@ +// 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: + + cgroup.cpp + +Abstract: + Read memory limits for the current process +--*/ + +#include "pal/dbgmsg.h" +SET_DEFAULT_DEBUG_CHANNEL(MISC); +#include "pal/palinternal.h" +#include <sys/resource.h> +#include "pal/virtual.h" + +#define PROC_MOUNTINFO_FILENAME "/proc/self/mountinfo" +#define PROC_CGROUP_FILENAME "/proc/self/cgroup" +#define PROC_STATM_FILENAME "/proc/self/statm" +#define MEM_LIMIT_FILENAME "/memory.limit_in_bytes" + +class CGroup +{ + char *m_memory_cgroup_path; +public: + CGroup() + { + m_memory_cgroup_path = nullptr; + char* memoryHierarchyMount = nullptr; + char *cgroup_path_relative_to_mount = nullptr; + size_t len; + memoryHierarchyMount = FindMemoryHierarchyMount(); + if (memoryHierarchyMount == nullptr) + goto done; + + cgroup_path_relative_to_mount = FindCGroupPathForMemorySubsystem(); + if (cgroup_path_relative_to_mount == nullptr) + goto done; + + len = strlen(memoryHierarchyMount); + len += strlen(cgroup_path_relative_to_mount); + m_memory_cgroup_path = (char*)PAL_malloc(len+1); + if (m_memory_cgroup_path == nullptr) + goto done; + + strcpy_s(m_memory_cgroup_path, len+1, memoryHierarchyMount); + strcat_s(m_memory_cgroup_path, len+1, cgroup_path_relative_to_mount); + + done: + PAL_free(memoryHierarchyMount); + PAL_free(cgroup_path_relative_to_mount); + } + ~CGroup() + { + PAL_free(m_memory_cgroup_path); + } + + bool GetPhysicalMemoryLimit(size_t *val) + { + char *mem_limit_filename = nullptr; + bool result = false; + + if (m_memory_cgroup_path == nullptr) + return result; + + size_t len = strlen(m_memory_cgroup_path); + len += strlen(MEM_LIMIT_FILENAME); + mem_limit_filename = (char*)PAL_malloc(len+1); + if (mem_limit_filename == nullptr) + return result; + + strcpy_s(mem_limit_filename, len+1, m_memory_cgroup_path); + strcat_s(mem_limit_filename, len+1, MEM_LIMIT_FILENAME); + result = ReadMemoryValueFromFile(mem_limit_filename, val); + PAL_free(mem_limit_filename); + return result; + } +private: + char* FindMemoryHierarchyMount() + { + char *line = nullptr; + size_t lineLen = 0, maxLineLen = 0; + char *filesystemType = nullptr; + char *options = nullptr; + char* mountpath = nullptr; + + FILE *mountinfofile = fopen(PROC_MOUNTINFO_FILENAME, "r"); + if (mountinfofile == nullptr) + goto done; + + while (getline(&line, &lineLen, mountinfofile) != -1) + { + if (filesystemType == nullptr || lineLen > maxLineLen) + { + PAL_free(filesystemType); + PAL_free(options); + filesystemType = (char*)PAL_malloc(lineLen+1); + if (filesystemType == nullptr) + goto done; + options = (char*)PAL_malloc(lineLen+1); + if (options == nullptr) + goto done; + maxLineLen = lineLen; + } + char* separatorChar = strchr(line, '-'); + + // See man page of proc to get format for /proc/self/mountinfo file + int sscanfRet = sscanf_s(separatorChar, + "- %s %*s %s", + filesystemType, lineLen+1, + options, lineLen+1); + if (sscanfRet != 2) + { + _ASSERTE(!"Failed to parse mount info file contents with sscanf_s."); + goto done; + } + + if (strncmp(filesystemType, "cgroup", 6) == 0) + { + char* context = nullptr; + char* strTok = strtok_s(options, ",", &context); + while (strTok != nullptr) + { + if (strncmp("memory", strTok, 6) == 0) + { + mountpath = (char*)PAL_malloc(lineLen+1); + if (mountpath == nullptr) + goto done; + + sscanfRet = sscanf_s(line, + "%*s %*s %*s %*s %s ", + mountpath, lineLen+1); + if (sscanfRet != 1) + { + PAL_free(mountpath); + mountpath = nullptr; + _ASSERTE(!"Failed to parse mount info file contents with sscanf_s."); + } + goto done; + } + strTok = strtok_s(nullptr, ",", &context); + } + } + } + done: + PAL_free(filesystemType); + PAL_free(options); + free(line); + if (mountinfofile) + fclose(mountinfofile); + return mountpath; + } + + char* FindCGroupPathForMemorySubsystem() + { + char *line = nullptr; + size_t lineLen = 0; + size_t maxLineLen = 0; + char *subsystem_list = nullptr; + char *cgroup_path = nullptr; + bool result = false; + + FILE *cgroupfile = fopen(PROC_CGROUP_FILENAME, "r"); + if (cgroupfile == nullptr) + goto done; + + while (!result && getline(&line, &lineLen, cgroupfile) != -1) + { + if (subsystem_list == nullptr || lineLen > maxLineLen) + { + PAL_free(subsystem_list); + PAL_free(cgroup_path); + subsystem_list = (char*)PAL_malloc(lineLen+1); + if (subsystem_list == nullptr) + goto done; + cgroup_path = (char*)PAL_malloc(lineLen+1); + if (cgroup_path == nullptr) + goto done; + maxLineLen = lineLen; + } + + // See man page of proc to get format for /proc/self/cgroup file + int sscanfRet = sscanf_s(line, + "%*[^:]:%[^:]:%s", + subsystem_list, lineLen+1, + cgroup_path, lineLen+1); + if (sscanfRet != 2) + { + _ASSERTE(!"Failed to parse cgroup info file contents with sscanf_s."); + goto done; + } + + char* context = nullptr; + char* strTok = strtok_s(subsystem_list, ",", &context); + while (strTok != nullptr) + { + if (strncmp("memory", strTok, 6) == 0) + { + result = true; + break; + } + strTok = strtok_s(nullptr, ",", &context); + } + } + done: + PAL_free(subsystem_list); + if (!result) + { + PAL_free(cgroup_path); + cgroup_path = nullptr; + } + free(line); + if (cgroupfile) + fclose(cgroupfile); + return cgroup_path; + } + + bool ReadMemoryValueFromFile(const char* filename, size_t* val) + { + bool result = false; + char *line = nullptr; + size_t lineLen = 0; + char* endptr = nullptr; + size_t num = 0, l, multiplier; + + if (val == nullptr) + return false; + + FILE* file = fopen(filename, "r"); + if (file == nullptr) + goto done; + + if (getline(&line, &lineLen, file) == -1) + goto done; + + errno = 0; + num = strtoull(line, &endptr, 0); + if (errno != 0) + goto done; + + multiplier = 1; + switch(*endptr) + { + case 'g': + case 'G': multiplier = 1024; + case 'm': + case 'M': multiplier = multiplier*1024; + case 'k': + case 'K': multiplier = multiplier*1024; + } + + *val = num * multiplier; + result = true; + if (*val/multiplier != num) + result = false; + done: + if (file) + fclose(file); + free(line); + return result; + } +}; + + +size_t +PALAPI +PAL_GetRestrictedPhysicalMemoryLimit() +{ + CGroup cgroup; + size_t physical_memory_limit; + + if (!cgroup.GetPhysicalMemoryLimit(&physical_memory_limit)) + physical_memory_limit = SIZE_T_MAX; + + struct rlimit curr_rlimit; + size_t rlimit_soft_limit = (size_t)RLIM_INFINITY; + if (getrlimit(RLIMIT_AS, &curr_rlimit) == 0) + { + rlimit_soft_limit = curr_rlimit.rlim_cur; + } + physical_memory_limit = min(physical_memory_limit, rlimit_soft_limit); + + // Ensure that limit is not greater than real memory size + long pages = sysconf(_SC_PHYS_PAGES); + if (pages != -1) + { + long pageSize = sysconf(_SC_PAGE_SIZE); + if (pageSize != -1) + { + physical_memory_limit = min(physical_memory_limit, + (size_t)pages * pageSize); + } + } + + if(physical_memory_limit == SIZE_T_MAX) + physical_memory_limit = 0; + return physical_memory_limit; +} + +BOOL +PALAPI +PAL_GetWorkingSetSize(size_t* val) +{ + BOOL result = false; + size_t linelen; + char* line = nullptr; + + if (val == nullptr) + return FALSE; + + FILE* file = fopen(PROC_STATM_FILENAME, "r"); + if (file != nullptr && getline(&line, &linelen, file) != -1) + { + char* context = nullptr; + char* strTok = strtok_s(line, " ", &context); + strTok = strtok_s(nullptr, " ", &context); + + errno = 0; + *val = strtoull(strTok, nullptr, 0); + if(errno == 0) + { + *val = *val * VIRTUAL_PAGE_SIZE; + result = true; + } + } + + if (file) + fclose(file); + free(line); + return result; +} diff --git a/src/pal/src/misc/dbgmsg.cpp b/src/pal/src/misc/dbgmsg.cpp index d6f173f160..5eb5ebf9ba 100644 --- a/src/pal/src/misc/dbgmsg.cpp +++ b/src/pal/src/misc/dbgmsg.cpp @@ -50,11 +50,7 @@ using namespace CorUnix; /* append mode file I/O is safer */ #define _PAL_APPEND_DBG_OUTPUT_ -#if defined(_PAL_APPEND_DBG_OUTPUT_) static const char FOPEN_FLAGS[] = "at"; -#else -static const char FOPEN_FLAGS[] = "wt"; -#endif /* number of ENTRY nesting levels to indicate with a '.' */ #define MAX_NESTING 50 diff --git a/src/pal/src/misc/environ.cpp b/src/pal/src/misc/environ.cpp index fed7b69f38..9fc13467c5 100644 --- a/src/pal/src/misc/environ.cpp +++ b/src/pal/src/misc/environ.cpp @@ -218,7 +218,13 @@ GetEnvironmentVariableW( } else if (size == 0) { - // handle error in GetEnvironmentVariableA + // If size is 0, it either means GetEnvironmentVariableA failed, or that + // it succeeded and the value of the variable is empty. Check GetLastError + // to determine which. If the call failed, we won't touch the buffer. + if (GetLastError() == ERROR_SUCCESS) + { + *lpBuffer = '\0'; + } } else { diff --git a/src/pal/src/misc/errorstrings.cpp b/src/pal/src/misc/errorstrings.cpp index 22443114ee..2c5243945c 100644 --- a/src/pal/src/misc/errorstrings.cpp +++ b/src/pal/src/misc/errorstrings.cpp @@ -76,7 +76,7 @@ ErrorString palErrorStrings[] = { ERROR_SEM_TIMEOUT, W("The semaphore timeout period has expired.\n") }, { ERROR_INSUFFICIENT_BUFFER, W("The data area passed to a system call is too small.\n") }, { ERROR_INVALID_NAME, W("The filename, directory name, or volume label syntax is incorrect.\n") }, - { ERROR_MOD_NOT_FOUND, W("The specified module could not be found.\n") }, + { ERROR_MOD_NOT_FOUND, W("The specified module or one of its dependencies could not be found.\n") }, { ERROR_PROC_NOT_FOUND, W("The specified procedure could not be found.\n") }, { ERROR_WAIT_NO_CHILDREN, W("There are no child processes to wait for.\n") }, { ERROR_NEGATIVE_SEEK, W("An attempt was made to move the file pointer before the beginning of the file.\n") }, diff --git a/src/pal/src/misc/sysinfo.cpp b/src/pal/src/misc/sysinfo.cpp index 3ccb35ab81..fff051818f 100644 --- a/src/pal/src/misc/sysinfo.cpp +++ b/src/pal/src/misc/sysinfo.cpp @@ -32,12 +32,20 @@ Revision History: #error Either sysctl or sysconf is required for GetSystemInfo. #endif +#if HAVE_SYSINFO +#include <sys/sysinfo.h> +#endif + #include <sys/param.h> #if HAVE_SYS_VMPARAM_H #include <sys/vmparam.h> #endif // HAVE_SYS_VMPARAM_H +#if HAVE_XSWDEV +#include <vm/vm_param.h> +#endif // HAVE_XSWDEV + #if HAVE_MACH_VM_TYPES_H #include <mach/vm_types.h> #endif // HAVE_MACH_VM_TYPES_H @@ -216,6 +224,8 @@ GlobalMemoryStatusEx( lpBuffer->ullAvailExtendedVirtual = 0; BOOL fRetVal = FALSE; + int mib[3]; + int rc; // Get the physical memory size #if HAVE_SYSCONF && HAVE__SC_PHYS_PAGES @@ -226,7 +236,6 @@ GlobalMemoryStatusEx( lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory; fRetVal = TRUE; #elif HAVE_SYSCTL - int mib[2]; int64_t physical_memory; size_t length; @@ -234,7 +243,7 @@ GlobalMemoryStatusEx( mib[0] = CTL_HW; mib[1] = HW_MEMSIZE; length = sizeof(INT64); - int rc = sysctl(mib, 2, &physical_memory, &length, NULL, 0); + rc = sysctl(mib, 2, &physical_memory, &length, NULL, 0); if (rc != 0) { ASSERT("sysctl failed for HW_MEMSIZE (%d)\n", errno); @@ -244,11 +253,65 @@ GlobalMemoryStatusEx( lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory; fRetVal = TRUE; } -#elif // HAVE_SYSINFO - // TODO: implement getting memory details via sysinfo. On Linux, it provides swap file details that - // we can use to fill in the xxxPageFile members. -#endif // HAVE_SYSCONF +#endif // HAVE_SYSCTL + + // Get swap file size, consider the ability to get the values optional + // (don't return FALSE from the GlobalMemoryStatusEx) +#if HAVE_XSW_USAGE + // This is available on OSX + struct xsw_usage xsu; + mib[0] = CTL_VM; + mib[1] = VM_SWAPUSAGE; + size_t length = sizeof(xsu); + rc = sysctl(mib, 2, &xsu, &length, NULL, 0); + if (rc == 0) + { + lpBuffer->ullTotalPageFile = xsu.xsu_total; + lpBuffer->ullAvailPageFile = xsu.xsu_avail; + } +#elif HAVE_XSWDEV + // E.g. FreeBSD + struct xswdev xsw; + + size_t length = 2; + rc = sysctlnametomib("vm.swap_info", mib, &length); + if (rc == 0) + { + int pagesize = getpagesize(); + // Aggregate the information for all swap files on the system + for (mib[2] = 0; ; mib[2]++) + { + length = sizeof(xsw); + rc = sysctl(mib, 3, &xsw, &length, NULL, 0); + if ((rc < 0) || (xsw.xsw_version != XSWDEV_VERSION)) + { + // All the swap files were processed or coreclr was built against + // a version of headers not compatible with the current XSWDEV_VERSION. + break; + } + + DWORDLONG avail = xsw.xsw_nblks - xsw.xsw_used; + lpBuffer->ullTotalPageFile += (DWORDLONG)xsw.xsw_nblks * pagesize; + lpBuffer->ullAvailPageFile += (DWORDLONG)avail * pagesize; + } + } +#elif HAVE_SYSINFO + // Linux + struct sysinfo info; + rc = sysinfo(&info); + if (rc == 0) + { + lpBuffer->ullTotalPageFile = info.totalswap; + lpBuffer->ullAvailPageFile = info.freeswap; +#if HAVE_SYSINFO_WITH_MEM_UNIT + // A newer version of the sysinfo structure represents all the sizes + // in mem_unit instead of bytes + lpBuffer->ullTotalPageFile *= info.mem_unit; + lpBuffer->ullAvailPageFile *= info.mem_unit; +#endif // HAVE_SYSINFO_WITH_MEM_UNIT + } +#endif // HAVE_SYSINFO // Get the physical memory in use - from it, we can get the physical memory available. // We do this only when we have the total physical memory available. diff --git a/src/pal/src/misc/time.cpp b/src/pal/src/misc/time.cpp index 918f92a90f..d16fb587ba 100644 --- a/src/pal/src/misc/time.cpp +++ b/src/pal/src/misc/time.cpp @@ -394,3 +394,35 @@ EXIT: return retval; } +/*++ +Function: + PAL_nanosleep + +Sleeps for the time specified in timeInNs. +Returns 0 on successful completion of the operation. +--*/ +PALAPI +INT +PAL_nanosleep( + IN long timeInNs + ) +{ + struct timespec req; + struct timespec rem; + int result; + + req.tv_sec = 0; + req.tv_nsec = timeInNs; + + do + { + // Sleep for the requested time. + result = nanosleep(&req, &rem); + + // Save the remaining time (used if the loop runs another iteration). + req = rem; + } + while(result == -1 && errno == EINTR); + + return result; +} diff --git a/src/pal/src/misc/utils.cpp b/src/pal/src/misc/utils.cpp index f0ff63439f..4eefd749ed 100644 --- a/src/pal/src/misc/utils.cpp +++ b/src/pal/src/misc/utils.cpp @@ -124,7 +124,12 @@ BOOL UTIL_IsExecuteBitsSet( struct stat * stat_data ) } /* Check for read permissions. */ - if ( stat_data->st_uid == geteuid() ) + if ( 0 == geteuid() ) + { + /* The process owner is root */ + bRetVal = TRUE; + } + else if ( stat_data->st_uid == geteuid() ) { /* The process owner is the file owner as well. */ if ( ( stat_data->st_mode & S_IXUSR ) ) diff --git a/src/pal/src/misc/version.cpp b/src/pal/src/misc/version.cpp deleted file mode 100644 index 7a9f90a320..0000000000 --- a/src/pal/src/misc/version.cpp +++ /dev/null @@ -1,119 +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: - - version.c - -Abstract: - - Implementation of functions for getting platform.OS versions. - -Revision History: - - - ---*/ - -#include "pal/palinternal.h" -#include "pal/dbgmsg.h" - -SET_DEFAULT_DEBUG_CHANNEL(MISC); - -/*++ -Function: - GetVersionExA - - - -GetVersionEx - -The GetVersionEx function obtains extended information about the -version of the operating system that is currently running. - -Parameters - -lpVersionInfo - [in/out] Pointer to an OSVERSIONINFO data structure that the - function fills with operating system version information. - - Before calling the GetVersionEx function, set the - dwOSVersionInfoSize member of the OSVERSIONINFO data structure - to sizeof(OSVERSIONINFO). - -Return Values - -If the function succeeds, the return value is a nonzero value. - -If the function fails, the return value is zero. To get extended error -information, call GetLastError. The function fails if you specify an -invalid value for the dwOSVersionInfoSize member of the OSVERSIONINFO -structure. - ---*/ -BOOL -PALAPI -GetVersionExA( - IN OUT LPOSVERSIONINFOA lpVersionInformation) -{ - BOOL bRet = TRUE; - PERF_ENTRY(GetVersionExA); - ENTRY("GetVersionExA (lpVersionInformation=%p)\n", lpVersionInformation); - - if (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA)) - { - lpVersionInformation->dwMajorVersion = 5; /* same as WIN2000 */ - lpVersionInformation->dwMinorVersion = 0; /* same as WIN2000 */ - lpVersionInformation->dwBuildNumber = 0; - lpVersionInformation->dwPlatformId = VER_PLATFORM_UNIX; - lpVersionInformation->szCSDVersion[0] = '\0'; /* no service pack */ - } - else - { - SetLastError(ERROR_INVALID_PARAMETER); - bRet = FALSE; - } - LOGEXIT("GetVersionExA returning BOOL %d\n", bRet); - PERF_EXIT(GetVersionExA); - return bRet; -} - - -/*++ -Function: - GetVersionExW - -See GetVersionExA ---*/ -BOOL -PALAPI -GetVersionExW( - IN OUT LPOSVERSIONINFOW lpVersionInformation) -{ - BOOL bRet = TRUE; - - PERF_ENTRY(GetVersionExW); - ENTRY("GetVersionExW (lpVersionInformation=%p)\n", lpVersionInformation); - - if (lpVersionInformation->dwOSVersionInfoSize == sizeof(OSVERSIONINFOW)) - { - lpVersionInformation->dwMajorVersion = 5; /* same as WIN2000 */ - lpVersionInformation->dwMinorVersion = 0; /* same as WIN2000 */ - lpVersionInformation->dwBuildNumber = 0; - lpVersionInformation->dwPlatformId = VER_PLATFORM_UNIX; - lpVersionInformation->szCSDVersion[0] = '\0'; /* no service pack */ - } - else - { - SetLastError(ERROR_INVALID_PARAMETER); - bRet = FALSE; - } - LOGEXIT("GetVersionExW returning BOOL %d\n", bRet); - PERF_EXIT(GetVersionExW); - return bRet; -} diff --git a/src/pal/src/thread/context.cpp b/src/pal/src/thread/context.cpp index 0449df568b..04a6fe5aaf 100644 --- a/src/pal/src/thread/context.cpp +++ b/src/pal/src/thread/context.cpp @@ -127,6 +127,8 @@ typedef int __ptrace_request; ASSIGN_REG(R12) #elif defined(_ARM64_) #define ASSIGN_CONTROL_REGS \ + ASSIGN_REG(Cpsr) \ + ASSIGN_REG(Fp) \ ASSIGN_REG(Sp) \ ASSIGN_REG(Lr) \ ASSIGN_REG(Pc) @@ -499,11 +501,13 @@ void CONTEXTFromNativeContext(const native_context_t *native, LPCONTEXT lpContex if ((contextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) { ASSIGN_CONTROL_REGS -#ifdef _ARM_ +#if defined(_ARM_) // WinContext assumes that the least bit of Pc is always 1 (denoting thumb) // although the pc value retrived from native context might not have set the least bit. // This becomes especially problematic if the context is on the JIT_WRITEBARRIER. lpContext->Pc |= 0x1; +#elif defined(_X86_) + lpContext->ResumeEsp = MCREG_Esp(native->uc_mcontext); #endif } @@ -613,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. @@ -945,6 +978,7 @@ CONTEXT_GetThreadContextFromThreadState( lpContext->Esi = pState->esi; lpContext->Ebp = pState->ebp; lpContext->Esp = pState->esp; + lpContext->ResumeEsp = pState->esp; lpContext->SegSs = pState->ss; lpContext->EFlags = pState->eflags; lpContext->Eip = pState->eip; diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp index ae069aec86..050665ce7c 100644 --- a/src/pal/src/thread/process.cpp +++ b/src/pal/src/thread/process.cpp @@ -2666,6 +2666,12 @@ CreateProcessModules( // VM_ALLOCATE 0000000105bac000-0000000105bad000 [ 4K] r--/rw- SM=SHM // MALLOC (admin) 0000000105bad000-0000000105bae000 [ 4K] r--/rwx SM=ZER // MALLOC 0000000105bae000-0000000105baf000 [ 4K] rw-/rwx SM=ZER + + // OS X Sierra (10.12.4 Beta) + // REGION TYPE START - END [ VSIZE RSDNT DIRTY SWAP] PRT/MAX SHRMOD PURGE REGION DETAIL + // Stack 00007fff5a930000-00007fff5b130000 [ 8192K 32K 32K 0K] rw-/rwx SM=PRV thread 0 + // __TEXT 00007fffa4a0b000-00007fffa4a0d000 [ 8K 8K 0K 0K] r-x/r-x SM=COW /usr/lib/libSystem.B.dylib + // __TEXT 00007fffa4bbe000-00007fffa4c15000 [ 348K 348K 0K 0K] r-x/r-x SM=COW /usr/lib/libc++.1.dylib char *line = NULL; size_t lineLen = 0; int count = 0; @@ -2686,9 +2692,8 @@ CreateProcessModules( { void *startAddress, *endAddress; char moduleName[PATH_MAX]; - int size; - if (sscanf_s(line, "__TEXT %p-%p [ %dK] %*[-/rwxsp] SM=%*[A-Z] %s\n", &startAddress, &endAddress, &size, moduleName, _countof(moduleName)) == 4) + if (sscanf_s(line, "__TEXT %p-%p [ %*[0-9K ]] %*[-/rwxsp] SM=%*[A-Z] %s\n", &startAddress, &endAddress, moduleName, _countof(moduleName)) == 3) { bool dup = false; for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) 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. |