This commit is contained in:
SwapnilGaikwad 2025-07-30 15:56:28 +02:00 committed by GitHub
commit d2c3d400aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 596 additions and 21 deletions

View File

@ -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)
@ -5821,6 +5825,11 @@ static bool IsTailCall(const BYTE * ip, ControllerStackInfo* info, TailCallFunct
TailCallTls* tls = GetThread()->GetTailCallTls();
LPVOID tailCallAwareRetAddr = tls->GetFrame()->TailCallAwareReturnAddress;
#if defined(TARGET_ARM64)
retAddr = PacStripPtr(retAddr);
tailCallAwareRetAddr = PacStripPtr(tailCallAwareRetAddr);
#endif // TARGET_ARM64
LOG((LF_CORDB,LL_INFO1000, "ITCTR: ret addr is %p, tailcall aware ret addr is %p\n",
retAddr, tailCallAwareRetAddr));

View File

@ -9,7 +9,9 @@ enum CFI_OPCODE
{
CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one.
CFI_DEF_CFA_REGISTER, // New register is used to compute CFA
CFI_REL_OFFSET // Register is saved at offset from the current CFA
CFI_REL_OFFSET, // Register is saved at offset from the current CFA
CFI_DEF_CFA, // Take address from register and add offset to it
CFI_NEGATE_RA_STATE, // Sign the return address in lr with paciaz
};
struct CFI_CODE

View File

@ -703,6 +703,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Rcpc, W("EnableArm64Rc
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Rcpc2, W("EnableArm64Rcpc2"), 1, "Allows Arm64 Rcpc2+ hardware intrinsics to be disabled")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Sve, W("EnableArm64Sve"), 1, "Allows Arm64 SVE hardware intrinsics to be disabled")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Sve2, W("EnableArm64Sve2"), 1, "Allows Arm64 SVE2 hardware intrinsics to be disabled")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitPacEnabled, W("JitPacEnabled"), 1, "Allows Arm64 Pointer Authentication (PAC) to be disabled")
#elif defined(TARGET_RISCV64)
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zba, W("EnableRiscV64Zba"), 1, "Allows RiscV64 Zba hardware intrinsics to be disabled")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbb, W("EnableRiscV64Zbb"), 1, "Allows RiscV64 Zbb hardware intrinsics to be disabled")

View File

@ -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)

View File

@ -271,6 +271,8 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog)
GetEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, spAdjust);
compiler->unwindAllocStack(spAdjust);
}
GetEmitter()->emitPacInEpilog();
}
//------------------------------------------------------------------------
@ -1342,6 +1344,7 @@ void CodeGen::genFuncletProlog(BasicBlock* block)
gcInfo.gcResetForBB();
compiler->unwindBegProlog();
GetEmitter()->emitPacInProlog();
regMaskTP maskSaveRegsFloat = genFuncletInfo.fiSaveRegs & RBM_ALLFLOAT;
regMaskTP maskSaveRegsInt = genFuncletInfo.fiSaveRegs & ~maskSaveRegsFloat;
@ -1628,6 +1631,8 @@ void CodeGen::genFuncletEpilog()
}
}
GetEmitter()->emitPacInEpilog();
inst_RV(INS_ret, REG_LR, TYP_I_IMPL);
compiler->unwindReturn(REG_LR);

View File

@ -4464,6 +4464,9 @@ void CodeGen::genPushCalleeSavedRegisters()
}
#endif // DEBUG
// Sign LR as part of Pointer Authentication (PAC) support
GetEmitter()->emitPacInProlog();
// The frameType number is arbitrary, is defined below, and corresponds to one of the frame styles we
// generate based on various sizes.
int frameType = 0;

View File

@ -8766,6 +8766,7 @@ public:
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)

View File

@ -3252,6 +3252,11 @@ private:
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);

View File

@ -1397,6 +1397,32 @@ static const char * const bRegNames[] =
// clang-format on
//------------------------------------------------------------------------
// emitPacInProlog: Sign LR as part of Pointer Authentication (PAC) support
//
void emitter::emitPacInProlog()
{
if (JitConfig.JitPacEnabled() == 0)
{
return;
}
emitIns(INS_paciaz);
emitComp->unwindPacSignLR();
}
//------------------------------------------------------------------------
// emitPacInEpilog: unsign LR as part of Pointer Authentication (PAC) support
//
void emitter::emitPacInEpilog()
{
if (JitConfig.JitPacEnabled() == 0)
{
return;
}
emitIns(INS_autiaz);
emitComp->unwindPacSignLR();
}
//------------------------------------------------------------------------
// emitRegName: Returns a general-purpose register name or SIMD and floating-point scalar register name.
//

View File

@ -125,6 +125,7 @@ CONFIG_STRING(JitInlineMethodsWithEHRange, "JitInlineMethodsWithEHRange")
CONFIG_INTEGER(JitLongAddress, "JitLongAddress", 0) // Force using the large pseudo instruction form for long address
CONFIG_INTEGER(JitMaxUncheckedOffset, "JitMaxUncheckedOffset", 8)
RELEASE_CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 1)
//
// MinOpts

View File

@ -413,6 +413,11 @@ void Compiler::DumpCfiInfo(bool isHotCode,
assert(dwarfReg == DWARF_REG_ILLEGAL);
printf(" CodeOffset: 0x%02X Op: AdjustCfaOffset Offset:0x%X\n", codeOffset, offset);
break;
case CFI_NEGATE_RA_STATE:
assert(dwarfReg == DWARF_REG_ILLEGAL);
assert(offset == 0);
printf(" CodeOffset: 0x%02X Op: NegateRAState\n", codeOffset);
break;
default:
printf(" Unrecognized CFI_CODE: 0x%llX\n", *(UINT64*)pCode);
break;

View File

@ -635,6 +635,33 @@ void Compiler::unwindSaveNext()
pu->AddCode(0xE6);
}
void Compiler::unwindPacSignLR()
{
if (JitConfig.JitPacEnabled() == 0)
{
return;
}
#if defined(FEATURE_CFI_SUPPORT)
if (generateCFIUnwindCodes())
{
FuncInfoDsc* func = funCurrentFunc();
UNATIVE_OFFSET cbProlog = 0;
if (compGeneratingProlog)
{
cbProlog = unwindGetCurrentOffset(func);
}
// Maps to DW_CFA_AARCH64_negate_ra_state
createCfiCode(func, cbProlog, CFI_NEGATE_RA_STATE, DWARF_REG_ILLEGAL);
return;
}
#endif // FEATURE_CFI_SUPPORT
// pac_sign_lr: 11111100: sign the return address in lr with paciaz
funCurrentFunc()->uwi.AddCode(0xFC);
}
void Compiler::unwindReturn(regNumber reg)
{
// Nothing to do; we will always have at least one trailing "end" opcode in our padding.
@ -1081,6 +1108,12 @@ void DumpUnwindInfo(Compiler* comp,
printf(" %02X save_next\n", b1);
}
else if (b1 == 0xFC)
{
// pac_sign_lr: 11111100 : sign the return address in lr with paciaz.
printf(" %02X pac_sign_lr\n", b1);
}
else
{
// Unknown / reserved unwind code

View File

@ -161,6 +161,11 @@ public:
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

View File

@ -65,6 +65,10 @@ EXTERN_C CODE_LOCATION RhpRethrow2;
#define FAILFAST_OR_DAC_FAIL_UNCONDITIONALLY(msg) { ASSERT_UNCONDITIONALLY(msg); RhFailFast(); }
#endif
#if defined(TARGET_ARM64)
extern "C" void* PacStripPtr(void* ptr);
#endif // TARGET_ARM64
StackFrameIterator::StackFrameIterator(Thread * pThreadToWalk, PInvokeTransitionFrame* pInitialTransitionFrame)
{
STRESS_LOG0(LF_STACKWALK, LL_INFO10000, "----Init---- [ GC ]\n");
@ -1786,7 +1790,11 @@ UnwindOutOfCurrentManagedFrame:
// if the thread is safe to walk, it better not have a hijack in place.
ASSERT(!m_pThread->IsHijacked());
#if defined(TARGET_ARM64)
SetControlPC(PacStripPtr(dac_cast<PTR_VOID>(PCODEToPINSTR(m_RegDisplay.GetIP()))));
#else
SetControlPC(dac_cast<PTR_VOID>(PCODEToPINSTR(m_RegDisplay.GetIP())));
#endif // TARGET_ARM64
PTR_VOID collapsingTargetFrame = NULL;
@ -2117,6 +2125,10 @@ void StackFrameIterator::CalculateCurrentMethodState()
return;
}
#if defined(TARGET_ARM64)
m_ControlPC = PacStripPtr(m_ControlPC);
#endif // TARGET_ARM64
// Assume that the caller is likely to be in the same module
if (m_pCodeManager == NULL || !m_pCodeManager->FindMethodInfo(m_ControlPC, &m_methodInfo))
{

View File

@ -107,6 +107,7 @@
// Fix the stack by restoring the original return address
//
ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress]
xpaclri
//
// Clear hijack state

View File

@ -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

View File

@ -3,3 +3,29 @@
#include <unixasmmacros.inc>
#include "AsmOffsets.inc"
// void* PacStripPtr(void *);
// This function strips the pointer of PAC info that is passed as an agrument.
// To avoid failing on non-PAC enabled machines, we use xpaclri (instead of xpaci) which strips lr explicitly.
// Thus we move need to move input in lr, strip it and copy it back to the result register.
.arch_extension pauth
LEAF_ENTRY PacStripPtr, _TEXT
mov x9, lr
mov lr, x0
xpaclri
mov x0, lr
ret x9
LEAF_END PacStripPtr, _TEXT
// void* PacSignPtr(void *);
// This function sign the input pointer using zero as salt.
// To avoid failing on non-PAC enabled machines, we use paciaz (instead of paciza) which signs lr explicitly.
// Thus we need to move input in lr, sign it and then copy it back to the result register.
.arch_extension pauth
LEAF_ENTRY PacSignPtr, _TEXT
mov x9, lr
mov lr, x0
paciaz
mov x0, lr
ret x9
LEAF_END PacSignPtr, _TEXT

View File

@ -5,4 +5,28 @@
TEXTAREA
; void* PacStripPtr(void *);
; This function strips the pointer of PAC info that is passed as an agrument.
; To avoid failing on non-PAC enabled machines, we use xpaclri (instead of xpaci) which strips lr explicitly.
; Thus we move need to move input in lr, strip it and copy it back to the result register.
LEAF_ENTRY PacStripPtr
mov x9, lr
mov lr, x0
DCD 0xD50320FF ; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers
mov x0, lr
ret x9
LEAF_END PacStripPtr
; void* PacSignPtr(void *);
; This function sign the input pointer using zero as salt.
; To avoid failing on non-PAC enabled machines, we use paciaz (instead of paciza) which signs lr explicitly.
; Thus we need to move input in lr, sign it and then copy it back to the result register.
LEAF_ENTRY PacSignPtr
mov x9, lr
mov lr, x0
DCD 0xD503231F ; paciaz instruction in binary to avoid error while compiling with non-PAC enabled compilers
mov x0, lr
ret x9
LEAF_END PacSignPtr
end

View File

@ -36,6 +36,11 @@ static Thread* g_RuntimeInitializingThread;
#endif //!DACCESS_COMPILE
#if defined(TARGET_ARM64)
extern "C" void* PacSignPtr(void* ptr);
extern "C" void* PacStripPtr(void* ptr);
#endif // TARGET_ARM64
ee_alloc_context::PerThreadRandom::PerThreadRandom()
{
minipal_xoshiro128pp_init(&random_state, (uint32_t)minipal_lowres_ticks());
@ -809,8 +814,14 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack
CrossThreadUnhijack();
void* pvRetAddr = *ppvRetAddrLocation;
ASSERT(pvRetAddr != NULL);
#if defined(TARGET_ARM64)
ASSERT(StackFrameIterator::IsValidReturnAddress(PacStripPtr(pvRetAddr)));
#else
ASSERT(StackFrameIterator::IsValidReturnAddress(pvRetAddr));
#endif // TARGET_ARM64
m_ppvHijackedReturnAddressLocation = ppvRetAddrLocation;
m_pvHijackedReturnAddress = pvRetAddr;
@ -820,7 +831,14 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack
frameIterator->GetRegisterSet()));
#endif
*ppvRetAddrLocation = (void*)pfnHijackFunction;
void* pvHijackedAddr = (void*)pfnHijackFunction;
#if defined(TARGET_ARM64)
if (frameIterator->GetCodeManager()->IsPacPresent(frameIterator->GetMethodInfo(), frameIterator->GetRegisterSet()))
{
pvHijackedAddr = PacSignPtr(pvHijackedAddr);
}
#endif // TARGET_ARM64
*ppvRetAddrLocation = pvHijackedAddr;
STRESS_LOG2(LF_STACKWALK, LL_INFO10000, "InternalHijack: TgtThread = %llx, IP = %p\n",
GetPalThreadIdForLogging(), frameIterator->GetRegisterSet()->GetIP());
@ -948,6 +966,7 @@ void Thread::UnhijackWorker()
// Restore the original return address.
ASSERT(m_ppvHijackedReturnAddressLocation != NULL);
*m_ppvHijackedReturnAddressLocation = m_pvHijackedReturnAddress;
// Clear the hijack state.

View File

@ -64,6 +64,84 @@ UnixNativeCodeManager::~UnixNativeCodeManager()
{
}
#if defined(TARGET_ARM64)
static size_t readULEB(const uint8_t *&p, const uint8_t *end)
{
size_t result = 0;
unsigned shift = 0;
while (p < end) {
uint8_t byte = *p++;
result |= size_t(byte & 0x7F) << shift;
if ((byte & 0x80) == 0) // clear top bit indicates the last by of the value
break;
shift += 7;
}
return result;
}
bool UnixNativeCodeManager::IsPacPresent(MethodInfo * pMethodInfo,
REGDISPLAY * pRegisterSet)
{
UnixNativeMethodInfo* pNativeMethodInfo = (UnixNativeMethodInfo*)pMethodInfo;
const uint8_t *p = (uint8_t *) pNativeMethodInfo->unwind_info;
const uint8_t *end = p + *((uint32_t *)p);
p += 4; // Skip length
assert(*((uint32_t *)p) != 0); // Ensure it's FDE entry
p += 4; // Skip offset to CIE
p += 4; // Skip PC start
p += 4; // Skip function length
size_t augmentationLength = readULEB(p, end);
p += augmentationLength; // skip augmentation data
while (p < end) {
uint8_t op = *p++;
if (op == DW_CFA_AARCH64_negate_ra_state)
{
return true;
}
if ((op & 0xC0) == DW_CFA_advance_loc)
{
continue;
}
if ((op & ~(0x3F)) == DW_CFA_offset)
{
readULEB(p, end); // offset
continue;
}
// Extended, singlebyte 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)
{

View File

@ -73,6 +73,10 @@ public:
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);

View File

@ -831,6 +831,55 @@ bool CoffNativeCodeManager::IsUnwindable(PTR_VOID pvAddress)
return true;
}
#if defined(TARGET_ARM64)
bool CoffNativeCodeManager::IsPacPresent(MethodInfo * pMethodInfo,
REGDISPLAY * pRegisterSet)
{
CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;
size_t unwindDataBlobSize;
PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize);
PTR_uint8_t UnwindCodePtr = dac_cast<PTR_uint8_t>(pUnwindDataBlob);
PTR_uint8_t UnwindCodesEndPtr = dac_cast<PTR_uint8_t>(pUnwindDataBlob) + unwindDataBlobSize;
while (UnwindCodePtr < UnwindCodesEndPtr)
{
uint8_t CurCode = * UnwindCodePtr;
if ((CurCode & 0xfe) == 0xe4) // The last unwind code
{
break;
}
if (CurCode == 0xFC) // Unwind code for PAC (pac_sign_lr)
{
return true;
}
if (CurCode < 0xC0)
{
UnwindCodePtr += 1;
}
else if (CurCode < 0xE0)
{
UnwindCodePtr += 2;
}
else
{
static const BYTE UnwindCodeSizeTable[32] =
{
4,1,2,1,1,1,1,3, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 2,3,4,5,1,1,1,1
};
UnwindCodePtr += UnwindCodeSizeTable[CurCode - 0xE0];
}
}
return false;
}
#endif //TARGET_ARM64
bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo,
REGDISPLAY * pRegisterSet, // in
PTR_PTR_VOID * ppvRetAddrLocation) // out

View File

@ -90,6 +90,8 @@ public:
bool IsUnwindable(PTR_VOID pvAddress);
bool IsPacPresent(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet);
bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo,
REGDISPLAY * pRegisterSet, // in
PTR_PTR_VOID * ppvRetAddrLocation); // out

View File

@ -11,6 +11,7 @@ namespace ILCompiler.ObjectWriter
CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one.
CFI_DEF_CFA_REGISTER, // New register is used to compute CFA
CFI_REL_OFFSET, // Register is saved at offset from the current CFA
CFI_DEF_CFA // Take address from register and add offset to it.
CFI_DEF_CFA, // Take address from register and add offset to it.
CFI_NEGATE_RA_STATE, // Sign the return address in lr with paciaz
}
}

View File

@ -51,6 +51,7 @@ namespace ILCompiler.ObjectWriter
{
codeOffset = Math.Max(codeOffset, blobData[offset++]);
CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset++];
short dwarfReg = BitConverter.ToInt16(blobData, offset);
offset += sizeof(short);
int cfiOffset = BitConverter.ToInt32(blobData, offset);
@ -113,6 +114,10 @@ namespace ILCompiler.ObjectWriter
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;
}
}

View File

@ -121,6 +121,10 @@ namespace ILCompiler.ObjectWriter
EmitSpAdjustment(cfiOffset);
}
break;
case CFI_OPCODE.CFI_NEGATE_RA_STATE:
// Do nothing here.
break;
}
}

View File

@ -790,6 +790,13 @@ namespace ILCompiler.ObjectWriter
{
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));

View File

@ -89,7 +89,8 @@ namespace Internal.JitInterface
CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one.
CFI_DEF_CFA_REGISTER, // New register is used to compute CFA
CFI_REL_OFFSET, // Register is saved at offset from the current CFA
CFI_DEF_CFA // Take address from register and add offset to it.
CFI_DEF_CFA, // Take address from register and add offset to it.
CFI_NEGATE_RA_STATE, // Sign the return address in lr with paciaz
}
// Get the CFI data in the same shape as clang/LLVM generated one. This improves the compatibility with libunwind and other unwind solutions
@ -120,6 +121,7 @@ namespace Internal.JitInterface
}
int offset = 0;
bool shouldAddPACOpCode = false;
while (offset < blobData.Length)
{
codeOffset = Math.Max(codeOffset, blobData[offset++]);
@ -173,6 +175,10 @@ namespace Internal.JitInterface
}
}
break;
case CFI_OPCODE.CFI_NEGATE_RA_STATE:
shouldAddPACOpCode = true;
break;
}
}
@ -182,6 +188,14 @@ namespace Internal.JitInterface
using (BinaryWriter cfiWriter = new BinaryWriter(cfiStream))
{
if (shouldAddPACOpCode)
{
cfiWriter.Write((byte)codeOffset);
cfiWriter.Write((byte)CFI_OPCODE.CFI_NEGATE_RA_STATE);
cfiWriter.Write((short)-1);
cfiWriter.Write(cfaOffset);
}
if (cfaRegister != -1)
{
cfiWriter.Write((byte)codeOffset);

View File

@ -31,6 +31,10 @@
#define FIELD_OFFSET(type, field) ((LONG)__builtin_offsetof(type, field))
#endif
#if !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER)
extern "C" void* PacStripPtr(void* ptr);
#endif // !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER)
#ifdef HOST_UNIX
#define RtlZeroMemory ZeroMemory
@ -251,16 +255,71 @@ do {
#endif // !defined(DEBUGGER_UNWIND)
//
// Macros for stripping pointer authentication (PAC) bits.
//
#if !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER)
#if !defined(DEBUGGER_STRIP_PAC)
#define STRIP_PAC(pointer) RtlStripPacOnline(pointer)
// NOTE: Pointer authentication is not used by .NET, so the implementation does nothing
#define STRIP_PAC(Params, pointer)
FORCEINLINE
VOID RtlStripPacOnline(_Inout_ PULONG64 Pointer)
#endif
/*++
Routine Description:
This routine strips the ARM64 Pointer Authentication Code (PAC) from a
pointer using the ARM64-native xpaci intrinsic directly. Hence this should
only be called when stripping a pointer at runtime (not debugger)
Arguments:
Pointer - Supplies a pointer to the pointer whose PAC will be stripped.
Return Value:
None.
--*/
{
*Pointer = (ULONG64)PacStripPtr((void *) (*Pointer));
}
#else
#define STRIP_PAC(pointer) RtlStripPacManual(pointer)
FORCEINLINE
VOID
RtlStripPacManual(
_Inout_ PULONG64 Pointer
)
/*++
Routine Description:
This routine manually strips the ARM64 Pointer Authentication Code (PAC)
from a pointer. This is functionally similar to the XPAC family of
instructions.
N.B. Even though PAC is only supported on ARM64, this routine is available
on all architectures to conveniently enable scenarios such as the
Debugger.
Arguments:
Pointer - Supplies a pointer to the pointer whose PAC will be stripped.
Return Value:
None.
--*/
{
*Pointer &= 0x0000FFFFFFFFFFFF;
return;
}
#endif // !defined(DACCESS_COMPILE) && !defined(FEATURE_CDAC_UNWINDER)
//
// Macros to clarify opcode parsing
@ -2343,7 +2402,7 @@ ExecuteCodes:
return STATUS_UNWIND_INVALID_SEQUENCE;
}
STRIP_PAC(UnwindParams, &ContextRecord->Lr);
STRIP_PAC(&ContextRecord->Lr);
//
// TODO: Implement support for UnwindFlags RTL_VIRTUAL_UNWIND2_VALIDATE_PAC.

View File

@ -161,11 +161,38 @@ NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler
EPILOG_RESTORE_REG_PAIR x25, x26, 64
EPILOG_RESTORE_REG_PAIR x27, x28, 80
EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 192
xpaclri
EPILOG_RETURN
NESTED_END OnHijackTripThread, _TEXT
#endif // FEATURE_HIJACK
// void* PacStripPtr(void *);
// This function strips the pointer of PAC info that is passed as an agrument.
// To avoid failing on non-PAC enabled machines, we use xpaclri (instead of xpaci) which strips lr explicitly.
// Thus we move need to move input in lr, strip it and copy it back to the result register.
.arch_extension pauth
LEAF_ENTRY PacStripPtr, _TEXT
mov x9, lr
mov lr, x0
xpaclri
mov x0, lr
ret x9
LEAF_END PacStripPtr, _TEXT
// void* PacSignPtr(void *);
// This function sign the input pointer using zero as salt.
// To avoid failing on non-PAC enabled machines, we use paciaz (instead of paciza) which signs lr explicitly.
// Thus we need to move input in lr, sign it and then copy it back to the result register.
.arch_extension pauth
LEAF_ENTRY PacSignPtr, _TEXT
mov x9, lr
mov lr, x0
paciaz
mov x0, lr
ret x9
LEAF_END PacSignPtr, _TEXT
// ------------------------------------------------------------------
// Redirection Stub for GC in fully interruptible method
//GenerateRedirectedHandledJITCaseStub GCThreadControl

View File

@ -497,11 +497,36 @@ COMToCLRDispatchHelper_RegSetup
EPILOG_RESTORE_REG_PAIR x25, x26, #64
EPILOG_RESTORE_REG_PAIR x27, x28, #80
EPILOG_RESTORE_REG_PAIR fp, lr, #192!
DCD 0xD50320FF ; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers
EPILOG_RETURN
NESTED_END
#endif ; FEATURE_HIJACK
; void* PacStripPtr(void *);
; This function strips the pointer of PAC info that is passed as an agrument.
; To avoid failing on non-PAC enabled machines, we use xpaclri (instead of xpaci) which strips lr explicitly.
; Thus we move need to move input in lr, strip it and copy it back to the result register.
LEAF_ENTRY PacStripPtr
mov x9, lr
mov lr, x0
DCD 0xD50320FF ; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers
mov x0, lr
ret x9
LEAF_END PacStripPtr
; void* PacSignPtr(void *);
; This function sign the input pointer using zero as salt.
; To avoid failing on non-PAC enabled machines, we use paciaz (instead of paciza) which signs lr explicitly.
; Thus we need to move input in lr, sign it and then copy it back to the result register.
LEAF_ENTRY PacSignPtr
mov x9, lr
mov lr, x0
DCD 0xD503231F ; paciaz instruction in binary to avoid error while compiling with non-PAC enabled compilers
mov x0, lr
ret x9
LEAF_END PacSignPtr
;; ------------------------------------------------------------------
;; Redirection Stub for GC in fully interruptible method
GenerateRedirectedHandledJITCaseStub GCThreadControl

View File

@ -44,7 +44,6 @@
EXTERN_C void getFPReturn(int fpSize, INT64 *pRetVal);
EXTERN_C void setFPReturn(int fpSize, INT64 retVal);
class ComCallMethodDesc;
extern PCODE GetPreStubEntryPoint();
@ -218,7 +217,8 @@ typedef struct _PROFILE_PLATFORM_SPECIFIC_DATA
inline PCODE GetIP(const T_CONTEXT * context) {
LIMITED_METHOD_DAC_CONTRACT;
return context->Pc;
//TODO-PAC: Strip/Authenticate while populating the context.
return (PCODE) context->Pc;
}
inline void SetIP(T_CONTEXT *context, PCODE eip) {

View File

@ -6915,6 +6915,82 @@ bool IsIPInEpilog(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, BOOL *pSaf
return fIsInEpilog;
}
#if defined(TARGET_ARM64)
// This function is used to check if Pointer Authentication (PAC) is enabled for this stack frame or not.
bool IsPacPresent(EECodeInfo *pCodeInfo)
{
_ASSERTE(pCodeInfo->IsValid());
// Lookup the function entry for the IP
PTR_RUNTIME_FUNCTION FunctionEntry = pCodeInfo->GetFunctionEntry();
// We should always get a function entry for a managed method
_ASSERTE(FunctionEntry != NULL);
DWORD_PTR ImageBase = pCodeInfo->GetModuleBase();
_ASSERTE((FunctionEntry->UnwindData & 3) == 0); // Packed unwind data are not used with managed code
ULONG_PTR UnwindDataPtr = (ULONG_PTR)(ImageBase + FunctionEntry->UnwindData);
// Read the header word. For unwind info layout details refer https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#arm64-exception-handling-information
DWORD HeaderWord = *(DWORD*)UnwindDataPtr;
UnwindDataPtr += 4;
_ASSERTE(((HeaderWord >> 18) & 3) == 0); // Version 0 is the only supported version.
ULONG UnwindWords = (HeaderWord >> 27) & 31;
ULONG EpilogScopeCount = (HeaderWord >> 22) & 31;
if (EpilogScopeCount == 0 && UnwindWords == 0)
{
EpilogScopeCount = *(DWORD*)UnwindDataPtr;
UnwindDataPtr += 4;
UnwindWords = (EpilogScopeCount >> 16) & 0xff;
EpilogScopeCount &= 0xffff;
}
if ((HeaderWord & (1 << 21)) != 0)
{
EpilogScopeCount = 0;
}
ULONG_PTR UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount;
ULONG_PTR UnwindCodesEndPtr = UnwindCodePtr + 4 * UnwindWords;
while (UnwindCodePtr < UnwindCodesEndPtr)
{
ULONG CurCode = *(BYTE*)UnwindCodePtr;
if ((CurCode & 0xfe) == 0xe4) // The last unwind code
{
break;
}
if (CurCode == 0xFC) // Unwind code for PAC (pac_sign_lr)
{
return true;
}
if (CurCode < 0xC0)
{
UnwindCodePtr += 1;
}
else if (CurCode < 0xE0)
{
UnwindCodePtr += 2;
}
else
{
static const BYTE UnwindCodeSizeTable[32] =
{
4,1,2,1,1,1,1,3, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 2,3,4,5,1,1,1,1
};
UnwindCodePtr += UnwindCodeSizeTable[CurCode - 0xE0];
}
}
return false;
}
#endif // TARGET_ARM64
#endif // FEATURE_HIJACK && (!TARGET_X86 || TARGET_UNIX)
#define EXCEPTION_VISUALCPP_DEBUGGER ((DWORD) (1<<30 | 0x6D<<16 | 5000))

View File

@ -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

View File

@ -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)
{
@ -25,7 +28,13 @@ FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr
Thread* thread = GetThread();
*retAddr = thread->GetReturnAddress(retAddrSlot);
void* retAddrFromSlot = thread->GetReturnAddress(retAddrSlot);
#if defined(TARGET_ARM64)
retAddrFromSlot = PacStripPtr(retAddrFromSlot);
#endif // TARGET_ARM64
*retAddr = retAddrFromSlot;
return thread->GetTailCallTls();
}
FCIMPLEND

View File

@ -2621,7 +2621,7 @@ public:
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

View File

@ -22,6 +22,10 @@
#define HIJACK_NONINTERRUPTIBLE_THREADS
#if defined(TARGET_ARM64)
extern "C" void* PacSignPtr(void* ptr);
#endif // TARGET_ARM64
bool ThreadSuspend::s_fSuspendRuntimeInProgress = false;
bool ThreadSuspend::s_fSuspended = false;
@ -4532,7 +4536,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;
@ -4600,6 +4604,13 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86
m_HijackedFunction = esb->m_pFD;
// Bash the stack to return to one of our stubs
#if defined(TARGET_ARM64)
if (isPacEnabledFrame)
{
pvHijackAddr = PacSignPtr(pvHijackAddr);
}
#endif // TARGET_ARM64
*esb->m_ppvRetAddrPtr = pvHijackAddr;
SetThreadState(TS_Hijacked);
}
@ -5309,9 +5320,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));
}
}
}
@ -5863,7 +5878,11 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext)
StackWalkerWalkingThreadHolder threadStackWalking(pThread);
// Hijack the return address to point to the appropriate routine based on the method's return type.
pThread->HijackThread(&executionState X86_ARG(returnKind) X86_ARG(hasAsyncRet));
ARM64_ONLY(bool isPacEnabledFrame);
#ifdef TARGET_ARM64
isPacEnabledFrame = IsPacPresent(&codeInfo);
#endif
pThread->HijackThread(&executionState X86_ARG(returnKind) X86_ARG(hasAsyncRet) ARM64_ARG(isPacEnabledFrame));
}
}

View File

@ -354,7 +354,18 @@ int DwarfInstructions<A, R>::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