From 33c53fa6e1d22abbd45a70dc995c39b9b3cd759f Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Fri, 6 Dec 2024 11:15:49 +0000 Subject: [PATCH 01/57] Add auth-strip in prolog and epilog of jitted methods --- src/coreclr/jit/codegenarm64.cpp | 6 ++++++ src/coreclr/jit/codegenarmarch.cpp | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index ab8d95125ff5c8..2cd974af041e3b 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -271,6 +271,9 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) GetEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, spAdjust); compiler->unwindAllocStack(spAdjust); } + + // TODO-PAC: emit autiasp + GetEmitter()->emitIns(INS_xpaclri); } //------------------------------------------------------------------------ @@ -1691,6 +1694,9 @@ void CodeGen::genFuncletEpilog() } } + // TODO-PAC: emit autiasp + GetEmitter()->emitIns(INS_xpaclri); + 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 07b08c3692a4e5..75d94de977b388 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -4746,6 +4746,9 @@ void CodeGen::genPushCalleeSavedRegisters() } #endif // DEBUG + // Sign LR as part of Pointer Authentication (PAC) support + GetEmitter()->emitIns(INS_paciasp); + // 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; From 19b977a708ea53e47c03829bd8b00255581f304d Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Fri, 24 Jan 2025 16:01:17 +0000 Subject: [PATCH 02/57] Use unwind info to indicate use of PAC - Add support for 'pac_sign_lr' unwind code - Use authenticate instruciton instead of stripping PAC in epilog --- src/coreclr/jit/codegenarm64.cpp | 17 +++++++++++++---- src/coreclr/jit/codegenarmarch.cpp | 1 + src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/unwindarm64.cpp | 19 +++++++++++++++++++ src/coreclr/unwinder/arm64/unwinder.cpp | 6 +++--- 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index a0ad9e05efb167..053a759abda811 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -272,8 +272,8 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) compiler->unwindAllocStack(spAdjust); } - // TODO-PAC: emit autiasp - GetEmitter()->emitIns(INS_xpaclri); + GetEmitter()->emitIns(INS_autiasp); + compiler->unwindPacSignLR(); } //------------------------------------------------------------------------ @@ -526,6 +526,11 @@ void CodeGen::genPrologSaveRegPair(regNumber reg1, compiler->unwindSaveRegPair(reg1, reg2, spOffset); } } + + if (reg2 == REG_LR) + { + compiler->unwindPacSignLR(); + } } //------------------------------------------------------------------------ @@ -1401,6 +1406,10 @@ void CodeGen::genFuncletProlog(BasicBlock* block) compiler->unwindBegProlog(); + // Sign LR as part of Pointer Authentication (PAC) support + GetEmitter()->emitIns(INS_paciasp); + compiler->unwindPacSignLR(); + regMaskTP maskSaveRegsFloat = genFuncletInfo.fiSaveRegs & RBM_ALLFLOAT; regMaskTP maskSaveRegsInt = genFuncletInfo.fiSaveRegs & ~maskSaveRegsFloat; @@ -1724,8 +1733,8 @@ void CodeGen::genFuncletEpilog() } } - // TODO-PAC: emit autiasp - GetEmitter()->emitIns(INS_xpaclri); + GetEmitter()->emitIns(INS_autiasp); + compiler->unwindPacSignLR(); 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 d22975d64e2658..f179dbac063083 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -4746,6 +4746,7 @@ void CodeGen::genPushCalleeSavedRegisters() // Sign LR as part of Pointer Authentication (PAC) support GetEmitter()->emitIns(INS_paciasp); + compiler->unwindPacSignLR(); // The frameType number is arbitrary, is defined below, and corresponds to one of the frame styles we // generate based on various sizes. diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 956e05e9b71f00..f8a4def55c800b 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -8948,6 +8948,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/unwindarm64.cpp b/src/coreclr/jit/unwindarm64.cpp index f842737171c0b4..8509b1987a491e 100644 --- a/src/coreclr/jit/unwindarm64.cpp +++ b/src/coreclr/jit/unwindarm64.cpp @@ -635,6 +635,19 @@ void Compiler::unwindSaveNext() pu->AddCode(0xE6); } +void Compiler::unwindPacSignLR() +{ +#if defined(FEATURE_CFI_SUPPORT) + // do not use unwindSaveNext when generating CFI codes as there is no code for this + assert(!generateCFIUnwindCodes()); +#endif // FEATURE_CFI_SUPPORT + + UnwindInfo* pu = &funCurrentFunc()->uwi; + + // pac_sign_lr: 11111100: sign the return address in lr with pacibsp + pu->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 +1094,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 pacibsp. + + printf(" %02X pac_sign_lr\n", b1); + } else { // Unknown / reserved unwind code diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index c7d04a70255fa1..15ee3e732dc499 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -258,7 +258,7 @@ do { #if !defined(DEBUGGER_STRIP_PAC) // NOTE: Pointer authentication is not used by .NET, so the implementation does nothing -#define STRIP_PAC(Params, pointer) +#define STRIP_PAC(pointer) *pointer & 0x0000FFFFFFFFFFFF #endif @@ -2335,7 +2335,7 @@ Return Value: } // - // pac (11111100): function has pointer authentication + // pac (11111100): function has pointer authentication // else if (CurCode == 0xfc) { @@ -2343,7 +2343,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. From c64cc1fc51b318155c3ebb7bd3a356942d13e856 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 30 Jan 2025 16:53:49 +0000 Subject: [PATCH 03/57] Switch to auth with zero and fix tail call optimisations --- .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 3 ++- src/coreclr/jit/codegenarm64.cpp | 8 +++++--- src/coreclr/jit/codegenarmarch.cpp | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index d60b48ec0e552e..67f291bac4a309 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -511,7 +511,8 @@ private static unsafe void DispatchTailCalls( IntPtr callersRetAddr; TailCallTls* tls = GetTailCallInfo(callersRetAddrSlot, &callersRetAddr); PortableTailCallFrame* prevFrame = tls->Frame; - if (callersRetAddr == prevFrame->TailCallAwareReturnAddress) + // TODO-PAC: Implement stripping of PAC from return addresses in coreclr + if ((callersRetAddr & 0x0000FFFFFFFFFFFF) == (prevFrame->TailCallAwareReturnAddress & 0x0000FFFFFFFFFFFF)) { prevFrame->NextCall = callTarget; return; diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index ad956572fc4216..c14e4938a97cf0 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -272,7 +272,8 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) compiler->unwindAllocStack(spAdjust); } - GetEmitter()->emitIns(INS_autiasp); + // TODO-PAC: emit autiasp + GetEmitter()->emitIns(INS_autiaz); compiler->unwindPacSignLR(); } @@ -1407,7 +1408,7 @@ void CodeGen::genFuncletProlog(BasicBlock* block) compiler->unwindBegProlog(); // Sign LR as part of Pointer Authentication (PAC) support - GetEmitter()->emitIns(INS_paciasp); + GetEmitter()->emitIns(INS_paciaz); compiler->unwindPacSignLR(); regMaskTP maskSaveRegsFloat = genFuncletInfo.fiSaveRegs & RBM_ALLFLOAT; @@ -1733,7 +1734,8 @@ void CodeGen::genFuncletEpilog() } } - GetEmitter()->emitIns(INS_autiasp); + // TODO-PAC: emit autiasp + GetEmitter()->emitIns(INS_autiaz); compiler->unwindPacSignLR(); inst_RV(INS_ret, REG_LR, TYP_I_IMPL); diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index f179dbac063083..e71d6bbd458996 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -4745,7 +4745,7 @@ void CodeGen::genPushCalleeSavedRegisters() #endif // DEBUG // Sign LR as part of Pointer Authentication (PAC) support - GetEmitter()->emitIns(INS_paciasp); + GetEmitter()->emitIns(INS_paciaz); compiler->unwindPacSignLR(); // The frameType number is arbitrary, is defined below, and corresponds to one of the frame styles we From cfddf715827be29a4225e5c634f2979ec8b3f7f6 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Thu, 30 Jan 2025 21:30:56 -0800 Subject: [PATCH 04/57] fix nativeaot CFI unwind code --- src/coreclr/jit/codegenarm64.cpp | 4 ++-- src/coreclr/jit/unwindarm64.cpp | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index c14e4938a97cf0..5c3fde80fc3807 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -274,7 +274,7 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) // TODO-PAC: emit autiasp GetEmitter()->emitIns(INS_autiaz); - compiler->unwindPacSignLR(); + //compiler->unwindPacSignLR(); } //------------------------------------------------------------------------ @@ -1736,7 +1736,7 @@ void CodeGen::genFuncletEpilog() // TODO-PAC: emit autiasp GetEmitter()->emitIns(INS_autiaz); - compiler->unwindPacSignLR(); + //compiler->unwindPacSignLR(); inst_RV(INS_ret, REG_LR, TYP_I_IMPL); compiler->unwindReturn(REG_LR); diff --git a/src/coreclr/jit/unwindarm64.cpp b/src/coreclr/jit/unwindarm64.cpp index 8509b1987a491e..29f3ada5cb26f2 100644 --- a/src/coreclr/jit/unwindarm64.cpp +++ b/src/coreclr/jit/unwindarm64.cpp @@ -638,14 +638,26 @@ void Compiler::unwindSaveNext() void Compiler::unwindPacSignLR() { #if defined(FEATURE_CFI_SUPPORT) - // do not use unwindSaveNext when generating CFI codes as there is no code for this - assert(!generateCFIUnwindCodes()); + if (generateCFIUnwindCodes()) + { + FuncInfoDsc* func = funCurrentFunc(); + UNATIVE_OFFSET cbProlog = 0; + if (compGeneratingProlog) + { + cbProlog = unwindGetCurrentOffset(func); + } + + // DW_CFA_GNU_window_save 0x2D + createCfiCode(func, cbProlog, CFI_DEF_CFA_REGISTER, DWARF_REG_ILLEGAL); + + return; + } #endif // FEATURE_CFI_SUPPORT - UnwindInfo* pu = &funCurrentFunc()->uwi; + assert(compGeneratingProlog); // pac_sign_lr: 11111100: sign the return address in lr with pacibsp - pu->AddCode(0xFC); + funCurrentFunc()->uwi.AddCode(0xFC); } void Compiler::unwindReturn(regNumber reg) From 3bf93460b92d972ead0c59101533d4b5b65a9a0f Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Thu, 30 Jan 2025 21:31:16 -0800 Subject: [PATCH 05/57] fix build --- src/coreclr/unwinder/arm64/unwinder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 15ee3e732dc499..69988195a3871e 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -258,7 +258,7 @@ do { #if !defined(DEBUGGER_STRIP_PAC) // NOTE: Pointer authentication is not used by .NET, so the implementation does nothing -#define STRIP_PAC(pointer) *pointer & 0x0000FFFFFFFFFFFF +#define STRIP_PAC(pointer) *pointer &= 0x0000FFFFFFFFFFFF #endif From 042210fc2a1f90ebd716b6ae06357a92f25eea50 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Wed, 5 Feb 2025 14:04:21 -0800 Subject: [PATCH 06/57] Add STRIP_PAC --- src/coreclr/unwinder/arm64/unwinder.cpp | 94 +++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 69988195a3871e..d0a3fabe7c19e2 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -255,13 +255,99 @@ do { // Macros for stripping pointer authentication (PAC) bits. // -#if !defined(DEBUGGER_STRIP_PAC) +#if !defined(DACCESS_COMPILE) + +#define STRIP_PAC(Params, pointer) RtlStripPacOnline(pointer) +//FORCEINLINE +//VOID +//RtlStripPacOnline ( +// _Inout_ PULONG64 Pointer +// ) +// +///*++ +// +//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. +// +//--*/ +// +//{ +// ULONG64 StrippedPointer; +// +// StrippedPointer = *Pointer; +// StrippedPointer = (ULONG64)__xpaci((PVOID)StrippedPointer); +// *Pointer = StrippedPointer; +// return; +//} -// NOTE: Pointer authentication is not used by .NET, so the implementation does nothing -#define STRIP_PAC(pointer) *pointer &= 0x0000FFFFFFFFFFFF +#else -#endif +#define STRIP_PAC(Params, 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. + +--*/ +{ + ULONG64 StrippedPointer; + + // For now hard code to mask everything but the bottom 55 bits + ULONG64 PointerAuthMask = (1ull << 55) - 1; + + StrippedPointer = *Pointer; + + // + // Strip the pointer manually. Bit 55 of the pointer is replicated across + // the "unused" bits (if bit 55 is 1, all unused bits should be 1; if bit + // 55 is 0, all unused bits should be 0). + // + + if (((StrippedPointer >> 55) & 0x1) == 0) { + StrippedPointer &= ~PointerAuthMask; + + } else { + StrippedPointer |= PointerAuthMask; + } + + *Pointer = StrippedPointer; + + return; +} +#endif // !defined(DACCESS_COMPILE) // // Macros to clarify opcode parsing // From 435a9016867c8f1006efc0e815afb60b47efd3df Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Wed, 5 Feb 2025 16:52:39 -0800 Subject: [PATCH 07/57] wip --- src/coreclr/inc/cfi.h | 3 ++- src/coreclr/jit/codegenarm64.cpp | 4 ++-- src/coreclr/jit/unwind.cpp | 5 +++++ src/coreclr/jit/unwindarm64.cpp | 14 ++++++++------ .../Compiler/ObjectWriter/Dwarf/DwarfFde.cs | 3 +++ .../JitInterface/CorInfoImpl.RyuJit.cs | 3 +++ src/coreclr/unwinder/arm64/unwinder.cpp | 4 ++-- 7 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/coreclr/inc/cfi.h b/src/coreclr/inc/cfi.h index 3d7ec0f4cc11f8..25a927956ec267 100644 --- a/src/coreclr/inc/cfi.h +++ b/src/coreclr/inc/cfi.h @@ -9,7 +9,8 @@ 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_NEGATE_RA_STATE, // Sign the return address in lr with pacibsp }; struct CFI_CODE diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 5c3fde80fc3807..c14e4938a97cf0 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -274,7 +274,7 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) // TODO-PAC: emit autiasp GetEmitter()->emitIns(INS_autiaz); - //compiler->unwindPacSignLR(); + compiler->unwindPacSignLR(); } //------------------------------------------------------------------------ @@ -1736,7 +1736,7 @@ void CodeGen::genFuncletEpilog() // TODO-PAC: emit autiasp GetEmitter()->emitIns(INS_autiaz); - //compiler->unwindPacSignLR(); + compiler->unwindPacSignLR(); inst_RV(INS_ret, REG_LR, TYP_I_IMPL); compiler->unwindReturn(REG_LR); diff --git a/src/coreclr/jit/unwind.cpp b/src/coreclr/jit/unwind.cpp index a51a52ab21d640..68cda2dd7ab9af 100644 --- a/src/coreclr/jit/unwind.cpp +++ b/src/coreclr/jit/unwind.cpp @@ -399,6 +399,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 29f3ada5cb26f2..9c74154cdcb7c1 100644 --- a/src/coreclr/jit/unwindarm64.cpp +++ b/src/coreclr/jit/unwindarm64.cpp @@ -647,17 +647,19 @@ void Compiler::unwindPacSignLR() cbProlog = unwindGetCurrentOffset(func); } - // DW_CFA_GNU_window_save 0x2D - createCfiCode(func, cbProlog, CFI_DEF_CFA_REGISTER, DWARF_REG_ILLEGAL); + // Maps to DW_CFA_AARCH64_negate_ra_state + createCfiCode(func, cbProlog, CFI_NEGATE_RA_STATE, DWARF_REG_ILLEGAL); return; } #endif // FEATURE_CFI_SUPPORT - assert(compGeneratingProlog); - - // pac_sign_lr: 11111100: sign the return address in lr with pacibsp - funCurrentFunc()->uwi.AddCode(0xFC); + if (compGeneratingProlog) + { + // pac_sign_lr: 11111100: sign the return address in lr with pacibsp + // needed only for prolog + funCurrentFunc()->uwi.AddCode(0xFC); + } } void Compiler::unwindReturn(regNumber reg) 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..ed9144e7724cf9 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 @@ -113,6 +113,9 @@ private static byte[] CfiCodeToInstructions(DwarfCie cie, byte[] blobData) cfaOffset = cfiOffset; cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)cfaOffset); break; + case CFI_NEGATE_RA_STATE: + cfiCode[cfiCodeOffset++] = DW_CFA_restore_state; + break; } } 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 b4933c28b25afc..f23785acdbff54 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -173,6 +173,9 @@ private static byte[] CompressARM64CFI(byte[] blobData) } } break; + case CFI_OPCODE.CFI_NEGATE_RA_STATE: + // Nothing to compress here. It just has the code. + break; } } diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index d0a3fabe7c19e2..c7ffc2ea16d780 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -257,7 +257,7 @@ do { #if !defined(DACCESS_COMPILE) -#define STRIP_PAC(Params, pointer) RtlStripPacOnline(pointer) +#define STRIP_PAC(pointer) RtlStripPacOnline(pointer) //FORCEINLINE //VOID //RtlStripPacOnline ( @@ -293,7 +293,7 @@ do { #else -#define STRIP_PAC(Params, pointer) RtlStripPacManual(pointer) +#define STRIP_PAC(pointer) RtlStripPacManual(pointer) FORCEINLINE VOID From e9f101c3035fa7cf98a2e4c560cbc2e31801c394 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Wed, 5 Feb 2025 17:00:19 -0800 Subject: [PATCH 08/57] Uncomment the implementation of RtlStripPacOnline() --- src/coreclr/unwinder/arm64/unwinder.cpp | 65 +++++++++++++------------ 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index c7ffc2ea16d780..f71b02179894a8 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -258,38 +258,39 @@ do { #if !defined(DACCESS_COMPILE) #define STRIP_PAC(pointer) RtlStripPacOnline(pointer) -//FORCEINLINE -//VOID -//RtlStripPacOnline ( -// _Inout_ PULONG64 Pointer -// ) -// -///*++ -// -//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. -// -//--*/ -// -//{ -// ULONG64 StrippedPointer; -// -// StrippedPointer = *Pointer; -// StrippedPointer = (ULONG64)__xpaci((PVOID)StrippedPointer); -// *Pointer = StrippedPointer; -// return; -//} + +FORCEINLINE +VOID +RtlStripPacOnline ( + _Inout_ PULONG64 Pointer + ) + +/*++ + +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. + +--*/ + +{ + ULONG64 StrippedPointer; + + StrippedPointer = *Pointer; + StrippedPointer = (ULONG64)__xpaci((PVOID)StrippedPointer); + *Pointer = StrippedPointer; + return; +} #else From 1dae16f3b476a7ae1201b8c01168877980977200 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Wed, 5 Feb 2025 17:06:57 -0800 Subject: [PATCH 09/57] Add some missing declaration of CFI_NEGATE_RA_STATE --- .../Compiler/ObjectWriter/Dwarf/DwarfCfiOpcode.cs | 3 ++- .../aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) 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..24575a0dfa81ba 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 pacibsp } } 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 f23785acdbff54..b4ba5ff6b3415b 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -89,7 +89,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 pacibsp } // Get the CFI data in the same shape as clang/LLVM generated one. This improves the compatibility with libunwind and other unwind solutions From a1265fba6c12c92aca4d955bcee1070f5cc32c3e Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Wed, 5 Feb 2025 19:02:45 -0800 Subject: [PATCH 10/57] build fix --- .../ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ed9144e7724cf9..bb778aa83dbcba 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 @@ -113,7 +113,7 @@ private static byte[] CfiCodeToInstructions(DwarfCie cie, byte[] blobData) cfaOffset = cfiOffset; cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)cfaOffset); break; - case CFI_NEGATE_RA_STATE: + case CFI_OPCODE.CFI_NEGATE_RA_STATE: cfiCode[cfiCodeOffset++] = DW_CFA_restore_state; break; } From 8a05d92f5da528a3ed224f773255f9e84238f05c Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Wed, 5 Feb 2025 19:17:45 -0800 Subject: [PATCH 11/57] Comment StripOnline --- src/coreclr/unwinder/arm64/unwinder.cpp | 120 ++++++++++++++++-------- 1 file changed, 80 insertions(+), 40 deletions(-) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index f71b02179894a8..78b0518b3c1c0c 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -254,45 +254,83 @@ do { // // Macros for stripping pointer authentication (PAC) bits. // - -#if !defined(DACCESS_COMPILE) - -#define STRIP_PAC(pointer) RtlStripPacOnline(pointer) - -FORCEINLINE -VOID -RtlStripPacOnline ( - _Inout_ PULONG64 Pointer - ) - -/*++ - -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. - ---*/ - -{ - ULONG64 StrippedPointer; - - StrippedPointer = *Pointer; - StrippedPointer = (ULONG64)__xpaci((PVOID)StrippedPointer); - *Pointer = StrippedPointer; - return; -} - -#else +// +//#if !defined(DACCESS_COMPILE) +// +//#define STRIP_PAC(pointer) RtlStripPacOnline(pointer) +// +// +//FORCEINLINE +////VOID RtlStripPacOnline(_Inout_ PULONG64 Pointer) +//VOID RtlStripPacOnline(void** Pointer) +// +///*++ +// +//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. +// +//--*/ +// +//{ +// // This is a key step: The pointer is passed as a reference to be modified. +// unsigned int key = 12345; // Example key (not used for XPACI, but for PAC authentication) +// +// // Inline assembly to invoke the `xpaci` instruction. +// // The pointer is passed as an argument and modified in-place. +// asm volatile ( +// "xpaci %[Pointer]" // Strip the Pointer Authentication Code (PAC) from the pointer +// : [Pointer] "+r" (Pointer) // The pointer is modified in-place +// : // No input operands needed (beyond the pointer) +// : "memory" // Memory is affected, prevent reordering +// ); +//} +// +//// +////FORCEINLINE +////VOID +////RtlStripPacOnline ( +//// _Inout_ PULONG64 Pointer +//// ) +//// +/////*++ +//// +////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. +//// +////--*/ +//// +////{ +//// ULONG64 StrippedPointer = CONTEXT_GetSveLengthFromOS(); +//// +//// StrippedPointer = *Pointer; +//// StrippedPointer = (ULONG64)__xpaci((PVOID)StrippedPointer); +//// *Pointer = StrippedPointer; +//// return; +////} +// +//#else #define STRIP_PAC(pointer) RtlStripPacManual(pointer) @@ -348,7 +386,9 @@ Return Value: return; } -#endif // !defined(DACCESS_COMPILE) +//#endif // !defined(DACCESS_COMPILE) + + // // Macros to clarify opcode parsing // From 98246b7df0aa62202452127fa3fbdedfb0ae7624 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Thu, 6 Feb 2025 12:13:50 -0800 Subject: [PATCH 12/57] Add JitPacEnabled() switch --- src/coreclr/jit/codegenarm64.cpp | 17 +++++++---------- src/coreclr/jit/codegenarmarch.cpp | 3 +-- src/coreclr/jit/emit.h | 5 +++++ src/coreclr/jit/emitarm64.cpp | 27 +++++++++++++++++++++++++++ src/coreclr/jit/jitconfigvalues.h | 1 + src/coreclr/jit/unwindarm64.cpp | 4 ++++ 6 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index c14e4938a97cf0..00a806cbbf20e0 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -272,9 +272,7 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog) compiler->unwindAllocStack(spAdjust); } - // TODO-PAC: emit autiasp - GetEmitter()->emitIns(INS_autiaz); - compiler->unwindPacSignLR(); + GetEmitter()->emitPacInEpilog(); } //------------------------------------------------------------------------ @@ -663,6 +661,10 @@ void CodeGen::genEpilogRestoreRegPair(regNumber reg1, compiler->unwindSaveRegPair(reg1, reg2, spOffset); } } + if (reg2 == REG_LR) + { + compiler->unwindPacSignLR(); + } } //------------------------------------------------------------------------ @@ -1406,10 +1408,7 @@ void CodeGen::genFuncletProlog(BasicBlock* block) gcInfo.gcResetForBB(); compiler->unwindBegProlog(); - - // Sign LR as part of Pointer Authentication (PAC) support - GetEmitter()->emitIns(INS_paciaz); - compiler->unwindPacSignLR(); + GetEmitter()->emitPacInProlog(); regMaskTP maskSaveRegsFloat = genFuncletInfo.fiSaveRegs & RBM_ALLFLOAT; regMaskTP maskSaveRegsInt = genFuncletInfo.fiSaveRegs & ~maskSaveRegsFloat; @@ -1734,9 +1733,7 @@ void CodeGen::genFuncletEpilog() } } - // TODO-PAC: emit autiasp - GetEmitter()->emitIns(INS_autiaz); - compiler->unwindPacSignLR(); + 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 e71d6bbd458996..d44061811c8330 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -4745,8 +4745,7 @@ void CodeGen::genPushCalleeSavedRegisters() #endif // DEBUG // Sign LR as part of Pointer Authentication (PAC) support - GetEmitter()->emitIns(INS_paciaz); - compiler->unwindPacSignLR(); + GetEmitter()->emitPacInProlog(); // The frameType number is arbitrary, is defined below, and corresponds to one of the frame styles we // generate based on various sizes. diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 02461633f3c547..e04cb77d3f066c 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -3062,6 +3062,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 3fd34318d9ec11..d0da2c2173ac68 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1382,6 +1382,33 @@ 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; + } + // TODO: should be autiasp + 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 134d5ffc99ca00..3e89b2b5792b07 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -121,6 +121,7 @@ CONFIG_INTEGER(JitInlineDepth, "JITInlineDepth", DEFAULT_MAX_INLINE_DEPTH) CONFIG_INTEGER(JitForceInlineDepth, "JITForceInlineDepth", DEFAULT_MAX_FORCE_INLINE_DEPTH) CONFIG_INTEGER(JitLongAddress, "JitLongAddress", 0) // Force using the large pseudo instruction form for long address CONFIG_INTEGER(JitMaxUncheckedOffset, "JitMaxUncheckedOffset", 8) +CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 1) // // MinOpts diff --git a/src/coreclr/jit/unwindarm64.cpp b/src/coreclr/jit/unwindarm64.cpp index 9c74154cdcb7c1..16aac90308a86e 100644 --- a/src/coreclr/jit/unwindarm64.cpp +++ b/src/coreclr/jit/unwindarm64.cpp @@ -637,6 +637,10 @@ void Compiler::unwindSaveNext() void Compiler::unwindPacSignLR() { + if (JitConfig.JitPacEnabled() == 0) + { + return; + } #if defined(FEATURE_CFI_SUPPORT) if (generateCFIUnwindCodes()) { From cb3879b53312c9ebaa593dc1a0df2dbb8490a89c Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Thu, 6 Feb 2025 20:30:28 -0800 Subject: [PATCH 13/57] Make PacEnabled a Release flag --- src/coreclr/jit/jitconfigvalues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 3e89b2b5792b07..ea8437133e7078 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -121,7 +121,7 @@ CONFIG_INTEGER(JitInlineDepth, "JITInlineDepth", DEFAULT_MAX_INLINE_DEPTH) CONFIG_INTEGER(JitForceInlineDepth, "JITForceInlineDepth", DEFAULT_MAX_FORCE_INLINE_DEPTH) CONFIG_INTEGER(JitLongAddress, "JitLongAddress", 0) // Force using the large pseudo instruction form for long address CONFIG_INTEGER(JitMaxUncheckedOffset, "JitMaxUncheckedOffset", 8) -CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 1) +RELEASE_CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 1) // // MinOpts From 5a24d2ad33af5b68c2595fe74cc68622751f086e Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Fri, 7 Feb 2025 08:10:28 -0800 Subject: [PATCH 14/57] Just strip from bit 48 onwards because thats what works for linux --- src/coreclr/unwinder/arm64/unwinder.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 78b0518b3c1c0c..5b3daba2019a5e 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -332,7 +332,8 @@ do { // //#else -#define STRIP_PAC(pointer) RtlStripPacManual(pointer) +//#define STRIP_PAC(pointer) RtlStripPacManual(pointer) +#define STRIP_PAC(pointer) *pointer &= 0x0000FFFFFFFFFFFF FORCEINLINE VOID From ba343d24502ed29ee25b74067c2128e506b7537d Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 13 Feb 2025 11:32:25 +0000 Subject: [PATCH 15/57] Fix return address hijacking --- src/coreclr/vm/threadsuspend.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 2da1584cdb5da7..0d1dc11b306200 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -4545,6 +4545,30 @@ struct ExecutionState ExecutionState() : m_FirstPass(TRUE) {LIMITED_METHOD_CONTRACT; } }; +#if defined(TARGET_ARM64) +static inline void* PacSignPtr(void* ptr) +{ + __asm__ volatile (".arch_extension pauth\n" + "paciza %0" + : "+r" (ptr) + : + : "memory" // Memory is affected, prevent reordering + ); + return ptr; +} + +static inline void* PacStripPtr(void* ptr) +{ + __asm__ volatile (".arch_extension pauth\n" + "xpaci %0" + : "+r" (ptr) + : + : "memory" // Memory is affected, prevent reordering + ); + return ptr; +} +#endif // TARGET_ARM64 + // Client is responsible for suspending the thread before calling void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) { @@ -4602,6 +4626,9 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) // Remember the place that the return would have gone m_pvHJRetAddr = *esb->m_ppvRetAddrPtr; +#if defined(TARGET_ARM64) + m_pvHJRetAddr = PacStripPtr(m_pvHJRetAddr); +#endif // TARGET_ARM64 IS_VALID_CODE_PTR((FARPROC) (TADDR)m_pvHJRetAddr); // TODO [DAVBR]: For the full fix for VsWhidbey 450273, the below @@ -4614,6 +4641,10 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) m_HijackedFunction = esb->m_pFD; // Bash the stack to return to one of our stubs +#if defined(TARGET_ARM64) + pvHijackAddr = PacSignPtr(pvHijackAddr); +#endif // TARGET_ARM64 + *esb->m_ppvRetAddrPtr = pvHijackAddr; SetThreadState(TS_Hijacked); } From e414fc77eb496a5e4826d1230fe51bd072a94efd Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Mon, 17 Feb 2025 15:59:10 -0800 Subject: [PATCH 16/57] Unwinder: Strip the Lr before storing in PC --- src/coreclr/unwinder/arm64/unwinder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 5b3daba2019a5e..ec2670d3298610 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -2522,7 +2522,7 @@ Return Value: // if (FinalPcFromLr) { - ContextRecord->Pc = ContextRecord->Lr; + ContextRecord->Pc = ContextRecord->Lr & 0x0000FFFFFFFFFFFF; } *EstablisherFrame = ContextRecord->Sp; From f3b9e6110fbd1e9dab88a5d3667c6833c132346e Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 19 Feb 2025 16:23:02 +0000 Subject: [PATCH 17/57] Emit pac unwind info in epilog --- src/coreclr/jit/codegenarm64.cpp | 9 --------- src/coreclr/jit/emitarm64.cpp | 3 ++- src/coreclr/jit/unwindarm64.cpp | 8 ++------ src/coreclr/unwinder/arm64/unwinder.cpp | 2 +- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 00a806cbbf20e0..8a125992bb51b6 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -525,11 +525,6 @@ void CodeGen::genPrologSaveRegPair(regNumber reg1, compiler->unwindSaveRegPair(reg1, reg2, spOffset); } } - - if (reg2 == REG_LR) - { - compiler->unwindPacSignLR(); - } } //------------------------------------------------------------------------ @@ -661,10 +656,6 @@ void CodeGen::genEpilogRestoreRegPair(regNumber reg1, compiler->unwindSaveRegPair(reg1, reg2, spOffset); } } - if (reg2 == REG_LR) - { - compiler->unwindPacSignLR(); - } } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index d0da2c2173ac68..ee3d2e9a1660f0 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1391,6 +1391,7 @@ void emitter::emitPacInProlog() { return; } + // TODO-PAC: should be paciasp emitIns(INS_paciaz); emitComp->unwindPacSignLR(); } @@ -1404,7 +1405,7 @@ void emitter::emitPacInEpilog() { return; } - // TODO: should be autiasp + // TODO-PAC: should be autiasp emitIns(INS_autiaz); emitComp->unwindPacSignLR(); } diff --git a/src/coreclr/jit/unwindarm64.cpp b/src/coreclr/jit/unwindarm64.cpp index 16aac90308a86e..60c06126c783b8 100644 --- a/src/coreclr/jit/unwindarm64.cpp +++ b/src/coreclr/jit/unwindarm64.cpp @@ -658,12 +658,8 @@ void Compiler::unwindPacSignLR() } #endif // FEATURE_CFI_SUPPORT - if (compGeneratingProlog) - { - // pac_sign_lr: 11111100: sign the return address in lr with pacibsp - // needed only for prolog - funCurrentFunc()->uwi.AddCode(0xFC); - } + // pac_sign_lr: 11111100: sign the return address in lr with pacibsp + funCurrentFunc()->uwi.AddCode(0xFC); } void Compiler::unwindReturn(regNumber reg) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index ec2670d3298610..5b3daba2019a5e 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -2522,7 +2522,7 @@ Return Value: // if (FinalPcFromLr) { - ContextRecord->Pc = ContextRecord->Lr & 0x0000FFFFFFFFFFFF; + ContextRecord->Pc = ContextRecord->Lr; } *EstablisherFrame = ContextRecord->Sp; From bb67312174f858611fc805513f40300612e3d5ea Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Fri, 21 Feb 2025 12:02:47 +0000 Subject: [PATCH 18/57] Add inline assembly variants for MSVC --- src/coreclr/vm/threadsuspend.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 0d1dc11b306200..f99d07ed4e9536 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -4548,23 +4548,31 @@ struct ExecutionState #if defined(TARGET_ARM64) static inline void* PacSignPtr(void* ptr) { + #if defined(_MSC_VER) + __asm paciza ptr + #else __asm__ volatile (".arch_extension pauth\n" "paciza %0" : "+r" (ptr) : : "memory" // Memory is affected, prevent reordering ); + #endif // _MSC_VER return ptr; } static inline void* PacStripPtr(void* ptr) { + #if defined(_MSC_VER) + __asm xpaci ptr + #else __asm__ volatile (".arch_extension pauth\n" "xpaci %0" : "+r" (ptr) : : "memory" // Memory is affected, prevent reordering ); + #endif // _MSC_VER return ptr; } #endif // TARGET_ARM64 From 0d0fc7f42ec009a5990ca713435ab4d7a29b5fa0 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Mon, 3 Mar 2025 14:32:26 +0000 Subject: [PATCH 19/57] Remove MSVC assembly --- src/coreclr/vm/threadsuspend.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 83e5cc336632f3..09ec86f5b1670d 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -4544,37 +4544,30 @@ struct ExecutionState ExecutionState() : m_FirstPass(TRUE) {LIMITED_METHOD_CONTRACT; } }; -#if defined(TARGET_ARM64) +#if defined(TARGET_ARM64) && defined(__GNUC__) static inline void* PacSignPtr(void* ptr) { - #if defined(_MSC_VER) - __asm paciza ptr - #else + __asm__ volatile (".arch_extension pauth\n" "paciza %0" : "+r" (ptr) : : "memory" // Memory is affected, prevent reordering ); - #endif // _MSC_VER return ptr; } static inline void* PacStripPtr(void* ptr) { - #if defined(_MSC_VER) - __asm xpaci ptr - #else __asm__ volatile (".arch_extension pauth\n" "xpaci %0" : "+r" (ptr) : : "memory" // Memory is affected, prevent reordering ); - #endif // _MSC_VER return ptr; } -#endif // TARGET_ARM64 +#endif // TARGET_ARM64 && __GNUC__ // Client is responsible for suspending the thread before calling void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) @@ -4633,9 +4626,9 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) // Remember the place that the return would have gone m_pvHJRetAddr = *esb->m_ppvRetAddrPtr; -#if defined(TARGET_ARM64) +#if defined(TARGET_ARM64) && defined(__GNUC__) m_pvHJRetAddr = PacStripPtr(m_pvHJRetAddr); -#endif // TARGET_ARM64 +#endif // TARGET_ARM64 && __GNUC__ IS_VALID_CODE_PTR((FARPROC) (TADDR)m_pvHJRetAddr); // TODO [DAVBR]: For the full fix for VsWhidbey 450273, the below @@ -4648,9 +4641,9 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) m_HijackedFunction = esb->m_pFD; // Bash the stack to return to one of our stubs -#if defined(TARGET_ARM64) +#if defined(TARGET_ARM64) && defined(__GNUC__) pvHijackAddr = PacSignPtr(pvHijackAddr); -#endif // TARGET_ARM64 +#endif // TARGET_ARM64 && __GNUC__ *esb->m_ppvRetAddrPtr = pvHijackAddr; SetThreadState(TS_Hijacked); From ddf6c3d615245445f422870875a223206db3bcd9 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 12 Mar 2025 09:24:05 +0000 Subject: [PATCH 20/57] Restore previous fix to check its impact on nativeaot failures --- src/coreclr/jit/unwindarm64.cpp | 7 +++++-- src/coreclr/unwinder/arm64/unwinder.cpp | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/unwindarm64.cpp b/src/coreclr/jit/unwindarm64.cpp index 60c06126c783b8..d28796ebbfd18c 100644 --- a/src/coreclr/jit/unwindarm64.cpp +++ b/src/coreclr/jit/unwindarm64.cpp @@ -658,8 +658,11 @@ void Compiler::unwindPacSignLR() } #endif // FEATURE_CFI_SUPPORT - // pac_sign_lr: 11111100: sign the return address in lr with pacibsp - funCurrentFunc()->uwi.AddCode(0xFC); + if (compGeneratingProlog) + { + // pac_sign_lr: 11111100: sign the return address in lr with pacibsp + funCurrentFunc()->uwi.AddCode(0xFC); + } } void Compiler::unwindReturn(regNumber reg) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 053f7694215dbc..7430dded025458 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -2536,7 +2536,7 @@ Return Value: // if (FinalPcFromLr) { - ContextRecord->Pc = ContextRecord->Lr; + ContextRecord->Pc = ContextRecord->Lr & 0x0000FFFFFFFFFFFF; } *EstablisherFrame = ContextRecord->Sp; From 2b6bf2bbde1d8c59cb10a8bef5eb947c1df93c06 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 12 Mar 2025 14:08:18 +0000 Subject: [PATCH 21/57] Fix basic AOT tests --- src/coreclr/jit/unwindarm64.cpp | 7 ++----- src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp | 5 ++++- src/coreclr/unwinder/arm64/unwinder.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/unwindarm64.cpp b/src/coreclr/jit/unwindarm64.cpp index d28796ebbfd18c..60c06126c783b8 100644 --- a/src/coreclr/jit/unwindarm64.cpp +++ b/src/coreclr/jit/unwindarm64.cpp @@ -658,11 +658,8 @@ void Compiler::unwindPacSignLR() } #endif // FEATURE_CFI_SUPPORT - if (compGeneratingProlog) - { - // pac_sign_lr: 11111100: sign the return address in lr with pacibsp - funCurrentFunc()->uwi.AddCode(0xFC); - } + // pac_sign_lr: 11111100: sign the return address in lr with pacibsp + funCurrentFunc()->uwi.AddCode(0xFC); } void Compiler::unwindReturn(regNumber reg) diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index 78a39612f907c0..83b329236c7f5f 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -1790,7 +1790,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()))); + //TODO-PAC: Strip pac while populating IP in m_RegDisplay + SetControlPC(dac_cast(PCODEToPINSTR(m_RegDisplay.GetIP() & (TADDR)0x0000FFFFFFFFFFFF))); PTR_VOID collapsingTargetFrame = NULL; @@ -2121,6 +2122,8 @@ void StackFrameIterator::CalculateCurrentMethodState() return; } + //TODO-PAC: Strip the address at the source + m_ControlPC = (PTR_VOID)((long)m_ControlPC & 0x0000FFFFFFFFFFFF); // 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/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 7430dded025458..053f7694215dbc 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -2536,7 +2536,7 @@ Return Value: // if (FinalPcFromLr) { - ContextRecord->Pc = ContextRecord->Lr & 0x0000FFFFFFFFFFFF; + ContextRecord->Pc = ContextRecord->Lr; } *EstablisherFrame = ContextRecord->Sp; From b408ec5a5ff9168bf642688c428be8008dc33868 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 12 Mar 2025 15:25:03 +0000 Subject: [PATCH 22/57] Fix reaturn address hijacking for AOT --- src/coreclr/nativeaot/Runtime/thread.cpp | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 77b00b10541ad7..bc5a131d8f0a22 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -787,6 +787,31 @@ void Thread::HijackReturnAddress(NATIVE_CONTEXT* pSuspendCtx, HijackFunc* pfnHij HijackReturnAddressWorker(&frameIterator, pfnHijackFunction); } +#if defined(TARGET_ARM64) && defined(__GNUC__) +static inline void* PacSignPtr(void* ptr) +{ + + __asm__ volatile (".arch_extension pauth\n" + "paciza %0" + : "+r" (ptr) + : + : "memory" // Memory is affected, prevent reordering + ); + return ptr; +} + +static inline void* PacStripPtr(void* ptr) +{ + __asm__ volatile (".arch_extension pauth\n" + "xpaci %0" + : "+r" (ptr) + : + : "memory" // Memory is affected, prevent reordering + ); + return ptr; +} +#endif // TARGET_ARM64 && __GNUC__ + void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, HijackFunc* pfnHijackFunction) { void** ppvRetAddrLocation; @@ -806,6 +831,10 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack CrossThreadUnhijack(); void* pvRetAddr = *ppvRetAddrLocation; +#if defined(TARGET_ARM64) && defined(__GNUC__) + pvRetAddr = PacStripPtr(pvRetAddr); +#endif // TARGET_ARM64 && __GNUC__ + ASSERT(pvRetAddr != NULL); ASSERT(StackFrameIterator::IsValidReturnAddress(pvRetAddr)); @@ -818,6 +847,9 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack #endif *ppvRetAddrLocation = (void*)pfnHijackFunction; +#if defined(TARGET_ARM64) && defined(__GNUC__) + *ppvRetAddrLocation = PacSignPtr(*ppvRetAddrLocation); +#endif // TARGET_ARM64 && __GNUC__ STRESS_LOG2(LF_STACKWALK, LL_INFO10000, "InternalHijack: TgtThread = %llx, IP = %p\n", GetPalThreadIdForLogging(), frameIterator->GetRegisterSet()->GetIP()); From 240b55188f6c0e789343b167ec80cabe223a5a01 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Tue, 18 Mar 2025 14:40:13 +0000 Subject: [PATCH 23/57] Disable emitting authenticate to see behaviour on windows --- src/coreclr/jit/emitarm64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 6a30a82ec13983..940b2a303b2320 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1421,7 +1421,7 @@ void emitter::emitPacInEpilog() return; } // TODO-PAC: should be autiasp - emitIns(INS_autiaz); + // emitIns(INS_autiaz); emitComp->unwindPacSignLR(); } From 09a6c40e1cb02c9b71036774664dafa163d477a4 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Tue, 18 Mar 2025 14:34:58 +0000 Subject: [PATCH 24/57] Fix return address comparison for tail calls in native aot --- src/coreclr/debug/ee/controller.cpp | 2 +- src/coreclr/jit/emitarm64.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index ca63a24170d127..ab83ad6c69848f 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -5833,7 +5833,7 @@ static bool IsTailCall(const BYTE * ip, ControllerStackInfo* info, TailCallFunct LOG((LF_CORDB,LL_INFO1000, "ITCTR: ret addr is %p, tailcall aware ret addr is %p\n", retAddr, tailCallAwareRetAddr)); - return retAddr == tailCallAwareRetAddr; + return ((ulong)retAddr & 0x0000FFFFFFFFFFFF) == ((ulong)tailCallAwareRetAddr & 0x0000FFFFFFFFFFFF); } // bool DebuggerStepper::TrapStep() TrapStep attepts to set a diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 940b2a303b2320..6a30a82ec13983 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1421,7 +1421,7 @@ void emitter::emitPacInEpilog() return; } // TODO-PAC: should be autiasp - // emitIns(INS_autiaz); + emitIns(INS_autiaz); emitComp->unwindPacSignLR(); } From 1dcf08361f9dbc6bbb8ea2e82e9926780d16516a Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Tue, 18 Mar 2025 14:35:51 +0000 Subject: [PATCH 25/57] Sign return address while restoring it in unhijacking a thread --- src/coreclr/nativeaot/Runtime/thread.cpp | 4 ++++ src/coreclr/vm/threadsuspend.cpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 41455c134dfc51..7467ce70018ddc 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -977,7 +977,11 @@ void Thread::UnhijackWorker() // Restore the original return address. ASSERT(m_ppvHijackedReturnAddressLocation != NULL); + *m_ppvHijackedReturnAddressLocation = m_pvHijackedReturnAddress; +#if defined(TARGET_ARM64) && defined(__GNUC__) + *m_ppvHijackedReturnAddressLocation = PacSignPtr(*m_ppvHijackedReturnAddressLocation); +#endif // TARGET_ARM64 && __GNUC__ // Clear the hijack state. m_ppvHijackedReturnAddressLocation = NULL; diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 09ec86f5b1670d..6f7d4f78e7c981 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -4674,6 +4674,9 @@ void Thread::UnhijackThread() STRESS_LOG2(LF_SYNC, LL_INFO100, "Unhijacking return address 0x%p for thread %p\n", m_pvHJRetAddr, this); // restore the return address and clear the flag *m_ppvHJRetAddrPtr = m_pvHJRetAddr; +#if defined(TARGET_ARM64) && defined(__GNUC__) + *m_ppvHJRetAddrPtr = PacSignPtr(*m_ppvHJRetAddrPtr); +#endif // TARGET_ARM64 && __GNUC__ ResetThreadState(TS_Hijacked); // But don't touch m_pvHJRetAddr. We may need that to resume a thread that From 25c7f7297ec5f6e3cce9cbf98fab8d9ac21ac533 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 19 Mar 2025 13:18:07 +0000 Subject: [PATCH 26/57] Fix build errors --- src/coreclr/debug/ee/controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index ab83ad6c69848f..a9a21104b93a8f 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -5833,7 +5833,7 @@ static bool IsTailCall(const BYTE * ip, ControllerStackInfo* info, TailCallFunct LOG((LF_CORDB,LL_INFO1000, "ITCTR: ret addr is %p, tailcall aware ret addr is %p\n", retAddr, tailCallAwareRetAddr)); - return ((ulong)retAddr & 0x0000FFFFFFFFFFFF) == ((ulong)tailCallAwareRetAddr & 0x0000FFFFFFFFFFFF); + return ((unsigned long)retAddr & 0x0000FFFFFFFFFFFF) == ((unsigned long)tailCallAwareRetAddr & 0x0000FFFFFFFFFFFF); } // bool DebuggerStepper::TrapStep() TrapStep attepts to set a From 1e6b5b6ff564fd15488d5d78564629572eff0549 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 19 Mar 2025 13:55:36 +0000 Subject: [PATCH 27/57] Fix build errors --- src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index a51a4a0b722771..6fa6b72e62c705 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -2122,7 +2122,7 @@ void StackFrameIterator::CalculateCurrentMethodState() } //TODO-PAC: Strip the address at the source - m_ControlPC = (PTR_VOID)((long)m_ControlPC & 0x0000FFFFFFFFFFFF); + m_ControlPC = (PTR_VOID)((unsigned long)m_ControlPC & 0x0000FFFFFFFFFFFF); // Assume that the caller is likely to be in the same module if (m_pCodeManager == NULL || !m_pCodeManager->FindMethodInfo(m_ControlPC, &m_methodInfo)) { From 6aebd8b9474f04feafc7dcc0e1d3621981c99a7b Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 19 Mar 2025 14:53:51 +0000 Subject: [PATCH 28/57] Fix build errors for casting --- src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index 6fa6b72e62c705..b37c1b0ad97bfc 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -2122,7 +2122,7 @@ void StackFrameIterator::CalculateCurrentMethodState() } //TODO-PAC: Strip the address at the source - m_ControlPC = (PTR_VOID)((unsigned long)m_ControlPC & 0x0000FFFFFFFFFFFF); + m_ControlPC = dac_cast(dac_cast(m_ControlPC) & (TADDR)0x0000FFFFFFFFFFFF); // Assume that the caller is likely to be in the same module if (m_pCodeManager == NULL || !m_pCodeManager->FindMethodInfo(m_ControlPC, &m_methodInfo)) { From b23051399d140f6749de14e2f0af527cd987dc15 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 19 Mar 2025 16:10:15 +0000 Subject: [PATCH 29/57] Fix build errors --- src/coreclr/debug/ee/controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index a9a21104b93a8f..70c21459e63bb6 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -5833,7 +5833,7 @@ static bool IsTailCall(const BYTE * ip, ControllerStackInfo* info, TailCallFunct LOG((LF_CORDB,LL_INFO1000, "ITCTR: ret addr is %p, tailcall aware ret addr is %p\n", retAddr, tailCallAwareRetAddr)); - return ((unsigned long)retAddr & 0x0000FFFFFFFFFFFF) == ((unsigned long)tailCallAwareRetAddr & 0x0000FFFFFFFFFFFF); + return ((unsigned long long)retAddr & 0x0000FFFFFFFFFFFFULL) == ((unsigned long long)(tailCallAwareRetAddr) & 0x0000FFFFFFFFFFFFULL); } // bool DebuggerStepper::TrapStep() TrapStep attepts to set a From 9c216f545bebc6e59469be992d06b96e2f9a6065 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 9 Apr 2025 22:26:04 +0100 Subject: [PATCH 30/57] Put dwarf cfi code for pac --- .../ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 bb778aa83dbcba..7b46de98c5ecd7 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 @@ -114,7 +114,7 @@ private static byte[] CfiCodeToInstructions(DwarfCie cie, byte[] blobData) cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)cfaOffset); break; case CFI_OPCODE.CFI_NEGATE_RA_STATE: - cfiCode[cfiCodeOffset++] = DW_CFA_restore_state; + cfiCode[cfiCodeOffset++] = DW_CFA_AARCH64_negate_ra_state; break; } } From f5b6ffad7d323eae5f7946dafeea0b3702feb5ce Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 10 Apr 2025 14:30:04 +0100 Subject: [PATCH 31/57] Add a placeholder for OSX ObjectWriter --- .../Compiler/ObjectWriter/MachObjectWriter.cs | 4 ++++ 1 file changed, 4 insertions(+) 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..9d7d9253cb038f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs @@ -844,6 +844,10 @@ private static uint GetArm64CompactUnwindCode(byte[] blobData) registerOffset[i] += cfiOffset; } break; + + case CFI_OPCODE.CFI_NEGATE_RA_STATE: + // Nothing to compress here. It just has the code. + break; } } From 7d92dae3e5d2c0fa357a462d4367ce2d6efd0bab Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Sun, 13 Apr 2025 13:30:57 +0100 Subject: [PATCH 32/57] Add DEF_CFA opcode for ilc --- src/coreclr/inc/cfi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/inc/cfi.h b/src/coreclr/inc/cfi.h index 25a927956ec267..9ebcdb304e535e 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, // Used by ILC, not emitted by JIT CFI_NEGATE_RA_STATE, // Sign the return address in lr with pacibsp }; From 6ef40256d6386c17fadc6cd14e24919d61b6944d Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Sun, 13 Apr 2025 16:08:04 +0100 Subject: [PATCH 33/57] Fix formatting --- src/coreclr/jit/lsraarm64.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index 54e5057420aa72..e79748f8555673 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -1481,14 +1481,16 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou { srcCount += BuildContainedCselUses(containedCselOp, delayFreeOp, candidates); } - else if ((intrin.category == HW_Category_SIMDByIndexedElement) && (genTypeSize(intrin.baseType) == 2) && !HWIntrinsicInfo::HasImmediateOperand(intrin.id)) + else if ((intrin.category == HW_Category_SIMDByIndexedElement) && (genTypeSize(intrin.baseType) == 2) && + !HWIntrinsicInfo::HasImmediateOperand(intrin.id)) { - // Some "Advanced SIMD scalar x indexed element" and "Advanced SIMD vector x indexed element" instructions (e.g. - // "MLA (by element)") have encoding that restricts what registers that can be used for the indexed element when - // the element size is H (i.e. 2 bytes). + // Some "Advanced SIMD scalar x indexed element" and "Advanced SIMD vector x indexed element" instructions + // (e.g. "MLA (by element)") have encoding that restricts what registers that can be used for the indexed + // element when the element size is H (i.e. 2 bytes). if (((opNum == 2) || (opNum == 3))) { - // For those intrinsics, just force the delay-free registers, so they do not conflict with the definition. + // For those intrinsics, just force the delay-free registers, so they do not conflict with the + // definition. srcCount += BuildDelayFreeUses(operand, nullptr, candidates); } else From 8e1a725fa96fafdef50b125292587eacf7ea6aac Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 17 Apr 2025 16:02:21 +0100 Subject: [PATCH 34/57] Enable PAC for non-jitted code --- eng/native/configurecompiler.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index e8ad8615f6d4bd..cc1f4af64ba6bf 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -705,6 +705,11 @@ if (CLR_CMAKE_HOST_UNIX) endif() endif(CLR_CMAKE_HOST_MACCATALYST) + if(CLR_CMAKE_HOST_ARCH_ARM64 AND CLR_CMAKE_HOST_LINUX) + # Enable Pointer Authentication (PAC) extension + add_compile_options(-mbranch-protection=pac-ret) + endif() + endif(CLR_CMAKE_HOST_UNIX) if(CLR_CMAKE_TARGET_UNIX) From 457dd8961b748f54961b7fc20df516204e92662c Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 24 Apr 2025 15:13:52 +0100 Subject: [PATCH 35/57] Intial clean-up: use xpacia instead of a mask --- .../RuntimeHelpers.CoreCLR.cs | 3 +- src/coreclr/debug/ee/controller.cpp | 20 ++- .../nativeaot/Runtime/StackFrameIterator.cpp | 26 +++- src/coreclr/unwinder/arm64/unwinder.cpp | 146 +++++------------- src/coreclr/vm/tailcallhelp.cpp | 20 +++ 5 files changed, 103 insertions(+), 112 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 67f291bac4a309..973f4657026f69 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -511,8 +511,7 @@ private static unsafe void DispatchTailCalls( IntPtr callersRetAddr; TailCallTls* tls = GetTailCallInfo(callersRetAddrSlot, &callersRetAddr); PortableTailCallFrame* prevFrame = tls->Frame; - // TODO-PAC: Implement stripping of PAC from return addresses in coreclr - if ((callersRetAddr & 0x0000FFFFFFFFFFFF) == (prevFrame->TailCallAwareReturnAddress & 0x0000FFFFFFFFFFFF)) + if ((callersRetAddr) == (prevFrame->TailCallAwareReturnAddress)) { prevFrame->NextCall = callTarget; return; diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 441c70fedc1813..8f8e7dc6414679 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -5796,6 +5796,19 @@ static bool IsTailCallJitHelper(const BYTE * ip) return TailCallStubManager::IsTailCallJitHelper(reinterpret_cast(ip)); } +#if defined(TARGET_ARM64) && defined(__GNUC__) +static inline void* PacStripPtr(void* ptr) +{ + __asm__ volatile (".arch_extension pauth\n" + "xpaci %0" + : "+r" (ptr) + : + : "memory" // Memory is affected, prevent reordering + ); + return ptr; +} +#endif // TARGET_ARM64 && __GNUC__ + // Check whether a call to an IP will be a tailcall dispatched by first // returning. When a tailcall cannot be performed just with a jump instruction, // the code will be doing a regular call to a managed function called the @@ -5842,10 +5855,15 @@ static bool IsTailCall(const BYTE * ip, ControllerStackInfo* info, TailCallFunct TailCallTls* tls = GetThread()->GetTailCallTls(); LPVOID tailCallAwareRetAddr = tls->GetFrame()->TailCallAwareReturnAddress; +#if defined(TARGET_ARM64) && defined(__GNUC__) + retAddr = PacStripPtr(retAddr); + tailCallAwareRetAddr = PacStripPtr(tailCallAwareRetAddr); +#endif // TARGET_ARM64 && __GNUC__ + LOG((LF_CORDB,LL_INFO1000, "ITCTR: ret addr is %p, tailcall aware ret addr is %p\n", retAddr, tailCallAwareRetAddr)); - return ((unsigned long long)retAddr & 0x0000FFFFFFFFFFFFULL) == ((unsigned long long)(tailCallAwareRetAddr) & 0x0000FFFFFFFFFFFFULL); + return retAddr == tailCallAwareRetAddr; } // bool DebuggerStepper::TrapStep() TrapStep attepts to set a diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index b825ac057c253f..1cc54e08f39b56 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -1599,6 +1599,19 @@ void StackFrameIterator::UnwindUniversalTransitionThunk() #define STACK_ALIGN_SIZE 4 #endif +#if defined(TARGET_ARM64) && defined(__GNUC__) +static inline void* PacStripPtr(void* ptr) +{ + __asm__ volatile (".arch_extension pauth\n" + "xpaci %0" + : "+r" (ptr) + : + : "memory" // Memory is affected, prevent reordering + ); + return ptr; +} +#endif // TARGET_ARM64 && __GNUC__ + void StackFrameIterator::UnwindThrowSiteThunk() { ASSERT((m_dwFlags & MethodStateCalculated) == 0); @@ -1790,8 +1803,11 @@ void StackFrameIterator::NextInternal() // if the thread is safe to walk, it better not have a hijack in place. ASSERT(!m_pThread->IsHijacked()); - //TODO-PAC: Strip pac while populating IP in m_RegDisplay - SetControlPC(dac_cast(PCODEToPINSTR(m_RegDisplay.GetIP() & (TADDR)0x0000FFFFFFFFFFFF))); +#if defined(TARGET_ARM64) && defined(__GNUC__) + SetControlPC(PacStripPtr(dac_cast(PCODEToPINSTR(m_RegDisplay.GetIP())))); +#else + SetControlPC(dac_cast(PCODEToPINSTR(m_RegDisplay.GetIP()))); +#endif // TARGET_ARM64 && __GNUC__ PTR_VOID collapsingTargetFrame = NULL; @@ -2122,8 +2138,10 @@ void StackFrameIterator::CalculateCurrentMethodState() return; } - //TODO-PAC: Strip the address at the source - m_ControlPC = dac_cast(dac_cast(m_ControlPC) & (TADDR)0x0000FFFFFFFFFFFF); +#if defined(TARGET_ARM64) && defined(__GNUC__) + m_ControlPC = PacStripPtr(m_ControlPC); +#endif // TARGET_ARM64 && __GNUC__ + // 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/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 053f7694215dbc..075369edc61d98 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -265,89 +265,47 @@ do { #endif // !defined(DEBUGGER_UNWIND) -// // Macros for stripping pointer authentication (PAC) bits. -// -// -//#if !defined(DACCESS_COMPILE) -// -//#define STRIP_PAC(pointer) RtlStripPacOnline(pointer) -// -// -//FORCEINLINE -////VOID RtlStripPacOnline(_Inout_ PULONG64 Pointer) -//VOID RtlStripPacOnline(void** Pointer) -// -///*++ -// -//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. -// -//--*/ -// -//{ -// // This is a key step: The pointer is passed as a reference to be modified. -// unsigned int key = 12345; // Example key (not used for XPACI, but for PAC authentication) -// -// // Inline assembly to invoke the `xpaci` instruction. -// // The pointer is passed as an argument and modified in-place. -// asm volatile ( -// "xpaci %[Pointer]" // Strip the Pointer Authentication Code (PAC) from the pointer -// : [Pointer] "+r" (Pointer) // The pointer is modified in-place -// : // No input operands needed (beyond the pointer) -// : "memory" // Memory is affected, prevent reordering -// ); -//} -// -//// -////FORCEINLINE -////VOID -////RtlStripPacOnline ( -//// _Inout_ PULONG64 Pointer -//// ) -//// -/////*++ -//// -////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. -//// -////--*/ -//// -////{ -//// ULONG64 StrippedPointer = CONTEXT_GetSveLengthFromOS(); -//// -//// StrippedPointer = *Pointer; -//// StrippedPointer = (ULONG64)__xpaci((PVOID)StrippedPointer); -//// *Pointer = StrippedPointer; -//// return; -////} -// -//#else +#if !defined(DACCESS_COMPILE) -//#define STRIP_PAC(pointer) RtlStripPacManual(pointer) -#define STRIP_PAC(pointer) *pointer &= 0x0000FFFFFFFFFFFF +#define STRIP_PAC(pointer) RtlStripPacOnline(pointer) + +FORCEINLINE +VOID RtlStripPacOnline(_Inout_ PULONG64 Pointer) + +/*++ + +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. + +--*/ + +{ + // Inline assembly to invoke the `xpaci` instruction. + // The pointer is passed as an argument and modified in-place. + ULONG64 StrippedPointer = *Pointer; + asm volatile (".arch_extension pauth\n" + "xpaci %0" + : "+r" (StrippedPointer) + : + : "memory" + ); + *Pointer = StrippedPointer; +} +#else + +#define STRIP_PAC(pointer) RtlStripPacManual(pointer) FORCEINLINE VOID @@ -376,33 +334,11 @@ Return Value: --*/ { - ULONG64 StrippedPointer; - - // For now hard code to mask everything but the bottom 55 bits - ULONG64 PointerAuthMask = (1ull << 55) - 1; - - StrippedPointer = *Pointer; - - // - // Strip the pointer manually. Bit 55 of the pointer is replicated across - // the "unused" bits (if bit 55 is 1, all unused bits should be 1; if bit - // 55 is 0, all unused bits should be 0). - // - - if (((StrippedPointer >> 55) & 0x1) == 0) { - StrippedPointer &= ~PointerAuthMask; - - } else { - StrippedPointer |= PointerAuthMask; - } - - *Pointer = StrippedPointer; - + *Pointer &= 0x0000FFFFFFFFFFFF; return; } -//#endif // !defined(DACCESS_COMPILE) - +#endif // !defined(DACCESS_COMPILE) // // Macros to clarify opcode parsing diff --git a/src/coreclr/vm/tailcallhelp.cpp b/src/coreclr/vm/tailcallhelp.cpp index d59a2861a80c42..5373387e5be9ab 100644 --- a/src/coreclr/vm/tailcallhelp.cpp +++ b/src/coreclr/vm/tailcallhelp.cpp @@ -19,6 +19,19 @@ FCIMPL2(void*, TailCallHelp::AllocTailCallArgBufferWorker, INT32 size, void* gcD } FCIMPLEND +#if defined(TARGET_ARM64) && defined(__GNUC__) +static inline void* PacStripPtr(void* ptr) +{ + __asm__ volatile (".arch_extension pauth\n" + "xpaci %0" + : "+r" (ptr) + : + : "memory" // Memory is affected, prevent reordering + ); + return ptr; +} +#endif // TARGET_ARM64 && __GNUC__ + FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr) { FCALL_CONTRACT; @@ -26,6 +39,13 @@ FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr Thread* thread = GetThread(); *retAddr = thread->GetReturnAddress(retAddrSlot); + +#if defined(TARGET_ARM64) && defined(__GNUC__) + *retAddr = PacStripPtr(*retAddr); + void* tailCallAwareReturnAddress = thread->GetTailCallTls()->GetFrame()->TailCallAwareReturnAddress; + tailCallAwareReturnAddress = PacStripPtr(tailCallAwareReturnAddress); +#endif // TARGET_ARM64 && __GNUC__ + return thread->GetTailCallTls(); } FCIMPLEND From 268a8955a36a878c43d8dbac0e613bf4fcf9e022 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Mon, 28 Apr 2025 11:01:47 +0100 Subject: [PATCH 36/57] Clean-up inline assembly --- src/coreclr/debug/ee/controller.cpp | 21 +++++----------- .../nativeaot/Runtime/StackFrameIterator.cpp | 25 ++++++------------- .../nativeaot/Runtime/arm64/MiscStubs.S | 7 ++++++ .../nativeaot/Runtime/arm64/MiscStubs.asm | 7 ++++++ src/coreclr/nativeaot/Runtime/thread.cpp | 19 +++++--------- src/coreclr/vm/arm64/asmhelpers.S | 7 ++++++ src/coreclr/vm/arm64/asmhelpers.asm | 7 ++++++ src/coreclr/vm/tailcallhelp.cpp | 20 ++++----------- src/coreclr/vm/threadsuspend.cpp | 19 +++++--------- 9 files changed, 59 insertions(+), 73 deletions(-) diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 8f8e7dc6414679..0dc9b5ec878cac 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) @@ -5796,19 +5800,6 @@ static bool IsTailCallJitHelper(const BYTE * ip) return TailCallStubManager::IsTailCallJitHelper(reinterpret_cast(ip)); } -#if defined(TARGET_ARM64) && defined(__GNUC__) -static inline void* PacStripPtr(void* ptr) -{ - __asm__ volatile (".arch_extension pauth\n" - "xpaci %0" - : "+r" (ptr) - : - : "memory" // Memory is affected, prevent reordering - ); - return ptr; -} -#endif // TARGET_ARM64 && __GNUC__ - // Check whether a call to an IP will be a tailcall dispatched by first // returning. When a tailcall cannot be performed just with a jump instruction, // the code will be doing a regular call to a managed function called the @@ -5855,10 +5846,10 @@ static bool IsTailCall(const BYTE * ip, ControllerStackInfo* info, TailCallFunct TailCallTls* tls = GetThread()->GetTailCallTls(); LPVOID tailCallAwareRetAddr = tls->GetFrame()->TailCallAwareReturnAddress; -#if defined(TARGET_ARM64) && defined(__GNUC__) +#if defined(TARGET_ARM64) retAddr = PacStripPtr(retAddr); tailCallAwareRetAddr = PacStripPtr(tailCallAwareRetAddr); -#endif // TARGET_ARM64 && __GNUC__ +#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/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index 1cc54e08f39b56..9c981719cd5eed 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -69,6 +69,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"); @@ -1599,19 +1603,6 @@ void StackFrameIterator::UnwindUniversalTransitionThunk() #define STACK_ALIGN_SIZE 4 #endif -#if defined(TARGET_ARM64) && defined(__GNUC__) -static inline void* PacStripPtr(void* ptr) -{ - __asm__ volatile (".arch_extension pauth\n" - "xpaci %0" - : "+r" (ptr) - : - : "memory" // Memory is affected, prevent reordering - ); - return ptr; -} -#endif // TARGET_ARM64 && __GNUC__ - void StackFrameIterator::UnwindThrowSiteThunk() { ASSERT((m_dwFlags & MethodStateCalculated) == 0); @@ -1803,11 +1794,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) && defined(__GNUC__) +#if defined(TARGET_ARM64) SetControlPC(PacStripPtr(dac_cast(PCODEToPINSTR(m_RegDisplay.GetIP())))); #else SetControlPC(dac_cast(PCODEToPINSTR(m_RegDisplay.GetIP()))); -#endif // TARGET_ARM64 && __GNUC__ +#endif // TARGET_ARM64 PTR_VOID collapsingTargetFrame = NULL; @@ -2138,9 +2129,9 @@ void StackFrameIterator::CalculateCurrentMethodState() return; } -#if defined(TARGET_ARM64) && defined(__GNUC__) +#if defined(TARGET_ARM64) m_ControlPC = PacStripPtr(m_ControlPC); -#endif // TARGET_ARM64 && __GNUC__ +#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/MiscStubs.S b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S index ea5d91a1a1c1f9..28d38c4726b637 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S +++ b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S @@ -3,3 +3,10 @@ #include #include "AsmOffsets.inc" + +// void* PacStripPtr(void *); +.arch_extension pauth + LEAF_ENTRY PacStripPtr, _TEXT + xpaci x0 + ret lr + LEAF_END PacStripPtr, _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..24db06a254ea74 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm @@ -5,4 +5,11 @@ TEXTAREA +; void* PacStripPtr(void *); + LEAF_ENTRY PacStripPtr + ; xpaci x0 + DCD 0xDAC143E0 + ret lr + LEAF_END PacStripPtr + end diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index bf25ff61d2715b..3fdb932957f039 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -38,6 +38,10 @@ static Thread* g_RuntimeInitializingThread; #endif //!DACCESS_COMPILE +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 + ee_alloc_context::PerThreadRandom::PerThreadRandom() { minipal_xoshiro128pp_init(&random_state, (uint32_t)PalGetTickCount64()); @@ -798,17 +802,6 @@ static inline void* PacSignPtr(void* ptr) ); return ptr; } - -static inline void* PacStripPtr(void* ptr) -{ - __asm__ volatile (".arch_extension pauth\n" - "xpaci %0" - : "+r" (ptr) - : - : "memory" // Memory is affected, prevent reordering - ); - return ptr; -} #endif // TARGET_ARM64 && __GNUC__ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, HijackFunc* pfnHijackFunction) @@ -830,9 +823,9 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack CrossThreadUnhijack(); void* pvRetAddr = *ppvRetAddrLocation; -#if defined(TARGET_ARM64) && defined(__GNUC__) +#if defined(TARGET_ARM64) pvRetAddr = PacStripPtr(pvRetAddr); -#endif // TARGET_ARM64 && __GNUC__ +#endif // TARGET_ARM64 ASSERT(pvRetAddr != NULL); ASSERT(StackFrameIterator::IsValidReturnAddress(pvRetAddr)); diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index 13a18cf7855390..b4a4598b0d6e5f 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -337,6 +337,13 @@ NESTED_END OnHijackTripThread, _TEXT #endif // FEATURE_HIJACK +// void* PacStripPtr(void *); +.arch_extension pauth + LEAF_ENTRY PacStripPtr, _TEXT + xpaci x0 + ret lr + LEAF_END PacStripPtr, _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 bd4f60ed08ea83..8b4f9f676b45a9 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -676,6 +676,13 @@ COMToCLRDispatchHelper_RegSetup #endif ; FEATURE_HIJACK +; void* PacStripPtr(void *); + LEAF_ENTRY PacStripPtr + ; xpaci x0 + DCD 0xDAC143E0 + ret lr + LEAF_END PacStripPtr + ;; ------------------------------------------------------------------ ;; Redirection Stub for GC in fully interruptible method GenerateRedirectedHandledJITCaseStub GCThreadControl diff --git a/src/coreclr/vm/tailcallhelp.cpp b/src/coreclr/vm/tailcallhelp.cpp index 5373387e5be9ab..314eb4ff43abb0 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 FCIMPL2(void*, TailCallHelp::AllocTailCallArgBufferWorker, INT32 size, void* gcDesc) { @@ -19,19 +22,6 @@ FCIMPL2(void*, TailCallHelp::AllocTailCallArgBufferWorker, INT32 size, void* gcD } FCIMPLEND -#if defined(TARGET_ARM64) && defined(__GNUC__) -static inline void* PacStripPtr(void* ptr) -{ - __asm__ volatile (".arch_extension pauth\n" - "xpaci %0" - : "+r" (ptr) - : - : "memory" // Memory is affected, prevent reordering - ); - return ptr; -} -#endif // TARGET_ARM64 && __GNUC__ - FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr) { FCALL_CONTRACT; @@ -40,11 +30,11 @@ FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr *retAddr = thread->GetReturnAddress(retAddrSlot); -#if defined(TARGET_ARM64) && defined(__GNUC__) +#if defined(TARGET_ARM64) *retAddr = PacStripPtr(*retAddr); void* tailCallAwareReturnAddress = thread->GetTailCallTls()->GetFrame()->TailCallAwareReturnAddress; tailCallAwareReturnAddress = PacStripPtr(tailCallAwareReturnAddress); -#endif // TARGET_ARM64 && __GNUC__ +#endif // TARGET_ARM64 return thread->GetTailCallTls(); } diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index d0a5deedbe433e..33ea64aafa17d3 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -22,6 +22,10 @@ #define HIJACK_NONINTERRUPTIBLE_THREADS +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 + bool ThreadSuspend::s_fSuspendRuntimeInProgress = false; bool ThreadSuspend::s_fSuspended = false; @@ -4562,17 +4566,6 @@ static inline void* PacSignPtr(void* ptr) ); return ptr; } - -static inline void* PacStripPtr(void* ptr) -{ - __asm__ volatile (".arch_extension pauth\n" - "xpaci %0" - : "+r" (ptr) - : - : "memory" // Memory is affected, prevent reordering - ); - return ptr; -} #endif // TARGET_ARM64 && __GNUC__ // Client is responsible for suspending the thread before calling @@ -4632,9 +4625,9 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) // Remember the place that the return would have gone m_pvHJRetAddr = *esb->m_ppvRetAddrPtr; -#if defined(TARGET_ARM64) && defined(__GNUC__) +#if defined(TARGET_ARM64) m_pvHJRetAddr = PacStripPtr(m_pvHJRetAddr); -#endif // TARGET_ARM64 && __GNUC__ +#endif // TARGET_ARM64 IS_VALID_CODE_PTR((FARPROC) (TADDR)m_pvHJRetAddr); // TODO [DAVBR]: For the full fix for VsWhidbey 450273, the below From 526b7c2c66f0bc33783d9065caa126d23823febd Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Mon, 28 Apr 2025 12:24:10 +0100 Subject: [PATCH 37/57] Remove PAC for non-jitted code, added using compile time flags --- eng/native/configurecompiler.cmake | 5 ----- 1 file changed, 5 deletions(-) diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index cc1f4af64ba6bf..e8ad8615f6d4bd 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -705,11 +705,6 @@ if (CLR_CMAKE_HOST_UNIX) endif() endif(CLR_CMAKE_HOST_MACCATALYST) - if(CLR_CMAKE_HOST_ARCH_ARM64 AND CLR_CMAKE_HOST_LINUX) - # Enable Pointer Authentication (PAC) extension - add_compile_options(-mbranch-protection=pac-ret) - endif() - endif(CLR_CMAKE_HOST_UNIX) if(CLR_CMAKE_TARGET_UNIX) From 711d1fc9a36b30dd146f515072fd6901dabebcab Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Mon, 28 Apr 2025 12:27:01 +0100 Subject: [PATCH 38/57] Fix windows build errors --- src/coreclr/unwinder/arm64/unwinder.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 075369edc61d98..68afc9faaf0a81 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -36,6 +36,9 @@ #ifdef HOST_UNIX #define RtlZeroMemory ZeroMemory +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 typedef enum ARM64_FNPDATA_FLAGS { PdataRefToFullXdata = 0, PdataPackedUnwindFunction = 1, @@ -293,15 +296,7 @@ Return Value: { // Inline assembly to invoke the `xpaci` instruction. - // The pointer is passed as an argument and modified in-place. - ULONG64 StrippedPointer = *Pointer; - asm volatile (".arch_extension pauth\n" - "xpaci %0" - : "+r" (StrippedPointer) - : - : "memory" - ); - *Pointer = StrippedPointer; + *Pointer = (ULONG64)PacStripPtr((void *) (*Pointer)); } #else From a99f6391e0a6ea4ce6036e8aae87a20b18d2c195 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Mon, 28 Apr 2025 14:47:45 +0100 Subject: [PATCH 39/57] Fix build errors --- src/coreclr/unwinder/arm64/unwinder.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 68afc9faaf0a81..3c8b3c87ebfe3b 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -33,12 +33,13 @@ #define FIELD_OFFSET(type, field) ((LONG)__builtin_offsetof(type, field)) #endif -#ifdef HOST_UNIX -#define RtlZeroMemory ZeroMemory - #if defined(TARGET_ARM64) extern "C" void* PacStripPtr(void* ptr); #endif // TARGET_ARM64 + +#ifdef HOST_UNIX +#define RtlZeroMemory ZeroMemory + typedef enum ARM64_FNPDATA_FLAGS { PdataRefToFullXdata = 0, PdataPackedUnwindFunction = 1, From eefb18be3996f6a2041deaace859b6992d4994b7 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 1 May 2025 15:06:23 +0100 Subject: [PATCH 40/57] Update strip/sign mechanism to use nop variants of PAC instructions --- .../RuntimeHelpers.CoreCLR.cs | 2 +- src/coreclr/jit/emitarm64.cpp | 2 -- .../nativeaot/Runtime/arm64/MiscStubs.S | 25 ++++++++++++++++--- .../nativeaot/Runtime/arm64/MiscStubs.asm | 23 ++++++++++++++--- src/coreclr/nativeaot/Runtime/thread.cpp | 23 ++++------------- src/coreclr/unwinder/arm64/unwinder.cpp | 9 +++---- src/coreclr/vm/arm64/asmhelpers.S | 23 +++++++++++++++-- src/coreclr/vm/arm64/asmhelpers.asm | 23 ++++++++++++++--- src/coreclr/vm/threadsuspend.cpp | 23 ++++------------- 9 files changed, 98 insertions(+), 55 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 973f4657026f69..d60b48ec0e552e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -511,7 +511,7 @@ private static unsafe void DispatchTailCalls( IntPtr callersRetAddr; TailCallTls* tls = GetTailCallInfo(callersRetAddrSlot, &callersRetAddr); PortableTailCallFrame* prevFrame = tls->Frame; - if ((callersRetAddr) == (prevFrame->TailCallAwareReturnAddress)) + if (callersRetAddr == prevFrame->TailCallAwareReturnAddress) { prevFrame->NextCall = callTarget; return; diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index f95268023f9d92..aa8673293a2562 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1406,7 +1406,6 @@ void emitter::emitPacInProlog() { return; } - // TODO-PAC: should be paciasp emitIns(INS_paciaz); emitComp->unwindPacSignLR(); } @@ -1420,7 +1419,6 @@ void emitter::emitPacInEpilog() { return; } - // TODO-PAC: should be autiasp emitIns(INS_autiaz); emitComp->unwindPacSignLR(); } diff --git a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S index 28d38c4726b637..a6c57ab30f1f8b 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S +++ b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.S @@ -5,8 +5,27 @@ #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 - xpaci x0 - ret lr - LEAF_END PacStripPtr, _TEXT \ No newline at end of file + 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 24db06a254ea74..37cf470f9c9a5a 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/MiscStubs.asm @@ -6,10 +6,27 @@ 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 - ; xpaci x0 - DCD 0xDAC143E0 - ret lr + 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 3fdb932957f039..a721fffadda5bc 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -40,6 +40,7 @@ static Thread* g_RuntimeInitializingThread; #if defined(TARGET_ARM64) extern "C" void* PacStripPtr(void* ptr); +extern "C" void* PacSignPtr(void* ptr); #endif // TARGET_ARM64 ee_alloc_context::PerThreadRandom::PerThreadRandom() @@ -790,20 +791,6 @@ void Thread::HijackReturnAddress(NATIVE_CONTEXT* pSuspendCtx, HijackFunc* pfnHij HijackReturnAddressWorker(&frameIterator, pfnHijackFunction); } -#if defined(TARGET_ARM64) && defined(__GNUC__) -static inline void* PacSignPtr(void* ptr) -{ - - __asm__ volatile (".arch_extension pauth\n" - "paciza %0" - : "+r" (ptr) - : - : "memory" // Memory is affected, prevent reordering - ); - return ptr; -} -#endif // TARGET_ARM64 && __GNUC__ - void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, HijackFunc* pfnHijackFunction) { void** ppvRetAddrLocation; @@ -839,9 +826,9 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack #endif *ppvRetAddrLocation = (void*)pfnHijackFunction; -#if defined(TARGET_ARM64) && defined(__GNUC__) +#if defined(TARGET_ARM64) *ppvRetAddrLocation = PacSignPtr(*ppvRetAddrLocation); -#endif // TARGET_ARM64 && __GNUC__ +#endif // TARGET_ARM64 STRESS_LOG2(LF_STACKWALK, LL_INFO10000, "InternalHijack: TgtThread = %llx, IP = %p\n", GetPalThreadIdForLogging(), frameIterator->GetRegisterSet()->GetIP()); @@ -971,9 +958,9 @@ void Thread::UnhijackWorker() ASSERT(m_ppvHijackedReturnAddressLocation != NULL); *m_ppvHijackedReturnAddressLocation = m_pvHijackedReturnAddress; -#if defined(TARGET_ARM64) && defined(__GNUC__) +#if defined(TARGET_ARM64) *m_ppvHijackedReturnAddressLocation = PacSignPtr(*m_ppvHijackedReturnAddressLocation); -#endif // TARGET_ARM64 && __GNUC__ +#endif // TARGET_ARM64 // Clear the hijack state. m_ppvHijackedReturnAddressLocation = NULL; diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 3c8b3c87ebfe3b..d01785afdf767f 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -33,9 +33,9 @@ #define FIELD_OFFSET(type, field) ((LONG)__builtin_offsetof(type, field)) #endif -#if defined(TARGET_ARM64) +#if !defined(DACCESS_COMPILE) && defined(TARGET_ARM64) extern "C" void* PacStripPtr(void* ptr); -#endif // TARGET_ARM64 +#endif // !defined(DACCESS_COMPILE) && TARGET_ARM64 #ifdef HOST_UNIX #define RtlZeroMemory ZeroMemory @@ -270,7 +270,7 @@ do { #endif // !defined(DEBUGGER_UNWIND) // Macros for stripping pointer authentication (PAC) bits. -#if !defined(DACCESS_COMPILE) +#if !defined(DACCESS_COMPILE) && defined(TARGET_ARM64) #define STRIP_PAC(pointer) RtlStripPacOnline(pointer) @@ -296,7 +296,6 @@ Return Value: --*/ { - // Inline assembly to invoke the `xpaci` instruction. *Pointer = (ULONG64)PacStripPtr((void *) (*Pointer)); } #else @@ -334,7 +333,7 @@ Return Value: return; } -#endif // !defined(DACCESS_COMPILE) +#endif // !defined(DACCESS_COMPILE) && defined(TARGET_ARM64) // // Macros to clarify opcode parsing diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index b4a4598b0d6e5f..f5bc3df2f81c08 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -338,12 +338,31 @@ 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 - xpaci x0 - ret lr + 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 8b4f9f676b45a9..0d70e5acdad5a9 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -677,12 +677,29 @@ COMToCLRDispatchHelper_RegSetup #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 - ; xpaci x0 - DCD 0xDAC143E0 - ret lr + 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/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 33ea64aafa17d3..6a39c5d873a5d6 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -24,6 +24,7 @@ #if defined(TARGET_ARM64) extern "C" void* PacStripPtr(void* ptr); +extern "C" void* PacSignPtr(void* ptr); #endif // TARGET_ARM64 bool ThreadSuspend::s_fSuspendRuntimeInProgress = false; @@ -4554,20 +4555,6 @@ struct ExecutionState ExecutionState() : m_FirstPass(TRUE) {LIMITED_METHOD_CONTRACT; } }; -#if defined(TARGET_ARM64) && defined(__GNUC__) -static inline void* PacSignPtr(void* ptr) -{ - - __asm__ volatile (".arch_extension pauth\n" - "paciza %0" - : "+r" (ptr) - : - : "memory" // Memory is affected, prevent reordering - ); - return ptr; -} -#endif // TARGET_ARM64 && __GNUC__ - // Client is responsible for suspending the thread before calling void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) { @@ -4640,9 +4627,9 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) m_HijackedFunction = esb->m_pFD; // Bash the stack to return to one of our stubs -#if defined(TARGET_ARM64) && defined(__GNUC__) +#if defined(TARGET_ARM64) pvHijackAddr = PacSignPtr(pvHijackAddr); -#endif // TARGET_ARM64 && __GNUC__ +#endif // TARGET_ARM64 *esb->m_ppvRetAddrPtr = pvHijackAddr; SetThreadState(TS_Hijacked); @@ -4673,9 +4660,9 @@ void Thread::UnhijackThread() STRESS_LOG2(LF_SYNC, LL_INFO100, "Unhijacking return address 0x%p for thread %p\n", m_pvHJRetAddr, this); // restore the return address and clear the flag *m_ppvHJRetAddrPtr = m_pvHJRetAddr; -#if defined(TARGET_ARM64) && defined(__GNUC__) +#if defined(TARGET_ARM64) *m_ppvHJRetAddrPtr = PacSignPtr(*m_ppvHJRetAddrPtr); -#endif // TARGET_ARM64 && __GNUC__ +#endif // TARGET_ARM64 ResetThreadState(TS_Hijacked); // But don't touch m_pvHJRetAddr. We may need that to resume a thread that From 36f166e58eee74d8038c729becbf3ffce7d4946b Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 15 May 2025 13:24:24 +0100 Subject: [PATCH 41/57] Incorporate review comments --- src/coreclr/unwinder/arm64/unwinder.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index d01785afdf767f..8cda3bd6995483 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -33,9 +33,9 @@ #define FIELD_OFFSET(type, field) ((LONG)__builtin_offsetof(type, field)) #endif -#if !defined(DACCESS_COMPILE) && defined(TARGET_ARM64) +#if !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) extern "C" void* PacStripPtr(void* ptr); -#endif // !defined(DACCESS_COMPILE) && TARGET_ARM64 +#endif // !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) #ifdef HOST_UNIX #define RtlZeroMemory ZeroMemory @@ -270,7 +270,7 @@ do { #endif // !defined(DEBUGGER_UNWIND) // Macros for stripping pointer authentication (PAC) bits. -#if !defined(DACCESS_COMPILE) && defined(TARGET_ARM64) +#if !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) #define STRIP_PAC(pointer) RtlStripPacOnline(pointer) @@ -333,7 +333,7 @@ Return Value: return; } -#endif // !defined(DACCESS_COMPILE) && defined(TARGET_ARM64) +#endif // !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) // // Macros to clarify opcode parsing From 05f873101345ec008cf8ed0b98ea46623e4877b1 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Fri, 16 May 2025 15:01:34 +0100 Subject: [PATCH 42/57] Incorporate review comments --- src/coreclr/nativeaot/Runtime/thread.cpp | 15 +++++++++------ src/coreclr/vm/tailcallhelp.cpp | 2 -- src/coreclr/vm/threadsuspend.cpp | 11 ++++++----- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index a721fffadda5bc..1817269b98d161 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -809,9 +809,10 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack // we only unhijack if we are going to install a new or better hijack. CrossThreadUnhijack(); - void* pvRetAddr = *ppvRetAddrLocation; #if defined(TARGET_ARM64) - pvRetAddr = PacStripPtr(pvRetAddr); + void* pvRetAddr = PacStripPtr(*ppvRetAddrLocation); +#else + void* pvRetAddr = *ppvRetAddrLocation; #endif // TARGET_ARM64 ASSERT(pvRetAddr != NULL); @@ -825,10 +826,11 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack frameIterator->GetRegisterSet())); #endif - *ppvRetAddrLocation = (void*)pfnHijackFunction; + void* pvHijacedkAddr = (void*)pfnHijackFunction; #if defined(TARGET_ARM64) - *ppvRetAddrLocation = PacSignPtr(*ppvRetAddrLocation); + pvHijacedkAddr = PacSignPtr(pvHijacedkAddr); #endif // TARGET_ARM64 + *ppvRetAddrLocation = pvHijacedkAddr; STRESS_LOG2(LF_STACKWALK, LL_INFO10000, "InternalHijack: TgtThread = %llx, IP = %p\n", GetPalThreadIdForLogging(), frameIterator->GetRegisterSet()->GetIP()); @@ -957,10 +959,11 @@ void Thread::UnhijackWorker() // Restore the original return address. ASSERT(m_ppvHijackedReturnAddressLocation != NULL); - *m_ppvHijackedReturnAddressLocation = m_pvHijackedReturnAddress; + void* pvHijackedRetAddr = m_pvHijackedReturnAddress; #if defined(TARGET_ARM64) - *m_ppvHijackedReturnAddressLocation = PacSignPtr(*m_ppvHijackedReturnAddressLocation); + pvHijackedRetAddr = PacSignPtr(pvHijackedRetAddr); #endif // TARGET_ARM64 + *m_ppvHijackedReturnAddressLocation = pvHijackedRetAddr; // Clear the hijack state. m_ppvHijackedReturnAddressLocation = NULL; diff --git a/src/coreclr/vm/tailcallhelp.cpp b/src/coreclr/vm/tailcallhelp.cpp index 314eb4ff43abb0..8024e67279ec29 100644 --- a/src/coreclr/vm/tailcallhelp.cpp +++ b/src/coreclr/vm/tailcallhelp.cpp @@ -32,8 +32,6 @@ FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr #if defined(TARGET_ARM64) *retAddr = PacStripPtr(*retAddr); - void* tailCallAwareReturnAddress = thread->GetTailCallTls()->GetFrame()->TailCallAwareReturnAddress; - tailCallAwareReturnAddress = PacStripPtr(tailCallAwareReturnAddress); #endif // TARGET_ARM64 return thread->GetTailCallTls(); diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 5c34fbd2fad7ff..822d07a72b8343 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -4604,11 +4604,11 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86 m_ppvHJRetAddrPtr = esb->m_ppvRetAddrPtr; // Remember the place that the return would have gone - m_pvHJRetAddr = *esb->m_ppvRetAddrPtr; + void* pvRetAddrPtr = *esb->m_ppvRetAddrPtr; #if defined(TARGET_ARM64) - m_pvHJRetAddr = PacStripPtr(m_pvHJRetAddr); + pvRetAddrPtr = PacStripPtr(pvRetAddrPtr); #endif // TARGET_ARM64 - + m_pvHJRetAddr = pvRetAddrPtr; IS_VALID_CODE_PTR((FARPROC) (TADDR)m_pvHJRetAddr); // TODO [DAVBR]: For the full fix for VsWhidbey 450273, the below // may be uncommented once isLegalManagedCodeCaller works properly @@ -4652,10 +4652,11 @@ void Thread::UnhijackThread() STRESS_LOG2(LF_SYNC, LL_INFO100, "Unhijacking return address 0x%p for thread %p\n", m_pvHJRetAddr, this); // restore the return address and clear the flag - *m_ppvHJRetAddrPtr = m_pvHJRetAddr; + void* pvHijackedRetAddr = m_pvHJRetAddr; #if defined(TARGET_ARM64) - *m_ppvHJRetAddrPtr = PacSignPtr(*m_ppvHJRetAddrPtr); + pvHijackedRetAddr = PacSignPtr(pvHijackedRetAddr); #endif // TARGET_ARM64 + *m_ppvHJRetAddrPtr = pvHijackedRetAddr; ResetThreadState(TS_Hijacked); // But don't touch m_pvHJRetAddr. We may need that to resume a thread that From 526717a230e24363703e24bb5b8dc57cb74a3bcb Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Mon, 19 May 2025 13:52:42 +0100 Subject: [PATCH 43/57] Disable JitPacEnabled flag by default --- src/coreclr/jit/jitconfigvalues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index b40418dd373eb0..7dca7042c1c19f 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -125,7 +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) +RELEASE_CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 0) // // MinOpts From 7b552e53a689ce449581b1b98196cb656a952bbd Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 12 Jun 2025 14:16:10 +0100 Subject: [PATCH 44/57] Use unwind info to detect if PAC is present for jitted code --- src/coreclr/inc/clrnt.h | 15 + .../Runtime/arm64/ExceptionHandling.S | 1 + .../Runtime/arm64/ExceptionHandling.asm | 1 + src/coreclr/nativeaot/Runtime/arm64/GcProbe.S | 1 + .../nativeaot/Runtime/arm64/GcProbe.asm | 1 + src/coreclr/nativeaot/Runtime/thread.cpp | 17 +- src/coreclr/unwinder/arm64/unwinder.cpp | 541 +++++++++++++----- src/coreclr/vm/arm64/asmhelpers.S | 1 + src/coreclr/vm/arm64/asmhelpers.asm | 1 + src/coreclr/vm/excep.cpp | 48 ++ src/coreclr/vm/excep.h | 4 +- src/coreclr/vm/tailcallhelp.cpp | 5 +- src/coreclr/vm/threads.h | 2 +- src/coreclr/vm/threadsuspend.cpp | 29 +- 14 files changed, 497 insertions(+), 170 deletions(-) diff --git a/src/coreclr/inc/clrnt.h b/src/coreclr/inc/clrnt.h index 8040117a28b8f6..317467d9151c36 100644 --- a/src/coreclr/inc/clrnt.h +++ b/src/coreclr/inc/clrnt.h @@ -369,6 +369,21 @@ RtlVirtualUnwind( IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL ); +EXTERN_C +NTSYSAPI +BOOLEAN +RtlpUnwindIsPacPresent ( + _In_ ULONG_PTR ImageBase, + _In_ ULONG_PTR ControlPc, + _In_ PRUNTIME_FUNCTION FunctionEntry, + _Out_ PVOID *HandlerData, + _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, + _Out_ PULONG_PTR EstablisherFrame, + _In_opt_ PULONG_PTR LowLimit, + _In_opt_ PULONG_PTR HighLimit, + _Outptr_opt_result_maybenull_ PEXCEPTION_ROUTINE *HandlerRoutine, + _In_ ULONG UnwindFlags + ); // Mirror the XSTATE_ARM64_SVE flags from winnt.h #ifndef XSTATE_ARM64_SVE diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S index 40c0b37b9917ec..05ae5a37d8bc4b 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S @@ -301,6 +301,7 @@ LOCAL_LABEL(TailCallWasHijacked): mov lr, x1 str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] + DCD 0xD50320FF ;; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers LOCAL_LABEL(ClearThreadState): diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm index 5918745625fd83..d1602dfb3bbfd7 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm @@ -302,6 +302,7 @@ TailCallWasHijacked mov lr, x1 str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] + xpaclri ClearThreadState diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S index 42ef0e3b510cc3..e0527b12bad76c 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 bd6e6d37652f23..16455fcf1417da 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/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 48f8ba5baae9db..41f57a4cf50642 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -37,7 +37,6 @@ static Thread* g_RuntimeInitializingThread; #endif //!DACCESS_COMPILE #if defined(TARGET_ARM64) -extern "C" void* PacStripPtr(void* ptr); extern "C" void* PacSignPtr(void* ptr); #endif // TARGET_ARM64 @@ -813,11 +812,7 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack // we only unhijack if we are going to install a new or better hijack. CrossThreadUnhijack(); -#if defined(TARGET_ARM64) - void* pvRetAddr = PacStripPtr(*ppvRetAddrLocation); -#else void* pvRetAddr = *ppvRetAddrLocation; -#endif // TARGET_ARM64 ASSERT(pvRetAddr != NULL); ASSERT(StackFrameIterator::IsValidReturnAddress(pvRetAddr)); @@ -832,7 +827,11 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack void* pvHijacedkAddr = (void*)pfnHijackFunction; #if defined(TARGET_ARM64) - pvHijacedkAddr = PacSignPtr(pvHijacedkAddr); + uint64_t isJitPacEnabled; + if (g_pRhConfig->ReadConfigValue("JitPacEnabled", &isJitPacEnabled, true /* decimal */) && isJitPacEnabled != 0) + { + pvHijacedkAddr = PacSignPtr(pvHijacedkAddr); + } #endif // TARGET_ARM64 *ppvRetAddrLocation = pvHijacedkAddr; @@ -963,11 +962,7 @@ void Thread::UnhijackWorker() // Restore the original return address. ASSERT(m_ppvHijackedReturnAddressLocation != NULL); - void* pvHijackedRetAddr = m_pvHijackedReturnAddress; -#if defined(TARGET_ARM64) - pvHijackedRetAddr = PacSignPtr(pvHijackedRetAddr); -#endif // TARGET_ARM64 - *m_ppvHijackedReturnAddressLocation = pvHijackedRetAddr; + *m_ppvHijackedReturnAddressLocation = m_pvHijackedReturnAddress; // Clear the hijack state. m_ppvHijackedReturnAddressLocation = NULL; diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 8cda3bd6995483..cd6cd9ca1a9235 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -1709,34 +1709,35 @@ Return Value: return STATUS_SUCCESS; } + NTSTATUS -RtlpUnwindFunctionFull ( - __in ULONG ControlPcRva, - __in ULONG_PTR ImageBase, - __in PRUNTIME_FUNCTION FunctionEntry, - __in IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *FunctionEntryExtended, - __inout PCONTEXT ContextRecord, - __out PULONG_PTR EstablisherFrame, - __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine, - __out PVOID *HandlerData, - __in PARM64_UNWIND_PARAMS UnwindParams, - __in ULONG UnwindFlags +RtlpProcessPdata ( + _Out_ PULONG_PTR UnwindCodePtr, + _Out_ PULONG_PTR UnwindCodesEndPtr, + _In_ PARM64_UNWIND_PARAMS UnwindParams, + _In_ ULONG ControlPcRva, + _In_ ULONG_PTR ImageBase, + _In_ PRUNTIME_FUNCTION FunctionEntry, + _In_ IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *FunctionEntryExtended, + _Inout_ PEXCEPTION_ROUTINE *ExceptionHandler, + _Inout_ PVOID *ExceptionHandlerData ) /*++ Routine Description: - This function virtually unwinds the specified function by parsing the - .xdata record to determine where in the function the provided ControlPc - is, and then executing unwind codes that map to the function's prolog - or epilog behavior. - - If a context pointers record is specified (in the UnwindParams), then - the address where each nonvolatile register is restored from is recorded - in the appropriate element of the context pointers record. + Processes the .pdata record of a function. Arguments: + UnwindCodePtr - Supplies a pointer to a variable that receives address + of the beginning of opcodes. + + UnwindCodesEndPtr - Supplies a pointer to a variable that receives address + of the end of opcodes. + + UnwindParams - Supplies address of a variable containing additional + parameters shared with caller. ControlPcRva - Supplies the address where control left the specified function, as an offset relative to the ImageBase. @@ -1748,72 +1749,35 @@ Routine Description: specified function. If appropriate, this should have already been probed. - ContextRecord - Supplies the address of a context record. - - EstablisherFrame - Supplies a pointer to a variable that receives the - the establisher frame pointer value. - - HandlerRoutine - Supplies an optional pointer to a variable that receives - the handler routine address. If control did not leave the specified - function in either the prolog or an epilog and a handler of the - proper type is associated with the function, then the address of the - language specific exception handler is returned. Otherwise, NULL is - returned. - - HandlerData - Supplies a pointer to a variable that receives a pointer - the the language handler data. + FunctionEntryExtended - Supplies a pointer to the variable containing + address of the extended function entry. It contains a non-NULL when + opcodes are present in .xdata. - UnwindParams - Additional parameters shared with caller. + ExceptionHandler - Supplies a pointer to a variable that receives + the exception handler routine address. - UnwindFlags - Supplies additional flags for the unwind operation. + ExceptionHandlerData - Supplies a pointer to a variable that receives + the exception handler data address. Return Value: STATUS_SUCCESS if the unwind could be completed, a failure status otherwise. - Unwind can only fail when validation bounds are specified. --*/ - { - ULONG AccumulatedSaveNexts; ULONG CurCode; + ULONG ScopeNum; + ULONG ScopeSize; + ULONG SkipWords; + ULONG ScopeStart; ULONG EpilogScopeCount; - PEXCEPTION_ROUTINE ExceptionHandler; - PVOID ExceptionHandlerData; - BOOLEAN FinalPcFromLr; ULONG FunctionLength; ULONG HeaderWord; - ULONG NextCode; ULONG OffsetInFunction; - ULONG ScopeNum; - ULONG ScopeSize; - ULONG ScopeStart; - ULONG SkipWords; - NTSTATUS Status; - ULONG_PTR UnwindCodePtr; - ULONG_PTR UnwindCodesEndPtr; ULONG_PTR UnwindDataPtr; ULONG UnwindIndex; ULONG UnwindWords; - UNREFERENCED_PARAMETER(UnwindFlags); - - // - // Unless a special frame is encountered, assume that any unwinding - // will return us to the return address of a call and set the flag - // appropriately (it will be cleared again if the special cases apply). - // - - ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; - - // - // By default, unwinding is done by popping to the LR, then copying - // that LR to the PC. However, some special opcodes require different - // behavior. - // - - FinalPcFromLr = TRUE; - // // Fetch the header word from the .xdata blob // @@ -1860,12 +1824,10 @@ Return Value: // If exception data is present, extract it now. // - ExceptionHandler = NULL; - ExceptionHandlerData = NULL; if ((HeaderWord & (1 << 20)) != 0) { - ExceptionHandler = (PEXCEPTION_ROUTINE)(ImageBase + + *ExceptionHandler = (PEXCEPTION_ROUTINE)(ImageBase + MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords))); - ExceptionHandlerData = (PVOID)(UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords + 1)); + *ExceptionHandlerData = (PVOID)(UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords + 1)); } // @@ -1873,8 +1835,8 @@ Return Value: // that immediately follow the epilog scope list. // - UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount; - UnwindCodesEndPtr = UnwindCodePtr + 4 * UnwindWords; + *UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount; + *UnwindCodesEndPtr = *UnwindCodePtr + 4 * UnwindWords; SkipWords = 0; // @@ -1892,104 +1854,403 @@ Return Value: // if (OffsetInFunction < 4 * UnwindWords) { - ScopeSize = RtlpComputeScopeSize(UnwindCodePtr, UnwindCodesEndPtr, FALSE, UnwindParams); + ScopeSize = RtlpComputeScopeSize(*UnwindCodePtr, *UnwindCodesEndPtr, FALSE, UnwindParams); if (OffsetInFunction < ScopeSize) { SkipWords = ScopeSize - OffsetInFunction; - ExceptionHandler = NULL; - ExceptionHandlerData = NULL; - goto ExecuteCodes; + *ExceptionHandler = NULL; + *ExceptionHandlerData = NULL; } } + else + { + // + // We're not in the prolog, now check to see if we are in the epilog. + // In the simple case, the 'E' bit is set indicating there is a single + // epilog that lives at the end of the function. If we're near the end + // of the function, compute the actual size of the epilog from the + // unwind codes. If we're in the midst of it, adjust the unwind code + // pointer to the start of the codes and determine how many we need to skip. + // + // N.B. Similar to the prolog case above, the maximum number of halfwords + // that an epilog can cover is limited by UnwindWords. In the epilog + // case, however, the starting index within the unwind code table is + // non-zero, and so the maximum number of unwind codes that can pertain + // to an epilog is (UnwindWords * 4 - UnwindIndex), thus further + // constraining the bounds of the epilog. + // + + if ((HeaderWord & (1 << 21)) != 0) { + if (OffsetInFunction + (4 * UnwindWords - UnwindIndex) >= FunctionLength) { + ScopeSize = RtlpComputeScopeSize(*UnwindCodePtr + UnwindIndex, *UnwindCodesEndPtr, TRUE, UnwindParams); + ScopeStart = FunctionLength - ScopeSize; + + // + // N.B. This code assumes that no handleable exceptions can occur in + // the prolog or in a chained shrink-wrapping prolog region. + // + if (OffsetInFunction >= ScopeStart) { + *UnwindCodePtr += UnwindIndex; + SkipWords = OffsetInFunction - ScopeStart; + *ExceptionHandler = NULL; + *ExceptionHandlerData = NULL; + } + } + } + + // + // In the multiple-epilog case, we scan forward to see if we are within + // shooting distance of any of the epilogs. If we are, we compute the + // actual size of the epilog from the unwind codes and proceed like the + // simple case above. + // + + else { + for (ScopeNum = 0; ScopeNum < EpilogScopeCount; ScopeNum++) { + HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr); + UnwindDataPtr += 4; + + // + // The scope records are stored in order. If we hit a record that + // starts after our current position, we must not be in an epilog. + // + + ScopeStart = HeaderWord & 0x3ffff; + if (OffsetInFunction < ScopeStart) { + break; + } + + UnwindIndex = HeaderWord >> 22; + if (OffsetInFunction < ScopeStart + (4 * UnwindWords - UnwindIndex)) { + ScopeSize = RtlpComputeScopeSize(*UnwindCodePtr + UnwindIndex, *UnwindCodesEndPtr, TRUE, UnwindParams); + + if (OffsetInFunction < ScopeStart + ScopeSize) { + + *UnwindCodePtr += UnwindIndex; + SkipWords = OffsetInFunction - ScopeStart; + *ExceptionHandler = NULL; + *ExceptionHandlerData = NULL; + break; + } + } + } + } + + } + // + // Skip over unwind codes until we account for the number of halfwords + // to skip. // - // We're not in the prolog, now check to see if we are in the epilog. - // In the simple case, the 'E' bit is set indicating there is a single - // epilog that lives at the end of the function. If we're near the end - // of the function, compute the actual size of the epilog from the - // unwind codes. If we're in the midst of it, adjust the unwind code - // pointer to the start of the codes and determine how many we need to skip. + + while (*UnwindCodePtr < *UnwindCodesEndPtr && SkipWords > 0) { + CurCode = MEMORY_READ_BYTE(UnwindParams, *UnwindCodePtr); + if (OPCODE_IS_END(CurCode)) { + break; + } + *UnwindCodePtr += RtlpGetUnwindCodeSize(CurCode, NULL); + SkipWords--; + } + + return STATUS_SUCCESS; +} + +NTSTATUS GetControlPcRva ( + _In_ ULONG_PTR ImageBase, + _In_ ULONG_PTR ControlPc, + _In_opt_ PRUNTIME_FUNCTION FunctionEntry, + _In_ ULONG UnwindType, + _Out_ PULONG ControlPcRva +) +{ // - // N.B. Similar to the prolog case above, the maximum number of halfwords - // that an epilog can cover is limited by UnwindWords. In the epilog - // case, however, the starting index within the unwind code table is - // non-zero, and so the maximum number of unwind codes that can pertain - // to an epilog is (UnwindWords * 4 - UnwindIndex), thus further - // constraining the bounds of the epilog. + // Unwind type 3 refers to a chained record. The top 30 bits of the + // unwind data contains the RVA of the parent pdata record. // - if ((HeaderWord & (1 << 21)) != 0) { - if (OffsetInFunction + (4 * UnwindWords - UnwindIndex) >= FunctionLength) { - ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams); - ScopeStart = FunctionLength - ScopeSize; + if (UnwindType == 3) { + if ((FunctionEntry->UnwindData & 4) == 0) { + FunctionEntry = (PRUNTIME_FUNCTION)(ImageBase + FunctionEntry->UnwindData - 3); + UnwindType = (FunctionEntry->UnwindData & 3); - // - // N.B. This code assumes that no handleable exceptions can occur in - // the prolog or in a chained shrink-wrapping prolog region. - // + UNWINDER_ASSERT(UnwindType != 3); - if (OffsetInFunction >= ScopeStart) { - UnwindCodePtr += UnwindIndex; - SkipWords = OffsetInFunction - ScopeStart; - ExceptionHandler = NULL; - ExceptionHandlerData = NULL; - } + *ControlPcRva = FunctionEntry->BeginAddress; + + } else { + return STATUS_UNWIND_UNSUPPORTED_VERSION; } + + } else { + *ControlPcRva = (ULONG)(ControlPc - ImageBase); + } + + return STATUS_SUCCESS; +} + +BOOLEAN +RtlpUnwindIsPacPresent ( + _In_ ULONG_PTR ImageBase, + _In_ ULONG_PTR ControlPc, + _In_ PRUNTIME_FUNCTION FunctionEntry, + _Out_ PVOID *HandlerData, + _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, + _Out_ PULONG_PTR EstablisherFrame, + _In_opt_ PULONG_PTR LowLimit, + _In_opt_ PULONG_PTR HighLimit, + _Outptr_opt_result_maybenull_ PEXCEPTION_ROUTINE *HandlerRoutine, + _In_ ULONG UnwindFlags + ) +/*++ + +Routine Description: + + This function goes through the unwinding data of the specified function + to look for an opcode representing Pointer Authentication (PAC). + +Arguments: + + ImageBase - Supplies the base address of the image that contains the + function being unwound. + + ControlPc - Supplies the address where control left the specified + function. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. If appropriate, this should have already been + probed. + + HandlerData - Supplies a pointer to a variable that receives a pointer + the the language handler data. + + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. + + LowLimit - Supplies an optional low limit used to bound the establisher + frame. This must be supplied in conjunction with a high limit. + + HighLimit - Supplies an optional high limit used to bound the establisher + frame. This must be supplied in conjunction with a low limit. + + HandlerRoutine - Supplies an optional pointer to a variable that receives + the handler routine address. If control did not leave the specified + function in either the prolog or an epilog and a handler of the + proper type is associated with the function, then the address of the + language specific exception handler is returned. Otherwise, NULL is + returned. + + UnwindFlags - Supplies additional flags for the unwind operation. + +Return Value: + + A boolean true if the unwind could be completed and an opcode for + Pointer Authentication (PAC) is found, false otherwise. + +--*/ + +{ + NTSTATUS Status; + PEXCEPTION_ROUTINE ExceptionHandler = NULL; + PVOID ExceptionHandlerData = NULL; + ULONG_PTR UnwindCodePtr = NULL; + ULONG_PTR UnwindCodesEndPtr = NULL; + ULONG ControlPcRva; + ULONG UnwindType; + ARM64_UNWIND_PARAMS UnwindParams; + ULONG ScopeSize; + BYTE Opcode; + + UNWINDER_ASSERT((UnwindFlags & ~RTL_VIRTUAL_UNWIND_VALID_FLAGS_ARM64) == 0); + + if (FunctionEntry == NULL) { + // Leaf function so no PAC + return false; } // - // In the multiple-epilog case, we scan forward to see if we are within - // shooting distance of any of the epilogs. If we are, we compute the - // actual size of the epilog from the unwind codes and proceed like the - // simple case above. + // Make sure out-of-bound stack accesses don't send us into an infinite + // unwinding loop. // +#if 0 +__try { +#endif + UnwindParams.ControlPc = ControlPc; + UnwindParams.LowLimit = LowLimit; + UnwindParams.HighLimit = HighLimit; + UnwindParams.ContextPointers = ContextPointers; - else { - for (ScopeNum = 0; ScopeNum < EpilogScopeCount; ScopeNum++) { - HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr); - UnwindDataPtr += 4; + UnwindType = (FunctionEntry->UnwindData & 3); - // - // The scope records are stored in order. If we hit a record that - // starts after our current position, we must not be in an epilog. - // + Status = GetControlPcRva(ImageBase, ControlPc, FunctionEntry, UnwindType, &ControlPcRva); - ScopeStart = HeaderWord & 0x3ffff; - if (OffsetInFunction < ScopeStart) { - break; - } + if (Status != STATUS_SUCCESS) + { + return false; + } - UnwindIndex = HeaderWord >> 22; - if (OffsetInFunction < ScopeStart + (4 * UnwindWords - UnwindIndex)) { - ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams); + // + // Identify the compact .pdata format versus the full .pdata+.xdata format. + // - if (OffsetInFunction < ScopeStart + ScopeSize) { + IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *FunctionEntryExtended = NULL; + struct LOCAL_XDATA { + IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA xdata; + char ops[60]; + } fnent_xdata = {}; - UnwindCodePtr += UnwindIndex; - SkipWords = OffsetInFunction - ScopeStart; - ExceptionHandler = NULL; - ExceptionHandlerData = NULL; - break; - } - } - } + if (UnwindType != 0) { + fnent_xdata.xdata.CodeWords = sizeof(fnent_xdata.ops) / 4; + RtlpExpandCompactToFull(FunctionEntry, &fnent_xdata.xdata); + FunctionEntryExtended = &fnent_xdata.xdata; } -ExecuteCodes: + UNREFERENCED_PARAMETER(UnwindFlags); + + Status = RtlpProcessPdata (&UnwindCodePtr, &UnwindCodesEndPtr, &UnwindParams, + ControlPcRva, ImageBase, FunctionEntry, FunctionEntryExtended, &ExceptionHandler, &ExceptionHandlerData); + + if (Status != STATUS_SUCCESS) + { + return false; + } + #if 0 + } // - // Skip over unwind codes until we account for the number of halfwords - // to skip. + // If we do take an exception here, fetch the exception code as the status + // and do not propagate the exception. Since the exception handler also + // uses this function, propagating it will most likely generate the same + // exception at the same point in the unwind, and continuing will typically + // overflow the kernel stack. // - while (UnwindCodePtr < UnwindCodesEndPtr && SkipWords > 0) { - CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); - if (OPCODE_IS_END(CurCode)) { + __except (EXCEPTION_EXECUTE_HANDLER) { + return false; + } +#endif // HOST_WINDOWS + + // + // Iterate through the unwind codes until we find PAC or hit an end marker. + // + + ScopeSize = 0; + Opcode = 0; + while (UnwindCodePtr < UnwindCodesEndPtr) { + Opcode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + if (OPCODE_IS_END(Opcode)) { break; } - UnwindCodePtr += RtlpGetUnwindCodeSize(CurCode, NULL); - SkipWords--; + if (Opcode == 0xfc) + { + return true; + } + + UnwindCodePtr += RtlpGetUnwindCodeSize(Opcode, &ScopeSize); } + return false; +} + +NTSTATUS +RtlpUnwindFunctionFull ( + __in ULONG ControlPcRva, + __in ULONG_PTR ImageBase, + __in PRUNTIME_FUNCTION FunctionEntry, + __in IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *FunctionEntryExtended, + __inout PCONTEXT ContextRecord, + __out PULONG_PTR EstablisherFrame, + __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine, + __out PVOID *HandlerData, + __in PARM64_UNWIND_PARAMS UnwindParams, + __in ULONG UnwindFlags + ) + +/*++ + +Routine Description: + + This function virtually unwinds the specified function by parsing the + .xdata record to determine where in the function the provided ControlPc + is, and then executing unwind codes that map to the function's prolog + or epilog behavior. + + If a context pointers record is specified (in the UnwindParams), then + the address where each nonvolatile register is restored from is recorded + in the appropriate element of the context pointers record. + +Arguments: + + ControlPcRva - Supplies the address where control left the specified + function, as an offset relative to the ImageBase. + + ImageBase - Supplies the base address of the image that contains the + function being unwound. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. If appropriate, this should have already been + probed. + + ContextRecord - Supplies the address of a context record. + + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. + + HandlerRoutine - Supplies an optional pointer to a variable that receives + the handler routine address. If control did not leave the specified + function in either the prolog or an epilog and a handler of the + proper type is associated with the function, then the address of the + language specific exception handler is returned. Otherwise, NULL is + returned. + + HandlerData - Supplies a pointer to a variable that receives a pointer + the the language handler data. + + UnwindParams - Additional parameters shared with caller. + + UnwindFlags - Supplies additional flags for the unwind operation. + +Return Value: + + STATUS_SUCCESS if the unwind could be completed, a failure status otherwise. + Unwind can only fail when validation bounds are specified. + +--*/ + +{ + ULONG AccumulatedSaveNexts; + ULONG CurCode; + BOOLEAN FinalPcFromLr; + ULONG NextCode; + NTSTATUS Status; + PEXCEPTION_ROUTINE ExceptionHandler = NULL; + PVOID ExceptionHandlerData = NULL; + ULONG_PTR UnwindCodePtr = NULL; + ULONG_PTR UnwindCodesEndPtr = NULL; + + UNREFERENCED_PARAMETER(UnwindFlags); + + // + // Unless a special frame is encountered, assume that any unwinding + // will return us to the return address of a call and set the flag + // appropriately (it will be cleared again if the special cases apply). + // + + ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + + Status = RtlpProcessPdata (&UnwindCodePtr, &UnwindCodesEndPtr, UnwindParams, + ControlPcRva, ImageBase, FunctionEntry, FunctionEntryExtended, &ExceptionHandler, &ExceptionHandlerData); + + if (Status != STATUS_SUCCESS) + { + return Status; + } + + // + // By default, unwinding is done by popping to the LR, then copying + // that LR to the PC. However, some special opcodes require different + // behavior. + // + + FinalPcFromLr = TRUE; // // Now execute codes until we hit the end. @@ -2559,6 +2820,7 @@ RtlpUnwindFunctionCompact ( return Status; } + #if !defined(DEBUGGER_UNWIND) NTSTATUS @@ -2780,7 +3042,6 @@ Return Value: } #endif // !defined(DEBUGGER_UNWIND) - BOOL OOPStackUnwinderArm64::Unwind(T_CONTEXT * pContext) { DWORD64 ImageBase = 0; diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index c997b1c58e05cc..063e933bd97db1 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -161,6 +161,7 @@ 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 diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index 5088edbe01ee57..cbfe10ab515b6a 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -495,6 +495,7 @@ 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 diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 84e96955cd3c8c..f888abecec8d27 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6930,6 +6930,54 @@ 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) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + TADDR ControlPc = pCodeInfo->GetCodeAddress(); + + _ASSERTE(pCodeInfo->IsValid()); + + DWORD_PTR EstablisherFrame = 0; + DWORD_PTR ImageBase = 0; + NTSTATUS Status; + PVOID HandlerData; + PEXCEPTION_ROUTINE HandlerRoutine = NULL; + PEXCEPTION_ROUTINE personalityRoutine = NULL; + + // 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); + + ImageBase = pCodeInfo->GetModuleBase(); + + KNONVOLATILE_CONTEXT_POINTERS ContextPointers; + ZeroMemory(&ContextPointers, sizeof(ContextPointers)); + + return RtlpUnwindIsPacPresent ( + ImageBase, + ControlPc, + (PIMAGE_ARM64_RUNTIME_FUNCTION_ENTRY)FunctionEntry, + &HandlerData, + &ContextPointers, + &EstablisherFrame, + NULL, + NULL, + &HandlerRoutine, + 0); +} +#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 f1b5aafbb96bc2..7c88875359ca99 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 8024e67279ec29..09c54019151ecc 100644 --- a/src/coreclr/vm/tailcallhelp.cpp +++ b/src/coreclr/vm/tailcallhelp.cpp @@ -28,11 +28,12 @@ FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr Thread* thread = GetThread(); - *retAddr = thread->GetReturnAddress(retAddrSlot); + void* retAddrFromSlot = thread->GetReturnAddress(retAddrSlot); #if defined(TARGET_ARM64) - *retAddr = PacStripPtr(*retAddr); + retAddrFromSlot = PacStripPtr(retAddrFromSlot); #endif // TARGET_ARM64 + *retAddr = retAddrFromSlot; return thread->GetTailCallTls(); } diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index fa3d3342e903e2..e9f75b51e3016d 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -2657,7 +2657,7 @@ friend class DebuggerController; 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 9e06a01631f43d..d360c25a569577 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -23,7 +23,6 @@ #define HIJACK_NONINTERRUPTIBLE_THREADS #if defined(TARGET_ARM64) -extern "C" void* PacStripPtr(void* ptr); extern "C" void* PacSignPtr(void* ptr); #endif // TARGET_ARM64 @@ -4549,7 +4548,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; @@ -4604,11 +4603,7 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86 m_ppvHJRetAddrPtr = esb->m_ppvRetAddrPtr; // Remember the place that the return would have gone - void* pvRetAddrPtr = *esb->m_ppvRetAddrPtr; -#if defined(TARGET_ARM64) - pvRetAddrPtr = PacStripPtr(pvRetAddrPtr); -#endif // TARGET_ARM64 - m_pvHJRetAddr = pvRetAddrPtr; + m_pvHJRetAddr = *esb->m_ppvRetAddrPtr; IS_VALID_CODE_PTR((FARPROC) (TADDR)m_pvHJRetAddr); // TODO [DAVBR]: For the full fix for VsWhidbey 450273, the below // may be uncommented once isLegalManagedCodeCaller works properly @@ -4621,7 +4616,10 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86 // Bash the stack to return to one of our stubs #if defined(TARGET_ARM64) - pvHijackAddr = PacSignPtr(pvHijackAddr); + if (isPacEnabledFrame) + { + pvHijackAddr = PacSignPtr(pvHijackAddr); + } #endif // TARGET_ARM64 *esb->m_ppvRetAddrPtr = pvHijackAddr; @@ -4652,11 +4650,7 @@ void Thread::UnhijackThread() STRESS_LOG2(LF_SYNC, LL_INFO100, "Unhijacking return address 0x%p for thread %p\n", m_pvHJRetAddr, this); // restore the return address and clear the flag - void* pvHijackedRetAddr = m_pvHJRetAddr; -#if defined(TARGET_ARM64) - pvHijackedRetAddr = PacSignPtr(pvHijackedRetAddr); -#endif // TARGET_ARM64 - *m_ppvHJRetAddrPtr = pvHijackedRetAddr; + *m_ppvHJRetAddrPtr = m_pvHJRetAddr; ResetThreadState(TS_Hijacked); // But don't touch m_pvHJRetAddr. We may need that to resume a thread that @@ -5337,9 +5331,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)); } } } @@ -5917,7 +5915,8 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext, bool susp 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)); + auto isPacEnabledFrame = IsPacPresent(&codeInfo); + pThread->HijackThread(&executionState X86_ARG(returnKind) X86_ARG(hasAsyncRet) ARM64_ARG(isPacEnabledFrame)); } } From f36e335c2dd081285e87878d67293bf657d3b87a Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 12 Jun 2025 15:32:25 +0100 Subject: [PATCH 45/57] Fix assert failure for return address --- src/coreclr/inc/clrconfigvalues.h | 1 + src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S | 2 +- src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm | 2 +- src/coreclr/nativeaot/Runtime/thread.cpp | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index ad65b364003df4..6fae4b9eef5cdb 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -712,6 +712,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"), 0, "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/nativeaot/Runtime/arm64/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S index 05ae5a37d8bc4b..7537426ea80187 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S @@ -301,7 +301,7 @@ LOCAL_LABEL(TailCallWasHijacked): mov lr, x1 str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] - DCD 0xD50320FF ;; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers + xpaclri LOCAL_LABEL(ClearThreadState): diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm index d1602dfb3bbfd7..c31e0f19e263b3 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm @@ -302,7 +302,7 @@ TailCallWasHijacked mov lr, x1 str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] - xpaclri + DCD 0xD50320FF ;; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers ClearThreadState diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 41f57a4cf50642..054583bd60f292 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -38,6 +38,7 @@ static Thread* g_RuntimeInitializingThread; #if defined(TARGET_ARM64) extern "C" void* PacSignPtr(void* ptr); +extern "C" void* PacStripPtr(void* ptr); #endif // TARGET_ARM64 ee_alloc_context::PerThreadRandom::PerThreadRandom() @@ -815,7 +816,7 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack void* pvRetAddr = *ppvRetAddrLocation; ASSERT(pvRetAddr != NULL); - ASSERT(StackFrameIterator::IsValidReturnAddress(pvRetAddr)); + ASSERT(StackFrameIterator::IsValidReturnAddress(PacStripPtr(pvRetAddr))); m_ppvHijackedReturnAddressLocation = ppvRetAddrLocation; m_pvHijackedReturnAddress = pvRetAddr; From a862791836606d27be8e56ba5260eef3bb897848 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 12 Jun 2025 16:24:06 +0100 Subject: [PATCH 46/57] Make IsPacPresent() Arm64 only --- src/coreclr/vm/threadsuspend.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index d360c25a569577..35726f94fc2df9 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -5915,7 +5915,10 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext, bool susp StackWalkerWalkingThreadHolder threadStackWalking(pThread); // Hijack the return address to point to the appropriate routine based on the method's return type. - auto isPacEnabledFrame = IsPacPresent(&codeInfo); + ARM64_ARG(bool isPacEnabledFrame); +#ifdef TARGET_ARM64 + isPacEnabledFrame = IsPacPresent(&codeInfo); +#endif pThread->HijackThread(&executionState X86_ARG(returnKind) X86_ARG(hasAsyncRet) ARM64_ARG(isPacEnabledFrame)); } } From 8f5b485253b1731f5296c8f57735181ec0d7cc15 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Thu, 12 Jun 2025 17:40:11 +0100 Subject: [PATCH 47/57] Fix build errors --- src/coreclr/inc/clrnt.h | 1 + src/coreclr/nativeaot/Runtime/thread.cpp | 5 +++++ src/coreclr/unwinder/arm64/unwinder.cpp | 8 ++++---- src/coreclr/vm/threadsuspend.cpp | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/coreclr/inc/clrnt.h b/src/coreclr/inc/clrnt.h index 317467d9151c36..86745c5d7e798a 100644 --- a/src/coreclr/inc/clrnt.h +++ b/src/coreclr/inc/clrnt.h @@ -372,6 +372,7 @@ RtlVirtualUnwind( EXTERN_C NTSYSAPI BOOLEAN +NTAPI RtlpUnwindIsPacPresent ( _In_ ULONG_PTR ImageBase, _In_ ULONG_PTR ControlPc, diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 054583bd60f292..c07dfe26f0dbb1 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -816,7 +816,12 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack void* pvRetAddr = *ppvRetAddrLocation; ASSERT(pvRetAddr != NULL); + +#if defined(TARGET_ARM64) + ASSERT(StackFrameIterator::IsValidReturnAddress(pvRetAddr)); +#else ASSERT(StackFrameIterator::IsValidReturnAddress(PacStripPtr(pvRetAddr))); +#endif // TARGET_ARM64 m_ppvHijackedReturnAddressLocation = ppvRetAddrLocation; m_pvHijackedReturnAddress = pvRetAddr; diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index cd6cd9ca1a9235..e0c3d314f3d6be 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -2052,8 +2052,8 @@ Return Value: NTSTATUS Status; PEXCEPTION_ROUTINE ExceptionHandler = NULL; PVOID ExceptionHandlerData = NULL; - ULONG_PTR UnwindCodePtr = NULL; - ULONG_PTR UnwindCodesEndPtr = NULL; + ULONG_PTR UnwindCodePtr; + ULONG_PTR UnwindCodesEndPtr; ULONG ControlPcRva; ULONG UnwindType; ARM64_UNWIND_PARAMS UnwindParams; @@ -2223,8 +2223,8 @@ Return Value: NTSTATUS Status; PEXCEPTION_ROUTINE ExceptionHandler = NULL; PVOID ExceptionHandlerData = NULL; - ULONG_PTR UnwindCodePtr = NULL; - ULONG_PTR UnwindCodesEndPtr = NULL; + ULONG_PTR UnwindCodePtr; + ULONG_PTR UnwindCodesEndPtr; UNREFERENCED_PARAMETER(UnwindFlags); diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 35726f94fc2df9..0de503c52875f3 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -5915,7 +5915,7 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext, bool susp StackWalkerWalkingThreadHolder threadStackWalking(pThread); // Hijack the return address to point to the appropriate routine based on the method's return type. - ARM64_ARG(bool isPacEnabledFrame); + ARM64_ONLY(bool isPacEnabledFrame); #ifdef TARGET_ARM64 isPacEnabledFrame = IsPacPresent(&codeInfo); #endif From e7bc0faf5a813e404c86ffae9125b831a92b97b2 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Fri, 13 Jun 2025 17:06:35 +0100 Subject: [PATCH 48/57] Fix build errors --- src/coreclr/inc/clrnt.h | 19 +++++++++--------- src/coreclr/unwinder/arm64/unwinder.cpp | 26 +++++++++---------------- src/coreclr/vm/excep.cpp | 3 --- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/src/coreclr/inc/clrnt.h b/src/coreclr/inc/clrnt.h index 86745c5d7e798a..7177c31f20b989 100644 --- a/src/coreclr/inc/clrnt.h +++ b/src/coreclr/inc/clrnt.h @@ -374,16 +374,15 @@ NTSYSAPI BOOLEAN NTAPI RtlpUnwindIsPacPresent ( - _In_ ULONG_PTR ImageBase, - _In_ ULONG_PTR ControlPc, - _In_ PRUNTIME_FUNCTION FunctionEntry, - _Out_ PVOID *HandlerData, - _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, - _Out_ PULONG_PTR EstablisherFrame, - _In_opt_ PULONG_PTR LowLimit, - _In_opt_ PULONG_PTR HighLimit, - _Outptr_opt_result_maybenull_ PEXCEPTION_ROUTINE *HandlerRoutine, - _In_ ULONG UnwindFlags + IN ULONG_PTR ImageBase, + IN ULONG_PTR ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + OUT PVOID *HandlerData, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, + OUT PULONG_PTR EstablisherFrame, + IN PULONG_PTR LowLimit, + IN PULONG_PTR HighLimit, + IN ULONG UnwindFlags ); // Mirror the XSTATE_ARM64_SVE flags from winnt.h diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index e0c3d314f3d6be..ccbadd7dceed2f 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -1990,16 +1990,15 @@ NTSTATUS GetControlPcRva ( BOOLEAN RtlpUnwindIsPacPresent ( - _In_ ULONG_PTR ImageBase, - _In_ ULONG_PTR ControlPc, - _In_ PRUNTIME_FUNCTION FunctionEntry, - _Out_ PVOID *HandlerData, - _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, - _Out_ PULONG_PTR EstablisherFrame, - _In_opt_ PULONG_PTR LowLimit, - _In_opt_ PULONG_PTR HighLimit, - _Outptr_opt_result_maybenull_ PEXCEPTION_ROUTINE *HandlerRoutine, - _In_ ULONG UnwindFlags + IN ULONG_PTR ImageBase, + IN ULONG_PTR ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + OUT PVOID *HandlerData, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, + OUT PULONG_PTR EstablisherFrame, + IN PULONG_PTR LowLimit, + IN PULONG_PTR HighLimit, + IN ULONG UnwindFlags ) /*++ @@ -2032,13 +2031,6 @@ Routine Description: HighLimit - Supplies an optional high limit used to bound the establisher frame. This must be supplied in conjunction with a low limit. - HandlerRoutine - Supplies an optional pointer to a variable that receives - the handler routine address. If control did not leave the specified - function in either the prolog or an epilog and a handler of the - proper type is associated with the function, then the address of the - language specific exception handler is returned. Otherwise, NULL is - returned. - UnwindFlags - Supplies additional flags for the unwind operation. Return Value: diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index f888abecec8d27..e2d33eb025613a 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6950,8 +6950,6 @@ bool IsPacPresent(EECodeInfo *pCodeInfo) DWORD_PTR ImageBase = 0; NTSTATUS Status; PVOID HandlerData; - PEXCEPTION_ROUTINE HandlerRoutine = NULL; - PEXCEPTION_ROUTINE personalityRoutine = NULL; // Lookup the function entry for the IP PTR_RUNTIME_FUNCTION FunctionEntry = pCodeInfo->GetFunctionEntry(); @@ -6973,7 +6971,6 @@ bool IsPacPresent(EECodeInfo *pCodeInfo) &EstablisherFrame, NULL, NULL, - &HandlerRoutine, 0); } #endif // TARGET_ARM64 From f53b929f0e941a30b07cbc023050175c050b0456 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Sun, 22 Jun 2025 22:59:37 +0100 Subject: [PATCH 49/57] Add a prototype of using unwind info to detect PAC --- src/coreclr/inc/cfi.h | 2 +- src/coreclr/inc/clrnt.h | 3 +- src/coreclr/nativeaot/Runtime/ICodeManager.h | 5 ++ src/coreclr/nativeaot/Runtime/thread.cpp | 13 ++-- .../Runtime/unix/UnixNativeCodeManager.cpp | 73 +++++++++++++++++++ .../Runtime/unix/UnixNativeCodeManager.h | 4 + .../Runtime/windows/CoffNativeCodeManager.cpp | 54 ++++++++++++++ .../Runtime/windows/CoffNativeCodeManager.h | 2 + .../Compiler/ObjectWriter/Dwarf/DwarfFde.cs | 1 + src/coreclr/unwinder/arm64/unwinder.cpp | 4 +- src/coreclr/vm/excep.cpp | 7 +- src/coreclr/vm/threadsuspend.cpp | 1 + 12 files changed, 156 insertions(+), 13 deletions(-) diff --git a/src/coreclr/inc/cfi.h b/src/coreclr/inc/cfi.h index 9ebcdb304e535e..6c6330a234bc3b 100644 --- a/src/coreclr/inc/cfi.h +++ b/src/coreclr/inc/cfi.h @@ -11,7 +11,7 @@ enum CFI_OPCODE 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, // Used by ILC, not emitted by JIT - CFI_NEGATE_RA_STATE, // Sign the return address in lr with pacibsp + CFI_NEGATE_RA_STATE, // Sign the return address in lr with paciaz }; struct CFI_CODE diff --git a/src/coreclr/inc/clrnt.h b/src/coreclr/inc/clrnt.h index 7177c31f20b989..fdc76cbc6f0897 100644 --- a/src/coreclr/inc/clrnt.h +++ b/src/coreclr/inc/clrnt.h @@ -370,9 +370,7 @@ RtlVirtualUnwind( ); EXTERN_C -NTSYSAPI BOOLEAN -NTAPI RtlpUnwindIsPacPresent ( IN ULONG_PTR ImageBase, IN ULONG_PTR ControlPc, @@ -384,6 +382,7 @@ RtlpUnwindIsPacPresent ( IN PULONG_PTR HighLimit, IN ULONG UnwindFlags ); + // Mirror the XSTATE_ARM64_SVE flags from winnt.h #ifndef XSTATE_ARM64_SVE diff --git a/src/coreclr/nativeaot/Runtime/ICodeManager.h b/src/coreclr/nativeaot/Runtime/ICodeManager.h index 0fd8f60a5458fd..ec43fc0391e255 100644 --- a/src/coreclr/nativeaot/Runtime/ICodeManager.h +++ b/src/coreclr/nativeaot/Runtime/ICodeManager.h @@ -163,6 +163,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/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index c07dfe26f0dbb1..811fa01668efe2 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -818,9 +818,9 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack ASSERT(pvRetAddr != NULL); #if defined(TARGET_ARM64) - ASSERT(StackFrameIterator::IsValidReturnAddress(pvRetAddr)); -#else ASSERT(StackFrameIterator::IsValidReturnAddress(PacStripPtr(pvRetAddr))); +#else + ASSERT(StackFrameIterator::IsValidReturnAddress(pvRetAddr)); #endif // TARGET_ARM64 m_ppvHijackedReturnAddressLocation = ppvRetAddrLocation; @@ -831,15 +831,14 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack frameIterator->GetRegisterSet())); #endif - void* pvHijacedkAddr = (void*)pfnHijackFunction; + void* pvHijackedAddr = (void*)pfnHijackFunction; #if defined(TARGET_ARM64) - uint64_t isJitPacEnabled; - if (g_pRhConfig->ReadConfigValue("JitPacEnabled", &isJitPacEnabled, true /* decimal */) && isJitPacEnabled != 0) + if (frameIterator->GetCodeManager()->IsPacPresent(frameIterator->GetMethodInfo(), frameIterator->GetRegisterSet())) { - pvHijacedkAddr = PacSignPtr(pvHijacedkAddr); + pvHijackedAddr = PacSignPtr(pvHijackedAddr); } #endif // TARGET_ARM64 - *ppvRetAddrLocation = pvHijacedkAddr; + *ppvRetAddrLocation = pvHijackedAddr; STRESS_LOG2(LF_STACKWALK, LL_INFO10000, "InternalHijack: TgtThread = %llx, IP = %p\n", GetPalThreadIdForLogging(), frameIterator->GetRegisterSet()->GetIP()); diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index 0321e2101817c8..6d8f364fede965 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -38,6 +38,7 @@ struct UnixNativeMethodInfo // Subset of unw_proc_info_t required for unwinding unw_word_t start_ip; unw_word_t unwind_info; + uint32_t unwind_info_size; uint32_t format; bool executionAborted; @@ -62,6 +63,77 @@ 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 + pNativeMethodInfo->unwind_info_size; + + 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) { @@ -97,6 +169,7 @@ bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC, pMethodInfo->start_ip = procInfo.start_ip; pMethodInfo->format = procInfo.format; pMethodInfo->unwind_info = procInfo.unwind_info; + pMethodInfo->unwind_info_size = procInfo.unwind_info_size; uintptr_t lsda = procInfo.lsda; 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 f709bc4650360d..2dc39dd506f9cd 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -829,6 +829,60 @@ bool CoffNativeCodeManager::IsUnwindable(PTR_VOID pvAddress) return true; } +#if defined(TARGET_ARM64) +bool CoffNativeCodeManager::IsPacPresent(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet) +{ +#if defined(HOST_WINDOWS) + return false; +#else + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + + size_t unwindDataBlobSize; + SIZE_T EstablisherFrame; + PVOID HandlerData; + CONTEXT context; + + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize); + + PTR_uint8_t p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + context.Sp = pRegisterSet->GetSP(); + context.Fp = pRegisterSet->GetFP(); + context.Pc = pRegisterSet->GetIP(); + context.Lr = *pRegisterSet->pLR; + + KNONVOLATILE_CONTEXT_POINTERS contextPointers; +#ifdef _DEBUG + memset(&contextPointers, 0xDD, sizeof(contextPointers)); +#endif + contextPointers.Lr = pRegisterSet->pLR; + + EstablisherFrame = 0; + HandlerData = NULL; + + return RtlpUnwindIsPacPresent ( + dac_cast(m_moduleBase), + pRegisterSet->IP, + (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction, + &HandlerData, + &contextPointers, + &EstablisherFrame, + NULL, + NULL, + 0); +#endif //HOST_WINDOWS +} +#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/DwarfFde.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs index 7b46de98c5ecd7..8c98a416d63e6b 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 @@ -113,6 +113,7 @@ 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/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index ccbadd7dceed2f..233857718b73a6 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -269,7 +269,9 @@ do { #endif // !defined(DEBUGGER_UNWIND) +// // Macros for stripping pointer authentication (PAC) bits. +// #if !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) #define STRIP_PAC(pointer) RtlStripPacOnline(pointer) @@ -2812,7 +2814,6 @@ RtlpUnwindFunctionCompact ( return Status; } - #if !defined(DEBUGGER_UNWIND) NTSTATUS @@ -3034,6 +3035,7 @@ Return Value: } #endif // !defined(DEBUGGER_UNWIND) + BOOL OOPStackUnwinderArm64::Unwind(T_CONTEXT * pContext) { DWORD64 ImageBase = 0; diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index e2d33eb025613a..4148e4ff1b56dc 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6934,6 +6934,9 @@ bool IsIPInEpilog(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, BOOL *pSaf // This function is used to check if Pointer Authentication (PAC) is enabled for this stack frame or not. bool IsPacPresent(EECodeInfo *pCodeInfo) { +#if defined(HOST_WINDOWS) + return false; +#else CONTRACTL { NOTHROW; @@ -6948,8 +6951,7 @@ bool IsPacPresent(EECodeInfo *pCodeInfo) DWORD_PTR EstablisherFrame = 0; DWORD_PTR ImageBase = 0; - NTSTATUS Status; - PVOID HandlerData; + PVOID HandlerData = NULL; // Lookup the function entry for the IP PTR_RUNTIME_FUNCTION FunctionEntry = pCodeInfo->GetFunctionEntry(); @@ -6972,6 +6974,7 @@ bool IsPacPresent(EECodeInfo *pCodeInfo) NULL, NULL, 0); +#endif // HOST_WINDOWS } #endif // TARGET_ARM64 diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 0de503c52875f3..61e43a3aff8a58 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -4604,6 +4604,7 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86 // Remember the place that the return would have gone m_pvHJRetAddr = *esb->m_ppvRetAddrPtr; + IS_VALID_CODE_PTR((FARPROC) (TADDR)m_pvHJRetAddr); // TODO [DAVBR]: For the full fix for VsWhidbey 450273, the below // may be uncommented once isLegalManagedCodeCaller works properly From 52f63d802622377d32761556c509d356a3069710 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Fri, 27 Jun 2025 23:12:04 +0100 Subject: [PATCH 50/57] Extract IsPacPresent() outside the undwinder.cpp --- src/coreclr/inc/clrconfigvalues.h | 2 +- src/coreclr/jit/jitconfigvalues.h | 2 +- .../Runtime/windows/CoffNativeCodeManager.cpp | 67 +- src/coreclr/unwinder/arm64/unwinder.cpp | 602 +++++------------- src/coreclr/vm/excep.cpp | 92 ++- 5 files changed, 236 insertions(+), 529 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 9c39eecc06c55e..0459350eb27b1e 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -701,7 +701,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"), 0, "Allows Arm64 Pointer Authentication (PAC) 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/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 9f24dd16dc543b..22fd2f756d3d0a 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -125,7 +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", 0) +RELEASE_CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 1) // // MinOpts diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp index bcbed73f602117..a95a7a0b125fd9 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -835,53 +835,48 @@ bool CoffNativeCodeManager::IsUnwindable(PTR_VOID pvAddress) bool CoffNativeCodeManager::IsPacPresent(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet) { -#if defined(HOST_WINDOWS) - return false; -#else CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; size_t unwindDataBlobSize; - SIZE_T EstablisherFrame; - PVOID HandlerData; - CONTEXT context; PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize); - PTR_uint8_t p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; - - uint8_t unwindBlockFlags = *p++; - - if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) - p += sizeof(int32_t); + PTR_uint8_t UnwindCodePtr = dac_cast(pUnwindDataBlob); + PTR_uint8_t UnwindCodesEndPtr = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; - if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) - p += sizeof(int32_t); + while (UnwindCodePtr < UnwindCodesEndPtr) + { + uint8_t CurCode = * UnwindCodePtr; + if ((CurCode & 0xfe) == 0xe4) // The last unwind code + { + break; + } - context.Sp = pRegisterSet->GetSP(); - context.Fp = pRegisterSet->GetFP(); - context.Pc = pRegisterSet->GetIP(); - context.Lr = *pRegisterSet->pLR; + if (CurCode == 0xFC) // Unwind code for PAC (pac_sign_lr) + { + return true; + } - KNONVOLATILE_CONTEXT_POINTERS contextPointers; -#ifdef _DEBUG - memset(&contextPointers, 0xDD, sizeof(contextPointers)); -#endif - contextPointers.Lr = pRegisterSet->pLR; + 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 + }; - EstablisherFrame = 0; - HandlerData = NULL; + UnwindCodePtr += UnwindCodeSizeTable[CurCode - 0xE0]; + } + } - return RtlpUnwindIsPacPresent ( - dac_cast(m_moduleBase), - pRegisterSet->IP, - (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction, - &HandlerData, - &contextPointers, - &EstablisherFrame, - NULL, - NULL, - 0); -#endif //HOST_WINDOWS + return false; } #endif //TARGET_ARM64 diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index 233857718b73a6..ed4238c98a6bf6 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -33,10 +33,6 @@ #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 @@ -272,70 +268,13 @@ do { // // Macros for stripping pointer authentication (PAC) bits. // -#if !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) - -#define STRIP_PAC(pointer) RtlStripPacOnline(pointer) - -FORCEINLINE -VOID RtlStripPacOnline(_Inout_ PULONG64 Pointer) - -/*++ - -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. +#if !defined(DEBUGGER_STRIP_PAC) -Return Value: - - None. - ---*/ -{ - *Pointer &= 0x0000FFFFFFFFFFFF; - return; -} +// NOTE: Pointer authentication is not used by .NET, so the implementation does nothing +#define STRIP_PAC(Params, pointer) -#endif // !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER) +#endif // // Macros to clarify opcode parsing @@ -1711,35 +1650,34 @@ Return Value: return STATUS_SUCCESS; } - NTSTATUS -RtlpProcessPdata ( - _Out_ PULONG_PTR UnwindCodePtr, - _Out_ PULONG_PTR UnwindCodesEndPtr, - _In_ PARM64_UNWIND_PARAMS UnwindParams, - _In_ ULONG ControlPcRva, - _In_ ULONG_PTR ImageBase, - _In_ PRUNTIME_FUNCTION FunctionEntry, - _In_ IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *FunctionEntryExtended, - _Inout_ PEXCEPTION_ROUTINE *ExceptionHandler, - _Inout_ PVOID *ExceptionHandlerData +RtlpUnwindFunctionFull ( + __in ULONG ControlPcRva, + __in ULONG_PTR ImageBase, + __in PRUNTIME_FUNCTION FunctionEntry, + __in IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *FunctionEntryExtended, + __inout PCONTEXT ContextRecord, + __out PULONG_PTR EstablisherFrame, + __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine, + __out PVOID *HandlerData, + __in PARM64_UNWIND_PARAMS UnwindParams, + __in ULONG UnwindFlags ) /*++ Routine Description: - Processes the .pdata record of a function. - -Arguments: - UnwindCodePtr - Supplies a pointer to a variable that receives address - of the beginning of opcodes. + This function virtually unwinds the specified function by parsing the + .xdata record to determine where in the function the provided ControlPc + is, and then executing unwind codes that map to the function's prolog + or epilog behavior. - UnwindCodesEndPtr - Supplies a pointer to a variable that receives address - of the end of opcodes. + If a context pointers record is specified (in the UnwindParams), then + the address where each nonvolatile register is restored from is recorded + in the appropriate element of the context pointers record. - UnwindParams - Supplies address of a variable containing additional - parameters shared with caller. +Arguments: ControlPcRva - Supplies the address where control left the specified function, as an offset relative to the ImageBase. @@ -1751,35 +1689,72 @@ Routine Description: specified function. If appropriate, this should have already been probed. - FunctionEntryExtended - Supplies a pointer to the variable containing - address of the extended function entry. It contains a non-NULL when - opcodes are present in .xdata. + ContextRecord - Supplies the address of a context record. - ExceptionHandler - Supplies a pointer to a variable that receives - the exception handler routine address. + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. - ExceptionHandlerData - Supplies a pointer to a variable that receives - the exception handler data address. + HandlerRoutine - Supplies an optional pointer to a variable that receives + the handler routine address. If control did not leave the specified + function in either the prolog or an epilog and a handler of the + proper type is associated with the function, then the address of the + language specific exception handler is returned. Otherwise, NULL is + returned. + + HandlerData - Supplies a pointer to a variable that receives a pointer + the the language handler data. + + UnwindParams - Additional parameters shared with caller. + + UnwindFlags - Supplies additional flags for the unwind operation. Return Value: STATUS_SUCCESS if the unwind could be completed, a failure status otherwise. + Unwind can only fail when validation bounds are specified. --*/ + { + ULONG AccumulatedSaveNexts; ULONG CurCode; - ULONG ScopeNum; - ULONG ScopeSize; - ULONG SkipWords; - ULONG ScopeStart; ULONG EpilogScopeCount; + PEXCEPTION_ROUTINE ExceptionHandler; + PVOID ExceptionHandlerData; + BOOLEAN FinalPcFromLr; ULONG FunctionLength; ULONG HeaderWord; + ULONG NextCode; ULONG OffsetInFunction; + ULONG ScopeNum; + ULONG ScopeSize; + ULONG ScopeStart; + ULONG SkipWords; + NTSTATUS Status; + ULONG_PTR UnwindCodePtr; + ULONG_PTR UnwindCodesEndPtr; ULONG_PTR UnwindDataPtr; ULONG UnwindIndex; ULONG UnwindWords; + UNREFERENCED_PARAMETER(UnwindFlags); + + // + // Unless a special frame is encountered, assume that any unwinding + // will return us to the return address of a call and set the flag + // appropriately (it will be cleared again if the special cases apply). + // + + ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + + // + // By default, unwinding is done by popping to the LR, then copying + // that LR to the PC. However, some special opcodes require different + // behavior. + // + + FinalPcFromLr = TRUE; + // // Fetch the header word from the .xdata blob // @@ -1826,10 +1801,12 @@ Return Value: // If exception data is present, extract it now. // + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; if ((HeaderWord & (1 << 20)) != 0) { - *ExceptionHandler = (PEXCEPTION_ROUTINE)(ImageBase + + ExceptionHandler = (PEXCEPTION_ROUTINE)(ImageBase + MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords))); - *ExceptionHandlerData = (PVOID)(UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords + 1)); + ExceptionHandlerData = (PVOID)(UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords + 1)); } // @@ -1837,8 +1814,8 @@ Return Value: // that immediately follow the epilog scope list. // - *UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount; - *UnwindCodesEndPtr = *UnwindCodePtr + 4 * UnwindWords; + UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount; + UnwindCodesEndPtr = UnwindCodePtr + 4 * UnwindWords; SkipWords = 0; // @@ -1856,396 +1833,105 @@ Return Value: // if (OffsetInFunction < 4 * UnwindWords) { - ScopeSize = RtlpComputeScopeSize(*UnwindCodePtr, *UnwindCodesEndPtr, FALSE, UnwindParams); + ScopeSize = RtlpComputeScopeSize(UnwindCodePtr, UnwindCodesEndPtr, FALSE, UnwindParams); if (OffsetInFunction < ScopeSize) { SkipWords = ScopeSize - OffsetInFunction; - *ExceptionHandler = NULL; - *ExceptionHandlerData = NULL; + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + goto ExecuteCodes; } } - else - { - // - // We're not in the prolog, now check to see if we are in the epilog. - // In the simple case, the 'E' bit is set indicating there is a single - // epilog that lives at the end of the function. If we're near the end - // of the function, compute the actual size of the epilog from the - // unwind codes. If we're in the midst of it, adjust the unwind code - // pointer to the start of the codes and determine how many we need to skip. - // - // N.B. Similar to the prolog case above, the maximum number of halfwords - // that an epilog can cover is limited by UnwindWords. In the epilog - // case, however, the starting index within the unwind code table is - // non-zero, and so the maximum number of unwind codes that can pertain - // to an epilog is (UnwindWords * 4 - UnwindIndex), thus further - // constraining the bounds of the epilog. - // - - if ((HeaderWord & (1 << 21)) != 0) { - if (OffsetInFunction + (4 * UnwindWords - UnwindIndex) >= FunctionLength) { - ScopeSize = RtlpComputeScopeSize(*UnwindCodePtr + UnwindIndex, *UnwindCodesEndPtr, TRUE, UnwindParams); - ScopeStart = FunctionLength - ScopeSize; - - // - // N.B. This code assumes that no handleable exceptions can occur in - // the prolog or in a chained shrink-wrapping prolog region. - // - - if (OffsetInFunction >= ScopeStart) { - *UnwindCodePtr += UnwindIndex; - SkipWords = OffsetInFunction - ScopeStart; - *ExceptionHandler = NULL; - *ExceptionHandlerData = NULL; - } - } - } - - // - // In the multiple-epilog case, we scan forward to see if we are within - // shooting distance of any of the epilogs. If we are, we compute the - // actual size of the epilog from the unwind codes and proceed like the - // simple case above. - // - - else { - for (ScopeNum = 0; ScopeNum < EpilogScopeCount; ScopeNum++) { - HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr); - UnwindDataPtr += 4; - - // - // The scope records are stored in order. If we hit a record that - // starts after our current position, we must not be in an epilog. - // - - ScopeStart = HeaderWord & 0x3ffff; - if (OffsetInFunction < ScopeStart) { - break; - } - - UnwindIndex = HeaderWord >> 22; - if (OffsetInFunction < ScopeStart + (4 * UnwindWords - UnwindIndex)) { - ScopeSize = RtlpComputeScopeSize(*UnwindCodePtr + UnwindIndex, *UnwindCodesEndPtr, TRUE, UnwindParams); - if (OffsetInFunction < ScopeStart + ScopeSize) { - - *UnwindCodePtr += UnwindIndex; - SkipWords = OffsetInFunction - ScopeStart; - *ExceptionHandler = NULL; - *ExceptionHandlerData = NULL; - break; - } - } - } - } - - } // - // Skip over unwind codes until we account for the number of halfwords - // to skip. - // - - while (*UnwindCodePtr < *UnwindCodesEndPtr && SkipWords > 0) { - CurCode = MEMORY_READ_BYTE(UnwindParams, *UnwindCodePtr); - if (OPCODE_IS_END(CurCode)) { - break; - } - *UnwindCodePtr += RtlpGetUnwindCodeSize(CurCode, NULL); - SkipWords--; - } - - return STATUS_SUCCESS; -} - -NTSTATUS GetControlPcRva ( - _In_ ULONG_PTR ImageBase, - _In_ ULONG_PTR ControlPc, - _In_opt_ PRUNTIME_FUNCTION FunctionEntry, - _In_ ULONG UnwindType, - _Out_ PULONG ControlPcRva -) -{ + // We're not in the prolog, now check to see if we are in the epilog. + // In the simple case, the 'E' bit is set indicating there is a single + // epilog that lives at the end of the function. If we're near the end + // of the function, compute the actual size of the epilog from the + // unwind codes. If we're in the midst of it, adjust the unwind code + // pointer to the start of the codes and determine how many we need to skip. // - // Unwind type 3 refers to a chained record. The top 30 bits of the - // unwind data contains the RVA of the parent pdata record. + // N.B. Similar to the prolog case above, the maximum number of halfwords + // that an epilog can cover is limited by UnwindWords. In the epilog + // case, however, the starting index within the unwind code table is + // non-zero, and so the maximum number of unwind codes that can pertain + // to an epilog is (UnwindWords * 4 - UnwindIndex), thus further + // constraining the bounds of the epilog. // - if (UnwindType == 3) { - if ((FunctionEntry->UnwindData & 4) == 0) { - FunctionEntry = (PRUNTIME_FUNCTION)(ImageBase + FunctionEntry->UnwindData - 3); - UnwindType = (FunctionEntry->UnwindData & 3); - - UNWINDER_ASSERT(UnwindType != 3); + if ((HeaderWord & (1 << 21)) != 0) { + if (OffsetInFunction + (4 * UnwindWords - UnwindIndex) >= FunctionLength) { + ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams); + ScopeStart = FunctionLength - ScopeSize; - *ControlPcRva = FunctionEntry->BeginAddress; + // + // N.B. This code assumes that no handleable exceptions can occur in + // the prolog or in a chained shrink-wrapping prolog region. + // - } else { - return STATUS_UNWIND_UNSUPPORTED_VERSION; + if (OffsetInFunction >= ScopeStart) { + UnwindCodePtr += UnwindIndex; + SkipWords = OffsetInFunction - ScopeStart; + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + } } - - } else { - *ControlPcRva = (ULONG)(ControlPc - ImageBase); - } - - return STATUS_SUCCESS; -} - -BOOLEAN -RtlpUnwindIsPacPresent ( - IN ULONG_PTR ImageBase, - IN ULONG_PTR ControlPc, - IN PRUNTIME_FUNCTION FunctionEntry, - OUT PVOID *HandlerData, - IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, - OUT PULONG_PTR EstablisherFrame, - IN PULONG_PTR LowLimit, - IN PULONG_PTR HighLimit, - IN ULONG UnwindFlags - ) -/*++ - -Routine Description: - - This function goes through the unwinding data of the specified function - to look for an opcode representing Pointer Authentication (PAC). - -Arguments: - - ImageBase - Supplies the base address of the image that contains the - function being unwound. - - ControlPc - Supplies the address where control left the specified - function. - - FunctionEntry - Supplies the address of the function table entry for the - specified function. If appropriate, this should have already been - probed. - - HandlerData - Supplies a pointer to a variable that receives a pointer - the the language handler data. - - EstablisherFrame - Supplies a pointer to a variable that receives the - the establisher frame pointer value. - - LowLimit - Supplies an optional low limit used to bound the establisher - frame. This must be supplied in conjunction with a high limit. - - HighLimit - Supplies an optional high limit used to bound the establisher - frame. This must be supplied in conjunction with a low limit. - - UnwindFlags - Supplies additional flags for the unwind operation. - -Return Value: - - A boolean true if the unwind could be completed and an opcode for - Pointer Authentication (PAC) is found, false otherwise. - ---*/ - -{ - NTSTATUS Status; - PEXCEPTION_ROUTINE ExceptionHandler = NULL; - PVOID ExceptionHandlerData = NULL; - ULONG_PTR UnwindCodePtr; - ULONG_PTR UnwindCodesEndPtr; - ULONG ControlPcRva; - ULONG UnwindType; - ARM64_UNWIND_PARAMS UnwindParams; - ULONG ScopeSize; - BYTE Opcode; - - UNWINDER_ASSERT((UnwindFlags & ~RTL_VIRTUAL_UNWIND_VALID_FLAGS_ARM64) == 0); - - if (FunctionEntry == NULL) { - // Leaf function so no PAC - return false; } // - // Make sure out-of-bound stack accesses don't send us into an infinite - // unwinding loop. + // In the multiple-epilog case, we scan forward to see if we are within + // shooting distance of any of the epilogs. If we are, we compute the + // actual size of the epilog from the unwind codes and proceed like the + // simple case above. // -#if 0 -__try { -#endif - UnwindParams.ControlPc = ControlPc; - UnwindParams.LowLimit = LowLimit; - UnwindParams.HighLimit = HighLimit; - UnwindParams.ContextPointers = ContextPointers; - - UnwindType = (FunctionEntry->UnwindData & 3); - - Status = GetControlPcRva(ImageBase, ControlPc, FunctionEntry, UnwindType, &ControlPcRva); - if (Status != STATUS_SUCCESS) - { - return false; - } - - // - // Identify the compact .pdata format versus the full .pdata+.xdata format. - // + else { + for (ScopeNum = 0; ScopeNum < EpilogScopeCount; ScopeNum++) { + HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr); + UnwindDataPtr += 4; - IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *FunctionEntryExtended = NULL; - struct LOCAL_XDATA { - IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA xdata; - char ops[60]; - } fnent_xdata = {}; + // + // The scope records are stored in order. If we hit a record that + // starts after our current position, we must not be in an epilog. + // - if (UnwindType != 0) { - fnent_xdata.xdata.CodeWords = sizeof(fnent_xdata.ops) / 4; - RtlpExpandCompactToFull(FunctionEntry, &fnent_xdata.xdata); - FunctionEntryExtended = &fnent_xdata.xdata; - } + ScopeStart = HeaderWord & 0x3ffff; + if (OffsetInFunction < ScopeStart) { + break; + } - UNREFERENCED_PARAMETER(UnwindFlags); + UnwindIndex = HeaderWord >> 22; + if (OffsetInFunction < ScopeStart + (4 * UnwindWords - UnwindIndex)) { + ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams); - Status = RtlpProcessPdata (&UnwindCodePtr, &UnwindCodesEndPtr, &UnwindParams, - ControlPcRva, ImageBase, FunctionEntry, FunctionEntryExtended, &ExceptionHandler, &ExceptionHandlerData); + if (OffsetInFunction < ScopeStart + ScopeSize) { - if (Status != STATUS_SUCCESS) - { - return false; - } - #if 0 + UnwindCodePtr += UnwindIndex; + SkipWords = OffsetInFunction - ScopeStart; + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + break; + } + } + } } - // - // If we do take an exception here, fetch the exception code as the status - // and do not propagate the exception. Since the exception handler also - // uses this function, propagating it will most likely generate the same - // exception at the same point in the unwind, and continuing will typically - // overflow the kernel stack. - // - - __except (EXCEPTION_EXECUTE_HANDLER) { - return false; - } -#endif // HOST_WINDOWS +ExecuteCodes: // - // Iterate through the unwind codes until we find PAC or hit an end marker. + // Skip over unwind codes until we account for the number of halfwords + // to skip. // - ScopeSize = 0; - Opcode = 0; - while (UnwindCodePtr < UnwindCodesEndPtr) { - Opcode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); - if (OPCODE_IS_END(Opcode)) { + while (UnwindCodePtr < UnwindCodesEndPtr && SkipWords > 0) { + CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + if (OPCODE_IS_END(CurCode)) { break; } - if (Opcode == 0xfc) - { - return true; - } - - UnwindCodePtr += RtlpGetUnwindCodeSize(Opcode, &ScopeSize); - } - return false; -} - -NTSTATUS -RtlpUnwindFunctionFull ( - __in ULONG ControlPcRva, - __in ULONG_PTR ImageBase, - __in PRUNTIME_FUNCTION FunctionEntry, - __in IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *FunctionEntryExtended, - __inout PCONTEXT ContextRecord, - __out PULONG_PTR EstablisherFrame, - __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine, - __out PVOID *HandlerData, - __in PARM64_UNWIND_PARAMS UnwindParams, - __in ULONG UnwindFlags - ) - -/*++ - -Routine Description: - - This function virtually unwinds the specified function by parsing the - .xdata record to determine where in the function the provided ControlPc - is, and then executing unwind codes that map to the function's prolog - or epilog behavior. - - If a context pointers record is specified (in the UnwindParams), then - the address where each nonvolatile register is restored from is recorded - in the appropriate element of the context pointers record. - -Arguments: - - ControlPcRva - Supplies the address where control left the specified - function, as an offset relative to the ImageBase. - - ImageBase - Supplies the base address of the image that contains the - function being unwound. - - FunctionEntry - Supplies the address of the function table entry for the - specified function. If appropriate, this should have already been - probed. - - ContextRecord - Supplies the address of a context record. - - EstablisherFrame - Supplies a pointer to a variable that receives the - the establisher frame pointer value. - - HandlerRoutine - Supplies an optional pointer to a variable that receives - the handler routine address. If control did not leave the specified - function in either the prolog or an epilog and a handler of the - proper type is associated with the function, then the address of the - language specific exception handler is returned. Otherwise, NULL is - returned. - - HandlerData - Supplies a pointer to a variable that receives a pointer - the the language handler data. - - UnwindParams - Additional parameters shared with caller. - - UnwindFlags - Supplies additional flags for the unwind operation. - -Return Value: - - STATUS_SUCCESS if the unwind could be completed, a failure status otherwise. - Unwind can only fail when validation bounds are specified. - ---*/ - -{ - ULONG AccumulatedSaveNexts; - ULONG CurCode; - BOOLEAN FinalPcFromLr; - ULONG NextCode; - NTSTATUS Status; - PEXCEPTION_ROUTINE ExceptionHandler = NULL; - PVOID ExceptionHandlerData = NULL; - ULONG_PTR UnwindCodePtr; - ULONG_PTR UnwindCodesEndPtr; - - UNREFERENCED_PARAMETER(UnwindFlags); - - // - // Unless a special frame is encountered, assume that any unwinding - // will return us to the return address of a call and set the flag - // appropriately (it will be cleared again if the special cases apply). - // - - ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; - - Status = RtlpProcessPdata (&UnwindCodePtr, &UnwindCodesEndPtr, UnwindParams, - ControlPcRva, ImageBase, FunctionEntry, FunctionEntryExtended, &ExceptionHandler, &ExceptionHandlerData); - - if (Status != STATUS_SUCCESS) - { - return Status; + UnwindCodePtr += RtlpGetUnwindCodeSize(CurCode, NULL); + SkipWords--; } - // - // By default, unwinding is done by popping to the LR, then copying - // that LR to the PC. However, some special opcodes require different - // behavior. - // - - FinalPcFromLr = TRUE; - // // Now execute codes until we hit the end. // @@ -2671,7 +2357,7 @@ Return Value: return STATUS_UNWIND_INVALID_SEQUENCE; } - STRIP_PAC(&ContextRecord->Lr); + STRIP_PAC(UnwindParams, &ContextRecord->Lr); // // TODO: Implement support for UnwindFlags RTL_VIRTUAL_UNWIND2_VALIDATE_PAC. diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 4406255bc9b94c..653caa13c0e1bf 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6934,47 +6934,73 @@ bool IsIPInEpilog(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, BOOL *pSaf // This function is used to check if Pointer Authentication (PAC) is enabled for this stack frame or not. bool IsPacPresent(EECodeInfo *pCodeInfo) { -#if defined(HOST_WINDOWS) - return false; -#else - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - TADDR ControlPc = pCodeInfo->GetCodeAddress(); - _ASSERTE(pCodeInfo->IsValid()); - DWORD_PTR EstablisherFrame = 0; - DWORD_PTR ImageBase = 0; - PVOID HandlerData = NULL; - // 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(); + ULONG_PTR UnwindDataPtr = (ULONG_PTR)(ImageBase + FunctionEntry->UnwindData); - ImageBase = pCodeInfo->GetModuleBase(); - - KNONVOLATILE_CONTEXT_POINTERS ContextPointers; - ZeroMemory(&ContextPointers, sizeof(ContextPointers)); - - return RtlpUnwindIsPacPresent ( - ImageBase, - ControlPc, - (PIMAGE_ARM64_RUNTIME_FUNCTION_ENTRY)FunctionEntry, - &HandlerData, - &ContextPointers, - &EstablisherFrame, - NULL, - NULL, - 0); -#endif // HOST_WINDOWS + // 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 From 79a3fbcf80f2999e2fe26da241928e66c9182627 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Sun, 29 Jun 2025 12:48:54 +0100 Subject: [PATCH 51/57] Remove clrnt.h changes --- src/coreclr/inc/clrnt.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/coreclr/inc/clrnt.h b/src/coreclr/inc/clrnt.h index fdc76cbc6f0897..8040117a28b8f6 100644 --- a/src/coreclr/inc/clrnt.h +++ b/src/coreclr/inc/clrnt.h @@ -369,20 +369,6 @@ RtlVirtualUnwind( IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL ); -EXTERN_C -BOOLEAN -RtlpUnwindIsPacPresent ( - IN ULONG_PTR ImageBase, - IN ULONG_PTR ControlPc, - IN PRUNTIME_FUNCTION FunctionEntry, - OUT PVOID *HandlerData, - IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, - OUT PULONG_PTR EstablisherFrame, - IN PULONG_PTR LowLimit, - IN PULONG_PTR HighLimit, - IN ULONG UnwindFlags - ); - // Mirror the XSTATE_ARM64_SVE flags from winnt.h #ifndef XSTATE_ARM64_SVE From e1ba37b3e40df059ed23679fddede5cd0968465a Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Sun, 29 Jun 2025 12:51:11 +0100 Subject: [PATCH 52/57] Address review comments --- src/coreclr/inc/cfi.h | 2 +- src/coreclr/vm/excep.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/inc/cfi.h b/src/coreclr/inc/cfi.h index 6c6330a234bc3b..95a7a0077c8e25 100644 --- a/src/coreclr/inc/cfi.h +++ b/src/coreclr/inc/cfi.h @@ -10,7 +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, // Used by ILC, not emitted by JIT + 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/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 653caa13c0e1bf..06260464163e5a 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6942,6 +6942,8 @@ bool IsPacPresent(EECodeInfo *pCodeInfo) // 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 From 75316bc429096dc6faac206b5b1ad7ef5a6b48b2 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 2 Jul 2025 13:59:46 +0100 Subject: [PATCH 53/57] Fix starting offset while unwinding dwarf info --- .../nativeaot/Runtime/arm64/ExceptionHandling.S | 1 - .../Runtime/unix/UnixNativeCodeManager.cpp | 13 +++++++++---- .../Compiler/ObjectWriter/Dwarf/DwarfFde.cs | 7 +++++++ .../ObjectWriter/Eabi/EabiUnwindConverter.cs | 4 ++++ .../JitInterface/CorInfoImpl.RyuJit.cs | 8 ++++++++ 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S index 7537426ea80187..40c0b37b9917ec 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S @@ -301,7 +301,6 @@ LOCAL_LABEL(TailCallWasHijacked): mov lr, x1 str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] - xpaclri LOCAL_LABEL(ClearThreadState): diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index d3ce6d1eecd96f..ea0f6ca0b22746 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -40,7 +40,6 @@ struct UnixNativeMethodInfo // Subset of unw_proc_info_t required for unwinding unw_word_t start_ip; unw_word_t unwind_info; - uint32_t unwind_info_size; uint32_t format; bool executionAborted; @@ -85,7 +84,14 @@ bool UnixNativeCodeManager::IsPacPresent(MethodInfo * pMethodInfo, { UnixNativeMethodInfo* pNativeMethodInfo = (UnixNativeMethodInfo*)pMethodInfo; const uint8_t *p = (uint8_t *) pNativeMethodInfo->unwind_info; - const uint8_t *end = p + pNativeMethodInfo->unwind_info_size; + 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++; @@ -99,7 +105,7 @@ bool UnixNativeCodeManager::IsPacPresent(MethodInfo * pMethodInfo, { continue; } - if ((op & 0x3F) == DW_CFA_offset) + if ((op & ~(0x3F)) == DW_CFA_offset) { readULEB(p, end); // offset continue; @@ -171,7 +177,6 @@ bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC, pMethodInfo->start_ip = procInfo.start_ip; pMethodInfo->format = procInfo.format; pMethodInfo->unwind_info = procInfo.unwind_info; - pMethodInfo->unwind_info_size = procInfo.unwind_info_size; uintptr_t lsda = procInfo.lsda; 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 8c98a416d63e6b..6f142d490f7667 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,13 @@ private static byte[] CfiCodeToInstructions(DwarfCie cie, byte[] blobData) { codeOffset = Math.Max(codeOffset, blobData[offset++]); CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset++]; + + if (opcode == CFI_OPCODE.CFI_NEGATE_RA_STATE) + { + cfiCode[cfiCodeOffset++] = DW_CFA_AARCH64_negate_ra_state; + continue; + } + short dwarfReg = BitConverter.ToInt16(blobData, offset); offset += sizeof(short); int cfiOffset = BitConverter.ToInt32(blobData, offset); 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.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index a420cfb8f95387..164ee9552314b7 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -121,6 +121,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 +177,7 @@ private static byte[] CompressARM64CFI(byte[] blobData) break; case CFI_OPCODE.CFI_NEGATE_RA_STATE: // Nothing to compress here. It just has the code. + shouldAddPACOpCode = true; break; } } @@ -186,6 +188,12 @@ 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); + } + if (cfaRegister != -1) { cfiWriter.Write((byte)codeOffset); From c9f8bd1489bbbb1941186bf38cc9ef2f8fda2a46 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Wed, 2 Jul 2025 16:38:17 +0100 Subject: [PATCH 54/57] Fix build failures of MacOS --- src/coreclr/jit/unwindarm64.cpp | 4 ++-- .../nativeaot/Runtime/arm64/ExceptionHandling.asm | 1 - .../Compiler/ObjectWriter/Dwarf/DwarfCfiOpcode.cs | 2 +- .../Compiler/ObjectWriter/Dwarf/DwarfFde.cs | 6 ------ .../Compiler/ObjectWriter/MachObjectWriter.cs | 11 +++++++---- .../JitInterface/CorInfoImpl.RyuJit.cs | 6 ++++-- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/coreclr/jit/unwindarm64.cpp b/src/coreclr/jit/unwindarm64.cpp index 60c06126c783b8..c950f50a11548b 100644 --- a/src/coreclr/jit/unwindarm64.cpp +++ b/src/coreclr/jit/unwindarm64.cpp @@ -658,7 +658,7 @@ void Compiler::unwindPacSignLR() } #endif // FEATURE_CFI_SUPPORT - // pac_sign_lr: 11111100: sign the return address in lr with pacibsp + // pac_sign_lr: 11111100: sign the return address in lr with paciaz funCurrentFunc()->uwi.AddCode(0xFC); } @@ -1110,7 +1110,7 @@ void DumpUnwindInfo(Compiler* comp, } else if (b1 == 0xFC) { - // pac_sign_lr: 11111100 : sign the return address in lr with pacibsp. + // pac_sign_lr: 11111100 : sign the return address in lr with paciaz. printf(" %02X pac_sign_lr\n", b1); } diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm index c31e0f19e263b3..5918745625fd83 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm @@ -302,7 +302,6 @@ TailCallWasHijacked mov lr, x1 str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] - DCD 0xD50320FF ;; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers ClearThreadState 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 24575a0dfa81ba..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 @@ -12,6 +12,6 @@ internal enum CFI_OPCODE 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 pacibsp + 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 6f142d490f7667..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 @@ -52,12 +52,6 @@ private static byte[] CfiCodeToInstructions(DwarfCie cie, byte[] blobData) codeOffset = Math.Max(codeOffset, blobData[offset++]); CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset++]; - if (opcode == CFI_OPCODE.CFI_NEGATE_RA_STATE) - { - cfiCode[cfiCodeOffset++] = DW_CFA_AARCH64_negate_ra_state; - continue; - } - short dwarfReg = BitConverter.ToInt16(blobData, offset); offset += sizeof(short); int cfiOffset = BitConverter.ToInt32(blobData, offset); 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 9d7d9253cb038f..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)); @@ -844,10 +851,6 @@ private static uint GetArm64CompactUnwindCode(byte[] blobData) registerOffset[i] += cfiOffset; } break; - - case CFI_OPCODE.CFI_NEGATE_RA_STATE: - // Nothing to compress here. It just has the code. - break; } } 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 164ee9552314b7..9b99d61a31eb76 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -90,7 +90,7 @@ private enum CFI_OPCODE 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 pacibsp + 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 @@ -175,8 +175,8 @@ private static byte[] CompressARM64CFI(byte[] blobData) } } break; + case CFI_OPCODE.CFI_NEGATE_RA_STATE: - // Nothing to compress here. It just has the code. shouldAddPACOpCode = true; break; } @@ -192,6 +192,8 @@ private static byte[] CompressARM64CFI(byte[] blobData) { cfiWriter.Write((byte)codeOffset); cfiWriter.Write((byte)CFI_OPCODE.CFI_NEGATE_RA_STATE); + cfiWriter.Write((short)-1); + cfiWriter.Write(cfaOffset); } if (cfaRegister != -1) From 333b5b2a3b9c71802e178191c67eb3a5dca080c8 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Mon, 7 Jul 2025 12:33:13 +0100 Subject: [PATCH 55/57] Fix SIGILL in NativeAOT during unwinding --- .../llvm-libunwind/src/DwarfInstructions.hpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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 From cecc9f4507615fca2e2da232f914bc61c2706396 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Mon, 7 Jul 2025 12:35:59 +0100 Subject: [PATCH 56/57] Fix segfaults in libraries tests --- src/coreclr/inc/gcinfodecoder.h | 6 +++++- src/coreclr/vm/arm64/cgencpu.h | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/coreclr/inc/gcinfodecoder.h b/src/coreclr/inc/gcinfodecoder.h index 87693d89d25c9f..8dc4de9b749f25 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/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index 769c7ab31d702b..22e30e957e83c3 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -44,6 +44,9 @@ EXTERN_C void getFPReturn(int fpSize, INT64 *pRetVal); EXTERN_C void setFPReturn(int fpSize, INT64 retVal); +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 class ComCallMethodDesc; @@ -218,7 +221,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) PacStripPtr((void *)context->Pc); } inline void SetIP(T_CONTEXT *context, PCODE eip) { From 34401fb08d91f230ca12b3ff311cdf67fd54c858 Mon Sep 17 00:00:00 2001 From: Swapnil Gaikwad Date: Mon, 7 Jul 2025 15:11:19 +0100 Subject: [PATCH 57/57] Restore JIT's stack unwinding that was lost while restoring unwinder.cpp --- src/coreclr/unwinder/arm64/unwinder.cpp | 73 ++++++++++++++++++++++--- src/coreclr/vm/arm64/cgencpu.h | 6 +- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index ed4238c98a6bf6..8cda3bd6995483 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -33,6 +33,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 @@ -265,16 +269,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 @@ -2357,7 +2416,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/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index 22e30e957e83c3..267a040cb9d999 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -44,10 +44,6 @@ EXTERN_C void getFPReturn(int fpSize, INT64 *pRetVal); EXTERN_C void setFPReturn(int fpSize, INT64 retVal); -#if defined(TARGET_ARM64) -extern "C" void* PacStripPtr(void* ptr); -#endif // TARGET_ARM64 - class ComCallMethodDesc; extern PCODE GetPreStubEntryPoint(); @@ -222,7 +218,7 @@ typedef struct _PROFILE_PLATFORM_SPECIFIC_DATA inline PCODE GetIP(const T_CONTEXT * context) { LIMITED_METHOD_DAC_CONTRACT; //TODO-PAC: Strip/Authenticate while populating the context. - return (PCODE) PacStripPtr((void *)context->Pc); + return (PCODE) context->Pc; } inline void SetIP(T_CONTEXT *context, PCODE eip) {