diff --git a/src/coreclr/inc/cfi.h b/src/coreclr/inc/cfi.h index 3c8e3d05c04980..a0f4c246a4b0b7 100644 --- a/src/coreclr/inc/cfi.h +++ b/src/coreclr/inc/cfi.h @@ -10,6 +10,7 @@ enum CFI_OPCODE CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one. CFI_DEF_CFA_REGISTER, // New register is used to compute CFA CFI_REL_OFFSET, // Register is saved at offset from the current CFA + CFI_DEF_CFA, // Take address from register and add offset to it CFI_NEGATE_RA_STATE, // Sign the return address in lr with paciasp }; diff --git a/src/coreclr/inc/clrnt.h b/src/coreclr/inc/clrnt.h index 0e3de58d2c8e04..f0c72b682b465e 100644 --- a/src/coreclr/inc/clrnt.h +++ b/src/coreclr/inc/clrnt.h @@ -333,7 +333,7 @@ RtlpGetFunctionEndAddress ( else { // Get from the xdata record. - FunctionLength = *(PTR_ULONG64)(ImageBase + FunctionLength) & 0x3ffff; + FunctionLength = *(PTR_uint64_t)(ImageBase + FunctionLength) & 0x3ffff; } return FunctionEntry->BeginAddress + 4 * FunctionLength; @@ -422,7 +422,7 @@ RtlpGetFunctionEndAddress ( if ((FunctionLength & 3) != 0) { FunctionLength = (FunctionLength >> 2) & 0x7ff; } else { - FunctionLength = *(PTR_ULONG64)(ImageBase + FunctionLength) & 0x3ffff; + FunctionLength = *(PTR_uint64_t)(ImageBase + FunctionLength) & 0x3ffff; } return FunctionEntry->BeginAddress + 4 * FunctionLength; @@ -478,7 +478,7 @@ RtlpGetFunctionEndAddress ( if ((FunctionLength & 3) != 0) { FunctionLength = (FunctionLength >> 2) & 0x7ff; } else { - FunctionLength = *(PTR_ULONG64)(ImageBase + FunctionLength) & 0x3ffff; + FunctionLength = *(PTR_uint64_t)(ImageBase + FunctionLength) & 0x3ffff; } return FunctionEntry->BeginAddress + 2 * FunctionLength; diff --git a/src/coreclr/inc/daccess.h b/src/coreclr/inc/daccess.h index b3f97772c24ef3..883843de0ba97c 100644 --- a/src/coreclr/inc/daccess.h +++ b/src/coreclr/inc/daccess.h @@ -2372,6 +2372,10 @@ typedef DPTR(TADDR) PTR_TADDR; #ifndef NATIVEAOT typedef ArrayDPTR(BYTE) PTR_BYTE; +typedef DPTR(WORD) PTR_WORD; +typedef DPTR(DWORD) PTR_DWORD; +typedef DPTR(ULONG64) PTR_ULONG64; +typedef DPTR(UINT64) PTR_UINT64; typedef DPTR(PTR_BYTE) PTR_PTR_BYTE; typedef DPTR(PTR_PTR_BYTE) PTR_PTR_PTR_BYTE; typedef ArrayDPTR(signed char) PTR_SBYTE; @@ -2379,16 +2383,12 @@ typedef ArrayDPTR(const BYTE) PTR_CBYTE; typedef DPTR(INT8) PTR_INT8; typedef DPTR(INT16) PTR_INT16; typedef DPTR(UINT16) PTR_UINT16; -typedef DPTR(WORD) PTR_WORD; typedef DPTR(USHORT) PTR_USHORT; -typedef DPTR(DWORD) PTR_DWORD; typedef DPTR(LONG) PTR_LONG; typedef DPTR(ULONG) PTR_ULONG; typedef DPTR(INT32) PTR_INT32; typedef DPTR(UINT32) PTR_UINT32; -typedef DPTR(ULONG64) PTR_ULONG64; typedef DPTR(INT64) PTR_INT64; -typedef DPTR(UINT64) PTR_UINT64; typedef DPTR(SIZE_T) PTR_SIZE_T; typedef DPTR(int) PTR_int; typedef DPTR(BOOL) PTR_BOOL; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 3f1d8e566f18f4..57c688f8516331 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 31a04b06-915e-42a0-bbd2-c9c397677ae5 */ - 0x31a04b06, - 0x915e, - 0x42a0, - {0xbb, 0xd2, 0xc9, 0xc3, 0x97, 0x67, 0x7a, 0xe5} +constexpr GUID JITEEVersionIdentifier = { /* 91460d2c-d6a8-460b-a95f-8342301bebe8 */ + 0x91460d2c, + 0xd6a8, + 0x460b, + {0xa9, 0x5f, 0x83, 0x42, 0x30, 0x1b, 0xeb, 0xe8} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 561191bd4411a5..5cfbf1434f3e1d 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -138,7 +138,7 @@ CONFIG_STRING(JitInlineMethodsWithEHRange, "JitInlineMethodsWithEHRange") CONFIG_INTEGER(JitLongAddress, "JitLongAddress", 0) // Force using the large pseudo instruction form for long address CONFIG_INTEGER(JitMaxUncheckedOffset, "JitMaxUncheckedOffset", 8) #if defined(TARGET_ARM64) -RELEASE_CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 0) +RELEASE_CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 1) #endif // Enable devirtualization for generic virtual methods diff --git a/src/coreclr/nativeaot/Runtime/AsmOffsets.h b/src/coreclr/nativeaot/Runtime/AsmOffsets.h index 72f27daccdf033..c46652f53fc843 100644 --- a/src/coreclr/nativeaot/Runtime/AsmOffsets.h +++ b/src/coreclr/nativeaot/Runtime/AsmOffsets.h @@ -54,7 +54,12 @@ ASM_OFFSET( 30, 48, Thread, m_pTransitionFrame) ASM_OFFSET( 34, 50, Thread, m_pDeferredTransitionFrame) ASM_OFFSET( 40, 68, Thread, m_ppvHijackedReturnAddressLocation) ASM_OFFSET( 44, 70, Thread, m_pvHijackedReturnAddress) +#if defined(TARGET_ARM64) +ASM_OFFSET( 48, 78, Thread, m_pSpForPacSign) +ASM_OFFSET( 4c, 80, Thread, m_pExInfoStackHead) +#else ASM_OFFSET( 48, 78, Thread, m_pExInfoStackHead) +#endif #ifdef TARGET_X86 ASM_OFFSET( 4c, FF, Thread, m_uHijackedReturnValueFlags) #endif diff --git a/src/coreclr/nativeaot/Runtime/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/CMakeLists.txt index e491f54a562445..a50f7c40982765 100644 --- a/src/coreclr/nativeaot/Runtime/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/CMakeLists.txt @@ -132,6 +132,15 @@ if (WIN32) list(APPEND FULL_RUNTIME_SOURCES windows/CoffNativeCodeManager.cpp) + if(CLR_CMAKE_TARGET_ARCH_ARM64) + list(APPEND FULL_RUNTIME_SOURCES + ../../unwinder/arm64/unwinder.cpp + ) + include_directories(../../inc) + include_directories(../../unwinder) + include_directories(../../unwinder/arm64) + endif() + set(ASM_SUFFIX asm) else() diff --git a/src/coreclr/nativeaot/Runtime/ICodeManager.h b/src/coreclr/nativeaot/Runtime/ICodeManager.h index e30a116bfe50fc..564bdf1ca0bbc9 100644 --- a/src/coreclr/nativeaot/Runtime/ICodeManager.h +++ b/src/coreclr/nativeaot/Runtime/ICodeManager.h @@ -165,7 +165,8 @@ class ICodeManager virtual bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation // out + PTR_PTR_VOID * ppvRetAddrLocation, // out + uintptr_t * pSpForArm64PacSign // out ) PURE_VIRTUAL #ifdef TARGET_X86 diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index d3b67edb83c65b..4e927ea030fcc6 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -64,6 +64,18 @@ EXTERN_C CODE_LOCATION RhpRethrow2; #define FAILFAST_OR_DAC_FAIL_UNCONDITIONALLY(msg) { ASSERT_UNCONDITIONALLY(msg); RhFailFast(); } #endif +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 + +static TADDR ReturnAddressToCanonicalPC(TADDR returnAddress) +{ +#if defined(TARGET_ARM64) + returnAddress = (TADDR)PacStripPtr((void*)returnAddress); +#endif // TARGET_ARM64 + return PCODEToPINSTR(dac_cast(returnAddress)); +} + StackFrameIterator::StackFrameIterator(Thread * pThreadToWalk, PInvokeTransitionFrame* pInitialTransitionFrame) { STRESS_LOG0(LF_STACKWALK, LL_INFO10000, "----Init---- [ GC ]\n"); @@ -164,7 +176,7 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF #if !defined(FEATURE_PORTABLE_HELPERS) // @TODO: no portable version of regdisplay memset(&m_RegDisplay, 0, sizeof(m_RegDisplay)); - m_RegDisplay.SetIP((PCODE)PCODEToPINSTR((PCODE)pFrame->m_RIP)); + m_RegDisplay.SetIP(ReturnAddressToCanonicalPC(dac_cast(pFrame->m_RIP))); SetControlPC(dac_cast(m_RegDisplay.GetIP())); PTR_uintptr_t pPreservedRegsCursor = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_PreservedRegs); @@ -413,14 +425,15 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CO // This codepath is used by the hijack stackwalk and we can get arbitrary ControlPCs from there. If this // context has a non-managed control PC, then we're done. - if (!m_pInstance->IsManaged(dac_cast(pCtx->GetIp()))) + TADDR controlPC = ReturnAddressToCanonicalPC(pCtx->GetIp()); + if (!m_pInstance->IsManaged(dac_cast(controlPC))) return; // // control state // m_RegDisplay.SP = pCtx->GetSp(); - m_RegDisplay.IP = PCODEToPINSTR(pCtx->GetIp()); + m_RegDisplay.IP = controlPC; SetControlPC(dac_cast(m_RegDisplay.GetIP())); #ifdef TARGET_ARM @@ -633,14 +646,15 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, NATIVE_CONTEXT* pC // This codepath is used by the hijack stackwalk. The IP must be in managed code // or in a conservatively reported assembly thunk. - ASSERT(IsValidReturnAddress((void*)pCtx->GetIp())); + TADDR controlPC = ReturnAddressToCanonicalPC(pCtx->GetIp()); + ASSERT(IsValidReturnAddress(dac_cast(controlPC))); // // control state // - SetControlPC(dac_cast(pCtx->GetIp())); + SetControlPC(dac_cast(controlPC)); m_RegDisplay.SP = pCtx->GetSp(); - m_RegDisplay.IP = pCtx->GetIp(); + m_RegDisplay.IP = controlPC; #ifdef TARGET_UNIX #define PTR_TO_REG(ptr, reg) (&((ptr)->reg())) @@ -1223,7 +1237,7 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() m_RegDisplay.pFP = SP++; - m_RegDisplay.SetIP(*SP++); + m_RegDisplay.SetIP(ReturnAddressToCanonicalPC(*SP++)); m_RegDisplay.pX19 = SP++; m_RegDisplay.pX20 = SP++; @@ -1636,7 +1650,7 @@ void StackFrameIterator::UnwindUniversalTransitionThunk() stackFrame->UnwindVolatileArgRegisters(&m_RegDisplay); PTR_uintptr_t addressOfPushedCallerIP = stackFrame->get_AddressOfPushedCallerIP(); - m_RegDisplay.SetIP(PCODEToPINSTR(*addressOfPushedCallerIP)); + m_RegDisplay.SetIP(ReturnAddressToCanonicalPC(*addressOfPushedCallerIP)); m_RegDisplay.SetSP((uintptr_t)dac_cast(stackFrame->get_CallerSP())); SetControlPC(dac_cast(m_RegDisplay.GetIP())); #if defined(TARGET_AMD64) && defined(TARGET_WINDOWS) @@ -1767,7 +1781,7 @@ void StackFrameIterator::UnwindThrowSiteThunk() ASSERT_UNCONDITIONALLY("NYI for this arch"); #endif - m_RegDisplay.SetIP(PCODEToPINSTR(pContext->IP)); + m_RegDisplay.SetIP(ReturnAddressToCanonicalPC(pContext->IP)); m_RegDisplay.SetSP(pContext->GetSp()); SetControlPC(dac_cast(m_RegDisplay.GetIP())); @@ -1862,7 +1876,8 @@ void StackFrameIterator::NextInternal() // if the thread is safe to walk, it better not have a hijack in place. ASSERT(!m_pThread->IsHijacked()); - SetControlPC(dac_cast(PCODEToPINSTR(m_RegDisplay.GetIP()))); + m_RegDisplay.SetIP(ReturnAddressToCanonicalPC(m_RegDisplay.GetIP())); + SetControlPC(dac_cast(m_RegDisplay.GetIP())); PTR_VOID collapsingTargetFrame = NULL; diff --git a/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h b/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h index 4aec05ba2cb5bd..b07404007f84b2 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h +++ b/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h @@ -224,6 +224,7 @@ TrashRegister32Bit SETS "w":CC:("$TrashRegister32Bit":RIGHT:((:LEN:TrashRegister str $trashReg1, [$trashReg2] str xzr, [$threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] str xzr, [$threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + str xzr, [$threadReg, #OFFSETOF__Thread__m_pSpForPacSign] 0 MEND diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S index aa8ac5e79069c3..b67466bc7e61cd 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S @@ -331,6 +331,7 @@ LOCAL_LABEL(ClearThreadState): // clear the Thread's hijack state str xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] str xzr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + str xzr, [x2, #OFFSETOF__Thread__m_pSpForPacSign] LOCAL_LABEL(NotHijacked): diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm index 6f87c632a51e1a..da61aa1cfd6e71 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm @@ -333,6 +333,7 @@ ClearThreadState ;; clear the Thread's hijack state str xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] str xzr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + str xzr, [x2, #OFFSETOF__Thread__m_pSpForPacSign] NotHijacked diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S index c327e304b8e290..66dcdfe6352956 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S @@ -4,6 +4,8 @@ #include #include "AsmOffsets.inc" +.arch_extension pauth + #define PROBE_FRAME_SIZE 0x140 // 4 * 8 for fixed part of PInvokeTransitionFrame (fp, lr, m_pThread, m_Flags) + // 10 * 8 for callee saved registers + // 1 * 8 for caller SP + @@ -103,7 +105,7 @@ // x9: thread pointer // x0-x7, q0-q7 preserved, x10 trashed // -.macro FixupHijackedCallstack +.macro FixupHijackedCallstack clearHijackState // x9 <- GetThread() @@ -150,22 +152,27 @@ #endif // - // Fix the stack by restoring the original return address + // Fix the stack by restoring and authenticating the original return address. // ldr lr, [x9, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + ldr x16, [x9, #OFFSETOF__Thread__m_pSpForPacSign] + cbz x16, \clearHijackState + autia lr, x16 +\clearHijackState: // // Clear hijack state // - // Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress + // Clear m_ppvHijackedReturnAddressLocation, m_pvHijackedReturnAddress, and m_pSpForPacSign stp xzr, xzr, [x9, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str xzr, [x9, #OFFSETOF__Thread__m_pSpForPacSign] .endm // // GC Probe Hijack target // NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler - FixupHijackedCallstack + FixupHijackedCallstack LOCAL_LABEL(RhpGcProbeClearHijackState) PREPARE_EXTERNAL_VAR_INDIRECT_W RhpTrapThreads, 10 tbnz x10, #TrapThreadsFlags_TrapThreads_Bit, LOCAL_LABEL(WaitForGC) diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm index bd44842a8a8be6..1683ad70cb5f14 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm @@ -112,25 +112,30 @@ PROBE_FRAME_SIZE field 0 ;; ;; Register state on exit: ;; x9: thread pointer -;; x0-x7 preserved, x10 trashed +;; x0-x7 preserved, x10 and x16 trashed ;; MACRO - FixupHijackedCallstack + FixupHijackedCallstack $ClearHijackStateLabel ;; x9 <- GetThread(), TRASHES x10 INLINE_GETTHREAD x9, x10 ;; - ;; Fix the stack by restoring the original return address + ;; Fix the stack by restoring and authenticating the original return address. ;; ldr lr, [x9, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + ldr x16, [x9, #OFFSETOF__Thread__m_pSpForPacSign] + cbz x16, $ClearHijackStateLabel + DCD 0xDAC1161E ;; autib lr, x16 instruction in binary to avoid requiring PAC-enabled assemblers +$ClearHijackStateLabel ;; ;; Clear hijack state ;; ASSERT OFFSETOF__Thread__m_pvHijackedReturnAddress == (OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation + 8) - ;; Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress + ;; Clear m_ppvHijackedReturnAddressLocation, m_pvHijackedReturnAddress, and m_pSpForPacSign stp xzr, xzr, [x9, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str xzr, [x9, #OFFSETOF__Thread__m_pSpForPacSign] MEND MACRO @@ -154,7 +159,7 @@ PROBE_FRAME_SIZE field 0 HijackTargetFakeProlog LABELED_RETURN_ADDRESS RhpGcProbeHijack - FixupHijackedCallstack + FixupHijackedCallstack RhpGcProbeClearHijackState ldr x10, =RhpTrapThreads ldr w10, [x10] @@ -201,7 +206,7 @@ WaitForGC ;; ;; LEAF_ENTRY RhpGcStressHijack - FixupHijackedCallstack + FixupHijackedCallstack RhpGcStressClearHijackState mov x12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0 + PTFF_SAVE_X1 + PTFF_SAVE_X2 + PTFF_SAVE_X3 + PTFF_SAVE_X4 + PTFF_SAVE_X5 + PTFF_SAVE_X6 + PTFF_SAVE_X7) b RhpGcStressProbe LEAF_END RhpGcStressHijack diff --git a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S index ea5d91a1a1c1f9..21c44d8fb38d15 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S +++ b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S @@ -3,3 +3,34 @@ #include #include "AsmOffsets.inc" + +// void* PacStripPtr(void *); +// This function strips the pointer of PAC info that is passed as an argument. +// We prefer to strip a pointer where it's not going to be used to branch execution to. +.arch_extension pauth + LEAF_ENTRY PacStripPtr, _TEXT + xpaci x0 + ret + LEAF_END PacStripPtr, _TEXT + +// void* PacSignPtr(void *, void *); +// This function signs the input pointer using x1 as salt. It is a no-op on non-PAC enabled machines. +.arch_extension pauth + LEAF_ENTRY PacSignPtr, _TEXT + mov x17, x0 + mov x16, x1 + pacia1716 + mov x0, x17 + ret + LEAF_END PacSignPtr, _TEXT + +// void* PacAuthPtr(void *, void *); +// This function authenticates the input signed-pointer using x1 as salt. It is a no-op on non-PAC enabled machines. +.arch_extension pauth + LEAF_ENTRY PacAuthPtr, _TEXT + mov x17, x0 + mov x16, x1 + autia1716 + mov x0, x17 + ret + LEAF_END PacAuthPtr, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm index 49baea4977259b..d31511558da154 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm @@ -5,4 +5,32 @@ TEXTAREA +; void* PacStripPtr(void *); +; This function strips the pointer of PAC info that is passed as an argument. +; We prefer to strip a pointer where it's not going to be used to branch execution to. + LEAF_ENTRY PacStripPtr + DCD 0xDAC143E0 ; xpaci x0 instruction in binary to avoid requiring PAC-enabled assemblers + ret + LEAF_END PacStripPtr + +; void* PacSignPtr(void *, void *); +; This function signs the input pointer using x1 as salt. It is a no-op on non-PAC enabled machines. + LEAF_ENTRY PacSignPtr + mov x17, x0 + mov x16, x1 + DCD 0xD503215F ; pacib1716 instruction in binary to avoid error while compiling with non-PAC enabled compilers + mov x0, x17 + ret + LEAF_END PacSignPtr + +; void* PacAuthPtr(void *, void *); +; This function authenticates the input signed-pointer using x1 as salt. It is a no-op on non-PAC enabled machines. + LEAF_ENTRY PacAuthPtr + mov x17, x0 + mov x16, x1 + DCD 0xD50321DF ; autib1716 instruction in binary to avoid error while compiling with non-PAC enabled compilers + mov x0, x17 + ret + LEAF_END PacAuthPtr + end diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 5aa1f024020460..158c2698eb14c9 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -37,6 +37,11 @@ static Thread* g_RuntimeInitializingThread; #endif //!DACCESS_COMPILE +#if defined(TARGET_ARM64) +extern "C" void* PacSignPtr(void* ptr, void* sp); +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 + ee_alloc_context::PerThreadRandom::PerThreadRandom() { minipal_xoshiro128pp_init(&random_state, (uint32_t)minipal_hires_ticks()); @@ -801,11 +806,14 @@ void Thread::HijackReturnAddress(NATIVE_CONTEXT* pSuspendCtx, HijackFunc* pfnHij void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, HijackFunc* pfnHijackFunction) { void** ppvRetAddrLocation; + uintptr_t spForPacSign = 0; frameIterator->CalculateCurrentMethodState(); + if (frameIterator->GetCodeManager()->GetReturnAddressHijackInfo(frameIterator->GetMethodInfo(), frameIterator->GetRegisterSet(), - &ppvRetAddrLocation)) + &ppvRetAddrLocation, + &spForPacSign)) { ASSERT(ppvRetAddrLocation != NULL); @@ -817,18 +825,34 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack CrossThreadUnhijack(); void* pvRetAddr = *ppvRetAddrLocation; + ASSERT(pvRetAddr != NULL); + +#if defined(TARGET_ARM64) + ASSERT(StackFrameIterator::IsValidReturnAddress(PacStripPtr(pvRetAddr))); +#else ASSERT(StackFrameIterator::IsValidReturnAddress(pvRetAddr)); +#endif // TARGET_ARM64 m_ppvHijackedReturnAddressLocation = ppvRetAddrLocation; m_pvHijackedReturnAddress = pvRetAddr; +#if defined(TARGET_ARM64) + m_pSpForPacSign = (void*)spForPacSign; +#endif // TARGET_ARM64 #if defined(TARGET_X86) bool isAsync = false; GCRefKind retKind = frameIterator->GetCodeManager()->GetReturnValueKind(frameIterator->GetMethodInfo(), frameIterator->GetRegisterSet(), &isAsync); m_uHijackedReturnValueFlags = ReturnKindToTransitionFrameFlags(retKind, isAsync); #endif - *ppvRetAddrLocation = (void*)pfnHijackFunction; + void* pvHijackedAddr = (void*)pfnHijackFunction; +#if defined(TARGET_ARM64) + if (spForPacSign != 0) + { + pvHijackedAddr = PacSignPtr(pvHijackedAddr, (void*)spForPacSign); + } +#endif // TARGET_ARM64 + *ppvRetAddrLocation = pvHijackedAddr; STRESS_LOG2(LF_STACKWALK, LL_INFO10000, "InternalHijack: TgtThread = %llx, IP = %p\n", GetOSThreadId(), frameIterator->GetRegisterSet()->GetIP()); @@ -951,6 +975,9 @@ void Thread::UnhijackWorker() if (m_pvHijackedReturnAddress == NULL) { ASSERT(m_ppvHijackedReturnAddressLocation == NULL); +#if defined(TARGET_ARM64) + ASSERT(m_pSpForPacSign == NULL); +#endif // TARGET_ARM64 return; } @@ -961,6 +988,9 @@ void Thread::UnhijackWorker() // Clear the hijack state. m_ppvHijackedReturnAddressLocation = NULL; m_pvHijackedReturnAddress = NULL; +#if defined(TARGET_ARM64) + m_pSpForPacSign = NULL; +#endif // TARGET_ARM64 #ifdef TARGET_X86 m_uHijackedReturnValueFlags = 0; #endif diff --git a/src/coreclr/nativeaot/Runtime/thread.h b/src/coreclr/nativeaot/Runtime/thread.h index a910894ec968df..e99b617a1ddb75 100644 --- a/src/coreclr/nativeaot/Runtime/thread.h +++ b/src/coreclr/nativeaot/Runtime/thread.h @@ -141,6 +141,9 @@ struct RuntimeThreadLocals #ifdef FEATURE_HIJACK void ** m_ppvHijackedReturnAddressLocation; void * m_pvHijackedReturnAddress; +#if defined(TARGET_ARM64) + void * m_pSpForPacSign; +#endif #endif // FEATURE_HIJACK PTR_ExInfo m_pExInfoStackHead; #ifdef TARGET_X86 diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index 266b56bd1f6e4e..dca47eb39c31c7 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -22,6 +22,10 @@ #include "eventtracebase.h" +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 + #define UBF_FUNC_KIND_MASK 0x03 #define UBF_FUNC_KIND_ROOT 0x00 #define UBF_FUNC_KIND_HANDLER 0x01 @@ -64,6 +68,202 @@ UnixNativeCodeManager::~UnixNativeCodeManager() { } +#if defined(TARGET_ARM64) +static size_t readULEB(const uint8_t *&p, const uint8_t *end) +{ + size_t result = 0; + unsigned shift = 0; + while (p < end) + { + uint8_t byte = *p++; + result |= size_t(byte & 0x7F) << shift; + if ((byte & 0x80) == 0) // clear top bit indicates the last by of the value + break; + shift += 7; + } + return result; +} + +static ssize_t readSLEB(const uint8_t *&p, const uint8_t *end) +{ + ssize_t result = 0; + unsigned shift = 0; + uint8_t byte = 0; + + while (p < end) + { + byte = *p++; + result |= ssize_t(byte & 0x7F) << shift; + shift += 7; + if ((byte & 0x80) == 0) // clear top bit indicates the last by of the value + { + break; + } + } + + if ((shift < (sizeof(result) * 8)) && ((byte & 0x40) != 0)) + { + result |= -((ssize_t)1 << shift); + } + + return result; +} + +struct PacFrameInfo +{ + bool hasPac; + int cfaOffset; + int lrOffset; + int pacCfaOffset; +}; + +static bool TryGetPacFrameInfo(UnixNativeMethodInfo *pNativeMethodInfo, + PacFrameInfo *pPacFrameInfo) +{ + const uint8_t* p = (const uint8_t*)pNativeMethodInfo->unwind_info; + uint32_t fdeLength = *dac_cast((uint8_t*)p); + const uint8_t* end = p + fdeLength; + p += sizeof(uint32_t); // FDE length + + if (*dac_cast((uint8_t*)p) == 0) + return false; + + p += sizeof(uint32_t); // CIE pointer + p += sizeof(uint32_t); // PC start + p += sizeof(uint32_t); // function length + + size_t augmentationLength = readULEB(p, end); + if ((size_t)(end - p) < augmentationLength) + return false; + p += augmentationLength; + + constexpr int DataAlignFactor = -4; + constexpr uint8_t ReturnAddressRegister = 30; + + int cfaOffset = 0; + int lrOffset = INT_MIN; + int pacCfaOffset = 0; + bool hasPac = false; + + while (p < end) + { + uint8_t op = *p++; + + if (op == DW_CFA_AARCH64_negate_ra_state) + { + pacCfaOffset = cfaOffset; + hasPac = true; + continue; + } + + if ((op & 0xC0) == DW_CFA_advance_loc) + { + continue; + } + + if ((op & 0xC0) == DW_CFA_offset) + { + uint8_t dwarfReg = op & 0x3F; + ssize_t offsetFactor = (ssize_t)readULEB(p, end); + if (dwarfReg == ReturnAddressRegister) + { + lrOffset = cfaOffset + (int)(offsetFactor * DataAlignFactor); + } + continue; + } + + switch (op) + { + case DW_CFA_nop: + break; + + case DW_CFA_advance_loc1: + p += sizeof(uint8_t); + break; + + case DW_CFA_advance_loc2: + p += sizeof(uint16_t); + break; + + case DW_CFA_advance_loc4: + p += sizeof(uint32_t); + break; + + case DW_CFA_offset_extended: + { + uint8_t dwarfReg = (uint8_t)readULEB(p, end); + ssize_t offsetFactor = (ssize_t)readULEB(p, end); + if (dwarfReg == ReturnAddressRegister) + { + lrOffset = cfaOffset + (int)(offsetFactor * DataAlignFactor); + } + break; + } + + case DW_CFA_offset_extended_sf: + { + uint8_t dwarfReg = (uint8_t)readULEB(p, end); + ssize_t offsetFactor = readSLEB(p, end); + if (dwarfReg == ReturnAddressRegister) + { + lrOffset = cfaOffset + (int)(offsetFactor * DataAlignFactor); + } + break; + } + + case DW_CFA_def_cfa: + readULEB(p, end); // register + cfaOffset = (int)readULEB(p, end); + break; + + case DW_CFA_def_cfa_register: + readULEB(p, end); // register + break; + + case DW_CFA_def_cfa_offset: + cfaOffset = (int)readULEB(p, end); + break; + + case DW_CFA_def_cfa_sf: + readULEB(p, end); // register + cfaOffset = (int)(readSLEB(p, end) * DataAlignFactor); + break; + + case DW_CFA_def_cfa_offset_sf: + cfaOffset = (int)(readSLEB(p, end) * DataAlignFactor); + break; + + default: + return false; + } + } + + pPacFrameInfo->hasPac = hasPac; + pPacFrameInfo->cfaOffset = cfaOffset; + pPacFrameInfo->lrOffset = lrOffset; + pPacFrameInfo->pacCfaOffset = pacCfaOffset; + return true; +} + +static bool TryGetSpForPacSigning(const PacFrameInfo& pacFrameInfo, + PTR_PTR_VOID ppvRetAddrLocation, + uintptr_t *pSpForPacSign) +{ + if (!pacFrameInfo.hasPac) + { + *pSpForPacSign = 0; + return true; + } + + if (ppvRetAddrLocation == NULL || pacFrameInfo.lrOffset == INT_MIN) + return false; + + *pSpForPacSign = dac_cast(ppvRetAddrLocation) + + (pacFrameInfo.cfaOffset - pacFrameInfo.lrOffset - pacFrameInfo.pacCfaOffset); + return true; +} +#endif // TARGET_ARM64 + // Virtually unwind stack to the caller of the context specified by the REGDISPLAY bool UnixNativeCodeManager::VirtualUnwind(MethodInfo* pMethodInfo, REGDISPLAY* pRegisterSet) { @@ -364,6 +564,10 @@ bool UnixNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, return false; } +#if defined(TARGET_ARM64) + pRegisterSet->SetIP((PCODE)PacStripPtr((void*)pRegisterSet->GetIP())); +#endif // TARGET_ARM64 + return true; } @@ -381,7 +585,7 @@ bool UnixNativeCodeManager::IsUnwindable(PTR_VOID pvAddress) pMethodInfo = &methodInfo; #endif -#if (defined(TARGET_APPLE) && defined(TARGET_ARM64)) || defined(TARGET_ARM) +#if defined(TARGET_ARM64) || defined(TARGET_ARM) // VirtualUnwind can't unwind epilogues and some prologues. return TrailingEpilogueInstructionsCount(pMethodInfo, pvAddress) == 0 && IsInProlog(pMethodInfo, pvAddress) != 1; #else @@ -501,7 +705,7 @@ static bool IsArmPrologInstruction(uint16_t* pInstr) #endif -#if (defined(TARGET_APPLE) && defined(TARGET_ARM64)) || defined(TARGET_ARM) +#if defined(TARGET_ARM64) || defined(TARGET_ARM) // checks for known prolog instructions generated by ILC and returns // 1 - in prolog // 0 - not in prolog @@ -870,6 +1074,16 @@ int UnixNativeCodeManager::TrailingEpilogueInstructionsCount(MethodInfo * pMetho #define LDP_BITS2 0x28400000 #define LDP_MASK2 0x7E400000 +// add sp, sp, #imm +// 1001 0001 0xxx xxxx xxxx xx11 1111 1111 +#define ADD_SP_SP_BITS 0x910003FF +#define ADD_SP_SP_MASK 0xFF8003FF + +// sub sp, fp, #imm +// 1101 0001 0xxx xxxx xxxx xx11 1011 1111 +#define SUB_SP_FP_BITS 0xD10003BF +#define SUB_SP_FP_MASK 0xFF8003FF + // Branches, Exception Generating and System instruction group // xxx1 01xx xxxx xxxx xxxx xxxx xxxx xxxx #define BEGS_BITS 0x14000000 @@ -924,6 +1138,26 @@ int UnixNativeCodeManager::TrailingEpilogueInstructionsCount(MethodInfo * pMetho return -1; } } + + // Post-index restore sequences such as "ldp x19, x20, [sp], #0x10" also adjust SP + // before the final AUTIASP/RET. We avoid signing with a partially-restored SP. + int baseRegister = (instr >> 5) & 0x1f; + if (baseRegister == 31) + { + if ((instr & LDP_MASK2) == LDP_BITS2 || + (instr & LDR_MASK2) == LDR_BITS2) + { + return -1; + } + } + + // Stack pointer adjustments can happen before AUTIASP/RET in some epilog layouts, + // so treat them as being in the epilog as well. + if ((instr & ADD_SP_SP_MASK) == ADD_SP_SP_BITS || + (instr & SUB_SP_FP_MASK) == SUB_SP_FP_BITS) + { + return -1; + } } #elif defined(TARGET_ARM) @@ -1147,7 +1381,8 @@ int UnixNativeCodeManager::TrailingEpilogueInstructionsCount(MethodInfo * pMetho bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation) // out + PTR_PTR_VOID * ppvRetAddrLocation, // out + uintptr_t * pSpForArm64PacSign) // out { UnixNativeMethodInfo* pNativeMethodInfo = (UnixNativeMethodInfo*)pMethodInfo; @@ -1164,6 +1399,22 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) return false; +#if defined(TARGET_ARM64) + PacFrameInfo pacFrameInfo = {}; + bool hasPacFrameInfo = TryGetPacFrameInfo(pNativeMethodInfo, &pacFrameInfo); + bool pacPresent = hasPacFrameInfo && pacFrameInfo.hasPac; + if (pacPresent) + { + // For PAC frames we only hijack locations where the current frame state is + // unambiguous. Partial prologs can save FP/LR before FP is established, and some + // epilog layouts adjust SP before the final AUTIASP/RET sequence. + if (IsInProlog(pMethodInfo, (PTR_VOID)pRegisterSet->IP) == 1) + { + return false; + } + } +#endif + #if defined(TARGET_ARM) // Ensure that PC doesn't have the Thumb bit set. Prolog and epilog // checks depend on it. @@ -1176,9 +1427,22 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn // can't figure, possibly a breakpoint instruction return false; } - else if (epilogueInstructions > 0) + +#if defined(TARGET_ARM64) + if (pacPresent && epilogueInstructions != 0) + { + return false; + } +#endif + + if (epilogueInstructions > 0) { + *pSpForArm64PacSign = 0; *ppvRetAddrLocation = (PTR_PTR_VOID)(pRegisterSet->GetSP() + (sizeof(TADDR) * (epilogueInstructions - 1))); +#if defined(TARGET_ARM64) + if (!TryGetSpForPacSigning(pacFrameInfo, *ppvRetAddrLocation, pSpForArm64PacSign)) + return false; +#endif return true; } @@ -1201,6 +1465,7 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn // 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 #if defined(TARGET_AMD64) + *pSpForArm64PacSign = 0; if (!VirtualUnwind(pMethodInfo, pRegisterSet)) { @@ -1211,6 +1476,7 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn return true; #elif defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) + *pSpForArm64PacSign = 0; if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) p += sizeof(int32_t); @@ -1235,6 +1501,17 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn return false; } +#if defined(TARGET_ARM64) + if (pacPresent) + { + // We hijack the caller frame later. To retrieve signing SP for correct PAC + // processing, we need to pacFrameInfo for the caller frame. Currently bail + // out of hijacking in this case. + // ToDo-PAC: Enable hijacking caller frame + return false; + } +#endif + PTR_uintptr_t oldLocation = pRegisterSet->GetReturnAddressRegisterLocation(); if (!VirtualUnwind(pMethodInfo, pRegisterSet)) { diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h index ca3f3f2272bde1..7d0969d85dedc2 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h @@ -63,7 +63,7 @@ class UnixNativeCodeManager : public ICodeManager bool IsUnwindable(PTR_VOID pvAddress); -#if (defined(TARGET_APPLE) && defined(TARGET_ARM64)) || defined(TARGET_ARM) +#if defined(TARGET_ARM64) || defined(TARGET_ARM) int IsInProlog(MethodInfo * pMethodInfo, PTR_VOID pvAddress); #endif @@ -71,7 +71,8 @@ class UnixNativeCodeManager : public ICodeManager bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation); // out + PTR_PTR_VOID * ppvRetAddrLocation, // out + uintptr_t * pSpForArm64PacSign);// out PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC); diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc index 3027a17d6c8fc1..635f71b08b6e61 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc @@ -323,6 +323,7 @@ C_FUNC(\Name): str \trashReg1, [\trashReg2] str xzr, [\threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] str xzr, [\threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + str xzr, [\threadReg, #OFFSETOF__Thread__m_pSpForPacSign] 0: .endm diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp index d6f7e224a5adf7..53eee6f20122ec 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -22,6 +22,21 @@ #include "eventtracebase.h" +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +EXTERN_C PEXCEPTION_ROUTINE NTAPI RtlVirtualUnwindWithSpForPacSign( + IN ULONG HandlerType, + IN ULONG64 ImageBase, + IN ULONG64 ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN OUT PCONTEXT ContextRecord, + OUT PVOID *HandlerData, + OUT PULONG64 EstablisherFrame, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL, + OUT PULONG64 SpForPacSign OPTIONAL + ); +#endif // TARGET_ARM64 + #ifdef TARGET_X86 // Disable contracts @@ -812,7 +827,7 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, &contextPointers); pRegisterSet->SP = context.Sp; - pRegisterSet->IP = context.Pc; + pRegisterSet->IP = (PCODE)PacStripPtr((void*)context.Pc); if (!(flags & USFF_GcUnwind)) { @@ -841,7 +856,8 @@ bool CoffNativeCodeManager::IsUnwindable(PTR_VOID pvAddress) bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation) // out + PTR_PTR_VOID * ppvRetAddrLocation, // out + uintptr_t * pSpForArm64PacSign) // out { CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; @@ -872,6 +888,7 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn #endif #if defined(TARGET_AMD64) + *pSpForArm64PacSign = 0; context.Rsp = pRegisterSet->GetSP(); context.Rbp = pRegisterSet->GetFP(); context.Rip = pRegisterSet->GetIP(); @@ -888,6 +905,7 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn *ppvRetAddrLocation = (PTR_PTR_VOID)(context.Rsp - sizeof (PVOID)); return true; #elif defined(TARGET_ARM64) + *pSpForArm64PacSign = 0; if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) p += sizeof(int32_t); @@ -923,14 +941,20 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn #endif contextPointers.Lr = pRegisterSet->pLR; - RtlVirtualUnwind(NULL, + RtlVirtualUnwindWithSpForPacSign(NULL, dac_cast(m_moduleBase), pRegisterSet->IP, (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction, &context, &HandlerData, &EstablisherFrame, - &contextPointers); + &contextPointers, + (PULONG64)pSpForArm64PacSign); + + if (context.Pc == 0) + { + return false; + } if (contextPointers.Lr == pRegisterSet->pLR) { @@ -945,6 +969,7 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn *ppvRetAddrLocation = (PTR_PTR_VOID)contextPointers.Lr; return true; #else + *pSpForArm64PacSign = 0; EstablisherFrame = 0; HandlerData = NULL; return false; diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h index 379f2ae8ceedcc..188b5fb8122851 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h @@ -92,7 +92,8 @@ class CoffNativeCodeManager : public ICodeManager bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation); // out + PTR_PTR_VOID * ppvRetAddrLocation, // out + uintptr_t * pSpForArm64PacSign); // out #ifdef TARGET_X86 GCRefKind GetReturnValueKind(MethodInfo * pMethodInfo, diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCfiOpcode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCfiOpcode.cs index 25d81deb39aabc..24a51314396555 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCfiOpcode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCfiOpcode.cs @@ -11,6 +11,7 @@ internal enum CFI_OPCODE CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one. CFI_DEF_CFA_REGISTER, // New register is used to compute CFA CFI_REL_OFFSET, // Register is saved at offset from the current CFA - CFI_DEF_CFA // Take address from register and add offset to it. + CFI_DEF_CFA, // Take address from register and add offset to it. + CFI_NEGATE_RA_STATE, // Sign the return address in lr with the platform PAC key } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs index 4c1444c179a701..4a8b2076872590 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs @@ -116,6 +116,10 @@ private static byte[] CfiCodeToInstructions(DwarfCie cie, byte[] blobData) cfaOffset = cfiOffset; cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)cfaOffset); break; + + case CFI_OPCODE.CFI_NEGATE_RA_STATE: + cfiCode[cfiCodeOffset++] = DW_CFA_AARCH64_negate_ra_state; + break; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiUnwindConverter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiUnwindConverter.cs index 5db4b2cfeb7e25..f5d6372535a9bd 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiUnwindConverter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiUnwindConverter.cs @@ -121,6 +121,10 @@ public static byte[] ConvertCFIToEabi(byte[] blobData) EmitSpAdjustment(cfiOffset); } break; + + case CFI_OPCODE.CFI_NEGATE_RA_STATE: + // Do nothing here. + break; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.Aot.cs index 6cb3f71f117d88..840405a4350d22 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.Aot.cs @@ -160,6 +160,10 @@ private static uint GetArm64CompactUnwindCode(byte[] blobData) switch (opcode) { + case CFI_OPCODE.CFI_NEGATE_RA_STATE: + // Fall back to DWARF so the AArch64 negate_ra_state opcode is preserved for libunwind. + return UNWIND_ARM64_MODE_DWARF; + case CFI_OPCODE.CFI_DEF_CFA_REGISTER: cfaRegister = dwarfReg; diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 6c22da04d78a90..c01e3cd6c4bf73 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -101,7 +101,8 @@ private enum CFI_OPCODE CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one. CFI_DEF_CFA_REGISTER, // New register is used to compute CFA CFI_REL_OFFSET, // Register is saved at offset from the current CFA - CFI_DEF_CFA // Take address from register and add offset to it. + CFI_DEF_CFA, // Take address from register and add offset to it. + CFI_NEGATE_RA_STATE, // Sign the return address in lr with the platform PAC key } // Get the CFI data in the same shape as clang/LLVM generated one. This improves the compatibility with libunwind and other unwind solutions @@ -132,6 +133,7 @@ private static byte[] CompressARM64CFI(byte[] blobData) } int offset = 0; + bool shouldAddPACOpCode = false; while (offset < blobData.Length) { codeOffset = Math.Max(codeOffset, blobData[offset++]); @@ -185,6 +187,10 @@ private static byte[] CompressARM64CFI(byte[] blobData) } } break; + + case CFI_OPCODE.CFI_NEGATE_RA_STATE: + shouldAddPACOpCode = true; + break; } } @@ -194,6 +200,14 @@ private static byte[] CompressARM64CFI(byte[] blobData) using (BinaryWriter cfiWriter = new BinaryWriter(cfiStream)) { + if (shouldAddPACOpCode) + { + cfiWriter.Write((byte)codeOffset); + cfiWriter.Write((byte)CFI_OPCODE.CFI_NEGATE_RA_STATE); + cfiWriter.Write((short)-1); + cfiWriter.Write(0); + } + if (cfaRegister != -1) { cfiWriter.Write((byte)codeOffset); diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 15d9e46bcbeea5..b49f2c3039c7a4 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -3,10 +3,36 @@ // +#if defined(NATIVEAOT) +#include "common.h" +#include +#include "rhassert.h" + +#ifndef T_CONTEXT +#define T_CONTEXT CONTEXT +#endif +#ifndef PT_CONTEXT +#define PT_CONTEXT PCONTEXT +#endif +#ifndef T_KNONVOLATILE_CONTEXT_POINTERS +#define T_KNONVOLATILE_CONTEXT_POINTERS KNONVOLATILE_CONTEXT_POINTERS +#endif +#ifndef PT_KNONVOLATILE_CONTEXT_POINTERS +#define PT_KNONVOLATILE_CONTEXT_POINTERS PKNONVOLATILE_CONTEXT_POINTERS +#endif +#ifndef T_RUNTIME_FUNCTION +#define T_RUNTIME_FUNCTION RUNTIME_FUNCTION +#endif +#ifndef PT_RUNTIME_FUNCTION +#define PT_RUNTIME_FUNCTION PRUNTIME_FUNCTION +#endif +#else #include "stdafx.h" #include "utilcode.h" #include "crosscomp.h" +#endif +#include "clrnt.h" #include "unwinder.h" #define NOTHING @@ -170,10 +196,10 @@ typedef struct _ARM64_VFP_STATE #if !defined(DEBUGGER_UNWIND) -#define MEMORY_READ_BYTE(params, addr) (*dac_cast(addr)) -#define MEMORY_READ_WORD(params, addr) (*dac_cast(addr)) -#define MEMORY_READ_DWORD(params, addr) (*dac_cast(addr)) -#define MEMORY_READ_QWORD(params, addr) (*dac_cast(addr)) +#define MEMORY_READ_BYTE(params, addr) (*dac_cast(addr)) +#define MEMORY_READ_WORD(params, addr) (*dac_cast(addr)) +#define MEMORY_READ_DWORD(params, addr) (*dac_cast(addr)) +#define MEMORY_READ_QWORD(params, addr) (*dac_cast(addr)) #endif @@ -2786,6 +2812,7 @@ Return Value: #endif // !defined(DEBUGGER_UNWIND) +#if !defined(NATIVEAOT) BOOL OOPStackUnwinderArm64::Unwind(T_CONTEXT * pContext) { DWORD64 ImageBase = 0; @@ -2853,6 +2880,7 @@ BOOL DacUnwindStackFrame(T_CONTEXT *pContext, T_KNONVOLATILE_CONTEXT_POINTERS* p return res; } +#endif // !defined(NATIVEAOT) #if defined(HOST_UNIX) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64/ARM64Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64/ARM64Unwinder.cs index 625f972958ca23..4f93aa50f20439 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64/ARM64Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64/ARM64Unwinder.cs @@ -763,6 +763,8 @@ private bool VirtualUnwindFull( return false; } + context.Lr &= 0x0000FFFFFFFFFFFF; + // // TODO: Implement support for UnwindFlags RTL_VIRTUAL_UNWIND2_VALIDATE_PAC. //