[llvm] [AArch64] Codegen for AArch64 Return Address Signing Hardening (PR #176187)

Anatoly Trosinenko via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 12 06:44:42 PDT 2026


================
@@ -548,6 +564,102 @@ bool AArch64PointerAuthImpl::run(MachineFunction &MF) {
     Modified = true;
   }
 
+  Modified |= emitSignReturnAddressHardening(MF);
+
+  return Modified;
+}
+
+bool AArch64PointerAuthImpl::emitSignReturnAddressHardening(
+    MachineFunction &MF) {
+  const auto *FI = MF.getInfo<AArch64FunctionInfo>();
+  assert(FI && "FI can't be null");
+  if (!FI->shouldSignReturnAddress(MF) || !FI->shouldHardenSignReturnAddress())
+    return false;
+  assert(Subtarget && "Subtarget must be initialized");
+
+  RegScavenger RS;
+  bool Modified = false;
+  for (MachineBasicBlock &MBB : MF) {
+    MachineBasicBlock::iterator RetInstIter = MBB.getFirstTerminator();
+
+    if (RetInstIter == MBB.end() || RetInstIter->getOpcode() != AArch64::RET)
+      continue;
+
+    assert(RetInstIter->getOperand(0).getReg() == AArch64::LR &&
+           "Return instruction must be returning via LR");
+
+    MachineBasicBlock::iterator InsertionPoint = RetInstIter;
+    // In the case of Windows SEH, the hardening sequence does not immediately
+    // precede the return instruction. Instead, it precedes the SEH_EpilogEnd
+    // pseudo-instruction, which itself is expected to be the predecessor of
+    // the return. Plus, each instruction in the sequence needs one SEH_Nop.
+    const bool NeedsWinCFI = MF.hasWinCFI();
+    if (NeedsWinCFI) {
+      --InsertionPoint;
+      assert(InsertionPoint->getOpcode() == AArch64::SEH_EpilogEnd);
+    }
+    DebugLoc DL = InsertionPoint->getDebugLoc();
+    const auto EmitSEHNopIfRequired = [&]() {
+      if (NeedsWinCFI)
+        BuildMI(MBB, InsertionPoint, DL, TII->get(AArch64::SEH_Nop))
+            .setMIFlag(MachineInstr::FrameDestroy);
+    };
+
+    RS.enterBasicBlockEnd(MBB);
+    Register XReg = RS.scavengeRegisterBackwards(
+        AArch64::GPR64RegClass, InsertionPoint,
+        /*RestoreAfter=*/false, /*SPAdj=*/0, /*AllowSpill=*/false);
+    if (XReg == AArch64::NoRegister) {
+      // Couldn't find a free register to use for the hardening. Skip.
+      MF.getContext().reportWarning(
+          SMLoc(), "harden-pac-ret failed for function " + MF.getName());
+      continue;
+    }
+
+    // Register copies are done using ORRXrs directly instead of using the
+    // pseudo-instruction COPY because this function can be called after
+    // pseudo-instruction expansion takes place, for example via the machine
+    // outliner pass.
+    emitMOVWithFrameDestroy(MBB, InsertionPoint, DL, TII, XReg, AArch64::LR);
+    EmitSEHNopIfRequired();
+
+    // The XPACI instruction is only available with FEAT_PAUTH. So if the
+    // subtarget does not have it, the alternative XPACLRI instruction must be
+    // used instead. The latter is in hint space, therefore can be present even
----------------
atrosinenko wrote:

[nit] "therefore can be **used**" probably sounds better.

https://github.com/llvm/llvm-project/pull/176187


More information about the llvm-commits mailing list