diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 94a2ee700a6365..20d6a7aa804a20 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -21,6 +21,10 @@ #include "../../vm/methoditer.h" #include "../../vm/tailcallhelp.h" +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 + const char *GetTType( TraceType tt); #define IsSingleStep(exception) ((exception) == EXCEPTION_SINGLE_STEP) @@ -5837,6 +5841,11 @@ static bool IsTailCall(const BYTE * ip, ControllerStackInfo* info, TailCallFunct TailCallTls* tls = GetThread()->GetTailCallTls(); LPVOID tailCallAwareRetAddr = tls->GetFrame()->TailCallAwareReturnAddress; +#if defined(TARGET_ARM64) + retAddr = PacStripPtr(retAddr); + tailCallAwareRetAddr = PacStripPtr(tailCallAwareRetAddr); +#endif // TARGET_ARM64 + LOG((LF_CORDB,LL_INFO1000, "ITCTR: ret addr is %p, tailcall aware ret addr is %p\n", retAddr, tailCallAwareRetAddr)); diff --git a/src/coreclr/inc/cfi.h b/src/coreclr/inc/cfi.h index 3d7ec0f4cc11f8..95a7a0077c8e25 100644 --- a/src/coreclr/inc/cfi.h +++ b/src/coreclr/inc/cfi.h @@ -9,7 +9,9 @@ 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_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 paciaz }; struct CFI_CODE diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 542510f79b4a74..1bfa1fff01d0e2 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -714,6 +714,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Rcpc, W("EnableArm64Rc RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Rcpc2, W("EnableArm64Rcpc2"), 1, "Allows Arm64 Rcpc2+ hardware intrinsics to be disabled") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Sve, W("EnableArm64Sve"), 1, "Allows Arm64 SVE hardware intrinsics to be disabled") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Sve2, W("EnableArm64Sve2"), 1, "Allows Arm64 SVE2 hardware intrinsics to be disabled") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitPacEnabled, W("JitPacEnabled"), 1, "Allows Arm64 Pointer Authentication (PAC) to be disabled") #elif defined(TARGET_RISCV64) RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zba, W("EnableRiscV64Zba"), 1, "Allows RiscV64 Zba hardware intrinsics to be disabled") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbb, W("EnableRiscV64Zbb"), 1, "Allows RiscV64 Zbb hardware intrinsics to be disabled") diff --git a/src/coreclr/inc/gcinfodecoder.h b/src/coreclr/inc/gcinfodecoder.h index 0836dfd0c54b68..e37002772129ad 100644 --- a/src/coreclr/inc/gcinfodecoder.h +++ b/src/coreclr/inc/gcinfodecoder.h @@ -76,6 +76,10 @@ typedef void * OBJECTREF; #ifndef __cgencpu_h__ +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 + inline void SetIP(T_CONTEXT* context, PCODE rip) { _ASSERTE(!"don't call this"); @@ -105,7 +109,7 @@ inline PCODE GetIP(T_CONTEXT* context) #elif defined(TARGET_ARM) return (PCODE)context->Pc; #elif defined(TARGET_ARM64) - return (PCODE)context->Pc; + return (PCODE) PacStripPtr((void *)context->Pc); #elif defined(TARGET_LOONGARCH64) return (PCODE)context->Pc; #elif defined(TARGET_RISCV64) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 4c3bf607017696..bccc7a99c7dd00 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -271,6 +271,8 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) GetEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, spAdjust); compiler->unwindAllocStack(spAdjust); } + + GetEmitter()->emitPacInEpilog(); } //------------------------------------------------------------------------ @@ -1342,6 +1344,7 @@ void CodeGen::genFuncletProlog(BasicBlock* block) gcInfo.gcResetForBB(); compiler->unwindBegProlog(); + GetEmitter()->emitPacInProlog(); regMaskTP maskSaveRegsFloat = genFuncletInfo.fiSaveRegs & RBM_ALLFLOAT; regMaskTP maskSaveRegsInt = genFuncletInfo.fiSaveRegs & ~maskSaveRegsFloat; @@ -1628,6 +1631,8 @@ void CodeGen::genFuncletEpilog() } } + GetEmitter()->emitPacInEpilog(); + inst_RV(INS_ret, REG_LR, TYP_I_IMPL); compiler->unwindReturn(REG_LR); diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 96f01019ac0ecd..619d4c7154c7dc 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -4474,6 +4474,9 @@ void CodeGen::genPushCalleeSavedRegisters() } #endif // DEBUG + // Sign LR as part of Pointer Authentication (PAC) support + GetEmitter()->emitPacInProlog(); + // The frameType number is arbitrary, is defined below, and corresponds to one of the frame styles we // generate based on various sizes. int frameType = 0; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 134d236f78a108..a3dc8765a2c516 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -8805,6 +8805,7 @@ class Compiler void unwindSaveRegPair(regNumber reg1, regNumber reg2, int offset); // stp reg1, reg2, [sp, #offset] void unwindSaveRegPairPreindexed(regNumber reg1, regNumber reg2, int offset); // stp reg1, reg2, [sp, #offset]! void unwindSaveNext(); // unwind code: save_next + void unwindPacSignLR(); // unwind code: pac_sign_lr void unwindReturn(regNumber reg); // ret lr #endif // defined(TARGET_ARM64) diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 398671531bfaa7..ff9ef09a609f70 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -3252,6 +3252,11 @@ class emitter instrDescAlign* emitNewInstrAlign(); #endif +#if defined(TARGET_ARM64) + void emitPacInProlog(); + void emitPacInEpilog(); +#endif + instrDesc* emitNewInstrSmall(emitAttr attr); instrDesc* emitNewInstr(emitAttr attr = EA_4BYTE); instrDesc* emitNewInstrSC(emitAttr attr, cnsval_ssize_t cns); diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index e88e10568712d3..9f647b78572a06 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1397,6 +1397,32 @@ static const char * const bRegNames[] = // clang-format on +//------------------------------------------------------------------------ +// emitPacInProlog: Sign LR as part of Pointer Authentication (PAC) support +// +void emitter::emitPacInProlog() +{ + if (JitConfig.JitPacEnabled() == 0) + { + return; + } + emitIns(INS_paciaz); + emitComp->unwindPacSignLR(); +} + +//------------------------------------------------------------------------ +// emitPacInEpilog: unsign LR as part of Pointer Authentication (PAC) support +// +void emitter::emitPacInEpilog() +{ + if (JitConfig.JitPacEnabled() == 0) + { + return; + } + emitIns(INS_autiaz); + emitComp->unwindPacSignLR(); +} + //------------------------------------------------------------------------ // emitRegName: Returns a general-purpose register name or SIMD and floating-point scalar register name. // diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 154054d8453041..f6f8f77e6eb7b5 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -125,6 +125,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) +RELEASE_CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 1) // // MinOpts diff --git a/src/coreclr/jit/unwind.cpp b/src/coreclr/jit/unwind.cpp index fac8165008b0bf..5b9bdeabbe04af 100644 --- a/src/coreclr/jit/unwind.cpp +++ b/src/coreclr/jit/unwind.cpp @@ -413,6 +413,11 @@ void Compiler::DumpCfiInfo(bool isHotCode, assert(dwarfReg == DWARF_REG_ILLEGAL); printf(" CodeOffset: 0x%02X Op: AdjustCfaOffset Offset:0x%X\n", codeOffset, offset); break; + case CFI_NEGATE_RA_STATE: + assert(dwarfReg == DWARF_REG_ILLEGAL); + assert(offset == 0); + printf(" CodeOffset: 0x%02X Op: NegateRAState\n", codeOffset); + break; default: printf(" Unrecognized CFI_CODE: 0x%llX\n", *(UINT64*)pCode); break; diff --git a/src/coreclr/jit/unwindarm64.cpp b/src/coreclr/jit/unwindarm64.cpp index f842737171c0b4..c950f50a11548b 100644 --- a/src/coreclr/jit/unwindarm64.cpp +++ b/src/coreclr/jit/unwindarm64.cpp @@ -635,6 +635,33 @@ void Compiler::unwindSaveNext() pu->AddCode(0xE6); } +void Compiler::unwindPacSignLR() +{ + if (JitConfig.JitPacEnabled() == 0) + { + return; + } +#if defined(FEATURE_CFI_SUPPORT) + if (generateCFIUnwindCodes()) + { + FuncInfoDsc* func = funCurrentFunc(); + UNATIVE_OFFSET cbProlog = 0; + if (compGeneratingProlog) + { + cbProlog = unwindGetCurrentOffset(func); + } + + // Maps to DW_CFA_AARCH64_negate_ra_state + createCfiCode(func, cbProlog, CFI_NEGATE_RA_STATE, DWARF_REG_ILLEGAL); + + return; + } +#endif // FEATURE_CFI_SUPPORT + + // pac_sign_lr: 11111100: sign the return address in lr with paciaz + funCurrentFunc()->uwi.AddCode(0xFC); +} + void Compiler::unwindReturn(regNumber reg) { // Nothing to do; we will always have at least one trailing "end" opcode in our padding. @@ -1081,6 +1108,12 @@ void DumpUnwindInfo(Compiler* comp, printf(" %02X save_next\n", b1); } + else if (b1 == 0xFC) + { + // pac_sign_lr: 11111100 : sign the return address in lr with paciaz. + + printf(" %02X pac_sign_lr\n", b1); + } else { // Unknown / reserved unwind code diff --git a/src/coreclr/nativeaot/Runtime/ICodeManager.h b/src/coreclr/nativeaot/Runtime/ICodeManager.h index a508aad49f769e..91b7e3c2b16ba1 100644 --- a/src/coreclr/nativeaot/Runtime/ICodeManager.h +++ b/src/coreclr/nativeaot/Runtime/ICodeManager.h @@ -161,6 +161,11 @@ class ICodeManager virtual bool IsUnwindable(PTR_VOID pvAddress) PURE_VIRTUAL +#ifdef TARGET_ARM64 + virtual bool IsPacPresent(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet ) PURE_VIRTUAL +#endif + virtual bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in PTR_PTR_VOID * ppvRetAddrLocation // out diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index 5762e817a88904..c29388344de973 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -65,6 +65,10 @@ 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 + StackFrameIterator::StackFrameIterator(Thread * pThreadToWalk, PInvokeTransitionFrame* pInitialTransitionFrame) { STRESS_LOG0(LF_STACKWALK, LL_INFO10000, "----Init---- [ GC ]\n"); @@ -1779,7 +1783,11 @@ void StackFrameIterator::NextInternal() // if the thread is safe to walk, it better not have a hijack in place. ASSERT(!m_pThread->IsHijacked()); +#if defined(TARGET_ARM64) + SetControlPC(PacStripPtr(dac_cast(PCODEToPINSTR(m_RegDisplay.GetIP())))); +#else SetControlPC(dac_cast(PCODEToPINSTR(m_RegDisplay.GetIP()))); +#endif // TARGET_ARM64 PTR_VOID collapsingTargetFrame = NULL; @@ -2110,6 +2118,10 @@ void StackFrameIterator::CalculateCurrentMethodState() return; } +#if defined(TARGET_ARM64) + m_ControlPC = PacStripPtr(m_ControlPC); +#endif // TARGET_ARM64 + // Assume that the caller is likely to be in the same module if (m_pCodeManager == NULL || !m_pCodeManager->FindMethodInfo(m_ControlPC, &m_methodInfo)) { diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S index fe0077f7a54c2e..3439e9d69e74c7 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S @@ -107,6 +107,7 @@ // Fix the stack by restoring the original return address // ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + xpaclri // // Clear hijack state diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm index f28ad05e86fcd4..657bfe4eedcf66 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm @@ -116,6 +116,7 @@ PROBE_FRAME_SIZE field 0 ;; Fix the stack by restoring the original return address ;; ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + DCD 0xD50320FF ;; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers ;; ;; Clear hijack state diff --git a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S index ea5d91a1a1c1f9..a6c57ab30f1f8b 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S +++ b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S @@ -3,3 +3,29 @@ #include #include "AsmOffsets.inc" + +// void* PacStripPtr(void *); +// This function strips the pointer of PAC info that is passed as an agrument. +// To avoid failing on non-PAC enabled machines, we use xpaclri (instead of xpaci) which strips lr explicitly. +// Thus we move need to move input in lr, strip it and copy it back to the result register. +.arch_extension pauth + LEAF_ENTRY PacStripPtr, _TEXT + mov x9, lr + mov lr, x0 + xpaclri + mov x0, lr + ret x9 + LEAF_END PacStripPtr, _TEXT + +// void* PacSignPtr(void *); +// This function sign the input pointer using zero as salt. +// To avoid failing on non-PAC enabled machines, we use paciaz (instead of paciza) which signs lr explicitly. +// Thus we need to move input in lr, sign it and then copy it back to the result register. +.arch_extension pauth + LEAF_ENTRY PacSignPtr, _TEXT + mov x9, lr + mov lr, x0 + paciaz + mov x0, lr + ret x9 + LEAF_END PacSignPtr, _TEXT \ No newline at end of file diff --git a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm index 49baea4977259b..37cf470f9c9a5a 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm @@ -5,4 +5,28 @@ TEXTAREA +; void* PacStripPtr(void *); +; This function strips the pointer of PAC info that is passed as an agrument. +; To avoid failing on non-PAC enabled machines, we use xpaclri (instead of xpaci) which strips lr explicitly. +; Thus we move need to move input in lr, strip it and copy it back to the result register. + LEAF_ENTRY PacStripPtr + mov x9, lr + mov lr, x0 + DCD 0xD50320FF ; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers + mov x0, lr + ret x9 + LEAF_END PacStripPtr + +; void* PacSignPtr(void *); +; This function sign the input pointer using zero as salt. +; To avoid failing on non-PAC enabled machines, we use paciaz (instead of paciza) which signs lr explicitly. +; Thus we need to move input in lr, sign it and then copy it back to the result register. + LEAF_ENTRY PacSignPtr + mov x9, lr + mov lr, x0 + DCD 0xD503231F ; paciaz instruction in binary to avoid error while compiling with non-PAC enabled compilers + mov x0, lr + ret x9 + LEAF_END PacSignPtr + end diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index fd63e8954eb963..b83c042851c800 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); +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 + ee_alloc_context::PerThreadRandom::PerThreadRandom() { minipal_xoshiro128pp_init(&random_state, (uint32_t)minipal_hires_ticks()); @@ -803,8 +808,14 @@ 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; @@ -814,7 +825,14 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack frameIterator->GetRegisterSet())); #endif - *ppvRetAddrLocation = (void*)pfnHijackFunction; + void* pvHijackedAddr = (void*)pfnHijackFunction; +#if defined(TARGET_ARM64) + if (frameIterator->GetCodeManager()->IsPacPresent(frameIterator->GetMethodInfo(), frameIterator->GetRegisterSet())) + { + pvHijackedAddr = PacSignPtr(pvHijackedAddr); + } +#endif // TARGET_ARM64 + *ppvRetAddrLocation = pvHijackedAddr; STRESS_LOG2(LF_STACKWALK, LL_INFO10000, "InternalHijack: TgtThread = %llx, IP = %p\n", GetPalThreadIdForLogging(), frameIterator->GetRegisterSet()->GetIP()); @@ -942,6 +960,7 @@ void Thread::UnhijackWorker() // Restore the original return address. ASSERT(m_ppvHijackedReturnAddressLocation != NULL); + *m_ppvHijackedReturnAddressLocation = m_pvHijackedReturnAddress; // Clear the hijack state. diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index 266b56bd1f6e4e..ea0f6ca0b22746 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -64,6 +64,84 @@ 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; +} + +bool UnixNativeCodeManager::IsPacPresent(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet) +{ + UnixNativeMethodInfo* pNativeMethodInfo = (UnixNativeMethodInfo*)pMethodInfo; + const uint8_t *p = (uint8_t *) pNativeMethodInfo->unwind_info; + const uint8_t *end = p + *((uint32_t *)p); + p += 4; // Skip length + assert(*((uint32_t *)p) != 0); // Ensure it's FDE entry + p += 4; // Skip offset to CIE + p += 4; // Skip PC start + p += 4; // Skip function length + size_t augmentationLength = readULEB(p, end); + p += augmentationLength; // skip augmentation data + + while (p < end) { + uint8_t op = *p++; + + if (op == DW_CFA_AARCH64_negate_ra_state) + { + return true; + } + + if ((op & 0xC0) == DW_CFA_advance_loc) + { + continue; + } + if ((op & ~(0x3F)) == DW_CFA_offset) + { + readULEB(p, end); // offset + continue; + } + + // Extended, single‐byte opcodes: + switch (op) { + case DW_CFA_advance_loc1: + case DW_CFA_def_cfa_register: + p++; // offset + break; + + case DW_CFA_offset_extended_sf: + case DW_CFA_offset_extended: + readULEB(p, end); // register + readULEB(p, end); // offset + break; + + case DW_CFA_def_cfa_offset: // DW_CFA_def_cfa_offset + readULEB(p, end); // offset + break; + + case DW_CFA_def_cfa: // DW_CFA_def_cfa + p++; // register + readULEB(p, end); // offset + break; + + default: // Unknown unwind op code + //TODO-PAC: Handle unknown op codes correctly. return false/assert false? + p++; + } + } + return false; +} +#endif // TARGET_ARM64 + // Virtually unwind stack to the caller of the context specified by the REGDISPLAY bool UnixNativeCodeManager::VirtualUnwind(MethodInfo* pMethodInfo, REGDISPLAY* pRegisterSet) { diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h index ca3f3f2272bde1..be455860b3277e 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h @@ -73,6 +73,10 @@ class UnixNativeCodeManager : public ICodeManager REGDISPLAY * pRegisterSet, // in PTR_PTR_VOID * ppvRetAddrLocation); // out +#if defined(TARGET_ARM64) + bool IsPacPresent(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet); +#endif // TARGET_ARM64 + PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC); bool EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumState); diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp index ec315adbd9e39c..a95a7a0b125fd9 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -831,6 +831,55 @@ bool CoffNativeCodeManager::IsUnwindable(PTR_VOID pvAddress) return true; } +#if defined(TARGET_ARM64) +bool CoffNativeCodeManager::IsPacPresent(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet) +{ + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + + size_t unwindDataBlobSize; + + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize); + + PTR_uint8_t UnwindCodePtr = dac_cast(pUnwindDataBlob); + PTR_uint8_t UnwindCodesEndPtr = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + + while (UnwindCodePtr < UnwindCodesEndPtr) + { + uint8_t CurCode = * UnwindCodePtr; + if ((CurCode & 0xfe) == 0xe4) // The last unwind code + { + break; + } + + if (CurCode == 0xFC) // Unwind code for PAC (pac_sign_lr) + { + return true; + } + + if (CurCode < 0xC0) + { + UnwindCodePtr += 1; + } + else if (CurCode < 0xE0) + { + UnwindCodePtr += 2; + } + else + { + static const BYTE UnwindCodeSizeTable[32] = + { + 4,1,2,1,1,1,1,3, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 2,3,4,5,1,1,1,1 + }; + + UnwindCodePtr += UnwindCodeSizeTable[CurCode - 0xE0]; + } + } + + return false; +} +#endif //TARGET_ARM64 + bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in PTR_PTR_VOID * ppvRetAddrLocation) // out diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h index c85f5250967793..0f54e591f95298 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h @@ -90,6 +90,8 @@ class CoffNativeCodeManager : public ICodeManager bool IsUnwindable(PTR_VOID pvAddress); + bool IsPacPresent(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet); + bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in PTR_PTR_VOID * ppvRetAddrLocation); // out 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..1e3a2a1f37b23e 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 paciaz } } 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 f405fddde48a6b..3fb98b81878645 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 @@ -51,6 +51,7 @@ private static byte[] CfiCodeToInstructions(DwarfCie cie, byte[] blobData) { codeOffset = Math.Max(codeOffset, blobData[offset++]); CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset++]; + short dwarfReg = BitConverter.ToInt16(blobData, offset); offset += sizeof(short); int cfiOffset = BitConverter.ToInt32(blobData, offset); @@ -113,6 +114,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.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs index 4a1742c36b8f58..c80340214bed5f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs @@ -790,6 +790,13 @@ private static uint GetArm64CompactUnwindCode(byte[] blobData) { codeOffset = Math.Max(codeOffset, blobData[offset++]); CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset++]; + + if (opcode == CFI_OPCODE.CFI_NEGATE_RA_STATE) + { + // Falling back to DWARF + return UNWIND_ARM64_MODE_DWARF; + } + short dwarfReg = BinaryPrimitives.ReadInt16LittleEndian(blobData.AsSpan(offset)); offset += sizeof(short); int cfiOffset = BinaryPrimitives.ReadInt32LittleEndian(blobData.AsSpan(offset)); 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 3c7f75281ebc7d..16b25ca73a6f1a 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -92,7 +92,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 paciaz } // Get the CFI data in the same shape as clang/LLVM generated one. This improves the compatibility with libunwind and other unwind solutions @@ -123,6 +124,7 @@ private static byte[] CompressARM64CFI(byte[] blobData) } int offset = 0; + bool shouldAddPACOpCode = false; while (offset < blobData.Length) { codeOffset = Math.Max(codeOffset, blobData[offset++]); @@ -176,6 +178,10 @@ private static byte[] CompressARM64CFI(byte[] blobData) } } break; + + case CFI_OPCODE.CFI_NEGATE_RA_STATE: + shouldAddPACOpCode = true; + break; } } @@ -185,6 +191,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(cfaOffset); + } + if (cfaRegister != -1) { cfiWriter.Write((byte)codeOffset); diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index f76a97c67f88ec..bb5294e584b5e1 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -31,6 +31,10 @@ #define FIELD_OFFSET(type, field) ((LONG)__builtin_offsetof(type, field)) #endif +#if !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) +extern "C" void* PacStripPtr(void* ptr); +#endif // !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) + #ifdef HOST_UNIX #define RtlZeroMemory ZeroMemory @@ -251,16 +255,71 @@ do { #endif // !defined(DEBUGGER_UNWIND) -// // Macros for stripping pointer authentication (PAC) bits. -// +#if !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) -#if !defined(DEBUGGER_STRIP_PAC) +#define STRIP_PAC(pointer) RtlStripPacOnline(pointer) -// NOTE: Pointer authentication is not used by .NET, so the implementation does nothing -#define STRIP_PAC(Params, pointer) +FORCEINLINE +VOID RtlStripPacOnline(_Inout_ PULONG64 Pointer) -#endif +/*++ + +Routine Description: + + This routine strips the ARM64 Pointer Authentication Code (PAC) from a + pointer using the ARM64-native xpaci intrinsic directly. Hence this should + only be called when stripping a pointer at runtime (not debugger) + +Arguments: + + Pointer - Supplies a pointer to the pointer whose PAC will be stripped. + +Return Value: + + None. + +--*/ + +{ + *Pointer = (ULONG64)PacStripPtr((void *) (*Pointer)); +} +#else + +#define STRIP_PAC(pointer) RtlStripPacManual(pointer) + +FORCEINLINE +VOID +RtlStripPacManual( + _Inout_ PULONG64 Pointer +) +/*++ + +Routine Description: + + This routine manually strips the ARM64 Pointer Authentication Code (PAC) + from a pointer. This is functionally similar to the XPAC family of + instructions. + + N.B. Even though PAC is only supported on ARM64, this routine is available + on all architectures to conveniently enable scenarios such as the + Debugger. + +Arguments: + + Pointer - Supplies a pointer to the pointer whose PAC will be stripped. + +Return Value: + + None. + +--*/ +{ + *Pointer &= 0x0000FFFFFFFFFFFF; + return; +} + +#endif // !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) // // Macros to clarify opcode parsing @@ -2343,7 +2402,7 @@ Return Value: return STATUS_UNWIND_INVALID_SEQUENCE; } - STRIP_PAC(UnwindParams, &ContextRecord->Lr); + STRIP_PAC(&ContextRecord->Lr); // // TODO: Implement support for UnwindFlags RTL_VIRTUAL_UNWIND2_VALIDATE_PAC. diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index b9576f72ff7f2c..e60e903f8421b7 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -161,11 +161,38 @@ NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler EPILOG_RESTORE_REG_PAIR x25, x26, 64 EPILOG_RESTORE_REG_PAIR x27, x28, 80 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 192 + xpaclri EPILOG_RETURN NESTED_END OnHijackTripThread, _TEXT #endif // FEATURE_HIJACK +// void* PacStripPtr(void *); +// This function strips the pointer of PAC info that is passed as an agrument. +// To avoid failing on non-PAC enabled machines, we use xpaclri (instead of xpaci) which strips lr explicitly. +// Thus we move need to move input in lr, strip it and copy it back to the result register. +.arch_extension pauth + LEAF_ENTRY PacStripPtr, _TEXT + mov x9, lr + mov lr, x0 + xpaclri + mov x0, lr + ret x9 + LEAF_END PacStripPtr, _TEXT + +// void* PacSignPtr(void *); +// This function sign the input pointer using zero as salt. +// To avoid failing on non-PAC enabled machines, we use paciaz (instead of paciza) which signs lr explicitly. +// Thus we need to move input in lr, sign it and then copy it back to the result register. +.arch_extension pauth + LEAF_ENTRY PacSignPtr, _TEXT + mov x9, lr + mov lr, x0 + paciaz + mov x0, lr + ret x9 + LEAF_END PacSignPtr, _TEXT + // ------------------------------------------------------------------ // Redirection Stub for GC in fully interruptible method //GenerateRedirectedHandledJITCaseStub GCThreadControl diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index 25d85f96458c56..4a4199ef93d672 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -496,11 +496,36 @@ COMToCLRDispatchHelper_RegSetup EPILOG_RESTORE_REG_PAIR x25, x26, #64 EPILOG_RESTORE_REG_PAIR x27, x28, #80 EPILOG_RESTORE_REG_PAIR fp, lr, #192! + DCD 0xD50320FF ; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers EPILOG_RETURN NESTED_END #endif ; FEATURE_HIJACK +; void* PacStripPtr(void *); +; This function strips the pointer of PAC info that is passed as an agrument. +; To avoid failing on non-PAC enabled machines, we use xpaclri (instead of xpaci) which strips lr explicitly. +; Thus we move need to move input in lr, strip it and copy it back to the result register. + LEAF_ENTRY PacStripPtr + mov x9, lr + mov lr, x0 + DCD 0xD50320FF ; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers + mov x0, lr + ret x9 + LEAF_END PacStripPtr + +; void* PacSignPtr(void *); +; This function sign the input pointer using zero as salt. +; To avoid failing on non-PAC enabled machines, we use paciaz (instead of paciza) which signs lr explicitly. +; Thus we need to move input in lr, sign it and then copy it back to the result register. + LEAF_ENTRY PacSignPtr + mov x9, lr + mov lr, x0 + DCD 0xD503231F ; paciaz instruction in binary to avoid error while compiling with non-PAC enabled compilers + mov x0, lr + ret x9 + LEAF_END PacSignPtr + ;; ------------------------------------------------------------------ ;; Redirection Stub for GC in fully interruptible method GenerateRedirectedHandledJITCaseStub GCThreadControl diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index aa668e54a99a2a..d2e92bb68ede57 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -44,7 +44,6 @@ EXTERN_C void getFPReturn(int fpSize, INT64 *pRetVal); EXTERN_C void setFPReturn(int fpSize, INT64 retVal); - class ComCallMethodDesc; extern PCODE GetPreStubEntryPoint(); @@ -218,7 +217,8 @@ typedef struct _PROFILE_PLATFORM_SPECIFIC_DATA inline PCODE GetIP(const T_CONTEXT * context) { LIMITED_METHOD_DAC_CONTRACT; - return context->Pc; + //TODO-PAC: Strip/Authenticate while populating the context. + return (PCODE) context->Pc; } inline void SetIP(T_CONTEXT *context, PCODE eip) { diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 9555397240830d..60d01b983eb885 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6909,6 +6909,82 @@ bool IsIPInEpilog(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, BOOL *pSaf return fIsInEpilog; } +#if defined(TARGET_ARM64) +// This function is used to check if Pointer Authentication (PAC) is enabled for this stack frame or not. +bool IsPacPresent(EECodeInfo *pCodeInfo) +{ + _ASSERTE(pCodeInfo->IsValid()); + + // Lookup the function entry for the IP + PTR_RUNTIME_FUNCTION FunctionEntry = pCodeInfo->GetFunctionEntry(); + + // We should always get a function entry for a managed method + _ASSERTE(FunctionEntry != NULL); + DWORD_PTR ImageBase = pCodeInfo->GetModuleBase(); + + _ASSERTE((FunctionEntry->UnwindData & 3) == 0); // Packed unwind data are not used with managed code + ULONG_PTR UnwindDataPtr = (ULONG_PTR)(ImageBase + FunctionEntry->UnwindData); + + // Read the header word. For unwind info layout details refer https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#arm64-exception-handling-information + DWORD HeaderWord = *(DWORD*)UnwindDataPtr; + UnwindDataPtr += 4; + + _ASSERTE(((HeaderWord >> 18) & 3) == 0); // Version 0 is the only supported version. + + ULONG UnwindWords = (HeaderWord >> 27) & 31; + ULONG EpilogScopeCount = (HeaderWord >> 22) & 31; + if (EpilogScopeCount == 0 && UnwindWords == 0) + { + EpilogScopeCount = *(DWORD*)UnwindDataPtr; + UnwindDataPtr += 4; + UnwindWords = (EpilogScopeCount >> 16) & 0xff; + EpilogScopeCount &= 0xffff; + } + + if ((HeaderWord & (1 << 21)) != 0) + { + EpilogScopeCount = 0; + } + + ULONG_PTR UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount; + ULONG_PTR UnwindCodesEndPtr = UnwindCodePtr + 4 * UnwindWords; + + while (UnwindCodePtr < UnwindCodesEndPtr) + { + ULONG CurCode = *(BYTE*)UnwindCodePtr; + if ((CurCode & 0xfe) == 0xe4) // The last unwind code + { + break; + } + + if (CurCode == 0xFC) // Unwind code for PAC (pac_sign_lr) + { + return true; + } + + if (CurCode < 0xC0) + { + UnwindCodePtr += 1; + } + else if (CurCode < 0xE0) + { + UnwindCodePtr += 2; + } + else + { + static const BYTE UnwindCodeSizeTable[32] = + { + 4,1,2,1,1,1,1,3, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 2,3,4,5,1,1,1,1 + }; + + UnwindCodePtr += UnwindCodeSizeTable[CurCode - 0xE0]; + } + } + + return false; +} +#endif // TARGET_ARM64 + #endif // FEATURE_HIJACK && (!TARGET_X86 || TARGET_UNIX) #define EXCEPTION_VISUALCPP_DEBUGGER ((DWORD) (1<<30 | 0x6D<<16 | 5000)) diff --git a/src/coreclr/vm/excep.h b/src/coreclr/vm/excep.h index 50361b6fd18e2c..66dd424b8d47e2 100644 --- a/src/coreclr/vm/excep.h +++ b/src/coreclr/vm/excep.h @@ -34,7 +34,9 @@ BOOL AdjustContextForJITHelpers(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pCo // General purpose functions for use on an IP in jitted code. bool IsIPInProlog(EECodeInfo *pCodeInfo); bool IsIPInEpilog(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, BOOL *pSafeToInjectThreadAbort); - +#if defined(TARGET_ARM64) +bool IsPacPresent(EECodeInfo *pCodeInfo); +#endif // TARGET_ARM64 #endif // FEATURE_HIJACK && (!TARGET_X86 || TARGET_UNIX) // Enums diff --git a/src/coreclr/vm/tailcallhelp.cpp b/src/coreclr/vm/tailcallhelp.cpp index da2dcca7d1da13..e6f385dad7b108 100644 --- a/src/coreclr/vm/tailcallhelp.cpp +++ b/src/coreclr/vm/tailcallhelp.cpp @@ -10,6 +10,9 @@ #include "gcrefmap.h" #include "threads.h" +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 FCIMPL0(void*, TailCallHelp::GetTailCallArgBuffer) { @@ -39,7 +42,13 @@ FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr Thread* thread = GetThread(); - *retAddr = thread->GetReturnAddress(retAddrSlot); + void* retAddrFromSlot = thread->GetReturnAddress(retAddrSlot); + +#if defined(TARGET_ARM64) + retAddrFromSlot = PacStripPtr(retAddrFromSlot); +#endif // TARGET_ARM64 + *retAddr = retAddrFromSlot; + return thread->GetTailCallTls(); } FCIMPLEND diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index ea982e831f0251..35c4a4e30c4d65 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -2599,7 +2599,7 @@ class Thread private: #ifdef FEATURE_HIJACK - void HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86_ARG(bool hasAsyncRet)); + void HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86_ARG(bool hasAsyncRet) ARM64_ARG(bool isPacEnabledFrame)); VOID *m_pvHJRetAddr; // original return address (before hijack) VOID **m_ppvHJRetAddrPtr; // place we bashed a new return address diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index e003ae328df79b..8ace16ecd57c0d 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -23,6 +23,10 @@ #define HIJACK_NONINTERRUPTIBLE_THREADS +#if defined(TARGET_ARM64) +extern "C" void* PacSignPtr(void* ptr); +#endif // TARGET_ARM64 + bool ThreadSuspend::s_fSuspendRuntimeInProgress = false; bool ThreadSuspend::s_fSuspended = false; @@ -4542,7 +4546,7 @@ struct ExecutionState }; // Client is responsible for suspending the thread before calling -void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86_ARG(bool hasAsyncRet)) +void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86_ARG(bool hasAsyncRet) ARM64_ARG(bool isPacEnabledFrame)) { CONTRACTL { NOTHROW; @@ -4610,6 +4614,13 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86 m_HijackedFunction = esb->m_pFD; // Bash the stack to return to one of our stubs +#if defined(TARGET_ARM64) + if (isPacEnabledFrame) + { + pvHijackAddr = PacSignPtr(pvHijackAddr); + } +#endif // TARGET_ARM64 + *esb->m_ppvRetAddrPtr = pvHijackAddr; SetThreadState(TS_Hijacked); } @@ -5316,9 +5327,13 @@ BOOL Thread::HandledJITCase() X86_ONLY(ReturnKind returnKind;) X86_ONLY(bool hasAsyncRet;) + ARM64_ONLY(bool isPacEnabledFrame;) if (GetReturnAddressHijackInfo(&codeInfo X86_ARG(&returnKind) X86_ARG(&hasAsyncRet))) { - HijackThread(&esb X86_ARG(returnKind) X86_ARG(hasAsyncRet)); +#ifdef TARGET_ARM64 + isPacEnabledFrame = IsPacPresent(&codeInfo); +#endif + HijackThread(&esb X86_ARG(returnKind) X86_ARG(hasAsyncRet) ARM64_ARG(isPacEnabledFrame)); } } } @@ -5870,7 +5885,11 @@ 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) X86_ARG(hasAsyncRet)); + ARM64_ONLY(bool isPacEnabledFrame); +#ifdef TARGET_ARM64 + isPacEnabledFrame = IsPacPresent(&codeInfo); +#endif + pThread->HijackThread(&executionState X86_ARG(returnKind) X86_ARG(hasAsyncRet) ARM64_ARG(isPacEnabledFrame)); } } diff --git a/src/native/external/llvm-libunwind/src/DwarfInstructions.hpp b/src/native/external/llvm-libunwind/src/DwarfInstructions.hpp index 32112839042b58..620ee10d9926a9 100644 --- a/src/native/external/llvm-libunwind/src/DwarfInstructions.hpp +++ b/src/native/external/llvm-libunwind/src/DwarfInstructions.hpp @@ -354,7 +354,18 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, if (cieInfo.addressesSignedWithBKey) asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716 else - asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716 + { + //TODO-PAC: Restore the authenticating with A key when signing with SP is in place. + //asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716 + __asm__ __volatile__ ("mov x9, lr\n\t" + "mov lr, %0\n\t" + "xpaclri\n\t" + "mov %0, lr\n\t" + "mov lr, x9" + : "+r"(x17) + : + : "x9", "lr"); // strip PAC + } } returnAddress = x17; #endif