clr_unknown_arch()
endif()
set(CLR_CMAKE_PLATFORM_LINUX 1)
+
+ # Detect Alpine Linux
+ SET(OS_RELEASE_FILENAME "/etc/os-release")
+ if (EXISTS ${OS_RELEASE_FILENAME})
+ file(READ ${OS_RELEASE_FILENAME} OS_RELEASE)
+ string(FIND "${OS_RELEASE}" "ID=alpine" CLR_CMAKE_PLATFORM_ALPINE_LINUX)
+ if(CLR_CMAKE_PLATFORM_ALPINE_LINUX EQUAL -1)
+ unset(CLR_CMAKE_PLATFORM_ALPINE_LINUX)
+ endif(CLR_CMAKE_PLATFORM_ALPINE_LINUX EQUAL -1)
+ endif(EXISTS ${OS_RELEASE_FILENAME})
endif(CMAKE_SYSTEM_NAME STREQUAL Linux)
if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
add_subdirectory(tests)
endif(CLR_CMAKE_BUILD_TESTS)
-include(definitionsconsistencycheck.cmake)
\ No newline at end of file
+include(definitionsconsistencycheck.cmake)
endif(CLR_CMAKE_PLATFORM_UNIX)
+if(CLR_CMAKE_PLATFORM_ALPINE_LINUX)
+ # Alpine Linux doesn't have fixed stack limit, this define disables some stack pointer
+ # sanity checks in debug / checked build that rely on a fixed stack limit
+ add_definitions(-DNO_FIXED_STACK_LIMIT)
+endif(CLR_CMAKE_PLATFORM_ALPINE_LINUX)
+
add_definitions(-D_BLD_CLR)
add_definitions(-DDEBUGGING_SUPPORTED)
add_definitions(-DPROFILING_SUPPORTED)
include_directories(SYSTEM /usr/local/include)
-# set kernel version to detect Alpine
-EXEC_PROGRAM(uname ARGS -v OUTPUT_VARIABLE CMAKE_SYSTEM_KERNEL_VERSION)
-string(FIND "${CMAKE_SYSTEM_KERNEL_VERSION}" "Alpine" PAL_SYSTEM_ALPINE)
-if(PAL_SYSTEM_ALPINE EQUAL -1)
- unset(PAL_SYSTEM_ALPINE)
-endif()
-
include(configure.cmake)
project(coreclrpal)
add_definitions(-D_WIN64=1)
endif()
+if(CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CLR_CMAKE_PLATFORM_ALPINE_LINUX)
+ # Currently the _xstate is not available on Alpine Linux
+ add_definitions(-DXSTATE_SUPPORTED)
+endif(CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CLR_CMAKE_PLATFORM_ALPINE_LINUX)
+
+if(CLR_CMAKE_PLATFORM_ALPINE_LINUX)
+ # Setting RLIMIT_NOFILE breaks debugging of coreclr on Alpine Linux for some reason
+ add_definitions(-DDONT_SET_RLIMIT_NOFILE)
+endif(CLR_CMAKE_PLATFORM_ALPINE_LINUX)
+
# turn off capability to remove unused functions (which was enabled in debug build with sanitizers)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -Wl,--no-gc-sections")
find_library(UNWIND_ARCH NAMES unwind-x86_64)
endif()
- if(PAL_SYSTEM_ALPINE)
+ if(CLR_CMAKE_PLATFORM_ALPINE_LINUX)
find_library(INTL intl)
endif()
#endif
}
+extern int g_common_signal_handler_context_locvar_offset;
+
BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
{
int st;
unw_context_t unwContext;
unw_cursor_t cursor;
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(_ARM64_) || defined(_ARM_)
- DWORD64 curPc;
-#endif
+ DWORD64 curPc = CONTEXTGetPC(context);
+
+#ifndef __APPLE__
+ // Check if the PC is the return address from the SEHProcessException in the common_signal_handler.
+ // If that's the case, extract its local variable containing the native_context_t of the hardware
+ // exception and return that. This skips the hardware signal handler trampoline that the libunwind
+ // cannot cross on some systems.
+ if ((void*)curPc == g_SEHProcessExceptionReturnAddress)
+ {
+ ULONG contextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_EXCEPTION_ACTIVE;
+
+ #if defined(_AMD64_)
+ contextFlags |= CONTEXT_XSTATE;
+ #endif
+ size_t nativeContext = *(size_t*)(CONTEXTGetFP(context) + g_common_signal_handler_context_locvar_offset);
+ CONTEXTFromNativeContext((const native_context_t *)nativeContext, context, contextFlags);
+
+ return TRUE;
+ }
+#endif
if ((context->ContextFlags & CONTEXT_EXCEPTION_ACTIVE) != 0)
{
// So we compensate it by incrementing the PC before passing it to the unwinder.
// Without it, the unwinder would not find unwind info if the hardware exception
// happened in the first instruction of a function.
- CONTEXTSetPC(context, CONTEXTGetPC(context) + 1);
+ CONTEXTSetPC(context, curPc + 1);
}
#if !UNWIND_CONTEXT_IS_UCONTEXT_T
WinContextToUnwindCursor(context, &cursor);
#endif
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(_ARM64_) || defined(_ARM_)
- // FreeBSD, NetBSD and OSX appear to do two different things when unwinding
- // 1: If it reaches where it cannot unwind anymore, say a
- // managed frame. It wil return 0, but also update the $pc
- // 2: If it unwinds all the way to _start it will return
- // 0 from the step, but $pc will stay the same.
- // The behaviour of libunwind from nongnu.org is to null the PC
- // So we bank the original PC here, so we can compare it after
- // the step
- curPc = CONTEXTGetPC(context);
-#endif
-
st = unw_step(&cursor);
if (st < 0)
{
// Update the passed in windows context to reflect the unwind
//
UnwindContextToWinContext(&cursor, context);
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(_ARM64_) || defined(_ARM_)
+
+ // FreeBSD, NetBSD, OSX and Alpine appear to do two different things when unwinding
+ // 1: If it reaches where it cannot unwind anymore, say a
+ // managed frame. It will return 0, but also update the $pc
+ // 2: If it unwinds all the way to _start it will return
+ // 0 from the step, but $pc will stay the same.
+ // So we detect that here and set the $pc to NULL in that case.
+ // This is the default behavior of the libunwind on Linux.
if (st == 0 && CONTEXTGetPC(context) == curPc)
{
CONTEXTSetPC(context, 0);
}
-#endif
if (contextPointers != NULL)
{
PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode = NULL;
+// Return address of the SEHProcessException, which is used to enable walking over
+// the signal handler trampoline on some Unixes where the libunwind cannot do that.
+void* g_SEHProcessExceptionReturnAddress = NULL;
+
/* Internal function definitions **********************************************/
/*++
BOOL
SEHProcessException(PAL_SEHException* exception)
{
+ g_SEHProcessExceptionReturnAddress = __builtin_return_address(0);
+
CONTEXT* contextRecord = exception->GetContextRecord();
EXCEPTION_RECORD* exceptionRecord = exception->GetExceptionRecord();
struct sigaction g_previous_activation;
#endif
+// 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;
+
/* public function definitions ************************************************/
/*++
the "pointers" parameter should contain a valid exception record pointer,
but the ContextRecord pointer will be overwritten.
--*/
+__attribute__((noinline))
static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...)
{
sigset_t signal_set;
native_context_t *ucontext;
ucontext = (native_context_t *)sigcontext;
+ g_common_signal_handler_context_locvar_offset = (int)((char*)&ucontext - (char*)__builtin_frame_address(0));
AllocateExceptionRecords(&exceptionRecord, &contextRecord);
/////////////////////
// Extended state
+#ifdef XSTATE_SUPPORTED
+
inline _fpx_sw_bytes *FPREG_FpxSwBytes(const ucontext_t *uc)
{
// Bytes 464..511 in the FXSAVE format are available for software to use for any purpose. In this case, they are used to
return reinterpret_cast<_xstate *>(FPREG_Fpstate(uc))->ymmh.ymmh_space;
}
+#endif // XSTATE_SUPPORTED
+
/////////////////////
#else // BIT64
#endif
}
+inline static DWORD64 CONTEXTGetFP(LPCONTEXT pContext)
+{
+#if defined(_AMD64_)
+ return pContext->Rbp;
+#elif defined(_ARM_)
+ return pContext->R7;
+#elif defined(_ARM64_)
+ return pContext->X29;
+#else
+#error don't know how to get the frame pointer for this architecture
+#endif
+}
+
/*++
Function :
CONTEXT_CaptureContext
--*/
static BOOL INIT_IncreaseDescriptorLimit(void)
{
+#ifndef DONT_SET_RLIMIT_NOFILE
struct rlimit rlp;
int result;
{
return FALSE;
}
-
+#endif // !DONT_SET_RLIMIT_NOFILE
return TRUE;
}
}
// TODO: Enable for all Unix systems
-#if defined(_AMD64_) && defined(__linux__)
+#if defined(_AMD64_) && defined(XSTATE_SUPPORTED)
if ((lpContext->ContextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE)
{
_ASSERTE(FPREG_HasExtendedState(native));
memcpy_s(FPREG_Xstate_Ymmh(native), sizeof(M128A) * 16, lpContext->VectorRegister, sizeof(M128A) * 16);
}
-#endif // _AMD64_
+#endif //_AMD64_ && XSTATE_SUPPORTED
}
/*++
#endif
}
- // TODO: Enable for all Unix systems
-#if defined(_AMD64_) && defined(__linux__)
+#ifdef _AMD64_
if ((contextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE)
{
+ // TODO: Enable for all Unix systems
+#if XSTATE_SUPPORTED
if (FPREG_HasExtendedState(native))
{
memcpy_s(lpContext->VectorRegister, sizeof(M128A) * 16, FPREG_Xstate_Ymmh(native), sizeof(M128A) * 16);
}
else
+#endif // XSTATE_SUPPORTED
{
// Reset the CONTEXT_XSTATE bit(s) so it's clear that the extended state data in
// the CONTEXT is not valid.
const ULONG xstateFlags = CONTEXT_XSTATE & ~(CONTEXT_CONTROL & CONTEXT_INTEGER);
lpContext->ContextFlags &= ~xstateFlags;
}
- }
+ }
#endif // _AMD64_
}
// to recover from AVs during profiler stackwalk.)
PTR_VOID newSP = PTR_VOID((TADDR)GetRegdisplaySP(m_crawl.pRD));
+#ifndef NO_FIXED_STACK_LIMIT
FAIL_IF_SPECULATIVE_WALK(newSP >= m_crawl.pThread->GetCachedStackLimit());
+#endif // !NO_FIXED_STACK_LIMIT
FAIL_IF_SPECULATIVE_WALK(newSP < m_crawl.pThread->GetCachedStackBase());
#undef FAIL_IF_SPECULATIVE_WALK
{
if (pRD->SP && pRD->_pThread)
{
+#ifndef NO_FIXED_STACK_LIMIT
_ASSERTE(PTR_VOID(pRD->SP) >= pRD->_pThread->GetCachedStackLimit());
+#endif // NO_FIXED_STACK_LIMIT
_ASSERTE(PTR_VOID(pRD->SP) < pRD->_pThread->GetCachedStackBase());
}
}