diff --git a/src/coreclr/gcdump/gcdumpnonx86.cpp b/src/coreclr/gcdump/gcdumpnonx86.cpp index d4366069f14a7f..919bf60d886703 100644 --- a/src/coreclr/gcdump/gcdumpnonx86.cpp +++ b/src/coreclr/gcdump/gcdumpnonx86.cpp @@ -361,6 +361,7 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, | DECODE_GENERICS_INST_CONTEXT | DECODE_GC_LIFETIMES | DECODE_PROLOG_LENGTH + | DECODE_RETURN_KIND #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) | DECODE_HAS_TAILCALLS #endif @@ -501,6 +502,9 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, gcPrintf("Size of parameter area: %x\n", hdrdecoder.GetSizeOfStackParameterArea()); #endif + ReturnKind returnKind = hdrdecoder.GetReturnKind(); + gcPrintf("Return Kind: %s\n", ReturnKindToString(returnKind)); + UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength(); gcPrintf("Code size: %x\n", cbEncodedMethodSize); diff --git a/src/coreclr/inc/eetwain.h b/src/coreclr/inc/eetwain.h index 32801bd23c540c..271e0de8ecb7f4 100644 --- a/src/coreclr/inc/eetwain.h +++ b/src/coreclr/inc/eetwain.h @@ -306,7 +306,7 @@ virtual size_t GetFunctionSize(GCInfoToken gcInfoToken) = 0; * returns true. * If hijacking is not possible for some reason, it return false. */ -virtual bool GetReturnAddressHijackInfo(GCInfoToken gcInfoToken X86_ARG(ReturnKind * returnKind)) = 0; +virtual bool GetReturnAddressHijackInfo(GCInfoToken gcInfoToken, ReturnKind * returnKind) = 0; #ifndef USE_GC_INFO_DECODER /* @@ -575,7 +575,7 @@ size_t GetFunctionSize(GCInfoToken gcInfoToken); * returns true. * If hijacking is not possible for some reason, it return false. */ -virtual bool GetReturnAddressHijackInfo(GCInfoToken gcInfoToken X86_ARG(ReturnKind * returnKind)); +virtual bool GetReturnAddressHijackInfo(GCInfoToken gcInfoToken, ReturnKind * returnKind); #ifndef USE_GC_INFO_DECODER /* diff --git a/src/coreclr/inc/gcinfodecoder.h b/src/coreclr/inc/gcinfodecoder.h index 0b51833ee19d5e..d91e10bc081d21 100644 --- a/src/coreclr/inc/gcinfodecoder.h +++ b/src/coreclr/inc/gcinfodecoder.h @@ -222,6 +222,7 @@ enum GcInfoDecoderFlags DECODE_PROLOG_LENGTH = 0x400, // length of the prolog (used to avoid reporting generics context) DECODE_EDIT_AND_CONTINUE = 0x800, DECODE_REVERSE_PINVOKE_VAR = 0x1000, + DECODE_RETURN_KIND = 0x2000, #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) DECODE_HAS_TAILCALLS = 0x4000, #endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 @@ -581,6 +582,7 @@ class GcInfoDecoder #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool HasTailCalls(); #endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || defined(TARGET_RISCV64) + ReturnKind GetReturnKind(); UINT32 GetCodeLength(); UINT32 GetStackBaseRegister(); UINT32 GetSizeOfEditAndContinuePreservedArea(); @@ -613,6 +615,7 @@ class GcInfoDecoder #ifdef TARGET_ARM64 UINT32 m_SizeOfEditAndContinueFixedStackFrame; #endif + ReturnKind m_ReturnKind; #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED UINT32 m_NumSafePoints; UINT32 m_SafePointIndex; diff --git a/src/coreclr/nativeaot/Runtime/AsmOffsets.h b/src/coreclr/nativeaot/Runtime/AsmOffsets.h index ef5031560bdc43..cb6bf8842e04b9 100644 --- a/src/coreclr/nativeaot/Runtime/AsmOffsets.h +++ b/src/coreclr/nativeaot/Runtime/AsmOffsets.h @@ -52,11 +52,9 @@ ASM_OFFSET( 30, 48, Thread, m_pTransitionFrame) ASM_OFFSET( 34, 50, Thread, m_pDeferredTransitionFrame) ASM_OFFSET( 44, 70, Thread, m_ppvHijackedReturnAddressLocation) ASM_OFFSET( 48, 78, Thread, m_pvHijackedReturnAddress) -ASM_OFFSET( 4c, 80, Thread, m_pExInfoStackHead) -ASM_OFFSET( 50, 88, Thread, m_threadAbortException) -#ifdef TARGET_X86 -ASM_OFFSET( 54, FF, Thread, m_uHijackedReturnValueFlags) -#endif +ASM_OFFSET( 4c, 80, Thread, m_uHijackedReturnValueFlags) +ASM_OFFSET( 50, 88, Thread, m_pExInfoStackHead) +ASM_OFFSET( 54, 90, Thread, m_threadAbortException) ASM_SIZEOF( 14, 20, EHEnum) diff --git a/src/coreclr/nativeaot/Runtime/ICodeManager.h b/src/coreclr/nativeaot/Runtime/ICodeManager.h index 0fd8f60a5458fd..f88304a40e8682 100644 --- a/src/coreclr/nativeaot/Runtime/ICodeManager.h +++ b/src/coreclr/nativeaot/Runtime/ICodeManager.h @@ -28,11 +28,92 @@ enum GCRefKind : unsigned char GCRK_Scalar = 0x00, GCRK_Object = 0x01, GCRK_Byref = 0x02, +#ifdef TARGET_64BIT + // Composite return kinds for value types returned in two registers (encoded with two bits per register) + GCRK_Scalar_Obj = (GCRK_Object << 2) | GCRK_Scalar, + GCRK_Obj_Obj = (GCRK_Object << 2) | GCRK_Object, + GCRK_Byref_Obj = (GCRK_Object << 2) | GCRK_Byref, + GCRK_Scalar_Byref = (GCRK_Byref << 2) | GCRK_Scalar, + GCRK_Obj_Byref = (GCRK_Byref << 2) | GCRK_Object, + GCRK_Byref_Byref = (GCRK_Byref << 2) | GCRK_Byref, + + GCRK_LastValid = GCRK_Byref_Byref, +#else // TARGET_ARM64 GCRK_LastValid = GCRK_Byref, +#endif // TARGET_ARM64 GCRK_Unknown = 0xFF, }; -#if defined(TARGET_X86) +#ifdef TARGET_ARM64 +// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back +C_ASSERT(PTFF_X0_IS_GCREF == ((uint64_t)GCRK_Object << 32)); +C_ASSERT(PTFF_X0_IS_BYREF == ((uint64_t)GCRK_Byref << 32)); +C_ASSERT(PTFF_X1_IS_GCREF == ((uint64_t)GCRK_Scalar_Obj << 32)); +C_ASSERT(PTFF_X1_IS_BYREF == ((uint64_t)GCRK_Scalar_Byref << 32)); + +inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) +{ + // just need to report gc ref bits here. + // appropriate PTFF_SAVE_ bits will be added by the frame building routine. + return ((uint64_t)returnKind << 32); +} + +inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) +{ + GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_X0_IS_GCREF | PTFF_X0_IS_BYREF | PTFF_X1_IS_GCREF | PTFF_X1_IS_BYREF)) >> 32); + ASSERT((returnKind == GCRK_Scalar) || ((transFrameFlags & PTFF_SAVE_X0) && (transFrameFlags & PTFF_SAVE_X1))); + return returnKind; +} + +#elif defined(TARGET_LOONGARCH64) +// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back +C_ASSERT(PTFF_R4_IS_GCREF == ((uint64_t)GCRK_Object << 31)); +C_ASSERT(PTFF_R4_IS_BYREF == ((uint64_t)GCRK_Byref << 31)); +C_ASSERT(PTFF_R5_IS_GCREF == ((uint64_t)GCRK_Scalar_Obj << 31)); +C_ASSERT(PTFF_R5_IS_BYREF == ((uint64_t)GCRK_Scalar_Byref << 31)); + +inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) +{ + // just need to report gc ref bits here. + // appropriate PTFF_SAVE_ bits will be added by the frame building routine. + return ((uint64_t)returnKind << 31); +} + +inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) +{ + GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_R4_IS_GCREF | PTFF_R4_IS_BYREF | PTFF_R5_IS_GCREF | PTFF_R5_IS_BYREF)) >> 31); + ASSERT((returnKind == GCRK_Scalar) || ((transFrameFlags & PTFF_SAVE_R4) && (transFrameFlags & PTFF_SAVE_R5))); + return returnKind; +} + +#elif defined(TARGET_AMD64) + +// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back +C_ASSERT(PTFF_RAX_IS_GCREF == ((uint64_t)GCRK_Object << 16)); +C_ASSERT(PTFF_RAX_IS_BYREF == ((uint64_t)GCRK_Byref << 16)); +C_ASSERT(PTFF_RDX_IS_GCREF == ((uint64_t)GCRK_Scalar_Obj << 16)); +C_ASSERT(PTFF_RDX_IS_BYREF == ((uint64_t)GCRK_Scalar_Byref << 16)); + +inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) +{ + // just need to report gc ref bits here. + // appropriate PTFF_SAVE_ bits will be added by the frame building routine. + return ((uint64_t)returnKind << 16); +} + +inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) +{ + GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_RAX_IS_GCREF | PTFF_RAX_IS_BYREF | PTFF_RDX_IS_GCREF | PTFF_RDX_IS_BYREF)) >> 16); +#if defined(TARGET_UNIX) + ASSERT((returnKind == GCRK_Scalar) || ((transFrameFlags & PTFF_SAVE_RAX) && (transFrameFlags & PTFF_SAVE_RDX))); +#else + ASSERT((returnKind == GCRK_Scalar) || (transFrameFlags & PTFF_SAVE_RAX)); +#endif + return returnKind; +} + +#elif defined(TARGET_X86) + // Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back C_ASSERT(PTFF_RAX_IS_GCREF == ((uint64_t)GCRK_Object << 16)); C_ASSERT(PTFF_RAX_IS_BYREF == ((uint64_t)GCRK_Byref << 16)); @@ -51,13 +132,40 @@ inline GCRefKind TransitionFrameFlagsToReturnKind(uintptr_t transFrameFlags) return returnKind; } +#elif defined(TARGET_ARM) + +// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back +C_ASSERT(PTFF_R0_IS_GCREF == ((uint64_t)GCRK_Object << 14)); +C_ASSERT(PTFF_R0_IS_BYREF == ((uint64_t)GCRK_Byref << 14)); + +inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) +{ + // just need to report gc ref bits here. + // appropriate PTFF_SAVE_ bits will be added by the frame building routine. + return ((uint64_t)returnKind << 14); +} + +inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) +{ + GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_R0_IS_GCREF | PTFF_R0_IS_BYREF)) >> 14); + ASSERT((returnKind == GCRK_Scalar) || (transFrameFlags & PTFF_SAVE_R0)); + return returnKind; +} + +#endif + // Extract individual GCRefKind components from a composite return kind -inline GCRefKind ExtractReturnKind(GCRefKind returnKind) +inline GCRefKind ExtractReg0ReturnKind(GCRefKind returnKind) { ASSERT(returnKind <= GCRK_LastValid); return (GCRefKind)(returnKind & (GCRK_Object | GCRK_Byref)); } -#endif + +inline GCRefKind ExtractReg1ReturnKind(GCRefKind returnKind) +{ + ASSERT(returnKind <= GCRK_LastValid); + return (GCRefKind)(returnKind >> 2); +} // // MethodInfo is placeholder type used to allocate space for MethodInfo. Maximum size @@ -164,14 +272,9 @@ class ICodeManager virtual bool IsUnwindable(PTR_VOID pvAddress) PURE_VIRTUAL virtual bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, - REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation // out - ) PURE_VIRTUAL - -#ifdef TARGET_X86 - virtual GCRefKind GetReturnValueKind(MethodInfo * pMethodInfo, - REGDISPLAY * pRegisterSet) PURE_VIRTUAL -#endif + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind) PURE_VIRTUAL // out virtual PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC) PURE_VIRTUAL diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index fab1a9b6f66188..f2f208c89174c1 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -77,17 +77,9 @@ StackFrameIterator::StackFrameIterator(Thread * pThreadToWalk, PInvokeTransition { InternalInit(pThreadToWalk, pThreadToWalk->GetInterruptedContext(), GcStackWalkFlags | ActiveStackFrame); } - else if (pInitialTransitionFrame == TOP_OF_STACK_MARKER) - { - InternalInit(pThreadToWalk, pInitialTransitionFrame, GcStackWalkFlags); - } else { - uint32_t flags = (pInitialTransitionFrame->m_Flags & PTFF_THREAD_HIJACK) == 0 ? - GcStackWalkFlags : - GcStackWalkFlags | ActiveStackFrame; - - InternalInit(pThreadToWalk, pInitialTransitionFrame, flags); + InternalInit(pThreadToWalk, pInitialTransitionFrame, GcStackWalkFlags); } PrepareToYieldFrame(); @@ -118,10 +110,8 @@ void StackFrameIterator::EnterInitialInvalidState(Thread * pThreadToWalk) m_pThread = pThreadToWalk; m_pInstance = GetRuntimeInstance(); m_pCodeManager = NULL; -#ifdef TARGET_X86 m_pHijackedReturnValue = NULL; m_HijackedReturnValueKind = GCRK_Unknown; -#endif m_pConservativeStackRangeLowerBound = NULL; m_pConservativeStackRangeUpperBound = NULL; m_pendingFuncletFramePointer = NULL; @@ -190,6 +180,17 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF if (pFrame->m_Flags & PTFF_SAVE_R3) { m_RegDisplay.pR3 = pPreservedRegsCursor++; } if (pFrame->m_Flags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_R0_IS_GCREF) + { + m_pHijackedReturnValue = (PTR_OBJECTREF) m_RegDisplay.pR0; + m_HijackedReturnValueKind = GCRK_Object; + } + if (pFrame->m_Flags & PTFF_R0_IS_BYREF) + { + m_pHijackedReturnValue = (PTR_OBJECTREF) m_RegDisplay.pR0; + m_HijackedReturnValueKind = GCRK_Byref; + } + #elif defined(TARGET_ARM64) m_RegDisplay.pFP = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_FramePointer); m_RegDisplay.pLR = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_RIP); @@ -231,6 +232,13 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF if (pFrame->m_Flags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; } + GCRefKind retValueKind = TransitionFrameFlagsToReturnKind(pFrame->m_Flags); + if (retValueKind != GCRK_Scalar) + { + m_pHijackedReturnValue = (PTR_OBJECTREF)m_RegDisplay.pX0; + m_HijackedReturnValueKind = retValueKind; + } + #elif defined(TARGET_LOONGARCH64) m_RegDisplay.pFP = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_FramePointer); m_RegDisplay.pRA = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_RIP); @@ -272,6 +280,13 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF if (pFrame->m_Flags & PTFF_SAVE_RA) { m_RegDisplay.pRA = pPreservedRegsCursor++; } + GCRefKind retValueKind = TransitionFrameFlagsToReturnKind(pFrame->m_Flags); + if (retValueKind != GCRK_Scalar) + { + m_pHijackedReturnValue = (PTR_OBJECTREF)m_RegDisplay.pR4; + m_HijackedReturnValueKind = retValueKind; + } + #else // TARGET_ARM if (pFrame->m_Flags & PTFF_SAVE_RBX) { m_RegDisplay.pRbx = pPreservedRegsCursor++; } if (pFrame->m_Flags & PTFF_SAVE_RSI) { m_RegDisplay.pRsi = pPreservedRegsCursor++; } @@ -299,14 +314,12 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF if (pFrame->m_Flags & PTFF_SAVE_R11) { m_RegDisplay.pR11 = pPreservedRegsCursor++; } #endif // TARGET_AMD64 -#ifdef TARGET_X86 GCRefKind retValueKind = TransitionFrameFlagsToReturnKind(pFrame->m_Flags); if (retValueKind != GCRK_Scalar) { m_pHijackedReturnValue = (PTR_OBJECTREF)m_RegDisplay.pRax; m_HijackedReturnValueKind = retValueKind; } -#endif #endif // TARGET_ARM @@ -1516,10 +1529,8 @@ void StackFrameIterator::NextInternal() m_dwFlags &= ~(ExCollide|MethodStateCalculated|UnwoundReversePInvoke|ActiveStackFrame); ASSERT(IsValid()); -#ifdef TARGET_X86 m_pHijackedReturnValue = NULL; m_HijackedReturnValueKind = GCRK_Unknown; -#endif #ifdef _DEBUG SetControlPC(dac_cast((void*)666)); @@ -1933,7 +1944,6 @@ void StackFrameIterator::CalculateCurrentMethodState() m_dwFlags |= MethodStateCalculated; } -#ifdef TARGET_X86 bool StackFrameIterator::GetHijackedReturnValueLocation(PTR_OBJECTREF * pLocation, GCRefKind * pKind) { if (GCRK_Unknown == m_HijackedReturnValueKind) @@ -1945,7 +1955,6 @@ bool StackFrameIterator::GetHijackedReturnValueLocation(PTR_OBJECTREF * pLocatio *pKind = m_HijackedReturnValueKind; return true; } -#endif void StackFrameIterator::SetControlPC(PTR_VOID controlPC) { diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h index c3fecb9f84e29e..f174edd4c473b2 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h @@ -51,9 +51,7 @@ class StackFrameIterator PTR_ICodeManager GetCodeManager(); MethodInfo * GetMethodInfo(); bool IsActiveStackFrame(); -#ifdef TARGET_X86 bool GetHijackedReturnValueLocation(PTR_OBJECTREF * pLocation, GCRefKind * pKind); -#endif void SetControlPC(PTR_VOID controlPC); static bool IsValidReturnAddress(PTR_VOID pvAddress); @@ -223,12 +221,10 @@ class StackFrameIterator PTR_ICodeManager m_pCodeManager; MethodInfo m_methodInfo; PTR_VOID m_effectiveSafePointAddress; -#ifdef TARGET_X86 PTR_OBJECTREF m_pHijackedReturnValue; GCRefKind m_HijackedReturnValueKind; -#endif - PTR_uintptr_t m_pConservativeStackRangeLowerBound; - PTR_uintptr_t m_pConservativeStackRangeUpperBound; + PTR_uintptr_t m_pConservativeStackRangeLowerBound; + PTR_uintptr_t m_pConservativeStackRangeUpperBound; uint32_t m_dwFlags; PTR_ExInfo m_pNextExInfo; PTR_VOID m_pendingFuncletFramePointer; diff --git a/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc b/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc index 4a3437f2f0618b..41c43252317d9a 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc +++ b/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc @@ -356,8 +356,9 @@ PTFF_SAVE_ALL_PRESERVED equ 000000F7h ;; NOTE: RBP is not included in this set PTFF_SAVE_RSP equ 00008000h PTFF_SAVE_RAX equ 00000100h ;; RAX is saved in hijack handler - in case it contains a GC ref PTFF_SAVE_ALL_SCRATCH equ 00007F00h +PTFF_RAX_IS_GCREF equ 00010000h ;; iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar +PTFF_RAX_IS_BYREF equ 00020000h ;; iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar PTFF_THREAD_ABORT equ 00100000h ;; indicates that ThreadAbortException should be thrown when returning from the transition -PTFF_THREAD_HIJACK equ 00200000h ;; indicates that this is a frame for a hijacked call ;; These must match the TrapThreadsFlags enum TrapThreadsFlags_None equ 0 diff --git a/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h index afeb2a408851a4..2a2e85e5e72baa 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h @@ -8,7 +8,7 @@ // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix #ifndef UNIX_AMD64_ABI -PLAT_ASM_SIZEOF(250, ExInfo) +PLAT_ASM_SIZEOF(260, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -16,7 +16,7 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(240, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(250, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) @@ -24,12 +24,12 @@ PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(220, StackFrameIterator) +PLAT_ASM_SIZEOF(230, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(210, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(218, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(220, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(228, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(100, PAL_LIMITED_CONTEXT) PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) @@ -73,7 +73,7 @@ PLAT_ASM_OFFSET(90, REGDISPLAY, Xmm) #else // !UNIX_AMD64_ABI -PLAT_ASM_SIZEOF(190, ExInfo) +PLAT_ASM_SIZEOF(1a0, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -81,7 +81,7 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(188, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(198, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) @@ -89,12 +89,12 @@ PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(168, StackFrameIterator) +PLAT_ASM_SIZEOF(178, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(158, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(160, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(168, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(170, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(50, PAL_LIMITED_CONTEXT) PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) diff --git a/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S index ef4709055f0101..d8a08e76f4588c 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S @@ -76,6 +76,7 @@ // // Register state on exit: // R11: thread pointer +// RCX: return value flags // RAX, RDX preserved, other volatile regs trashed // .macro FixupHijackedCallstack @@ -94,10 +95,14 @@ mov rcx, [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress] push rcx + // Fetch the return address flags + mov rcx, [r11 + OFFSETOF__Thread__m_uHijackedReturnValueFlags] + // Clear hijack state - xor ecx, ecx - mov [r11 + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], rcx - mov [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress], rcx + xor r9, r9 + mov [r11 + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], r9 + mov [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress], r9 + mov [r11 + OFFSETOF__Thread__m_uHijackedReturnValueFlags], r9 .endm // @@ -112,7 +117,7 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler ret LOCAL_LABEL(WaitForGC): - mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RDX + PTFF_THREAD_HIJACK + or ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RDX jmp C_FUNC(RhpWaitForGC) NESTED_END RhpGcProbeHijack, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm index 7410d851d5a1c4..531762eedd8da4 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm +++ b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm @@ -76,6 +76,7 @@ endm ;; ;; Register state on exit: ;; RDX: thread pointer +;; RCX: return value flags ;; RAX: preserved, other volatile regs trashed ;; FixupHijackedCallstack macro @@ -86,10 +87,14 @@ FixupHijackedCallstack macro mov rcx, [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress] push rcx + ;; Fetch the return address flags + mov rcx, [rdx + OFFSETOF__Thread__m_uHijackedReturnValueFlags] + ;; Clear hijack state - xor ecx, ecx - mov [rdx + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], rcx - mov [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress], rcx + xor r9, r9 + mov [rdx + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], r9 + mov [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress], r9 + mov [rdx + OFFSETOF__Thread__m_uHijackedReturnValueFlags], r9 endm EXTERN RhpPInvokeExceptionGuard : PROC @@ -105,7 +110,7 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, RhpPInvokeExceptionGuard jnz @f ret @@: - mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_THREAD_HIJACK + or ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX jmp RhpWaitForGC NESTED_END RhpGcProbeHijack, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h index 5e7a0203df889b..ab5b991274b421 100644 --- a/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h @@ -7,7 +7,7 @@ // // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix -PLAT_ASM_SIZEOF(128, ExInfo) +PLAT_ASM_SIZEOF(130, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(4, ExInfo, m_pExContext) PLAT_ASM_OFFSET(8, ExInfo, m_exception) @@ -15,7 +15,7 @@ PLAT_ASM_OFFSET(0c, ExInfo, m_kind) PLAT_ASM_OFFSET(0d, ExInfo, m_passNumber) PLAT_ASM_OFFSET(10, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(18, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(120, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(128, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(4, PInvokeTransitionFrame, m_RIP) @@ -23,12 +23,12 @@ PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(c, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(108, StackFrameIterator) +PLAT_ASM_SIZEOF(110, StackFrameIterator) PLAT_ASM_OFFSET(08, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(0c, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(10, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(100, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(104, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(108, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(10c, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(70, PAL_LIMITED_CONTEXT) PLAT_ASM_OFFSET(24, PAL_LIMITED_CONTEXT, IP) diff --git a/src/coreclr/nativeaot/Runtime/arm/GcProbe.S b/src/coreclr/nativeaot/Runtime/arm/GcProbe.S index 0092be687bcec5..8277d9035b0d05 100644 --- a/src/coreclr/nativeaot/Runtime/arm/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/arm/GcProbe.S @@ -75,11 +75,13 @@ // Fix the stack by restoring the original return address ldr lr, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + ldr r12, [r2, #OFFSETOF__Thread__m_uHijackedReturnValueFlags] // Clear hijack state mov r3, #0 str r3, [r2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] str r3, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + str r3, [r2, #OFFSETOF__Thread__m_uHijackedReturnValueFlags] .endm NESTED_ENTRY RhpWaitForGC, _TEXT, NoHandler @@ -124,8 +126,8 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler bne LOCAL_LABEL(WaitForGC) bx lr LOCAL_LABEL(WaitForGC): - mov r12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0) - orr r12, r12, #PTFF_THREAD_HIJACK + mov r3, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0) + orr r12, r3 b RhpWaitForGC NESTED_END RhpGcProbeHijack diff --git a/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h b/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h index 90e1b5d7779947..2f6e83e2cf9b66 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h +++ b/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h @@ -60,10 +60,17 @@ PTFF_SAVE_ALL_SCRATCH equ 0x3FFFF800 ;; NOTE: X0-X18 PTFF_SAVE_FP equ 0x40000000 PTFF_SAVE_LR equ 0x80000000 -PTFF_THREAD_HIJACK_HI equ 0x00000002 // upper 32 bits of the PTFF_THREAD_HIJACK +;; NOTE: The following flags represent the upper 32 bits of the PInvokeTransitionFrameFlags. +;; Since the assembler doesn't support 64 bit constants in any way, we need to define just +;; the upper bits here +PTFF_X0_IS_GCREF_HI equ 0x00000001 ;; iff PTFF_SAVE_X0 : set->x0 is Object, clear->x0 is scalar +PTFF_X0_IS_BYREF_HI equ 0x00000002 ;; iff PTFF_SAVE_X0 : set->x0 is ByRef, clear->x0 is Object or scalar +PTFF_X1_IS_GCREF_HI equ 0x00000004 ;; iff PTFF_SAVE_X1 : set->x1 is Object, clear->x1 is scalar +PTFF_X1_IS_BYREF_HI equ 0x00000008 ;; iff PTFF_SAVE_X1 : set->x1 is ByRef, clear->x1 is Object or scalar +PTFF_THREAD_ABORT_HI equ 0x00000010 ;; indicates that ThreadAbortException should be thrown when returning from the transition ;; Bit position for the flags above, to be used with tbz / tbnz instructions -PTFF_THREAD_ABORT_BIT equ 32 +PTFF_THREAD_ABORT_BIT equ 36 ;; These must match the TrapThreadsFlags enum TrapThreadsFlags_None equ 0 diff --git a/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h index 62c34004d7613b..193692feae810a 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h @@ -7,7 +7,7 @@ // // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix -PLAT_ASM_SIZEOF(278, ExInfo) +PLAT_ASM_SIZEOF(288, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -15,7 +15,7 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(270, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(280, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_RIP) @@ -23,12 +23,12 @@ PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(250, StackFrameIterator) +PLAT_ASM_SIZEOF(260, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(240, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(248, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(250, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(258, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(C0, PAL_LIMITED_CONTEXT) diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S index b8ae78630a6eab..0a1b71eeae738f 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S @@ -93,6 +93,7 @@ // // Register state on exit: // x2: thread pointer +// x12: transition frame flags for the return registers x0 and x1 // .macro FixupHijackedCallstack @@ -106,13 +107,17 @@ // // Fix the stack by restoring the original return address // - ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + // Load m_pvHijackedReturnAddress and m_uHijackedReturnValueFlags + ldp lr, x12, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] // // Clear hijack state // // Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress stp xzr, xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + // Clear m_uHijackedReturnValueFlags + str xzr, [x2, #OFFSETOF__Thread__m_uHijackedReturnValueFlags] + .endm // @@ -126,8 +131,7 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler ret LOCAL_LABEL(WaitForGC): - mov x12, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0 + PTFF_SAVE_X1 - movk x12, PTFF_THREAD_HIJACK_HI, lsl #32 + orr x12, x12, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0 + PTFF_SAVE_X1 b C_FUNC(RhpWaitForGC) NESTED_END RhpGcProbeHijack diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm index b9048873ea6dea..12de9d9fe9a1da 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm @@ -105,6 +105,7 @@ PROBE_FRAME_SIZE field 0 ;; Register state on exit: ;; x2: thread pointer ;; x3: trashed +;; x12: transition frame flags for the return registers x0 and x1 ;; MACRO FixupHijackedCallstack @@ -115,7 +116,9 @@ PROBE_FRAME_SIZE field 0 ;; ;; Fix the stack by restoring the original return address ;; - ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + ASSERT OFFSETOF__Thread__m_uHijackedReturnValueFlags == (OFFSETOF__Thread__m_pvHijackedReturnAddress + 8) + ;; Load m_pvHijackedReturnAddress and m_uHijackedReturnValueFlags + ldp lr, x12, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] ;; ;; Clear hijack state @@ -123,6 +126,9 @@ PROBE_FRAME_SIZE field 0 ASSERT OFFSETOF__Thread__m_pvHijackedReturnAddress == (OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation + 8) ;; Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress stp xzr, xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + ;; Clear m_uHijackedReturnValueFlags + str xzr, [x2, #OFFSETOF__Thread__m_uHijackedReturnValueFlags] + MEND MACRO @@ -155,8 +161,7 @@ PROBE_FRAME_SIZE field 0 ret WaitForGC - mov x12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0 + PTFF_SAVE_X1) - movk x12, #PTFF_THREAD_HIJACK_HI, lsl #32 + orr x12, x12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0 + PTFF_SAVE_X1) b RhpWaitForGC NESTED_END RhpGcProbeHijackWrapper diff --git a/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc b/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc index 8ee2e79f44fde0..9541f73940215a 100644 --- a/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc +++ b/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc @@ -125,6 +125,8 @@ PTFF_SAVE_ALL_PRESERVED equ 00000007h ;; NOTE: RBP is not included in this set PTFF_SAVE_RSP equ 00008000h PTFF_SAVE_RAX equ 00000100h ;; RAX is saved if it contains a GC ref and we're in hijack handler PTFF_SAVE_ALL_SCRATCH equ 00000700h +PTFF_RAX_IS_GCREF equ 00010000h ;; iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar +PTFF_RAX_IS_BYREF equ 00020000h ;; iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar PTFF_THREAD_ABORT equ 00100000h ;; indicates that ThreadAbortException should be thrown when returning from the transition ;; These must match the TrapThreadsFlags enum diff --git a/src/coreclr/nativeaot/Runtime/inc/rhbinder.h b/src/coreclr/nativeaot/Runtime/inc/rhbinder.h index 84f10d2257ea57..21a15a01c905e4 100644 --- a/src/coreclr/nativeaot/Runtime/inc/rhbinder.h +++ b/src/coreclr/nativeaot/Runtime/inc/rhbinder.h @@ -271,9 +271,10 @@ enum PInvokeTransitionFrameFlags // a return address pointing into the hijacked method and that method's // lr register, which may hold a gc pointer - PTFF_THREAD_ABORT = 0x00004000, // indicates that ThreadAbortException should be thrown when returning from the transition + PTFF_R0_IS_GCREF = 0x00004000, // used by hijack handler to report return value of hijacked method + PTFF_R0_IS_BYREF = 0x00008000, // used by hijack handler to report return value of hijacked method - PTFF_THREAD_HIJACK = 0x00008000, // indicates that this is a frame for a hijacked call + PTFF_THREAD_ABORT = 0x00010000, // indicates that ThreadAbortException should be thrown when returning from the transition }; #elif defined(TARGET_ARM64) enum PInvokeTransitionFrameFlags : uint64_t @@ -328,9 +329,13 @@ enum PInvokeTransitionFrameFlags : uint64_t // a return address pointing into the hijacked method and that method's // lr register, which may hold a gc pointer - PTFF_THREAD_ABORT = 0x0000000100000000, // indicates that ThreadAbortException should be thrown when returning from the transition + // used by hijack handler to report return value of hijacked method + PTFF_X0_IS_GCREF = 0x0000000100000000, + PTFF_X0_IS_BYREF = 0x0000000200000000, + PTFF_X1_IS_GCREF = 0x0000000400000000, + PTFF_X1_IS_BYREF = 0x0000000800000000, - PTFF_THREAD_HIJACK = 0x0000000200000000, // indicates that this is a frame for a hijacked call + PTFF_THREAD_ABORT = 0x0000001000000000, // indicates that ThreadAbortException should be thrown when returning from the transition }; #elif defined(TARGET_LOONGARCH64) @@ -385,9 +390,13 @@ enum PInvokeTransitionFrameFlags : uint64_t // a return address pointing into the hijacked method and that method's // ra register, which may hold a gc pointer - PTFF_THREAD_ABORT = 0x0000000080000000, // indicates that ThreadAbortException should be thrown when returning from the transition + // used by hijack handler to report return value of hijacked method + PTFF_R4_IS_GCREF = 0x0000000080000000, + PTFF_R4_IS_BYREF = 0x0000000100000000, + PTFF_R5_IS_GCREF = 0x0000000200000000, + PTFF_R5_IS_BYREF = 0x0000000400000000, - PTFF_THREAD_HIJACK = 0x0000000100000000, // indicates that this is a frame for a hijacked call + PTFF_THREAD_ABORT = 0x0000000800000000, // indicates that ThreadAbortException should be thrown when returning from the transition }; #else // TARGET_ARM @@ -425,14 +434,12 @@ enum PInvokeTransitionFrameFlags PTFF_SAVE_R10 = 0x00002000, PTFF_SAVE_R11 = 0x00004000, -#if defined(TARGET_X86) PTFF_RAX_IS_GCREF = 0x00010000, // used by hijack handler to report return value of hijacked method - PTFF_RAX_IS_BYREF = 0x00020000, -#endif + PTFF_RAX_IS_BYREF = 0x00020000, + PTFF_RDX_IS_GCREF = 0x00040000, + PTFF_RDX_IS_BYREF = 0x00080000, PTFF_THREAD_ABORT = 0x00100000, // indicates that ThreadAbortException should be thrown when returning from the transition - - PTFF_THREAD_HIJACK = 0x00200000, // indicates that this is a frame for a hijacked call }; #endif // TARGET_ARM diff --git a/src/coreclr/nativeaot/Runtime/loongarch64/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/loongarch64/AsmOffsetsCpu.h index 0f48b5f227f8e7..cf6c7f5ae690ab 100644 --- a/src/coreclr/nativeaot/Runtime/loongarch64/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/loongarch64/AsmOffsetsCpu.h @@ -7,7 +7,7 @@ // // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix -PLAT_ASM_SIZEOF(270, ExInfo) +PLAT_ASM_SIZEOF(280, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -15,7 +15,7 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(268, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(278, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_RIP) @@ -23,12 +23,12 @@ PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(248, StackFrameIterator) +PLAT_ASM_SIZEOF(258, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(238, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(240, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(248, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(250, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(B8, PAL_LIMITED_CONTEXT) diff --git a/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S b/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S index d5b7d7f3526496..a581021fb56026 100644 --- a/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S @@ -98,6 +98,7 @@ // // Register state on exit: // a2: thread pointer +// t3: transition frame flags for the return registers a0 and a1 // .macro FixupHijackedCallstack @@ -111,8 +112,9 @@ // // Fix the stack by restoring the original return address // - // Load m_pvHijackedReturnAddress + // Load m_pvHijackedReturnAddress and m_uHijackedReturnValueFlags ld.d $ra, $a2, OFFSETOF__Thread__m_pvHijackedReturnAddress + ld.d $t3, $a2, OFFSETOF__Thread__m_pvHijackedReturnAddress + 8 // // Clear hijack state @@ -120,6 +122,9 @@ // Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress st.d $zero, $a2, OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation st.d $zero, $a2, OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation + 8 + // Clear m_uHijackedReturnValueFlags + st.d $zero, $a2, OFFSETOF__Thread__m_uHijackedReturnValueFlags + .endm // @@ -134,8 +139,9 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler jirl $r0, $ra, 0 LOCAL_LABEL(WaitForGC): - lu12i.w $t3, ((DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R4 + PTFF_SAVE_R5 + PTFF_THREAD_HIJACK) >> 12) & 0xfffff - ori $t3, $t3, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R4 + PTFF_SAVE_R5 + PTFF_THREAD_HIJACK) & 0xfff + lu12i.w $t7, ((DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R4 + PTFF_SAVE_R5) >> 12) & 0xfffff + ori $t7, $t7, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R4 + PTFF_SAVE_R5) & 0xfff + or $t3, $t3, $t7 b C_FUNC(RhpWaitForGC) NESTED_END RhpGcProbeHijack diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index aad728782b18be..76beff6c96cf13 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -451,16 +451,24 @@ void Thread::GcScanRootsWorker(ScanFunc * pfnEnumCallback, ScanContext * pvCallb PTR_OBJECTREF pHijackedReturnValue = NULL; GCRefKind returnValueKind = GCRK_Unknown; -#ifdef TARGET_X86 if (frameIterator.GetHijackedReturnValueLocation(&pHijackedReturnValue, &returnValueKind)) { - GCRefKind returnKind = ExtractReturnKind(returnValueKind); - if (returnKind != GCRK_Scalar) + GCRefKind reg0Kind = ExtractReg0ReturnKind(returnValueKind); + if (reg0Kind != GCRK_Scalar) + { + EnumGcRef(pHijackedReturnValue, reg0Kind, pfnEnumCallback, pvCallbackData); + } + +#if defined(TARGET_ARM64) || defined(TARGET_UNIX) + GCRefKind reg1Kind = ExtractReg1ReturnKind(returnValueKind); + if (reg1Kind != GCRK_Scalar) { - EnumGcRef(pHijackedReturnValue, returnKind, pfnEnumCallback, pvCallbackData); + // X0/X1 or RAX/RDX are saved in hijack frame next to each other in this order + EnumGcRef(pHijackedReturnValue + 1, reg1Kind, pfnEnumCallback, pvCallbackData); } +#endif // TARGET_ARM64 || TARGET_UNIX + } -#endif #ifndef DACCESS_COMPILE if (GetRuntimeInstance()->IsConservativeStackReportingEnabled()) @@ -785,11 +793,13 @@ void Thread::HijackReturnAddress(NATIVE_CONTEXT* pSuspendCtx, HijackFunc* pfnHij void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, HijackFunc* pfnHijackFunction) { void** ppvRetAddrLocation; + GCRefKind retValueKind; frameIterator->CalculateCurrentMethodState(); if (frameIterator->GetCodeManager()->GetReturnAddressHijackInfo(frameIterator->GetMethodInfo(), frameIterator->GetRegisterSet(), - &ppvRetAddrLocation)) + &ppvRetAddrLocation, + &retValueKind)) { ASSERT(ppvRetAddrLocation != NULL); @@ -806,12 +816,7 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack m_ppvHijackedReturnAddressLocation = ppvRetAddrLocation; m_pvHijackedReturnAddress = pvRetAddr; -#if defined(TARGET_X86) - m_uHijackedReturnValueFlags = ReturnKindToTransitionFrameFlags( - frameIterator->GetCodeManager()->GetReturnValueKind(frameIterator->GetMethodInfo(), - frameIterator->GetRegisterSet())); -#endif - + m_uHijackedReturnValueFlags = ReturnKindToTransitionFrameFlags(retValueKind); *ppvRetAddrLocation = (void*)pfnHijackFunction; STRESS_LOG2(LF_STACKWALK, LL_INFO10000, "InternalHijack: TgtThread = %llx, IP = %p\n", @@ -945,9 +950,7 @@ void Thread::UnhijackWorker() // Clear the hijack state. m_ppvHijackedReturnAddressLocation = NULL; m_pvHijackedReturnAddress = NULL; -#ifdef TARGET_X86 m_uHijackedReturnValueFlags = 0; -#endif } bool Thread::IsHijacked() @@ -1359,4 +1362,4 @@ EXTERN_C void QCALLTYPE RhSetCurrentThreadName(const TCHAR* name) #else PalSetCurrentThreadName(name); #endif -} +} \ No newline at end of file diff --git a/src/coreclr/nativeaot/Runtime/thread.h b/src/coreclr/nativeaot/Runtime/thread.h index 7252e20e84a3ad..c75ec9a5c4a722 100644 --- a/src/coreclr/nativeaot/Runtime/thread.h +++ b/src/coreclr/nativeaot/Runtime/thread.h @@ -142,11 +142,11 @@ struct RuntimeThreadLocals #ifdef FEATURE_HIJACK void ** m_ppvHijackedReturnAddressLocation; void * m_pvHijackedReturnAddress; + uintptr_t m_uHijackedReturnValueFlags; #endif // FEATURE_HIJACK PTR_ExInfo m_pExInfoStackHead; Object* m_threadAbortException; // ThreadAbortException instance -set only during thread abort #ifdef TARGET_X86 - uintptr_t m_uHijackedReturnValueFlags; PCODE m_LastRedirectIP; uint64_t m_SpinCount; #endif diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index 7f1fdfecc58662..9f982f630bcec7 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -1168,7 +1168,8 @@ GCRefKind GetGcRefKind(ReturnKind returnKind) bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation) // out + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind) // out { UnixNativeMethodInfo* pNativeMethodInfo = (UnixNativeMethodInfo*)pMethodInfo; @@ -1185,6 +1186,21 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) return false; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + // Decode the GC info for the current method to determine its return type + GcInfoDecoderFlags flags = DECODE_RETURN_KIND; +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) + flags = (GcInfoDecoderFlags)(flags | DECODE_HAS_TAILCALLS); +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 + + GcInfoDecoder decoder(GCInfoToken(p), flags); + *pRetValueKind = GetGcRefKind(decoder.GetReturnKind()); + #if defined(TARGET_ARM) // Ensure that PC doesn't have the Thumb bit set. Prolog and epilog // checks depend on it. @@ -1231,17 +1247,8 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn *ppvRetAddrLocation = (PTR_PTR_VOID)(pRegisterSet->GetSP() - sizeof(TADDR)); return true; -#elif defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) - - if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) - p += sizeof(int32_t); - - if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) - p += sizeof(int32_t); +#elif defined(TARGET_ARM64) || defined(TARGET_ARM) - // Decode the GC info for the current method to determine if there are tailcalls - GcInfoDecoderFlags flags = DECODE_HAS_TAILCALLS; - GcInfoDecoder decoder(GCInfoToken(p), flags); if (decoder.HasTailCalls()) { // Do not hijack functions that have tail calls, since there are two problems: @@ -1256,7 +1263,6 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn return false; } -#ifndef TARGET_LOONGARCH64 PTR_uintptr_t pLR = pRegisterSet->pLR; if (!VirtualUnwind(pMethodInfo, pRegisterSet)) { @@ -1274,7 +1280,24 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn } *ppvRetAddrLocation = (PTR_PTR_VOID)pRegisterSet->pLR; -#elif + return true; + +#elif defined(TARGET_LOONGARCH64) + + if (decoder.HasTailCalls()) + { + // Do not hijack functions that have tail calls, since there are two problems: + // 1. When a function that tail calls another one is hijacked, the RA may be + // stored at a different location in the stack frame of the tail call target. + // So just by performing tail call, the hijacked location becomes invalid and + // unhijacking would corrupt stack by writing to that location. + // 2. There is a small window after the caller pops RA from the stack in its + // epilog and before the tail called function pushes RA in its prolog when + // the hijacked return address would not be not on the stack and so we would + // not be able to unhijack. + return false; + } + PTR_uintptr_t pRA = pRegisterSet->pRA; if (!VirtualUnwind(pMethodInfo, pRegisterSet)) { @@ -1292,8 +1315,6 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn } *ppvRetAddrLocation = (PTR_PTR_VOID)pRegisterSet->pRA; -#endif // TARGET_LOONGARCH64 - return true; #else return false; diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h index ad9dd8cfed24c1..d7bfe94ac5ff4e 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h @@ -69,7 +69,8 @@ class UnixNativeCodeManager : public ICodeManager bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation); // out + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind); // out PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC); diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc index e4195ac8ac8311..05667a351a9d8f 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc @@ -263,7 +263,6 @@ C_FUNC(\Name): #define PTFF_SAVE_RDX 0x00000400 // RDX is saved in hijack handler - in case it contains a GC ref #define PTFF_SAVE_ALL_SCRATCH 0x00007F00 #define PTFF_THREAD_ABORT 0x00100000 // indicates that ThreadAbortException should be thrown when returning from the transition -#define PTFF_THREAD_HIJACK 0x00200000 // indicates that this is a frame for a hijacked call // These must match the TrapThreadsFlags enum #define TrapThreadsFlags_None 0 diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc index 8aeb084f8e3cd7..4ccd38b19c7bef 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc @@ -18,8 +18,7 @@ #define PTFF_SAVE_R9 0x00000020 #define PTFF_SAVE_SP 0x00000100 #define PTFF_SAVE_R0 0x00000200 -#define PTFF_THREAD_ABORT 0x00004000 -#define PTFF_THREAD_HIJACK 0x00008000 // indicates that this is a frame for a hijacked call +#define PTFF_THREAD_ABORT 0x00100000 #define DEFAULT_FRAME_SAVE_FLAGS (PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP) diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc index 36698fece5050b..a02a770aa3b0af 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc @@ -306,7 +306,6 @@ C_FUNC(\Name): #define PTFF_SAVE_X0 0x00000800 #define PTFF_SAVE_X1 0x00001000 #define PTFF_SAVE_ALL_PRESERVED 0x000003FF // NOTE: x19-x28 -#define PTFF_THREAD_HIJACK_HI 0x00000002 // upper 32 bits of the PTFF_THREAD_HIJACK #define DEFAULT_FRAME_SAVE_FLAGS (PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP) @@ -346,7 +345,7 @@ C_FUNC(\Name): .endm // Bit position for the flags above, to be used with tbz / tbnz instructions -#define PTFF_THREAD_ABORT_BIT 32 +#define PTFF_THREAD_ABORT_BIT 36 // // CONSTANTS -- INTEGER diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc index f8b9d1aa633351..a035a3558cc632 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc @@ -309,7 +309,7 @@ C_FUNC(\Name): .endm // Bit position for the flags above, to be used with bstrpick.d+beq/bne instructions -#define PTFF_THREAD_ABORT_BIT 31 +#define PTFF_THREAD_ABORT_BIT 35 // // CONSTANTS -- INTEGER diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp index 5fe6e446702836..66c775e0e2c05e 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -860,7 +860,8 @@ GCRefKind GetGcRefKind(ReturnKind returnKind) bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation) // out + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind) // out { CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; @@ -871,6 +872,9 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + // Check whether this is a funclet if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT) return false; @@ -880,7 +884,19 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) return false; + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + #ifdef USE_GC_INFO_DECODER + // Decode the GC info for the current method to determine its return type + GcInfoDecoderFlags flags = DECODE_RETURN_KIND; +#if defined(TARGET_ARM64) + flags = (GcInfoDecoderFlags)(flags | DECODE_HAS_TAILCALLS); +#endif // TARGET_ARM64 + GcInfoDecoder decoder(GCInfoToken(p), flags); + + *pRetValueKind = GetGcRefKind(decoder.GetReturnKind()); + // Unwind the current method context to the caller's context to get its stack pointer // and obtain the location of the return address on the stack SIZE_T EstablisherFrame; @@ -908,15 +924,6 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn return true; #elif defined(TARGET_ARM64) - if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) - p += sizeof(int32_t); - - if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) - p += sizeof(int32_t); - - // Decode the GC info for the current method to determine if there are tailcalls - GcInfoDecoderFlags flags = DECODE_HAS_TAILCALLS; - GcInfoDecoder decoder(GCInfoToken(p), flags); if (decoder.HasTailCalls()) { // Do not hijack functions that have tail calls, since there are two problems: @@ -995,22 +1002,12 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn } *ppvRetAddrLocation = (PTR_PTR_VOID)registerSet.PCTAddr; + *pRetValueKind = GetGcRefKind(infoBuf.returnKind); + return true; #endif } -#ifdef TARGET_X86 -GCRefKind CoffNativeCodeManager::GetReturnValueKind(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet) -{ - PTR_uint8_t gcInfo; - uint32_t codeOffset = GetCodeOffset(pMethodInfo, (PTR_VOID)pRegisterSet->IP, &gcInfo); - hdrInfo infoBuf; - size_t infoSize = DecodeGCHdrInfo(GCInfoToken(gcInfo), codeOffset, &infoBuf); - - return GetGcRefKind(infoBuf.returnKind); -} -#endif - PTR_VOID CoffNativeCodeManager::RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC) { // GCInfo decoder needs to know whether execution of the method is aborted diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h index c85f5250967793..c1dacbfd8f9866 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h @@ -91,13 +91,9 @@ class CoffNativeCodeManager : public ICodeManager bool IsUnwindable(PTR_VOID pvAddress); bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, - REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation); // out - -#ifdef TARGET_X86 - GCRefKind GetReturnValueKind(MethodInfo * pMethodInfo, - REGDISPLAY * pRegisterSet); -#endif + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation, // out + GCRefKind * pRetValueKind); // out PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC); diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index cef0bea5d64793..18ccde9888fb7b 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -1676,7 +1676,6 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + sizeof(struct HijackArgs); pRD->pCurrentContext->R0 = m_Args->R0; - pRD->volatileCurrContextPointers.R0 = &m_Args->R0; pRD->pCurrentContext->R4 = m_Args->R4; pRD->pCurrentContext->R5 = m_Args->R5; diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index 8f30ba580e39e2..6cf5a7ec845896 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -789,10 +789,6 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ; pRD->pCurrentContext->X0 = m_Args->X0; - pRD->pCurrentContext->X1 = m_Args->X1; - - pRD->volatileCurrContextPointers.X0 = &m_Args->X0; - pRD->volatileCurrContextPointers.X1 = &m_Args->X1; pRD->pCurrentContext->X19 = m_Args->X19; pRD->pCurrentContext->X20 = m_Args->X20; diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp index 25a4c73b65ea0b..c0655aa471c1c8 100644 --- a/src/coreclr/vm/eetwain.cpp +++ b/src/coreclr/vm/eetwain.cpp @@ -2122,7 +2122,7 @@ size_t EECodeManager::GetFunctionSize(GCInfoToken gcInfoToken) * returns true. * If hijacking is not possible for some reason, it return false. */ -bool EECodeManager::GetReturnAddressHijackInfo(GCInfoToken gcInfoToken X86_ARG(ReturnKind * returnKind)) +bool EECodeManager::GetReturnAddressHijackInfo(GCInfoToken gcInfoToken, ReturnKind * returnKind) { CONTRACTL{ NOTHROW; @@ -2145,7 +2145,7 @@ bool EECodeManager::GetReturnAddressHijackInfo(GCInfoToken gcInfoToken X86_ARG(R return true; #else // !USE_GC_INFO_DECODER - GcInfoDecoder gcInfoDecoder(gcInfoToken, GcInfoDecoderFlags(DECODE_REVERSE_PINVOKE_VAR)); + GcInfoDecoder gcInfoDecoder(gcInfoToken, GcInfoDecoderFlags(DECODE_RETURN_KIND | DECODE_REVERSE_PINVOKE_VAR)); if (gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME) { @@ -2153,6 +2153,7 @@ bool EECodeManager::GetReturnAddressHijackInfo(GCInfoToken gcInfoToken X86_ARG(R return false; } + *returnKind = gcInfoDecoder.GetReturnKind(); return true; #endif // USE_GC_INFO_DECODER } diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index 4b3fd5279303be..e6afc8995ef8e1 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -1202,7 +1202,7 @@ BOOL IsProtectedByGCFrame(OBJECTREF *ppObjectRef) #endif //!DACCESS_COMPILE #ifdef FEATURE_HIJACK -#ifdef TARGET_X86 + void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc) { LIMITED_METHOD_CONTRACT; @@ -1220,7 +1220,9 @@ void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc) switch (r) { +#ifdef TARGET_X86 case RT_Float: // Fall through +#endif case RT_Scalar: // nothing to report break; @@ -1246,7 +1248,7 @@ void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc) regNo++; } while (moreRegisters); } -#endif // TARGET_X86 + #endif // FEATURE_HIJACK void ProtectByRefsFrame::GcScanRoots(promote_func *fn, ScanContext *sc) diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 8fb46b6041fa5a..4ea6e33341d0a3 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -2097,22 +2097,7 @@ class HijackFrame : public Frame } virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); - -#ifdef TARGET_X86 - // On x86 we need to specialcase return values virtual void GcScanRoots(promote_func *fn, ScanContext* sc); -#else - // On non-x86 platforms HijackFrame is just a more compact form of a resumable - // frame with main difference that OnHijackTripThread captures just the registers - // that can possibly contain GC roots. - // The regular reporting of a top frame will report everything that is live - // after the call as specified in GC info, thus we do not need to worry about - // return values. - virtual unsigned GetFrameAttribs() { - LIMITED_METHOD_DAC_CONTRACT; - return FRAME_ATTR_RESUMABLE; // Treat the next frame as the top frame. - } -#endif // HijackFrames are created by trip functions. See OnHijackTripThread() // They are real C++ objects on the stack. diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp index 4197e456d8263f..40c5c686c6583a 100644 --- a/src/coreclr/vm/gcinfodecoder.cpp +++ b/src/coreclr/vm/gcinfodecoder.cpp @@ -91,10 +91,9 @@ bool GcInfoDecoder::PredecodeFatHeader(int remainingFlags) int numFlagBits = (m_Version == 1) ? GC_INFO_FLAGS_BIT_SIZE_VERSION_1 : GC_INFO_FLAGS_BIT_SIZE; m_headerFlags = (GcInfoHeaderFlags)m_Reader.Read(numFlagBits); - // skip over the unused return kind. - m_Reader.Read(SIZE_OF_RETURN_KIND_IN_FAT_HEADER); + m_ReturnKind = (ReturnKind)((UINT32)m_Reader.Read(SIZE_OF_RETURN_KIND_IN_FAT_HEADER)); - remainingFlags &= ~DECODE_VARARG; + remainingFlags &= ~(DECODE_RETURN_KIND | DECODE_VARARG); #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) remainingFlags &= ~DECODE_HAS_TAILCALLS; #endif @@ -260,6 +259,7 @@ GcInfoDecoder::GcInfoDecoder( : m_Reader(dac_cast(gcInfoToken.Info)) , m_InstructionOffset(breakOffset) , m_IsInterruptible(false) + , m_ReturnKind(RT_Illegal) #ifdef _DEBUG , m_Flags( flags ) , m_GcInfoAddress(dac_cast(gcInfoToken.Info)) @@ -299,10 +299,9 @@ GcInfoDecoder::GcInfoDecoder( m_StackBaseRegister = NO_STACK_BASE_REGISTER; } - // skip over the unused return kind. - m_Reader.Read(SIZE_OF_RETURN_KIND_IN_SLIM_HEADER); + m_ReturnKind = (ReturnKind)((UINT32)m_Reader.Read(SIZE_OF_RETURN_KIND_IN_SLIM_HEADER)); - remainingFlags &= ~DECODE_VARARG; + remainingFlags &= ~(DECODE_RETURN_KIND | DECODE_VARARG); #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) remainingFlags &= ~DECODE_HAS_TAILCALLS; #endif @@ -659,6 +658,13 @@ UINT32 GcInfoDecoder::GetCodeLength() return m_CodeLength; } +ReturnKind GcInfoDecoder::GetReturnKind() +{ + // SUPPORTS_DAC; + _ASSERTE( m_Flags & DECODE_RETURN_KIND ); + return m_ReturnKind; +} + UINT32 GcInfoDecoder::GetStackBaseRegister() { return m_StackBaseRegister; diff --git a/src/coreclr/vm/loongarch64/stubs.cpp b/src/coreclr/vm/loongarch64/stubs.cpp index a73727ee45b783..fc047df10fbd45 100644 --- a/src/coreclr/vm/loongarch64/stubs.cpp +++ b/src/coreclr/vm/loongarch64/stubs.cpp @@ -819,10 +819,6 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ; pRD->pCurrentContext->A0 = m_Args->A0; - pRD->pCurrentContext->A1 = m_Args->A1; - - pRD->volatileCurrContextPointers.A0 = &m_Args->A0; - pRD->volatileCurrContextPointers.A1 = &m_Args->A1; pRD->pCurrentContext->S0 = m_Args->S0; pRD->pCurrentContext->S1 = m_Args->S1; diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 026a43c4821906..c170b004388931 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -739,11 +739,6 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ; pRD->pCurrentContext->A0 = m_Args->A0; - pRD->pCurrentContext->A1 = m_Args->A1; - - pRD->volatileCurrContextPointers.A0 = &m_Args->A0; - pRD->volatileCurrContextPointers.A1 = &m_Args->A1; - pRD->pCurrentContext->S1 = m_Args->S1; pRD->pCurrentContext->S2 = m_Args->S2; diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 8951a68905a237..e89b6c7d94c1d0 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1498,10 +1498,7 @@ Thread::Thread() #ifdef FEATURE_PERFTRACING memset(&m_activityId, 0, sizeof(m_activityId)); #endif // FEATURE_PERFTRACING - -#ifdef TARGET_X86 m_HijackReturnKind = RT_Illegal; -#endif m_currentPrepareCodeConfig = nullptr; m_isInForbidSuspendForDebuggerRegion = false; diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 40b182b728ef7f..2e125d4ddd78be 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -2672,7 +2672,7 @@ class Thread private: #ifdef FEATURE_HIJACK - void HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)); + void HijackThread(ReturnKind returnKind, ExecutionState *esb); VOID *m_pvHJRetAddr; // original return address (before hijack) VOID **m_ppvHJRetAddrPtr; // place we bashed a new return address @@ -3843,14 +3843,15 @@ class Thread #endif // FEATURE_PERFTRACING #ifdef FEATURE_HIJACK - -#ifdef TARGET_X86 private: + // By the time a frame is scanned by the runtime, m_pHijackReturnKind always // identifies the gc-ness of the return register(s) + ReturnKind m_HijackReturnKind; public: + ReturnKind GetHijackReturnKind() { LIMITED_METHOD_CONTRACT; @@ -3864,7 +3865,6 @@ class Thread m_HijackReturnKind = returnKind; } -#endif #endif // FEATURE_HIJACK public: diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index a8e17f902ab0e8..505ceb174684f3 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -4546,7 +4546,7 @@ struct ExecutionState }; // Client is responsible for suspending the thread before calling -void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) +void Thread::HijackThread(ReturnKind returnKind, ExecutionState *esb) { CONTRACTL { NOTHROW; @@ -4554,6 +4554,8 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) } CONTRACTL_END; + _ASSERTE(IsValidReturnKind(returnKind)); + VOID *pvHijackAddr = reinterpret_cast(OnHijackTripThread); #if defined(TARGET_WINDOWS) @@ -4565,13 +4567,10 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) #endif // TARGET_WINDOWS #ifdef TARGET_X86 - _ASSERTE(IsValidReturnKind(returnKind)); if (returnKind == RT_Float) { pvHijackAddr = reinterpret_cast(OnHijackFPTripThread); } - - SetHijackReturnKind(returnKind); #endif // TARGET_X86 // Don't hijack if are in the first level of running a filter/finally/catch. @@ -4591,6 +4590,8 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) return; } + SetHijackReturnKind(returnKind); + if (m_State & TS_Hijacked) UnhijackThread(); @@ -4908,10 +4909,10 @@ void STDCALL OnHijackWorker(HijackArgs * pArgs) #endif // HIJACK_NONINTERRUPTIBLE_THREADS } -static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo X86_ARG(ReturnKind * returnKind)) +static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo, ReturnKind *pReturnKind) { GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken(); - return pCodeInfo->GetCodeManager()->GetReturnAddressHijackInfo(gcInfoToken X86_ARG(returnKind)); + return pCodeInfo->GetCodeManager()->GetReturnAddressHijackInfo(gcInfoToken, pReturnKind); } #ifndef TARGET_UNIX @@ -5311,10 +5312,11 @@ BOOL Thread::HandledJITCase() // it or not. EECodeInfo codeInfo(ip); - X86_ONLY(ReturnKind returnKind;) - if (GetReturnAddressHijackInfo(&codeInfo X86_ARG(&returnKind))) + ReturnKind returnKind; + + if (GetReturnAddressHijackInfo(&codeInfo, &returnKind)) { - HijackThread(&esb X86_ARG(returnKind)); + HijackThread(returnKind, &esb); } } } @@ -5850,8 +5852,9 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext) if (executionState.m_ppvRetAddrPtr == NULL) return; - X86_ONLY(ReturnKind returnKind;) - if (!GetReturnAddressHijackInfo(&codeInfo X86_ARG(&returnKind))) + ReturnKind returnKind; + + if (!GetReturnAddressHijackInfo(&codeInfo, &returnKind)) { return; } @@ -5865,7 +5868,7 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext) StackWalkerWalkingThreadHolder threadStackWalking(pThread); // Hijack the return address to point to the appropriate routine based on the method's return type. - pThread->HijackThread(&executionState X86_ARG(returnKind)); + pThread->HijackThread(returnKind, &executionState); } }