From 867ca0cdb6f40cb7b2f14baf138bd25cf6bfb30f Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 18 Sep 2015 15:57:49 +0200 Subject: [PATCH] Implement runtime suspension for OSX This change implements runtime suspension for OSX using activation injection, the same mechanism as Linux uses. I have modified the activation injection mechanism by adding a new function that CoreCLR registers with PAL and PAL calls it to check if an instruction address is safe for injection. The injection is performed only if this new function returns true. The activation on OSX is performed by suspending the target thread, checking the instruction address in the suspended thread's context using the above mentioned function and modifying the context to perform the injection only at injection safe place. --- CMakeLists.txt | 4 +- src/pal/inc/pal.h | 4 +- src/pal/src/CMakeLists.txt | 1 + .../src/arch/i386/activationhandlerwrapper.S | 31 +++++ src/pal/src/arch/i386/asmconstants.h | 105 ++++++++++++++++ src/pal/src/arch/i386/context2.S | 104 ++-------------- .../src/arch/i386/dispatchexceptionwrapper.S | 113 +++--------------- src/pal/src/debug/debug.cpp | 2 - src/pal/src/exception/machexception.cpp | 111 ++++++++++++++++- src/pal/src/exception/signal.cpp | 90 ++------------ src/pal/src/include/pal/context.h | 2 - src/pal/src/include/pal/thread.hpp | 18 +++ src/pal/src/thread/context.cpp | 4 - src/pal/src/thread/process.cpp | 3 +- src/pal/src/thread/thread.cpp | 99 ++++++++++++++- src/pal/src/thread/threadsusp.cpp | 87 +++++++++----- src/vm/threadsuspend.cpp | 14 ++- 17 files changed, 470 insertions(+), 322 deletions(-) create mode 100644 src/pal/src/arch/i386/activationhandlerwrapper.S create mode 100644 src/pal/src/arch/i386/asmconstants.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dbb3c85495d..543e4ffb239b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -545,9 +545,7 @@ if(WIN32) endif(WIN32) add_definitions(-DFEATURE_EXCEPTIONDISPATCHINFO) add_definitions(-DFEATURE_FRAMEWORK_INTERNAL) -if(WIN32 OR (CLR_CMAKE_PLATFORM_LINUX AND CLR_CMAKE_PLATFORM_UNIX_TARGET_AMD64)) - add_definitions(-DFEATURE_HIJACK) -endif(WIN32 OR (CLR_CMAKE_PLATFORM_LINUX AND CLR_CMAKE_PLATFORM_UNIX_TARGET_AMD64)) +add_definitions(-DFEATURE_HIJACK) add_definitions(-DFEATURE_HOST_ASSEMBLY_RESOLVER) add_definitions(-DFEATURE_HOSTED_BINDER) if(WIN32) diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index 26252c264260..6f88513d2909 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -5446,12 +5446,14 @@ PALAPI FlushProcessWriteBuffers(); typedef void (*PAL_ActivationFunction)(CONTEXT *context); +typedef BOOL (*PAL_SafeActivationCheckFunction)(SIZE_T ip); PALIMPORT VOID PALAPI PAL_SetActivationFunction( - IN PAL_ActivationFunction pActivationFunction); + IN PAL_ActivationFunction pActivationFunction, + IN PAL_SafeActivationCheckFunction pSafeActivationCheckFunction); PALIMPORT BOOL diff --git a/src/pal/src/CMakeLists.txt b/src/pal/src/CMakeLists.txt index c814e10aac1d..854655621f53 100644 --- a/src/pal/src/CMakeLists.txt +++ b/src/pal/src/CMakeLists.txt @@ -40,6 +40,7 @@ endif() if(CMAKE_SYSTEM_NAME STREQUAL Darwin) add_definitions(-D_TARGET_MAC64) set(PLATFORM_SOURCES + arch/i386/activationhandlerwrapper.S arch/i386/context.S arch/i386/dispatchexceptionwrapper.S exception/machexception.cpp diff --git a/src/pal/src/arch/i386/activationhandlerwrapper.S b/src/pal/src/arch/i386/activationhandlerwrapper.S new file mode 100644 index 000000000000..06151b3d12f4 --- /dev/null +++ b/src/pal/src/arch/i386/activationhandlerwrapper.S @@ -0,0 +1,31 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +.intel_syntax noprefix +#include "unixasmmacros.inc" +#include "asmconstants.h" + +#ifdef BIT64 +// Offset of the return address from the ActivationHandler in the ActivationHandlerWrapper +.globl C_FUNC(ActivationHandlerReturnOffset) +C_FUNC(ActivationHandlerReturnOffset): + .int LOCAL_LABEL(ActivationHandlerReturn)-C_FUNC(ActivationHandlerWrapper) + +NESTED_ENTRY ActivationHandlerWrapper, _TEXT, NoHandler + push_nonvol_reg rbp + mov rbp, rsp + set_cfa_register rbp, 0 + alloc_stack (CONTEXT_Size) + mov rdi, rsp + int3 + call C_FUNC(ActivationHandler) +LOCAL_LABEL(ActivationHandlerReturn): + int3 + free_stack (CONTEXT_Size) + pop_nonvol_reg rbp + ret +NESTED_END ActivationHandlerWrapper, _TEXT + +#endif // BIT64 diff --git a/src/pal/src/arch/i386/asmconstants.h b/src/pal/src/arch/i386/asmconstants.h new file mode 100644 index 000000000000..575edc2308d0 --- /dev/null +++ b/src/pal/src/arch/i386/asmconstants.h @@ -0,0 +1,105 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#ifdef BIT64 + +#define CONTEXT_AMD64 0x100000 + +#define CONTEXT_CONTROL 1 // SegSs, Rsp, SegCs, Rip, and EFlags +#define CONTEXT_INTEGER 2 // Rax, Rcx, Rdx, Rbx, Rbp, Rsi, Rdi, R8-R15 +#define CONTEXT_SEGMENTS 4 // SegDs, SegEs, SegFs, SegGs +#define CONTEXT_FLOATING_POINT 8 +#define CONTEXT_DEBUG_REGISTERS 16 // Dr0-Dr3 and Dr6-Dr7 + +#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) + +#define CONTEXT_ContextFlags 6*8 +#define CONTEXT_SegCs CONTEXT_ContextFlags+8 +#define CONTEXT_SegDs CONTEXT_SegCs+2 +#define CONTEXT_SegEs CONTEXT_SegDs+2 +#define CONTEXT_SegFs CONTEXT_SegEs+2 +#define CONTEXT_SegGs CONTEXT_SegFs+2 +#define CONTEXT_SegSs CONTEXT_SegGs+2 +#define CONTEXT_EFlags CONTEXT_SegSs+2 +#define CONTEXT_Dr0 CONTEXT_EFlags+4 +#define CONTEXT_Dr1 CONTEXT_Dr0+8 +#define CONTEXT_Dr2 CONTEXT_Dr1+8 +#define CONTEXT_Dr3 CONTEXT_Dr2+8 +#define CONTEXT_Dr6 CONTEXT_Dr3+8 +#define CONTEXT_Dr7 CONTEXT_Dr6+8 +#define CONTEXT_Rax CONTEXT_Dr7+8 +#define CONTEXT_Rcx CONTEXT_Rax+8 +#define CONTEXT_Rdx CONTEXT_Rcx+8 +#define CONTEXT_Rbx CONTEXT_Rdx+8 +#define CONTEXT_Rsp CONTEXT_Rbx+8 +#define CONTEXT_Rbp CONTEXT_Rsp+8 +#define CONTEXT_Rsi CONTEXT_Rbp+8 +#define CONTEXT_Rdi CONTEXT_Rsi+8 +#define CONTEXT_R8 CONTEXT_Rdi+8 +#define CONTEXT_R9 CONTEXT_R8+8 +#define CONTEXT_R10 CONTEXT_R9+8 +#define CONTEXT_R11 CONTEXT_R10+8 +#define CONTEXT_R12 CONTEXT_R11+8 +#define CONTEXT_R13 CONTEXT_R12+8 +#define CONTEXT_R14 CONTEXT_R13+8 +#define CONTEXT_R15 CONTEXT_R14+8 +#define CONTEXT_Rip CONTEXT_R15+8 +#define CONTEXT_FltSave CONTEXT_Rip+8 +#define FLOATING_SAVE_AREA_SIZE 4*8+24*16+96 +#define CONTEXT_Xmm0 CONTEXT_FltSave+FLOATING_SAVE_AREA_SIZE // was 10*16 +#define CONTEXT_Xmm1 CONTEXT_Xmm0+16 +#define CONTEXT_Xmm2 CONTEXT_Xmm1+16 +#define CONTEXT_Xmm3 CONTEXT_Xmm2+16 +#define CONTEXT_Xmm4 CONTEXT_Xmm3+16 +#define CONTEXT_Xmm5 CONTEXT_Xmm4+16 +#define CONTEXT_Xmm6 CONTEXT_Xmm5+16 +#define CONTEXT_Xmm7 CONTEXT_Xmm6+16 +#define CONTEXT_Xmm8 CONTEXT_Xmm7+16 +#define CONTEXT_Xmm9 CONTEXT_Xmm8+16 +#define CONTEXT_Xmm10 CONTEXT_Xmm9+16 +#define CONTEXT_Xmm11 CONTEXT_Xmm10+16 +#define CONTEXT_Xmm12 CONTEXT_Xmm11+16 +#define CONTEXT_Xmm13 CONTEXT_Xmm12+16 +#define CONTEXT_Xmm14 CONTEXT_Xmm13+16 +#define CONTEXT_Xmm15 CONTEXT_Xmm14+16 +#define CONTEXT_VectorRegister CONTEXT_Xmm15+16 +#define CONTEXT_VectorControl CONTEXT_VectorRegister+16*26 +#define CONTEXT_DebugControl CONTEXT_VectorControl+8 +#define CONTEXT_LastBranchToRip CONTEXT_DebugControl+8 +#define CONTEXT_LastBranchFromRip CONTEXT_LastBranchToRip+8 +#define CONTEXT_LastExceptionToRip CONTEXT_LastBranchFromRip+8 +#define CONTEXT_LastExceptionFromRip CONTEXT_LastExceptionToRip+8 +#define CONTEXT_Size CONTEXT_LastExceptionFromRip+8 + +#else // BIT64 + +#define CONTEXT_ContextFlags 0 +#define CONTEXT_FLOATING_POINT 8 +#define CONTEXT_FloatSave 7*4 +#define FLOATING_SAVE_AREA_SIZE 8*4+80 +#define CONTEXT_Edi CONTEXT_FloatSave + FLOATING_SAVE_AREA_SIZE + 4*4 +#define CONTEXT_Esi CONTEXT_Edi+4 +#define CONTEXT_Ebx CONTEXT_Esi+4 +#define CONTEXT_Edx CONTEXT_Ebx+4 +#define CONTEXT_Ecx CONTEXT_Edx+4 +#define CONTEXT_Eax CONTEXT_Ecx+4 +#define CONTEXT_Ebp CONTEXT_Eax+4 +#define CONTEXT_Eip CONTEXT_Ebp+4 +#define CONTEXT_SegCs CONTEXT_Eip+4 +#define CONTEXT_EFlags CONTEXT_SegCs+4 +#define CONTEXT_Esp CONTEXT_EFlags+4 +#define CONTEXT_SegSs CONTEXT_Esp+4 +#define CONTEXT_EXTENDED_REGISTERS 32 +#define CONTEXT_ExtendedRegisters CONTEXT_SegSs+4 +#define CONTEXT_Xmm0 CONTEXT_ExtendedRegisters+160 +#define CONTEXT_Xmm1 CONTEXT_Xmm0+16 +#define CONTEXT_Xmm2 CONTEXT_Xmm1+16 +#define CONTEXT_Xmm3 CONTEXT_Xmm2+16 +#define CONTEXT_Xmm4 CONTEXT_Xmm3+16 +#define CONTEXT_Xmm5 CONTEXT_Xmm4+16 +#define CONTEXT_Xmm6 CONTEXT_Xmm5+16 +#define CONTEXT_Xmm7 CONTEXT_Xmm6+16 + +#endif // BIT64 diff --git a/src/pal/src/arch/i386/context2.S b/src/pal/src/arch/i386/context2.S index 27875b722ae4..e6b0c2fe39dd 100644 --- a/src/pal/src/arch/i386/context2.S +++ b/src/pal/src/arch/i386/context2.S @@ -10,76 +10,10 @@ .intel_syntax noprefix #include "unixasmmacros.inc" +#include "asmconstants.h" #ifdef BIT64 -#define CONTEXT_AMD64 0x100000 - -#define CONTEXT_CONTROL 1 // SegSs, Rsp, SegCs, Rip, and EFlags -#define CONTEXT_INTEGER 2 // Rax, Rcx, Rdx, Rbx, Rbp, Rsi, Rdi, R8-R15 -#define CONTEXT_SEGMENTS 4 // SegDs, SegEs, SegFs, SegGs -#define CONTEXT_FLOATING_POINT 8 -#define CONTEXT_DEBUG_REGISTERS 16 // Dr0-Dr3 and Dr6-Dr7 - -#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) - -#define CONTEXT_ContextFlags 6*8 -#define CONTEXT_SegCs CONTEXT_ContextFlags+8 -#define CONTEXT_SegDs CONTEXT_SegCs+2 -#define CONTEXT_SegEs CONTEXT_SegDs+2 -#define CONTEXT_SegFs CONTEXT_SegEs+2 -#define CONTEXT_SegGs CONTEXT_SegFs+2 -#define CONTEXT_SegSs CONTEXT_SegGs+2 -#define CONTEXT_EFlags CONTEXT_SegSs+2 -#define CONTEXT_Dr0 CONTEXT_EFlags+4 -#define CONTEXT_Dr1 CONTEXT_Dr0+8 -#define CONTEXT_Dr2 CONTEXT_Dr1+8 -#define CONTEXT_Dr3 CONTEXT_Dr2+8 -#define CONTEXT_Dr6 CONTEXT_Dr3+8 -#define CONTEXT_Dr7 CONTEXT_Dr6+8 -#define CONTEXT_Rax CONTEXT_Dr7+8 -#define CONTEXT_Rcx CONTEXT_Rax+8 -#define CONTEXT_Rdx CONTEXT_Rcx+8 -#define CONTEXT_Rbx CONTEXT_Rdx+8 -#define CONTEXT_Rsp CONTEXT_Rbx+8 -#define CONTEXT_Rbp CONTEXT_Rsp+8 -#define CONTEXT_Rsi CONTEXT_Rbp+8 -#define CONTEXT_Rdi CONTEXT_Rsi+8 -#define CONTEXT_R8 CONTEXT_Rdi+8 -#define CONTEXT_R9 CONTEXT_R8+8 -#define CONTEXT_R10 CONTEXT_R9+8 -#define CONTEXT_R11 CONTEXT_R10+8 -#define CONTEXT_R12 CONTEXT_R11+8 -#define CONTEXT_R13 CONTEXT_R12+8 -#define CONTEXT_R14 CONTEXT_R13+8 -#define CONTEXT_R15 CONTEXT_R14+8 -#define CONTEXT_Rip CONTEXT_R15+8 -#define CONTEXT_FltSave CONTEXT_Rip+8 -#define FLOATING_SAVE_AREA_SIZE 4*8+24*16+96 -#define CONTEXT_Xmm0 CONTEXT_FltSave+10*16 -#define CONTEXT_Xmm1 CONTEXT_Xmm0+16 -#define CONTEXT_Xmm2 CONTEXT_Xmm1+16 -#define CONTEXT_Xmm3 CONTEXT_Xmm2+16 -#define CONTEXT_Xmm4 CONTEXT_Xmm3+16 -#define CONTEXT_Xmm5 CONTEXT_Xmm4+16 -#define CONTEXT_Xmm6 CONTEXT_Xmm5+16 -#define CONTEXT_Xmm7 CONTEXT_Xmm6+16 -#define CONTEXT_Xmm8 CONTEXT_Xmm7+16 -#define CONTEXT_Xmm9 CONTEXT_Xmm8+16 -#define CONTEXT_Xmm10 CONTEXT_Xmm9+16 -#define CONTEXT_Xmm11 CONTEXT_Xmm10+16 -#define CONTEXT_Xmm12 CONTEXT_Xmm11+16 -#define CONTEXT_Xmm13 CONTEXT_Xmm12+16 -#define CONTEXT_Xmm14 CONTEXT_Xmm13+16 -#define CONTEXT_Xmm15 CONTEXT_Xmm14+16 -#define CONTEXT_VectorRegister CONTEXT_Xmm15+16 -#define CONTEXT_VectorControl CONTEXT_VectorRegister+16*26 -#define CONTEXT_DebugControl CONTEXT_VectorControl+8 -#define CONTEXT_LastBranchToRip CONTEXT_DebugControl+8 -#define CONTEXT_LastBranchFromRip CONTEXT_LastBranchToRip+8 -#define CONTEXT_LastExceptionToRip CONTEXT_LastBranchFromRip+8 -#define CONTEXT_LastExceptionFromRip CONTEXT_LastExceptionToRip+8 - #define IRETFRAME_Rip 0 #define IRETFRAME_SegCs IRETFRAME_Rip+8 #define IRETFRAME_EFlags IRETFRAME_SegCs+8 @@ -198,7 +132,16 @@ LOCAL_LABEL(Done_Restore_CONTEXT_FLOATING_POINT): // The control registers are restored via the iret instruction // so we build the frame for the iret on the stack. +#ifdef __APPLE__ +.att_syntax + // On OSX, we cannot read SS via the thread_get_context and RtlRestoreContext + // needs to be used on context extracted by thread_get_context. So we + // don't change the SS. + mov %ss, %ax +.intel_syntax noprefix +#else mov ax, [rdi + CONTEXT_SegSs] +#endif mov [rsp + IRETFRAME_SegSs], ax mov rax, [rdi + CONTEXT_Rsp] mov [rsp + IRETFRAME_Rsp], rax @@ -249,33 +192,6 @@ LEAF_END RtlRestoreContext, _TEXT #else -#define CONTEXT_ContextFlags 0 -#define CONTEXT_FLOATING_POINT 8 -#define CONTEXT_FloatSave 7*4 -#define FLOATING_SAVE_AREA_SIZE 8*4+80 -#define CONTEXT_Edi CONTEXT_FloatSave + FLOATING_SAVE_AREA_SIZE + 4*4 -#define CONTEXT_Esi CONTEXT_Edi+4 -#define CONTEXT_Ebx CONTEXT_Esi+4 -#define CONTEXT_Edx CONTEXT_Ebx+4 -#define CONTEXT_Ecx CONTEXT_Edx+4 -#define CONTEXT_Eax CONTEXT_Ecx+4 -#define CONTEXT_Ebp CONTEXT_Eax+4 -#define CONTEXT_Eip CONTEXT_Ebp+4 -#define CONTEXT_SegCs CONTEXT_Eip+4 -#define CONTEXT_EFlags CONTEXT_SegCs+4 -#define CONTEXT_Esp CONTEXT_EFlags+4 -#define CONTEXT_SegSs CONTEXT_Esp+4 -#define CONTEXT_EXTENDED_REGISTERS 32 -#define CONTEXT_ExtendedRegisters CONTEXT_SegSs+4 -#define CONTEXT_Xmm0 CONTEXT_ExtendedRegisters+160 -#define CONTEXT_Xmm1 CONTEXT_Xmm0+16 -#define CONTEXT_Xmm2 CONTEXT_Xmm1+16 -#define CONTEXT_Xmm3 CONTEXT_Xmm2+16 -#define CONTEXT_Xmm4 CONTEXT_Xmm3+16 -#define CONTEXT_Xmm5 CONTEXT_Xmm4+16 -#define CONTEXT_Xmm6 CONTEXT_Xmm5+16 -#define CONTEXT_Xmm7 CONTEXT_Xmm6+16 - .globl C_FUNC(CONTEXT_CaptureContext) C_FUNC(CONTEXT_CaptureContext): push %eax diff --git a/src/pal/src/arch/i386/dispatchexceptionwrapper.S b/src/pal/src/arch/i386/dispatchexceptionwrapper.S index 952e5a0cbb3a..c8382e968d0f 100644 --- a/src/pal/src/arch/i386/dispatchexceptionwrapper.S +++ b/src/pal/src/arch/i386/dispatchexceptionwrapper.S @@ -13,10 +13,9 @@ // and PAL_DispatchException that throws an SEH exception for // the fault, to make the stack unwindable. // -// On Mac OS X 10.6, the unwinder fails to operate correctly -// on our original int3; int3 body. The workaround is to -// increase the size of the function to include a call statement, -// even though it will never be executed. + +.intel_syntax noprefix +#include "unixasmmacros.inc" #if defined(__x86_64__) #define PAL_DISPATCHEXCEPTION __Z21PAL_DispatchExceptionmmmmmmP8_CONTEXTP17_EXCEPTION_RECORD @@ -24,33 +23,16 @@ #define PAL_DISPATCHEXCEPTION __Z21PAL_DispatchExceptionP8_CONTEXTP17_EXCEPTION_RECORD #endif // defined(_AMD64_) - .text - .globl __Z21PAL_DispatchExceptionP8_CONTEXTP17_EXCEPTION_RECORD - .globl _PAL_DispatchExceptionWrapper -_PAL_DispatchExceptionWrapper: -LBegin: - int3 - call PAL_DISPATCHEXCEPTION - int3 -LEnd: +// Offset of the return address from the PAL_DISPATCHEXCEPTION in the PAL_DispatchExceptionWrapper +.globl C_FUNC(PAL_DispatchExceptionReturnOffset) +C_FUNC(PAL_DispatchExceptionReturnOffset): + .int LOCAL_LABEL(PAL_DispatchExceptionReturn)-C_FUNC(PAL_DispatchExceptionWrapper) // // PAL_DispatchExceptionWrapper will never be called; it only serves // to be referenced from a stack frame on the faulting thread. Its // unwinding behavior is equivalent to any standard function having -// an ebp frame. The FDE below is analogous to the one generated -// by "g++ -S" for the following source file. -// -// --- snip --- -// struct CONTEXT -// { -// char reserved[716]; -// }; -// -// struct EXCEPTION_RECORD -// { -// char reserved[80]; -// }; +// an ebp frame. It is analogous to the following source file. // // void PAL_DispatchException(CONTEXT *pContext, EXCEPTION_RECORD *pExceptionRecord); // @@ -60,74 +42,17 @@ LEnd: // EXCEPTION_RECORD ExceptionRecord; // PAL_DispatchException(&Context, &ExceptionRecord); // } -// --- snip --- // - .section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support -CIE_DispatchExceptionPersonality: - .long LECIE1-LSCIE1 -LSCIE1: - .long 0x0 - .byte 0x1 - .ascii "zLR\0" - .byte 0x1 -#ifdef BIT64 - .byte 0x78 // data_align: -8 - .byte 16 // return address register: rip -#else // BIT64 - .byte 0x7c // data_align: -4 - .byte 0x8 // return address register: eip -#endif // BIT64 else - .byte 0x2 - .byte 0x10 - .byte 0x10 - .byte 0xc // DW_CFA_def_cfa -#ifdef BIT64 - .byte 0x7 // operand1 = rsp - .byte 0x8 // operand2 = offset 8 - .byte 0x80 | 16 // DW_CFA_offset of return address register -#else // BIT64 - .byte 0x5 // operand1 = esp - .byte 0x4 // operand2 = offset 4 - .byte 0x80 | 8 // DW_CFA_offset of return address register -#endif // BIT64 else - .byte 0x1 // operand1 = 1 word - .align 2 -LECIE1: +NESTED_ENTRY PAL_DispatchExceptionWrapper, _TEXT, NoHandler + push_nonvol_reg rbp + mov rbp, rsp + set_cfa_register rbp, 0 + int3 + call PAL_DISPATCHEXCEPTION +LOCAL_LABEL(PAL_DispatchExceptionReturn): + int3 + pop_nonvol_reg rbp + ret +NESTED_END PAL_DispatchExceptionWrapper, _TEXT - .globl _PAL_DispatchExceptionWrapper.eh -_PAL_DispatchExceptionWrapper.eh: -LSFDE1: - .set LLFDE1,LEFDE1-LASFDE1 - .set LLength,LEnd-LBegin - .long LLFDE1 -LASFDE1: - .long LASFDE1-CIE_DispatchExceptionPersonality -#ifdef BIT64 - .quad LBegin-. - .quad LLength - .byte 0x8 - .quad 0x0 -#else // BIT64 - .long LBegin-. - .long LLength - .byte 0x4 - .long 0x0 -#endif // BIT64 else - .byte 0xe // DW_CFA_def_cfa_offset -#ifdef BIT64 - .byte 0x10 - .byte 0x80 | 6 // DW_CFA_offset rbp -#else // BIT64 - .byte 0x8 - .byte 0x80 | 4 // DW_CFA_offset ebp -#endif // BIT64 else - .byte 0x2 - .byte 0xd // DW_CFA_def_cfa_register -#ifdef BIT64 - .byte 6 // operand1 = rbp -#else // BIT64 - .byte 4 // operand1 = ebp -#endif // BIT64 - .align 2 -LEFDE1: diff --git a/src/pal/src/debug/debug.cpp b/src/pal/src/debug/debug.cpp index f0376ce771ef..6c36011ecf91 100644 --- a/src/pal/src/debug/debug.cpp +++ b/src/pal/src/debug/debug.cpp @@ -435,7 +435,6 @@ GetThreadContext( ret = CONTEXT_GetThreadContext( GetCurrentProcessId(), pTargetThread->GetPThreadSelf(), - pTargetThread->GetLwpId(), lpContext ); } @@ -498,7 +497,6 @@ SetThreadContext( ret = CONTEXT_SetThreadContext( GetCurrentProcessId(), pTargetThread->GetPThreadSelf(), - pTargetThread->GetLwpId(), lpContext ); } diff --git a/src/pal/src/exception/machexception.cpp b/src/pal/src/exception/machexception.cpp index d4350c748943..201aa80b9c89 100644 --- a/src/pal/src/exception/machexception.cpp +++ b/src/pal/src/exception/machexception.cpp @@ -238,7 +238,7 @@ PAL_ERROR CorUnix::CPalThread::EnableMachExceptions() NONPAL_TRACE("Enabling %s handlers for thread %08X\n", hExceptionPort == s_TopExceptionPort ? "top" : "bottom", - pthread_mach_thread_np(GetPThreadSelf())); + GetMachPortSelf()); // Swap current handlers into temporary storage first. That's because it's possible (even likely) that // some or all of the handlers might still be ours. In those cases we don't want to overwrite the @@ -541,6 +541,7 @@ void PAL_DispatchException(DWORD64 dwRDI, DWORD64 dwRSI, DWORD64 dwRDX, DWORD64 #if defined(_X86_) || defined(_AMD64_) extern "C" void PAL_DispatchExceptionWrapper(); +extern "C" int PAL_DispatchExceptionReturnOffset; #endif // _X86_ || _AMD64_ /*++ @@ -932,7 +933,7 @@ catch_exception_raise( FramePointer[0] = pContext; FramePointer[1] = pExceptionRecord; // Place the return address to right after the fake call in PAL_DispatchExceptionWrapper - FramePointer[-1] = (void *)((ULONG_PTR)PAL_DispatchExceptionWrapper + 6); + FramePointer[-1] = (void *)((ULONG_PTR)PAL_DispatchExceptionWrapper + PAL_DispatchExceptionReturnOffset); // Make the instruction register point to DispatchException ThreadState.eip = (unsigned)PAL_DispatchException; @@ -1287,9 +1288,25 @@ void *SEHExceptionThread(void *args) CONTEXT sContext; hThread = sMessage.GetThreadContext(&sContext); - MachRet = thread_suspend(hThread); - CHECK_MACH("thread_suspend", MachRet); + while (true) + { + MachRet = thread_suspend(hThread); + CHECK_MACH("thread_suspend", MachRet); + + // Ensure that if the thread was running in the kernel, the kernel operation + // is safely aborted so that it can be restarted later. + MachRet = thread_abort_safely(hThread); + if (MachRet == KERN_SUCCESS) + { + break; + } + // The thread was running in the kernel executing a non-atomic operation + // that cannot be restarted, so we need to resume the thread and retry + MachRet = thread_resume(hThread); + CHECK_MACH("thread_resume", MachRet); + } + MachRet = CONTEXT_SetThreadContextOnPort(hThread, &sContext); CHECK_MACH("CONTEXT_SetThreadContextOnPort", MachRet); @@ -1757,4 +1774,90 @@ void SEHCleanupExceptionPort(void) s_DebugInitialized = FALSE; } +extern "C" void ActivationHandler(CONTEXT* context) +{ + if (g_activationFunction != NULL) + { + g_activationFunction(context); + } + + RtlRestoreContext(context, NULL); + DebugBreak(); +} + +extern "C" void ActivationHandlerWrapper(); +extern "C" int ActivationHandlerReturnOffset; + +PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread) +{ + PAL_ERROR palError; + CPalThread *pCurrentThread = InternalGetCurrentThread(); + DWORD dwSuspendCount; + + palError = pCurrentThread->suspensionInfo.InternalSuspendThreadFromData( + pCurrentThread, + pThread, + &dwSuspendCount + ); + + if (palError == NO_ERROR) + { + mach_port_t thread = pThread->GetMachPortSelf(); + x86_thread_state64_t ThreadState; + mach_msg_type_number_t count = sizeof(ThreadState)/sizeof(natural_t); + kern_return_t MachRet; + + MachRet = thread_get_state(thread, + x86_THREAD_STATE64, + (thread_state_t)&ThreadState, + &count); + _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_get_state\n"); + + if ((g_safeActivationCheckFunction != NULL) && g_safeActivationCheckFunction(ThreadState.__rip)) + { + // TODO: it would be nice to preserve the red zone in case a jitter would want to use it + // Do we really care about unwinding through the wrapper? + size_t* sp = (size_t*)ThreadState.__rsp; + *(--sp) = ThreadState.__rip; + *(--sp) = ThreadState.__rbp; + size_t rbpAddress = (size_t)sp; + size_t contextAddress = (((size_t)sp) - sizeof(CONTEXT)) & ~15; + size_t returnAddressAddress = contextAddress - sizeof(size_t); + *(size_t*)(returnAddressAddress) = ActivationHandlerReturnOffset + (size_t)ActivationHandlerWrapper; + + // Fill in the context in the helper frame with the full context of the suspended thread. + // The ActivationHandler will use the context to resume the execution of the thread + // after the activation function returns. + CONTEXT *pContext = (CONTEXT *)contextAddress; + pContext->ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS; + MachRet = CONTEXT_GetThreadContextFromPort(thread, pContext); + _ASSERT_MSG(MachRet == KERN_SUCCESS, "CONTEXT_GetThreadContextFromPort\n"); + + // Make the instruction register point to ActivationHandler + ThreadState.__rip = (size_t)ActivationHandler; + ThreadState.__rsp = returnAddressAddress; + ThreadState.__rbp = rbpAddress; + ThreadState.__rdi = contextAddress; + + MachRet = thread_set_state(thread, + x86_THREAD_STATE64, + (thread_state_t)&ThreadState, + count); + _ASSERT_MSG(MachRet == KERN_SUCCESS, "thread_set_state\n"); + } + + palError = pCurrentThread->suspensionInfo.InternalResumeThreadFromData( + pCurrentThread, + pThread, + &dwSuspendCount + ); + } + else + { + printf("Suspension failed with error 0x%x\n", palError); + } + + return palError; +} + #endif // HAVE_MACH_EXCEPTIONS diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp index 444639a9579d..d11d5ffd2cd6 100644 --- a/src/pal/src/exception/signal.cpp +++ b/src/pal/src/exception/signal.cpp @@ -101,9 +101,6 @@ int g_signalPipe[2] = { 0, 0 }; DWORD g_dwExternalSignalHandlerThreadId = 0; -// Activation function that gets called when an activation is injected into a thread. -PAL_ActivationFunction g_activationFunction = NULL; - /* public function definitions ************************************************/ /*++ @@ -568,28 +565,6 @@ static void sigbus_handler(int code, siginfo_t *siginfo, void *context) } } -/*++ -Function: - PAL_SetActivationFunction - - Register an activation function that gets called when an activation is injected - into a thread. - -Parameters: - pActivationFunction - activation function - -Return value: - None ---*/ -PALIMPORT -VOID -PALAPI -PAL_SetActivationFunction( - IN PAL_ActivationFunction pActivationFunction) -{ - g_activationFunction = pActivationFunction; -} - /*++ Function : inject_activation_handler @@ -609,6 +584,8 @@ static void inject_activation_handler(int code, siginfo_t *siginfo, void *contex { if (g_activationFunction != NULL) { + _ASSERTE(g_safeActivationCheckFunction != NULL); + native_context_t *ucontext = (native_context_t *)context; CONTEXT winContext; @@ -617,7 +594,10 @@ static void inject_activation_handler(int code, siginfo_t *siginfo, void *contex &winContext, CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT); - g_activationFunction(&winContext); + if (g_safeActivationCheckFunction(winContext.Rip)) + { + g_activationFunction(&winContext); + } // Activation function may have modified the context, so update it. CONTEXTToNativeContext(&winContext, ucontext); @@ -637,7 +617,7 @@ Parameters : (no return value) --*/ -void InjectActivationInternal(CorUnix::CPalThread* pThread) +PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread) { int status = pthread_kill(pThread->GetPThreadSelf(), INJECT_ACTIVATION_SIGNAL); if (status != 0) @@ -647,62 +627,8 @@ void InjectActivationInternal(CorUnix::CPalThread* pThread) // if the thread doesn't exist anymore. abort(); } -} - -/*++ -Function: -PAL_InjectActivation - -Interrupt the specified thread and have it call an activation function registered -using the PAL_SetActivationFunction - -Parameters: -hThread - handle of the target thread - -Return: -TRUE if it succeeded, FALSE otherwise. ---*/ -BOOL -PALAPI -PAL_InjectActivation( - IN HANDLE hThread) -{ - PERF_ENTRY(PAL_InjectActivation); - ENTRY("PAL_InjectActivation(hThread=%p)\n", hThread); - - CPalThread *pCurrentThread; - CPalThread *pTargetThread; - IPalObject *pobjThread = NULL; - - pCurrentThread = InternalGetCurrentThread(); - - PAL_ERROR palError = InternalGetThreadDataFromHandle( - pCurrentThread, - hThread, - 0, - &pTargetThread, - &pobjThread - ); - - if (palError == NO_ERROR) - { - InjectActivationInternal(pTargetThread); - } - else - { - pCurrentThread->SetLastError(palError); - } - - if (pobjThread != NULL) - { - pobjThread->ReleaseReference(pCurrentThread); - } - - BOOL success = (palError == NO_ERROR); - LOGEXIT("PAL_InjectActivation returns:d\n", success); - PERF_EXIT(PAL_InjectActivation); - return success; + return NO_ERROR; } /*++ diff --git a/src/pal/src/include/pal/context.h b/src/pal/src/include/pal/context.h index 5f0718725b63..51dcedfcd2fc 100644 --- a/src/pal/src/include/pal/context.h +++ b/src/pal/src/include/pal/context.h @@ -371,7 +371,6 @@ BOOL CONTEXT_SetThreadContext( DWORD dwProcessId, pthread_t self, - DWORD dwLwpId, CONST CONTEXT *lpContext ); @@ -393,7 +392,6 @@ BOOL CONTEXT_GetThreadContext( DWORD dwProcessId, pthread_t self, - DWORD dwLwpId, LPCONTEXT lpContext); #if HAVE_MACH_EXCEPTIONS diff --git a/src/pal/src/include/pal/thread.hpp b/src/pal/src/include/pal/thread.hpp index 24739eb56db0..7e5e6fbd7f51 100644 --- a/src/pal/src/include/pal/thread.hpp +++ b/src/pal/src/include/pal/thread.hpp @@ -314,6 +314,10 @@ namespace CorUnix DWORD m_dwLwpId; pthread_t m_pthreadSelf; +#if HAVE_MACH_THREADS + mach_port_t m_machPortSelf; +#endif + // // Start info // @@ -394,6 +398,7 @@ namespace CorUnix m_threadId(0), m_dwLwpId(0), m_pthreadSelf(0), + m_machPortSelf(0), m_lpStartAddress(NULL), m_lpStartParameter(NULL), m_bCreateSuspended(FALSE), @@ -548,6 +553,16 @@ namespace CorUnix return m_pthreadSelf; }; +#if HAVE_MACH_THREADS + mach_port_t + GetMachPortSelf( + void + ) + { + return m_machPortSelf; + }; +#endif + LPTHREAD_START_ROUTINE GetStartAddress( void @@ -746,6 +761,9 @@ WaitForEndingThreads( extern int free_threads_spinlock; +extern PAL_ActivationFunction g_activationFunction; +extern PAL_SafeActivationCheckFunction g_safeActivationCheckFunction; + /*++ Macro: THREADSilentGetCurrentThreadId diff --git a/src/pal/src/thread/context.cpp b/src/pal/src/thread/context.cpp index b8b343c96ac3..6006aad01c07 100644 --- a/src/pal/src/thread/context.cpp +++ b/src/pal/src/thread/context.cpp @@ -272,7 +272,6 @@ BOOL CONTEXT_GetThreadContext( DWORD dwProcessId, pthread_t self, - DWORD dwLwpId, LPCONTEXT lpContext) { BOOL ret = FALSE; @@ -343,7 +342,6 @@ BOOL CONTEXT_SetThreadContext( DWORD dwProcessId, pthread_t self, - DWORD dwLwpId, CONST CONTEXT *lpContext) { BOOL ret = FALSE; @@ -999,7 +997,6 @@ BOOL CONTEXT_GetThreadContext( DWORD dwProcessId, pthread_t self, - DWORD dwLwpId, LPCONTEXT lpContext) { BOOL ret = FALSE; @@ -1232,7 +1229,6 @@ BOOL CONTEXT_SetThreadContext( DWORD dwProcessId, pthread_t self, - DWORD dwLwpId, CONST CONTEXT *lpContext) { BOOL ret = FALSE; diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp index 0e177ff1fee1..fcca7e3b5832 100644 --- a/src/pal/src/thread/process.cpp +++ b/src/pal/src/thread/process.cpp @@ -4043,8 +4043,7 @@ CorUnix::CPalThread *PROCThreadFromMachPort(mach_port_t hTargetThread) pThread = pGThreadList; while (pThread) { - pthread_t pPThread = pThread->GetPThreadSelf(); - mach_port_t hThread = pthread_mach_thread_np(pPThread); + mach_port_t hThread = pThread->GetMachPortSelf(); if (hThread == hTargetThread) break; diff --git a/src/pal/src/thread/thread.cpp b/src/pal/src/thread/thread.cpp index 33dbfbc91416..a1c6eb6c1452 100644 --- a/src/pal/src/thread/thread.cpp +++ b/src/pal/src/thread/thread.cpp @@ -20,6 +20,7 @@ Module Name: --*/ #include "pal/corunix.hpp" +#include "pal/context.h" #include "pal/thread.hpp" #include "pal/mutex.hpp" #include "pal/handlemgr.hpp" @@ -85,6 +86,11 @@ pthread_mutex_t ptmEndThread; pthread_cond_t ptcEndThread; static int iEndingThreads = 0; +// Activation function that gets called when an activation is injected into a thread. +PAL_ActivationFunction g_activationFunction = NULL; +// Function to check if an activation can be safely injected at a specified context +PAL_SafeActivationCheckFunction g_safeActivationCheckFunction = NULL; + void ThreadCleanupRoutine( CPalThread *pThread, @@ -1364,7 +1370,7 @@ GetThreadTimes( pthrTarget->Lock(pthrCurrent); mach_port_t mhThread; - mhThread = pthread_mach_thread_np(pthrTarget->GetPThreadSelf()); + mhThread = pthrTarget->GetMachPortSelf(); kern_return_t status; status = thread_info( @@ -1466,6 +1472,9 @@ CPalThread::ThreadEntry( pThread->m_threadId = THREADSilentGetCurrentThreadId(); pThread->m_pthreadSelf = pthread_self(); +#if HAVE_MACH_THREADS + pThread->m_machPortSelf = pthread_mach_thread_np(pThread->m_pthreadSelf); +#endif #if HAVE_THREAD_SELF pThread->m_dwLwpId = (DWORD) thread_self(); #elif HAVE__LWP_SELF @@ -1638,6 +1647,9 @@ CorUnix::CreateThreadData( pThread->m_threadId = THREADSilentGetCurrentThreadId(); pThread->m_pthreadSelf = pthread_self(); +#if HAVE_MACH_THREADS + pThread->m_machPortSelf = pthread_mach_thread_np(pThread->m_pthreadSelf); +#endif #if HAVE_THREAD_SELF pThread->m_dwLwpId = (DWORD) thread_self(); #elif HAVE__LWP_SELF @@ -2491,7 +2503,92 @@ PAL_GetStackLimit() #endif } +PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread); + +/*++ +Function: + PAL_SetActivationFunction + + Register an activation function that gets called when an activation is injected + into a thread. + +Parameters: + pActivationFunction - activation function + pSafeActivationCheckFunction - function to check if an activation can be safely + injected at a specified context +Return value: + None +--*/ +PALIMPORT +VOID +PALAPI +PAL_SetActivationFunction( + IN PAL_ActivationFunction pActivationFunction, + IN PAL_SafeActivationCheckFunction pSafeActivationCheckFunction) +{ + g_activationFunction = pActivationFunction; + g_safeActivationCheckFunction = pSafeActivationCheckFunction; +} + +/*++ +Function: +PAL_InjectActivation + +Interrupt the specified thread and have it call an activation function registered +using the PAL_SetActivationFunction + +Parameters: +hThread - handle of the target thread + +Return: +TRUE if it succeeded, FALSE otherwise. +--*/ +BOOL +PALAPI +PAL_InjectActivation( + IN HANDLE hThread) +{ + PERF_ENTRY(PAL_InjectActivation); + ENTRY("PAL_InjectActivation(hThread=%p)\n", hThread); + + CPalThread *pCurrentThread; + CPalThread *pTargetThread; + IPalObject *pobjThread = NULL; + + pCurrentThread = InternalGetCurrentThread(); + + PAL_ERROR palError = InternalGetThreadDataFromHandle( + pCurrentThread, + hThread, + 0, + &pTargetThread, + &pobjThread + ); + + if (palError == NO_ERROR) + { + palError = InjectActivationInternal(pTargetThread); + } + + if (palError == NO_ERROR) + { + pCurrentThread->SetLastError(palError); + } + + if (pobjThread != NULL) + { + pobjThread->ReleaseReference(pCurrentThread); + } + + BOOL success = (palError == NO_ERROR); + LOGEXIT("PAL_InjectActivation returns:d\n", success); + PERF_EXIT(PAL_InjectActivation); + + return success; +} + #if HAVE_MACH_EXCEPTIONS + extern mach_port_t s_ExceptionPort; extern mach_port_t s_TopExceptionPort; diff --git a/src/pal/src/thread/threadsusp.cpp b/src/pal/src/thread/threadsusp.cpp index e20fc3f054ab..e3d1b01123e0 100644 --- a/src/pal/src/thread/threadsusp.cpp +++ b/src/pal/src/thread/threadsusp.cpp @@ -226,6 +226,14 @@ CThreadSuspensionInfo::InternalSuspendNewThreadFromData( palError = ERROR_INTERNAL_ERROR; } + if (palError == NO_ERROR) + { + AcquireSuspensionLock(pThread); + pThread->suspensionInfo.DecrSuspCount(); + pThread->suspensionInfo.SetSelfSusp(FALSE); + ReleaseSuspensionLock(pThread); + } + // Close the pipes regardless of whether we were successful. close(pipe_descs[0]); close(pipe_descs[1]); @@ -1087,7 +1095,7 @@ CThreadSuspensionInfo::WaitOnSuspendSemaphore() } // If the target has already acknowledged the suspend we shouldn't wait. - if (!m_fSuspended) + while (!m_fSuspended) { // We got here before the target could signal. Wait on them (which atomically releases // the mutex during the wait). @@ -1197,7 +1205,7 @@ CThreadSuspensionInfo::WaitOnResumeSemaphore() } // If the target has already acknowledged the resume we shouldn't wait. - if (!m_fResumed) + while (!m_fResumed) { // We got here before the target could signal. Wait on them (which atomically releases // the mutex during the wait). @@ -1840,43 +1848,62 @@ CThreadSuspensionInfo::THREADHandleSuspendNative(CPalThread *pthrTarget) { pthrTarget->SetStartStatus(TRUE); } - + + BOOL retry = FALSE; + do + { + #if HAVE_PTHREAD_SUSPEND - dwPthreadRet = pthread_suspend(pthrTarget->GetPThreadSelf()); + dwPthreadRet = pthread_suspend(pthrTarget->GetPThreadSelf()); #elif HAVE_MACH_THREADS - dwPthreadRet = thread_suspend(pthread_mach_thread_np(pthrTarget->GetPThreadSelf())); + mach_port_t threadPort = pthrTarget->GetMachPortSelf(); + dwPthreadRet = thread_suspend(threadPort); #elif HAVE_PTHREAD_SUSPEND_NP #if SELF_SUSPEND_FAILS_WITH_NATIVE_SUSPENSION - if (pthrTarget->suspensionInfo.GetSelfSusp()) - { - pthrTarget->suspensionInfo.WaitOnSuspendSemaphore(); - } - else + if (pthrTarget->suspensionInfo.GetSelfSusp()) + { + pthrTarget->suspensionInfo.WaitOnSuspendSemaphore(); + } + else #endif // SELF_SUSPEND_FAILS_WITH_NATIVE_SUSPENSION - { - dwPthreadRet = pthread_suspend_np(pthrTarget->GetPThreadSelf()); - } + { + dwPthreadRet = pthread_suspend_np(pthrTarget->GetPThreadSelf()); + } #else - #error "Don't know how to suspend threads on this platform!" - return FALSE; + #error "Don't know how to suspend threads on this platform!" + return FALSE; #endif - // A self suspending thread that reaches this point would have been resumed - // by a call to THREADHandleResumeNative. The self suspension has been - // completed so it can set its selfsusp flag to FALSE. Reset the selfsusp flag - // before checking the return value in case the suspend itself failed. - if (pthrTarget->suspensionInfo.GetSelfSusp()) - { - pthrTarget->suspensionInfo.SetSelfSusp(FALSE); - } + // A self suspending thread that reaches this point would have been resumed + // by a call to THREADHandleResumeNative. The self suspension has been + // completed so it can set its selfsusp flag to FALSE. Reset the selfsusp flag + // before checking the return value in case the suspend itself failed. + if (pthrTarget->suspensionInfo.GetSelfSusp()) + { + pthrTarget->suspensionInfo.SetSelfSusp(FALSE); + } - if (dwPthreadRet != 0) - { - ASSERT("[THREADHandleSuspendNative] native suspend_thread call failed [thread id=%d thread_state=%d errno=%d (%s)]\n", - pthrTarget->GetThreadId(), pthrTarget->synchronizationInfo.GetThreadState(), - dwPthreadRet, strerror(dwPthreadRet)); - return FALSE; + if (dwPthreadRet != 0) + { + ASSERT("[THREADHandleSuspendNative] native suspend_thread call failed [thread id=%d thread_state=%d errno=%d (%s)]\n", + pthrTarget->GetThreadId(), pthrTarget->synchronizationInfo.GetThreadState(), + dwPthreadRet, strerror(dwPthreadRet)); + return FALSE; + } + +#if HAVE_MACH_THREADS + dwPthreadRet = thread_abort_safely(threadPort); + if (dwPthreadRet != 0) + { + // The thread was suspended in a kernel non-atomic operation that cannot be safely + // restarted, so we need to resume the thread and retry + thread_resume(threadPort); + retry = TRUE; + } +#endif } + while (retry); + return TRUE; } @@ -1920,7 +1947,7 @@ CThreadSuspensionInfo::THREADHandleResumeNative(CPalThread *pthrTarget) #if HAVE_PTHREAD_CONTINUE dwPthreadRet = pthread_continue(pthrTarget->GetPThreadSelf()); #elif HAVE_MACH_THREADS - dwPthreadRet = thread_resume(pthread_mach_thread_np(pthrTarget->GetPThreadSelf())); + dwPthreadRet = thread_resume(pthrTarget->GetMachPortSelf()); #elif HAVE_PTHREAD_CONTINUE_NP dwPthreadRet = pthread_continue_np((pthrTarget->GetPThreadSelf()); #elif HAVE_PTHREAD_RESUME_NP diff --git a/src/vm/threadsuspend.cpp b/src/vm/threadsuspend.cpp index cc9080116d72..10ea699faa14 100644 --- a/src/vm/threadsuspend.cpp +++ b/src/vm/threadsuspend.cpp @@ -8252,6 +8252,13 @@ void ThreadSuspend::SuspendEE(SUSPEND_REASON reason) #if defined(FEATURE_HIJACK) && defined(PLATFORM_UNIX) +// This function is called by PAL to check if the specified instruction pointer +// is in a function where we can safely inject activation. +BOOL PALAPI CheckActivationSafePoint(SIZE_T ip) +{ + return ExecutionManager::IsManagedCode(ip); +} + // This function is called when a GC is pending. It tries to ensure that the current // thread is taken to a GC-safe place as quickly as possible. It does this by doing // one of the following: @@ -8276,8 +8283,9 @@ void PALAPI HandleGCSuspensionForInterruptedThread(CONTEXT *interruptedContext) PCODE ip = GetIP(interruptedContext); - if (ExecutionManager::IsManagedCode(ip) != TRUE) - return; + // This function can only be called when the interrupted thread is in + // an activation safe point. + _ASSERTE(CheckActivationSafePoint(ip)); Thread::WorkingOnThreadContextHolder workingOnThreadContext(pThread); if (!workingOnThreadContext.Acquired()) @@ -8383,7 +8391,7 @@ bool Thread::InjectGcSuspension() void ThreadSuspend::Initialize() { #if defined(FEATURE_HIJACK) && defined(PLATFORM_UNIX) - ::PAL_SetActivationFunction(HandleGCSuspensionForInterruptedThread); + ::PAL_SetActivationFunction(HandleGCSuspensionForInterruptedThread, CheckActivationSafePoint); #endif }