[clang] [llvm] [Clang][AArch64] Command-line options for A-profile's Sign Return Address Hardening (PR #176171)

Victor Campos via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 7 11:33:08 PDT 2026


https://github.com/vhscampos updated https://github.com/llvm/llvm-project/pull/176171

>From 4f775ae42fbc2729697b32ed5b5310a86f9cbfc4 Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Tue, 13 Jan 2026 20:39:02 +0000
Subject: [PATCH 1/9] [Clang][AArch64] Command-line options for A-profile's
 Sign Return Address Hardening

This patch introduces a new command-line option to enable the AArch64
A-profile's Sign Return Address Hardening. It also introduces a new
function attribute with the same naming as the new command-line option.

At the time of this patch, this new option enables the hardening against
the PACMAN attack [1] using a load of the return address [2].

The new option, -mharden-pac-ret, can take one of two values:

 - none: disable hardening. (The default if the option is absent)
 - load-return-address: enables hardening using the mitigation based on
   load of return address.

The corresponding function attribute takes the option and its possible
values using the same naming.

1: https://pacmanattack.com
2: https://developer.arm.com/documentation/101754/0624/armclang-Reference/armclang-Command-line-Options/-mharden-pac-ret
---
 .../clang/Basic/DiagnosticDriverKinds.td      |  2 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  5 +++
 clang/include/clang/Basic/LangOptions.def     |  2 +
 clang/include/clang/Basic/LangOptions.h       | 12 ++++++
 clang/include/clang/Basic/TargetInfo.h        | 22 ++++++++++
 clang/include/clang/Options/Options.td        |  9 ++++
 clang/lib/Basic/Targets/AArch64.cpp           | 16 +++++++
 clang/lib/Basic/Targets/AArch64.h             |  2 +
 clang/lib/CodeGen/TargetInfo.cpp              |  4 ++
 clang/lib/CodeGen/Targets/AArch64.cpp         |  3 ++
 clang/lib/Driver/ToolChains/Clang.cpp         | 22 +++++++++-
 clang/lib/Sema/SemaDeclAttr.cpp               | 18 +++++++-
 .../aarch64-sign-return-address-harden.c      | 13 ++++++
 clang/test/Driver/aarch64-security-options.c  | 30 +++++++++++++
 clang/test/Driver/arm-security-options.c      | 19 ++++++++
 .../Frontend/aarch64-harden-pac-ret-err.c     |  3 ++
 .../aarch64-harden-pac-ret-attr-err-warn.c    | 21 +++++++++
 clang/test/Sema/aarch64-harden-pac-ret-attr.c | 43 +++++++++++++++++++
 18 files changed, 244 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGen/aarch64-sign-return-address-harden.c
 create mode 100644 clang/test/Frontend/aarch64-harden-pac-ret-err.c
 create mode 100644 clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
 create mode 100644 clang/test/Sema/aarch64-harden-pac-ret-attr.c

diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index db0f521b73544..d16fbd45f104e 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -544,6 +544,8 @@ def warn_ignoring_verify_debuginfo_preserve_export : Warning<
   InGroup<UnusedCommandLineArgument>;
 def warn_unsupported_branch_protection: Warning <
   "invalid branch protection option '%0' in '%1'">, InGroup<BranchProtection>;
+def warn_harden_pac_ret_requires_pac_ret: Warning<
+  "ignoring '-mharden-pac-ret' as it requires return address signing">, InGroup<UnusedCommandLineArgument>;
 def err_sls_hardening_arm_not_supported : Error<
   "-mharden-sls is only supported on armv7-a or later">;
 def warn_drv_large_data_threshold_invalid_code_model: Warning<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5cbbc7d130c99..fdfe7792441b9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3384,6 +3384,11 @@ def err_invalid_branch_protection_spec : Error<
   "invalid or misplaced branch protection specification '%0'">;
 def warn_unsupported_branch_protection_spec : Warning<
   "unsupported branch protection specification '%0'">, InGroup<BranchProtection>;
+def warn_attribute_harden_pac_ret_requires_pac_ret: Warning<
+  "'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored">,
+  InGroup<IgnoredAttributes>;
+def err_invalid_harden_pac_ret_spec : Error<
+  "invalid or misplaced pac-ret hardening specification '%0'">;
 def err_attribute_invalid_atomic_argument : Error<
   "invalid argument '%0' to atomic attribute; valid options are: "
   "'remote_memory', 'fine_grained_memory', 'ignore_denormal_mode' (optionally "
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 8cba1dbaee24e..f3c5613d3dab7 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -454,6 +454,8 @@ ENUM_LANGOPT(SignReturnAddressScope, SignReturnAddressScopeKind, 2, SignReturnAd
              "Scope of return address signing")
 ENUM_LANGOPT(SignReturnAddressKey, SignReturnAddressKeyKind, 1, SignReturnAddressKeyKind::AKey, NotCompatible,
              "Key used for return address signing")
+ENUM_LANGOPT(SignReturnAddressHardening, SignReturnAddressHardeningKind, 1,
+             SignReturnAddressHardeningKind::None, NotCompatible, "Hardening of return address signing")
 LANGOPT(BranchTargetEnforcement, 1, 0, NotCompatible, "Branch-target enforcement enabled")
 LANGOPT(BranchProtectionPAuthLR, 1, 0, NotCompatible, "Use PC as a diversifier using PAuthLR NOP instructions.")
 LANGOPT(GuardedControlStack, 1, 0, NotCompatible, "Guarded control stack enabled")
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index ebd0436fa154b..c0e82b3edd2cb 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -299,6 +299,13 @@ class LangOptionsBase {
     BKey
   };
 
+  enum class SignReturnAddressHardeningKind {
+    /// Regular return address signing.
+    None,
+    /// Hardened return address signing with load of return address.
+    LoadReturnAddress
+  };
+
   enum class ThreadModelKind {
     /// POSIX Threads.
     POSIX,
@@ -728,6 +735,11 @@ class LangOptions : public LangOptionsBase {
     return getSignReturnAddressScope() == SignReturnAddressScopeKind::All;
   }
 
+  bool hasSignReturnAddressHardening() const {
+    return getSignReturnAddressHardening() !=
+           SignReturnAddressHardeningKind::None;
+  }
+
   bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; }
 
   bool hasDefaultVisibilityExportMapping() const {
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 83467b8e93b6a..10779518275b4 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -62,10 +62,12 @@ struct ParsedTargetAttr {
   StringRef CPU;
   StringRef Tune;
   StringRef BranchProtection;
+  StringRef SignReturnAddrHardening;
   StringRef Duplicate;
   bool operator ==(const ParsedTargetAttr &Other) const {
     return Duplicate == Other.Duplicate && CPU == Other.CPU &&
            Tune == Other.Tune && BranchProtection == Other.BranchProtection &&
+           SignReturnAddrHardening == Other.SignReturnAddrHardening &&
            Features == Other.Features;
   }
 };
@@ -1471,6 +1473,7 @@ class TargetInfo : public TransferrableTargetInfo,
   public:
     LangOptions::SignReturnAddressScopeKind SignReturnAddr;
     LangOptions::SignReturnAddressKeyKind SignKey;
+    LangOptions::SignReturnAddressHardeningKind SignReturnAddressHardening;
     bool BranchTargetEnforcement;
     bool BranchProtectionPAuthLR;
     bool GuardedControlStack;
@@ -1497,9 +1500,21 @@ class TargetInfo : public TransferrableTargetInfo,
       llvm_unreachable("Unexpected SignReturnAddressKeyKind");
     }
 
+    const char *getSignReturnAddressHardeningStr() const {
+      switch (SignReturnAddressHardening) {
+      case LangOptions::SignReturnAddressHardeningKind::None:
+        return "none";
+      case LangOptions::SignReturnAddressHardeningKind::LoadReturnAddress:
+        return "load-return-address";
+      }
+      llvm_unreachable("Unexpected SignReturnAddressHardeningKind");
+    }
+
     BranchProtectionInfo()
         : SignReturnAddr(LangOptions::SignReturnAddressScopeKind::None),
           SignKey(LangOptions::SignReturnAddressKeyKind::AKey),
+          SignReturnAddressHardening(
+              LangOptions::SignReturnAddressHardeningKind::None),
           BranchTargetEnforcement(false), BranchProtectionPAuthLR(false),
           GuardedControlStack(false) {}
 
@@ -1513,6 +1528,7 @@ class TargetInfo : public TransferrableTargetInfo,
       SignKey = LangOpts.isSignReturnAddressWithAKey()
                     ? LangOptions::SignReturnAddressKeyKind::AKey
                     : LangOptions::SignReturnAddressKeyKind::BKey;
+      SignReturnAddressHardening = LangOpts.getSignReturnAddressHardening();
       BranchTargetEnforcement = LangOpts.BranchTargetEnforcement;
       BranchProtectionPAuthLR = LangOpts.BranchProtectionPAuthLR;
       GuardedControlStack = LangOpts.GuardedControlStack;
@@ -1535,6 +1551,12 @@ class TargetInfo : public TransferrableTargetInfo,
     return false;
   }
 
+  /// Validate the Return Address Signing Hardening specification
+  virtual std::optional<LangOptions::SignReturnAddressHardeningKind>
+  validateSignReturnAddressHardening(StringRef Spec) const {
+    return std::nullopt;
+  }
+
   /// Perform initialization based on the user configured
   /// set of features (e.g., +sse4).
   ///
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 2f57a5b13b917..3ac001d05be82 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5521,6 +5521,15 @@ def msign_return_address_EQ : Joined<["-"], "msign-return-address=">,
   Visibility<[ClangOption, CC1Option]>,
   Group<m_Group>, Values<"none,all,non-leaf">,
   HelpText<"Select return address signing scope">;
+
+def mharden_pac_ret_EQ : Joined<["-"], "mharden-pac-ret=">,
+  Visibility<[ClangOption, CC1Option]>,
+  Flags<[TargetSpecific]>, Group<m_Group>,
+  HelpText<"Select the return address signing hardening scheme. <arg> must be: none, load-return-address">,
+  Values<"none,load-return-address">, NormalizedValues<["None", "LoadReturnAddress"]>,
+  NormalizedValuesScope<"LangOptions::SignReturnAddressHardeningKind">,
+  MarshallingInfoEnum<LangOpts<"SignReturnAddressHardening">, "None">;
+
 let Flags = [TargetSpecific] in {
 def mbranch_protection_EQ : Joined<["-"], "mbranch-protection=">,
   Group<m_Group>,
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index fe407e9fc1789..639540fc13d87 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -276,6 +276,17 @@ bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
   return true;
 }
 
+std::optional<LangOptions::SignReturnAddressHardeningKind>
+AArch64TargetInfo::validateSignReturnAddressHardening(StringRef Spec) const {
+  assert(!Spec.empty() && "Spec must not be empty");
+  return llvm::StringSwitch<
+             std::optional<LangOptions::SignReturnAddressHardeningKind>>(Spec)
+      .Case("load-return-address",
+            LangOptions::SignReturnAddressHardeningKind::LoadReturnAddress)
+      .Case("none", LangOptions::SignReturnAddressHardeningKind::None)
+      .Default(std::nullopt);
+}
+
 bool AArch64TargetInfo::isValidCPUName(StringRef Name) const {
   return llvm::AArch64::parseCpu(Name).has_value();
 }
@@ -1290,6 +1301,11 @@ ParsedTargetAttr AArch64TargetInfo::parseTargetAttr(StringRef Features) const {
       continue;
     }
 
+    if (Feature.starts_with("harden-pac-ret=")) {
+      Ret.SignReturnAddrHardening = Feature.split('=').second.trim();
+      continue;
+    }
+
     if (Feature.starts_with("arch=")) {
       if (FoundArch)
         Ret.Duplicate = "arch=";
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index 2d3b8d2a8d950..c0e52319ba628 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -147,6 +147,8 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
                                 BranchProtectionInfo &BPI,
                                 const LangOptions &LO,
                                 StringRef &Err) const override;
+  std::optional<LangOptions::SignReturnAddressHardeningKind>
+  validateSignReturnAddressHardening(StringRef Spec) const override;
 
   bool isValidCPUName(StringRef Name) const override;
   void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp
index 342a3af0ac1ee..530304f9530ad 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -233,6 +233,10 @@ void TargetCodeGenInfo::setBranchProtectionFnAttributes(
   if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) {
     F.addFnAttr("sign-return-address", BPI.getSignReturnAddrStr());
     F.addFnAttr("sign-return-address-key", BPI.getSignKeyStr());
+    if (BPI.SignReturnAddressHardening !=
+        LangOptions::SignReturnAddressHardeningKind::None)
+      F.addFnAttr("sign-return-address-harden",
+                  BPI.getSignReturnAddressHardeningStr());
   } else {
     if (F.hasFnAttribute("sign-return-address"))
       F.removeFnAttr("sign-return-address");
diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp
index 963b74927036a..2d197e27dc115 100644
--- a/clang/lib/CodeGen/Targets/AArch64.cpp
+++ b/clang/lib/CodeGen/Targets/AArch64.cpp
@@ -160,6 +160,9 @@ class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {
             Attr.BranchProtection, Attr.CPU, BPI, CGM.getLangOpts(), Error);
         assert(Error.empty());
       }
+      if (!Attr.SignReturnAddrHardening.empty())
+        Fn->addFnAttr("sign-return-address-harden",
+                      Attr.SignReturnAddrHardening);
     }
     setBranchProtectionFnAttributes(BPI, *Fn);
     setPointerAuthFnAttributes(CGM.getCodeGenOpts().PointerAuth, *Fn);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 4ca98600d6e93..f91e1fd367043 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -1378,6 +1378,19 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
                      ? Args.getLastArg(options::OPT_msign_return_address_EQ,
                                        options::OPT_mbranch_protection_EQ)
                      : Args.getLastArg(options::OPT_mbranch_protection_EQ);
+  const Arg *HardenPACRetArg = Args.getLastArg(options::OPT_mharden_pac_ret_EQ);
+  const Driver &D = TC.getDriver();
+
+  if (HardenPACRetArg) {
+    if (!isAArch64) {
+      D.Diag(diag::err_drv_unsupported_opt_for_target)
+          << HardenPACRetArg->getSpelling() << TC.getTriple().str();
+      return;
+    }
+    if (!A)
+      D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
+  }
+
   if (!A) {
     if (Triple.isOSOpenBSD() && isAArch64) {
       CmdArgs.push_back("-msign-return-address=non-leaf");
@@ -1387,7 +1400,6 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
     return;
   }
 
-  const Driver &D = TC.getDriver();
   if (!(isAArch64 || (Triple.isArmT32() && Triple.isArmMClass())))
     D.Diag(diag::warn_incompatible_branch_protection_option)
         << Triple.getArchName();
@@ -1466,6 +1478,14 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
 
   if (GuardedControlStack)
     CmdArgs.push_back("-mguarded-control-stack");
+
+  if (HardenPACRetArg) {
+    if (Scope == "none")
+      D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
+    else
+      CmdArgs.push_back(Args.MakeArgString(Twine("-mharden-pac-ret=") +
+                                           HardenPACRetArg->getValue()));
+  }
 }
 
 void Clang::AddARMTargetArgs(const llvm::Triple &Triple, const ArgList &Args,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index d762bcd789bf5..a514be7fd217f 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3428,8 +3428,11 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
 
   TargetInfo::BranchProtectionInfo BPI{};
   StringRef DiagMsg;
-  if (ParsedAttrs.BranchProtection.empty())
+  if (ParsedAttrs.BranchProtection.empty()) {
+    if (!ParsedAttrs.SignReturnAddrHardening.empty())
+      Diag(LiteralLoc, diag::warn_attribute_harden_pac_ret_requires_pac_ret);
     return false;
+  }
   if (!Context.getTargetInfo().validateBranchProtection(
           ParsedAttrs.BranchProtection, ParsedAttrs.CPU, BPI,
           Context.getLangOpts(), DiagMsg)) {
@@ -3442,6 +3445,19 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
   if (!DiagMsg.empty())
     Diag(LiteralLoc, diag::warn_unsupported_branch_protection_spec) << DiagMsg;
 
+  if (!ParsedAttrs.SignReturnAddrHardening.empty()) {
+    auto SignReturnAddrOption =
+        Context.getTargetInfo().validateSignReturnAddressHardening(
+            ParsedAttrs.SignReturnAddrHardening);
+    if (!SignReturnAddrOption)
+      return Diag(LiteralLoc, diag::err_invalid_harden_pac_ret_spec)
+             << ParsedAttrs.SignReturnAddrHardening;
+
+    if (BPI.SignReturnAddr == LangOptions::SignReturnAddressScopeKind::None)
+      return Diag(LiteralLoc,
+                  diag::warn_attribute_harden_pac_ret_requires_pac_ret);
+  }
+
   return false;
 }
 
diff --git a/clang/test/CodeGen/aarch64-sign-return-address-harden.c b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
new file mode 100644
index 0000000000000..6d4db64c5efde
--- /dev/null
+++ b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
@@ -0,0 +1,13 @@
+// RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=none \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret -mharden-pac-ret=none \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret -mharden-pac-ret=load-return-address \
+// RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
+
+void foo() {}
+
+// CHECK-NO-HARDEN-NOT: attributes #0 = {{.*}}"sign-return-address-harden"
+// CHECK-HARDEN:        attributes #0 = {{.*}}"sign-return-address-harden"="load-return-address"
\ No newline at end of file
diff --git a/clang/test/Driver/aarch64-security-options.c b/clang/test/Driver/aarch64-security-options.c
index 146add2d1cf70..fbcf3d75071c6 100644
--- a/clang/test/Driver/aarch64-security-options.c
+++ b/clang/test/Driver/aarch64-security-options.c
@@ -32,6 +32,36 @@
 
 // WARN-NOT: warning: ignoring '-mbranch-protection=' option because the 'aarch64' architecture does not support it [-Wbranch-protection]
 
+// RUN: %clang -target aarch64 -c %s -### -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
+
+// RUN: %clang -target aarch64 -c %s -### -mharden-pac-ret=load-return-address 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=none -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=none -mharden-pac-ret=load-return-address 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=pac-ret -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-NONE
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=pac-ret -mharden-pac-ret=load-return-address 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-LRA
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-NONE
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=load-return-address 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-LRA
+
+// NO-RA-HARDEN:        ignoring '-mharden-pac-ret' as it requires return address signing
+// NO-RA-HARDEN-NOT:    "-mharden-pac-ret"
+// NO-RA-HARDEN-LRA:    ignoring '-mharden-pac-ret' as it requires return address signing
+// RA-HARDEN-NONE:      "-mharden-pac-ret=none"
+// RA-HARDEN-LRA:       "-mharden-pac-ret=load-return-address"
+
 // RA-OFF: "-msign-return-address=none"
 // RA-NON-LEAF: "-msign-return-address=non-leaf"
 // RA-ALL: "-msign-return-address=all"
diff --git a/clang/test/Driver/arm-security-options.c b/clang/test/Driver/arm-security-options.c
index 613945c24eede..db1888bec52db 100644
--- a/clang/test/Driver/arm-security-options.c
+++ b/clang/test/Driver/arm-security-options.c
@@ -75,6 +75,23 @@
 // RUN: %clang -target arm-arm-none-eabi -march=armv7-r -c %s -### -mbranch-protection=bti 2>&1 | \
 // RUN: FileCheck %s --check-prefix=INCOMPATIBLE-ARCH
 
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8m.main -c %s -### -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -mbranch-protection=bti -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -mbranch-protection=pac-ret -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -mbranch-protection=pac-ret+bti -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -mbranch-protection=pac-ret+leaf -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -msign-return-address=all -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -msign-return-address=non-leaf -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+
 // RA-OFF: "-msign-return-address=none"
 // RA-NON-LEAF: "-msign-return-address=non-leaf"
 // RA-ALL: "-msign-return-address=all"
@@ -91,3 +108,5 @@
 // BAD-LEAF-COMBINATION: unsupported argument 'leaf' to option '-mbranch-protection='
 
 // INCOMPATIBLE-ARCH: '-mbranch-protection=' option is incompatible with the '{{.*}}' architecture
+
+// RA-HARDEN-INCOMPATIBLE-ARCH: unsupported option '-mharden-pac-ret=' for target 'arm-arm-none-eabi'
diff --git a/clang/test/Frontend/aarch64-harden-pac-ret-err.c b/clang/test/Frontend/aarch64-harden-pac-ret-err.c
new file mode 100644
index 0000000000000..9fc2cba7e8205
--- /dev/null
+++ b/clang/test/Frontend/aarch64-harden-pac-ret-err.c
@@ -0,0 +1,3 @@
+// RUN: not %clang_cc1 -fsyntax-only -triple aarch64 %s -mharden-pac-ret=foo 2>&1 | FileCheck %s
+
+// CHECK: invalid value 'foo' in '-mharden-pac-ret=foo'
diff --git a/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c b/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
new file mode 100644
index 0000000000000..3e218253137d6
--- /dev/null
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -triple aarch64 -verify -fsyntax-only %s
+
+__attribute__((target("harden-pac-ret=none"))) // expected-warning {{'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored}}
+void
+badvalue0(void) {}
+
+__attribute__((target("harden-pac-ret=load-return-address"))) // expected-warning {{'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored}}
+void
+badvalue1(void) {}
+
+__attribute__((target("branch-protection=bti,harden-pac-ret=none"))) // expected-warning {{'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored}}
+void
+badvalue2(void) {}
+
+__attribute__((target("branch-protection=bti,harden-pac-ret=load-return-address"))) // expected-warning {{'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored}}
+void
+badvalue3(void) {}
+
+__attribute__((target("branch-protection=bti,harden-pac-ret=inexistent"))) // expected-error {{invalid or misplaced pac-ret hardening specification 'inexistent'}}
+void
+badvalue4(void) {}
diff --git a/clang/test/Sema/aarch64-harden-pac-ret-attr.c b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
new file mode 100644
index 0000000000000..c05e33ee28055
--- /dev/null
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -triple aarch64 -emit-llvm  -target-cpu generic -target-feature +v8.5a %s -o - \
+// RUN: | FileCheck %s --check-prefix=CHECK
+
+__attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=none")))
+void f1() {}
+// CHECK: define{{.*}} void @f1() #[[#F1:]]
+
+__attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=load-return-address")))
+void f2() {}
+// CHECK: define{{.*}} void @f2() #[[#F2:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf,harden-pac-ret=none")))
+void f3() {}
+// CHECK: define{{.*}} void @f3() #[[#F3:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf,harden-pac-ret=load-return-address")))
+void f4() {}
+// CHECK: define{{.*}} void @f4() #[[#F4:]]
+
+__attribute__ ((target("branch-protection=pac-ret+b-key,harden-pac-ret=none")))
+void f5() {}
+// CHECK: define{{.*}} void @f5() #[[#F5:]]
+
+__attribute__ ((target("branch-protection=pac-ret+b-key,harden-pac-ret=load-return-address")))
+void f6() {}
+// CHECK: define{{.*}} void @f6() #[[#F6:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=none")))
+void f7() {}
+// CHECK: define{{.*}} void @f7() #[[#F7:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=load-return-address")))
+void f8() {}
+// CHECK: define{{.*}} void @f8() #[[#F8:]]
+
+// CHECK-DAG: attributes #[[#F1]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="none"
+// CHECK-DAG: attributes #[[#F2]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address"
+// CHECK-DAG: attributes #[[#F3]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="none"
+// CHECK-DAG: attributes #[[#F4]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address"
+// CHECK-DAG: attributes #[[#F5]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="none" "sign-return-address-key"="b_key"
+// CHECK-DAG: attributes #[[#F6]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"
+// CHECK-DAG: attributes #[[#F7]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="none" "sign-return-address-key"="b_key"
+// CHECK-DAG: attributes #[[#F8]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"

>From 97c8d752b6115d3a2e7d6e4c84c35ea0baa641bc Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Mon, 13 Apr 2026 11:39:18 +0100
Subject: [PATCH 2/9] Address code reviews

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +-
 clang/include/clang/Basic/LangOptions.h       |   5 -
 clang/include/clang/Basic/TargetInfo.h        |   4 +-
 clang/lib/Basic/Targets/AArch64.cpp           |   3 +-
 clang/lib/Basic/Targets/AArch64.h             |   2 +-
 clang/lib/CodeGen/TargetInfo.cpp              |  13 ++
 clang/lib/CodeGen/Targets/AArch64.cpp         |  13 +-
 clang/lib/Driver/ToolChains/Clang.cpp         | 127 +++++++++---------
 clang/lib/Sema/SemaDeclAttr.cpp               |  12 +-
 .../aarch64-sign-return-address-harden.c      |   8 ++
 clang/test/Driver/aarch64-security-options.c  |  17 ++-
 .../aarch64-harden-pac-ret-attr-err-warn.c    |   2 +-
 clang/test/Sema/aarch64-harden-pac-ret-attr.c |  31 +++--
 13 files changed, 144 insertions(+), 95 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index fdfe7792441b9..e963eb7d1399b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3388,7 +3388,7 @@ def warn_attribute_harden_pac_ret_requires_pac_ret: Warning<
   "'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored">,
   InGroup<IgnoredAttributes>;
 def err_invalid_harden_pac_ret_spec : Error<
-  "invalid or misplaced pac-ret hardening specification '%0'">;
+  "invalid or misspelled pac-ret hardening specification '%0'">;
 def err_attribute_invalid_atomic_argument : Error<
   "invalid argument '%0' to atomic attribute; valid options are: "
   "'remote_memory', 'fine_grained_memory', 'ignore_denormal_mode' (optionally "
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index c0e82b3edd2cb..bc7deae9c5122 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -735,11 +735,6 @@ class LangOptions : public LangOptionsBase {
     return getSignReturnAddressScope() == SignReturnAddressScopeKind::All;
   }
 
-  bool hasSignReturnAddressHardening() const {
-    return getSignReturnAddressHardening() !=
-           SignReturnAddressHardeningKind::None;
-  }
-
   bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; }
 
   bool hasDefaultVisibilityExportMapping() const {
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 10779518275b4..7d64a9d7bd4bb 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1551,9 +1551,9 @@ class TargetInfo : public TransferrableTargetInfo,
     return false;
   }
 
-  /// Validate the Return Address Signing Hardening specification
+  /// Parse the Return Address Signing Hardening specification.
   virtual std::optional<LangOptions::SignReturnAddressHardeningKind>
-  validateSignReturnAddressHardening(StringRef Spec) const {
+  parseSignReturnAddressHardening(StringRef Spec) const {
     return std::nullopt;
   }
 
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index 639540fc13d87..da10cd384b963 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -277,8 +277,7 @@ bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
 }
 
 std::optional<LangOptions::SignReturnAddressHardeningKind>
-AArch64TargetInfo::validateSignReturnAddressHardening(StringRef Spec) const {
-  assert(!Spec.empty() && "Spec must not be empty");
+AArch64TargetInfo::parseSignReturnAddressHardening(StringRef Spec) const {
   return llvm::StringSwitch<
              std::optional<LangOptions::SignReturnAddressHardeningKind>>(Spec)
       .Case("load-return-address",
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index c0e52319ba628..9312b785dd20b 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -148,7 +148,7 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
                                 const LangOptions &LO,
                                 StringRef &Err) const override;
   std::optional<LangOptions::SignReturnAddressHardeningKind>
-  validateSignReturnAddressHardening(StringRef Spec) const override;
+  parseSignReturnAddressHardening(StringRef Spec) const override;
 
   bool isValidCPUName(StringRef Name) const override;
   void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp
index 530304f9530ad..9e0b7bb709cc9 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -230,6 +230,7 @@ void TargetCodeGenInfo::setBranchProtectionFnAttributes(
   // Called on already created and initialized function where attributes already
   // set from command line attributes but some might need to be removed as the
   // actual BPI is different.
+
   if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) {
     F.addFnAttr("sign-return-address", BPI.getSignReturnAddrStr());
     F.addFnAttr("sign-return-address-key", BPI.getSignKeyStr());
@@ -244,6 +245,14 @@ void TargetCodeGenInfo::setBranchProtectionFnAttributes(
       F.removeFnAttr("sign-return-address-key");
   }
 
+  if (BPI.SignReturnAddressHardening ==
+      LangOptions::SignReturnAddressHardeningKind::None) {
+    F.removeFnAttr("sign-return-address-harden");
+  } else {
+    F.addFnAttr("sign-return-address-harden",
+                BPI.getSignReturnAddressHardeningStr());
+  }
+
   auto AddRemoveAttributeAsSet = [&](bool Set, const StringRef &ModAttr) {
     if (Set)
       F.addFnAttr(ModAttr);
@@ -266,6 +275,10 @@ void TargetCodeGenInfo::initBranchProtectionFnAttributes(
     FuncAttrs.addAttribute("sign-return-address", BPI.getSignReturnAddrStr());
     FuncAttrs.addAttribute("sign-return-address-key", BPI.getSignKeyStr());
   }
+  if (BPI.SignReturnAddressHardening !=
+      LangOptions::SignReturnAddressHardeningKind::None)
+    FuncAttrs.addAttribute("sign-return-address-harden",
+                           BPI.getSignReturnAddressHardeningStr());
   if (BPI.BranchTargetEnforcement)
     FuncAttrs.addAttribute("branch-target-enforcement");
   if (BPI.BranchProtectionPAuthLR)
diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp
index 2d197e27dc115..87219df5cbc83 100644
--- a/clang/lib/CodeGen/Targets/AArch64.cpp
+++ b/clang/lib/CodeGen/Targets/AArch64.cpp
@@ -159,10 +159,17 @@ class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {
         (void)CGM.getTarget().validateBranchProtection(
             Attr.BranchProtection, Attr.CPU, BPI, CGM.getLangOpts(), Error);
         assert(Error.empty());
+
+        // Hardening is only accepted in the target attribute if PAC-RET is also
+        // present there. Invalid combinations are handled in Sema.
+        if (BPI.SignReturnAddr !=
+            LangOptions::SignReturnAddressScopeKind::None) {
+          if (auto Hardening = CGM.getTarget().parseSignReturnAddressHardening(
+                  Attr.SignReturnAddrHardening)) {
+            BPI.SignReturnAddressHardening = *Hardening;
+          }
+        }
       }
-      if (!Attr.SignReturnAddrHardening.empty())
-        Fn->addFnAttr("sign-return-address-harden",
-                      Attr.SignReturnAddrHardening);
     }
     setBranchProtectionFnAttributes(BPI, *Fn);
     setPointerAuthFnAttributes(CGM.getCodeGenOpts().PointerAuth, *Fn);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index f91e1fd367043..ed200b6ec3ec9 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -1381,79 +1381,85 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
   const Arg *HardenPACRetArg = Args.getLastArg(options::OPT_mharden_pac_ret_EQ);
   const Driver &D = TC.getDriver();
 
+  // Check CmdArgs because some toolchains bypass the driver args and add to
+  // the frontend args directly.
+  bool HasPtrauthReturns = llvm::is_contained(CmdArgs, "-fptrauth-returns") ||
+                           Args.hasArgNoClaim(options::OPT_fno_ptrauth_returns,
+                                              options::OPT_fptrauth_returns);
+
   if (HardenPACRetArg) {
     if (!isAArch64) {
       D.Diag(diag::err_drv_unsupported_opt_for_target)
           << HardenPACRetArg->getSpelling() << TC.getTriple().str();
       return;
     }
-    if (!A)
-      D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
+    StringRef ArgValue = HardenPACRetArg->getValue();
+    if (ArgValue != "none" && ArgValue != "load-return-address") {
+      D.Diag(diag::err_drv_unsupported_option_argument)
+          << HardenPACRetArg->getSpelling() << ArgValue;
+      return;
+    }
   }
 
-  if (!A) {
-    if (Triple.isOSOpenBSD() && isAArch64) {
-      CmdArgs.push_back("-msign-return-address=non-leaf");
-      CmdArgs.push_back("-msign-return-address-key=a_key");
-      CmdArgs.push_back("-mbranch-target-enforce");
-    }
+  if (!A && Triple.isOSOpenBSD() && isAArch64) {
+    CmdArgs.push_back("-msign-return-address=non-leaf");
+    CmdArgs.push_back("-msign-return-address-key=a_key");
+    CmdArgs.push_back("-mbranch-target-enforce");
     return;
   }
 
-  if (!(isAArch64 || (Triple.isArmT32() && Triple.isArmMClass())))
+  if (A && !(isAArch64 || (Triple.isArmT32() && Triple.isArmMClass())))
     D.Diag(diag::warn_incompatible_branch_protection_option)
         << Triple.getArchName();
 
-  StringRef Scope, Key;
-  bool IndirectBranches, BranchProtectionPAuthLR, GuardedControlStack;
-
-  if (A->getOption().matches(options::OPT_msign_return_address_EQ)) {
-    Scope = A->getValue();
-    if (Scope != "none" && Scope != "non-leaf" && Scope != "all")
-      D.Diag(diag::err_drv_unsupported_option_argument)
-          << A->getSpelling() << Scope;
-    Key = "a_key";
-    IndirectBranches = Triple.isOSOpenBSD() && isAArch64;
-    BranchProtectionPAuthLR = false;
-    GuardedControlStack = false;
-  } else {
-    StringRef DiagMsg;
-    llvm::ARM::ParsedBranchProtection PBP;
-    bool EnablePAuthLR = false;
-
-    // To know if we need to enable PAuth-LR As part of the standard branch
-    // protection option, it needs to be determined if the feature has been
-    // activated in the `march` argument. This information is stored within the
-    // CmdArgs variable and can be found using a search.
-    if (isAArch64) {
-      auto isPAuthLR = [](const char *member) {
-        llvm::AArch64::ExtensionInfo pauthlr_extension =
-            llvm::AArch64::getExtensionByID(llvm::AArch64::AEK_PAUTHLR);
-        return pauthlr_extension.PosTargetFeature == member;
-      };
+  StringRef Scope = "none", Key;
+  bool IndirectBranches = false, BranchProtectionPAuthLR = false,
+       GuardedControlStack = false;
 
-      if (llvm::any_of(CmdArgs, isPAuthLR))
-        EnablePAuthLR = true;
+  if (A) {
+    if (A->getOption().matches(options::OPT_msign_return_address_EQ)) {
+      Scope = A->getValue();
+      if (Scope != "none" && Scope != "non-leaf" && Scope != "all")
+        D.Diag(diag::err_drv_unsupported_option_argument)
+            << A->getSpelling() << Scope;
+      Key = "a_key";
+      IndirectBranches = Triple.isOSOpenBSD() && isAArch64;
+      BranchProtectionPAuthLR = false;
+      GuardedControlStack = false;
+    } else {
+      StringRef DiagMsg;
+      llvm::ARM::ParsedBranchProtection PBP;
+      bool EnablePAuthLR = false;
+
+      // To know if we need to enable PAuth-LR As part of the standard branch
+      // protection option, it needs to be determined if the feature has been
+      // activated in the `march` argument. This information is stored within
+      // the CmdArgs variable and can be found using a search.
+      if (isAArch64) {
+        auto isPAuthLR = [](const char *member) {
+          llvm::AArch64::ExtensionInfo pauthlr_extension =
+              llvm::AArch64::getExtensionByID(llvm::AArch64::AEK_PAUTHLR);
+          return pauthlr_extension.PosTargetFeature == member;
+        };
+
+        if (llvm::any_of(CmdArgs, isPAuthLR))
+          EnablePAuthLR = true;
+      }
+      if (!llvm::ARM::parseBranchProtection(A->getValue(), PBP, DiagMsg,
+                                            EnablePAuthLR))
+        D.Diag(diag::err_drv_unsupported_option_argument)
+            << A->getSpelling() << DiagMsg;
+      if (!isAArch64 && PBP.Key == "b_key")
+        D.Diag(diag::warn_unsupported_branch_protection)
+            << "b-key" << A->getAsString(Args);
+      Scope = PBP.Scope;
+      Key = PBP.Key;
+      BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
+      IndirectBranches = PBP.BranchTargetEnforcement;
+      GuardedControlStack = PBP.GuardedControlStack;
     }
-    if (!llvm::ARM::parseBranchProtection(A->getValue(), PBP, DiagMsg,
-                                          EnablePAuthLR))
-      D.Diag(diag::err_drv_unsupported_option_argument)
-          << A->getSpelling() << DiagMsg;
-    if (!isAArch64 && PBP.Key == "b_key")
-      D.Diag(diag::warn_unsupported_branch_protection)
-          << "b-key" << A->getAsString(Args);
-    Scope = PBP.Scope;
-    Key = PBP.Key;
-    BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
-    IndirectBranches = PBP.BranchTargetEnforcement;
-    GuardedControlStack = PBP.GuardedControlStack;
-  }
-
-  Arg *PtrauthReturnsArg = Args.getLastArg(options::OPT_fptrauth_returns,
-                                           options::OPT_fno_ptrauth_returns);
-  bool HasPtrauthReturns =
-      PtrauthReturnsArg &&
-      PtrauthReturnsArg->getOption().matches(options::OPT_fptrauth_returns);
+  }
+
   // GCS is currently untested with ptrauth-returns, but enabling this could be
   // allowed in future after testing with a suitable system.
   if (Scope != "none" || BranchProtectionPAuthLR || GuardedControlStack) {
@@ -1465,8 +1471,9 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
           << A->getAsString(Args) << "-fptrauth-returns";
   }
 
-  CmdArgs.push_back(
-      Args.MakeArgString(Twine("-msign-return-address=") + Scope));
+  if (A)
+    CmdArgs.push_back(
+        Args.MakeArgString(Twine("-msign-return-address=") + Scope));
   if (Scope != "none")
     CmdArgs.push_back(
         Args.MakeArgString(Twine("-msign-return-address-key=") + Key));
@@ -1480,7 +1487,7 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
     CmdArgs.push_back("-mguarded-control-stack");
 
   if (HardenPACRetArg) {
-    if (Scope == "none")
+    if (Scope == "none" && !HasPtrauthReturns)
       D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
     else
       CmdArgs.push_back(Args.MakeArgString(Twine("-mharden-pac-ret=") +
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a514be7fd217f..9118f7bef778e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3426,13 +3426,15 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
              << Unsupported << None << CurFeature << Target;
   }
 
-  TargetInfo::BranchProtectionInfo BPI{};
-  StringRef DiagMsg;
   if (ParsedAttrs.BranchProtection.empty()) {
     if (!ParsedAttrs.SignReturnAddrHardening.empty())
       Diag(LiteralLoc, diag::warn_attribute_harden_pac_ret_requires_pac_ret);
     return false;
   }
+
+  TargetInfo::BranchProtectionInfo BPI{};
+  StringRef DiagMsg;
+
   if (!Context.getTargetInfo().validateBranchProtection(
           ParsedAttrs.BranchProtection, ParsedAttrs.CPU, BPI,
           Context.getLangOpts(), DiagMsg)) {
@@ -3446,10 +3448,10 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
     Diag(LiteralLoc, diag::warn_unsupported_branch_protection_spec) << DiagMsg;
 
   if (!ParsedAttrs.SignReturnAddrHardening.empty()) {
-    auto SignReturnAddrOption =
-        Context.getTargetInfo().validateSignReturnAddressHardening(
+    auto SignReturnAddrHardenOpt =
+        Context.getTargetInfo().parseSignReturnAddressHardening(
             ParsedAttrs.SignReturnAddrHardening);
-    if (!SignReturnAddrOption)
+    if (!SignReturnAddrHardenOpt)
       return Diag(LiteralLoc, diag::err_invalid_harden_pac_ret_spec)
              << ParsedAttrs.SignReturnAddrHardening;
 
diff --git a/clang/test/CodeGen/aarch64-sign-return-address-harden.c b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
index 6d4db64c5efde..4b078bbc367b3 100644
--- a/clang/test/CodeGen/aarch64-sign-return-address-harden.c
+++ b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
@@ -1,11 +1,19 @@
 // RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=none \
 // RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64-linux-pauthtest -S -emit-llvm -o - %s -fno-ptrauth-returns \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
 // RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret \
 // RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64-linux-pauthtest -S -emit-llvm -o - %s -fptrauth-returns \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
 // RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret -mharden-pac-ret=none \
 // RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64-linux-pauthtest -S -emit-llvm -o - %s -fptrauth-returns -mharden-pac-ret=none \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
 // RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret -mharden-pac-ret=load-return-address \
 // RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
+// RUN: %clang -target aarch64-linux-pauthtest -S -emit-llvm -o - %s -fptrauth-returns -mharden-pac-ret=load-return-address \
+// RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
 
 void foo() {}
 
diff --git a/clang/test/Driver/aarch64-security-options.c b/clang/test/Driver/aarch64-security-options.c
index fbcf3d75071c6..321205abd8e1a 100644
--- a/clang/test/Driver/aarch64-security-options.c
+++ b/clang/test/Driver/aarch64-security-options.c
@@ -32,6 +32,9 @@
 
 // WARN-NOT: warning: ignoring '-mbranch-protection=' option because the 'aarch64' architecture does not support it [-Wbranch-protection]
 
+// RUN: %clang -target aarch64 -c %s -### 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=ABSENT-RA-HARDEN
+
 // RUN: %clang -target aarch64 -c %s -### -mharden-pac-ret=none 2>&1 | \
 // RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
 
@@ -56,11 +59,15 @@
 // RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=load-return-address 2>&1 | \
 // RUN: FileCheck %s --check-prefixes=RA-HARDEN-LRA
 
-// NO-RA-HARDEN:        ignoring '-mharden-pac-ret' as it requires return address signing
-// NO-RA-HARDEN-NOT:    "-mharden-pac-ret"
-// NO-RA-HARDEN-LRA:    ignoring '-mharden-pac-ret' as it requires return address signing
-// RA-HARDEN-NONE:      "-mharden-pac-ret=none"
-// RA-HARDEN-LRA:       "-mharden-pac-ret=load-return-address"
+// RUN: not %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=foo 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=BAD-HARDEN-PROTECTION
+
+// ABSENT-RA-HARDEN-NOT: "-mharden-pac-ret"
+// NO-RA-HARDEN:         ignoring '-mharden-pac-ret' as it requires return address signing
+// NO-RA-HARDEN-NOT:     "-mharden-pac-ret"
+// RA-HARDEN-NONE:       "-mharden-pac-ret=none"
+// RA-HARDEN-LRA:        "-mharden-pac-ret=load-return-address"
+// BAD-HARDEN-PROTECTION: unsupported argument 'foo' to option '-mharden-pac-ret='
 
 // RA-OFF: "-msign-return-address=none"
 // RA-NON-LEAF: "-msign-return-address=non-leaf"
diff --git a/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c b/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
index 3e218253137d6..a98a80c85c819 100644
--- a/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
@@ -16,6 +16,6 @@ __attribute__((target("branch-protection=bti,harden-pac-ret=load-return-address"
 void
 badvalue3(void) {}
 
-__attribute__((target("branch-protection=bti,harden-pac-ret=inexistent"))) // expected-error {{invalid or misplaced pac-ret hardening specification 'inexistent'}}
+__attribute__((target("branch-protection=bti,harden-pac-ret=inexistent"))) // expected-error {{invalid or misspelled pac-ret hardening specification 'inexistent'}}
 void
 badvalue4(void) {}
diff --git a/clang/test/Sema/aarch64-harden-pac-ret-attr.c b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
index c05e33ee28055..28795003e3a5b 100644
--- a/clang/test/Sema/aarch64-harden-pac-ret-attr.c
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
@@ -1,5 +1,8 @@
-// RUN: %clang_cc1 -triple aarch64 -emit-llvm  -target-cpu generic -target-feature +v8.5a %s -o - \
-// RUN: | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -triple aarch64 -emit-llvm -target-cpu generic -target-feature +v8.5a %s -o - | FileCheck %s
+
+// The following test that the function attributes take precedence over command-line options
+// RUN: %clang_cc1 -triple aarch64 -emit-llvm -target-cpu generic -target-feature +v8.5a %s -msign-return-address=all -mharden-pac-ret=none -o - | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64 -emit-llvm -target-cpu generic -target-feature +v8.5a %s -msign-return-address=all -mharden-pac-ret=load-return-address -o - | FileCheck %s
 
 __attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=none")))
 void f1() {}
@@ -33,11 +36,19 @@ __attribute__ ((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=load
 void f8() {}
 // CHECK: define{{.*}} void @f8() #[[#F8:]]
 
-// CHECK-DAG: attributes #[[#F1]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="none"
-// CHECK-DAG: attributes #[[#F2]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address"
-// CHECK-DAG: attributes #[[#F3]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="none"
-// CHECK-DAG: attributes #[[#F4]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address"
-// CHECK-DAG: attributes #[[#F5]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="none" "sign-return-address-key"="b_key"
-// CHECK-DAG: attributes #[[#F6]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"
-// CHECK-DAG: attributes #[[#F7]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="none" "sign-return-address-key"="b_key"
-// CHECK-DAG: attributes #[[#F8]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"
+// These check patterns rely on the fact that "sign-return-address-harden" appears after "sign-return-address"
+
+// CHECK:     attributes #[[#F1]] = { {{.*}} "sign-return-address"="non-leaf"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     attributes #[[#F2]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address"
+// CHECK:     attributes #[[#F3]] = { {{.*}} "sign-return-address"="all"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     attributes #[[#F4]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address"
+// CHECK:     attributes #[[#F5]] = { {{.*}} "sign-return-address"="non-leaf"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#F6]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#F7]] = { {{.*}} "sign-return-address"="all"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#F8]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"

>From 63514f49bd2b1cf82f7d924c62ba12ff12ac7e29 Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Thu, 16 Jul 2026 14:58:12 +0100
Subject: [PATCH 3/9] Changes:  - Emit error if
 -mharden-pac-ret=load-return-address is used alongside -mexecute-only.  - Add
 driver tests for the point above and also for the use of hardening with
 -fptrauth-returns.  - Create new doc page for the feature.

---
 clang/docs/ReturnAddressSigningHardening.rst | 178 +++++++++++++++++++
 clang/docs/index.rst                         |   2 +-
 clang/include/clang/Options/Options.td       |   2 +-
 clang/lib/Driver/ToolChains/Clang.cpp        |  14 +-
 clang/test/Driver/aarch64-security-options.c |  20 +++
 5 files changed, 211 insertions(+), 5 deletions(-)
 create mode 100644 clang/docs/ReturnAddressSigningHardening.rst

diff --git a/clang/docs/ReturnAddressSigningHardening.rst b/clang/docs/ReturnAddressSigningHardening.rst
new file mode 100644
index 0000000000000..063132d6e6f0b
--- /dev/null
+++ b/clang/docs/ReturnAddressSigningHardening.rst
@@ -0,0 +1,178 @@
+##################################
+ Return Address Signing Hardening
+##################################
+
+.. contents::
+   :local:
+
+**************
+ Introduction
+**************
+
+Return Address Signing Hardening is a mitigation against the PACMAN
+attack, which aims to bypass Pointer Authentication on AArch64 targets.
+The hardening mechanism described here is specific to pointer
+authentication of return addresses.
+
+Return Address Signing, also known as Pointer Authentication Code
+(PAC-RET), is a feature devised to protect programs against Return
+Oriented Programming (ROP), in which attackers may hijack the return
+address of functions in order to direct execution to malicious code.
+
+PAC-RET can be enabled via different command-line options:
+
+   -  ``-mbranch-protection=`` with ``pac-ret``, ``pac-ret+leaf`` or
+      ``standard`` as value.
+   -  ``-msign-return-address=`` with ``non-leaf`` or ``all`` as value.
+   -  ``-fptrauth-returns``.
+
+More information can be found in :doc:`Pointer Authentication
+<PointerAuthentication>`.
+
+Return Address Signing Hardening is a mechanism to strenghthen Return
+Address Signing against the PACMAN attack in AArch64 targets. It can be
+enabled with ``-mharden-pac-ret=load-return-address``.
+
+***************
+ PACMAN attack
+***************
+
+PACMAN is an attack that aims to extract valuable information about
+pointer authentication codes using side-channels in speculative
+execution.
+
+It is performed with the use of gadgets to try and guess PAC codes.
+These guesses raise no faults because they are done in speculation. By
+observing the effects of the guessed PAC code on the processor's cache,
+it might be possible to determine the valid PAC code for the address to
+which the attacker wants the program to return.
+
+A usual PACMAN gadget looks like this:
+
+.. code:: C
+
+   void function() {
+     ...
+     if (condition)
+       return;
+     ...
+   }
+
+Such code would be compiled to:
+
+.. code:: asm
+
+   paciasp
+   ...
+   cbz w0, .LBB0_2
+   autiasp
+   ret
+   ...
+
+This code segment may be used as a gadget. A speculative execution of
+this segment can happen as follows:
+
+   -  If the Link Register (LR) has the right PAC code, ``autiasp`` will
+      succeed and strip the PAC code out of the it. The processor's
+      instruction fetcher will then bring the code after the return into
+      the cache (that is, the instructions located at the address
+      pointed by LR).
+
+   -  If the LR has the wrong PAC code, ``autiasp`` will not succeed and
+      hence will write a predefined error value to the LR's higher bits.
+      Because of this, the instruction fetcher will not bring the code
+      after the return into the cache.
+
+This difference in behavior is what drives the PACMAN attack. An
+attacker can try to guess PAC codes and monitor cache behavior until the
+code after the return is observed to have been brought into the cache.
+
+Details can be found in https://pacmanattack.com.
+
+***********
+ Hardening
+***********
+
+In order to mitigate the PACMAN attack, a hardening mechanism can be
+enabled with ``-mharden-pac-ret=load-return-address``.
+
+.. code:: asm
+
+   paciasp
+   ...
+   cbz w0, .LBB0_2
+   autiasp
+   mov     x8, x30
+   xpaclri
+   ldr     w30, [x30]
+   ret     x8
+   ...
+
+The idea is to always bring the code after the return into cache (the
+instructions located at the address pointed by LR), therefore minimizing
+the difference between a speculative execution with a correct PAC code
+and with an incorrect one.
+
+   -  ``autiasp`` performs the authentication step.
+   -  ``mov x8, x30`` copies the return address (LR and x30 are
+      synonyms) to a temporary.
+   -  ``xpaclri`` strips the PAC code out of the return address in x30.
+   -  ``ldr w30, [x30]`` performs a load of the return address in x30.
+   -  ``ret x8`` returns to the authenticated return address.
+
+The load operation brings the code into the cache even if the
+authentication step fails. As a consequence, in either case the code is
+loaded into the cache. Furthermore, the return operation uses the
+original return address before stripping, so the return address
+protection is still kept in place in a normal non-speculative execution.
+
+If FEAT_PAUTH is present, the code sequence can use instructions only
+available with said feature with no change in semantics:
+
+.. code:: asm
+
+   autiasp
+   mov     x8, x30
+   xpaci   x8
+   ldr     w8, [x8]
+   ret
+
+*********************
+ Command-line option
+*********************
+
+Return address signing hardening can be enabled at module level with
+``-mharden-pac-ret=load-return-address``. It requires PAC-RET to be
+enabled at module level as well.
+
+********************
+ Function attribute
+********************
+
+In addition to the command-line option
+``-mharden-pac-ret=load-return-address``, the developer can enable the
+mitigation at function level with the use of the corresponding function
+attribute.
+
+.. code:: C
+
+   __attribute__((target("branch-protection=pac-ret,harden-pac-ret=load-return-address")))
+   void function()  {
+     ...
+   }
+
+*********
+ Caveats
+*********
+
+The load of return address brings the code into the shared
+instruction/data cache, therefore this cache level can't be used as an
+oracle to find out whether the authentication succeeded or not. However,
+in the case of authentication success, the code is also fetched into the
+instruction cache. An attacker who is able to measure this cache level
+specifically may still be able to carry out the exploit.
+
+Another caveat is if the code at the return address contains a load
+operation within the speculation window. If this is the case, this load
+will only execute speculatively if authentication succeeds, thus opening
+the program up for exploitation despite the mitigation.
diff --git a/clang/docs/index.rst b/clang/docs/index.rst
index 70c8737a2fe0d..92120f9559a93 100644
--- a/clang/docs/index.rst
+++ b/clang/docs/index.rst
@@ -47,6 +47,7 @@ Using Clang as a Compiler
    ControlFlowIntegrity
    LTOVisibility
    PointerAuthentication
+   ReturnAddressSigningHardening
    SafeStack
    ShadowCallStack
    SourceBasedCodeCoverage
@@ -128,4 +129,3 @@ Indices and tables
 
 * :ref:`genindex`
 * :ref:`search`
-
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 3ac001d05be82..4268f0530816f 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5525,7 +5525,7 @@ def msign_return_address_EQ : Joined<["-"], "msign-return-address=">,
 def mharden_pac_ret_EQ : Joined<["-"], "mharden-pac-ret=">,
   Visibility<[ClangOption, CC1Option]>,
   Flags<[TargetSpecific]>, Group<m_Group>,
-  HelpText<"Select the return address signing hardening scheme. <arg> must be: none, load-return-address">,
+  HelpText<"Select the return address signing hardening scheme">,
   Values<"none,load-return-address">, NormalizedValues<["None", "LoadReturnAddress"]>,
   NormalizedValuesScope<"LangOptions::SignReturnAddressHardeningKind">,
   MarshallingInfoEnum<LangOpts<"SignReturnAddressHardening">, "None">;
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index ed200b6ec3ec9..a1ba6f5aed527 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -1383,9 +1383,10 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
 
   // Check CmdArgs because some toolchains bypass the driver args and add to
   // the frontend args directly.
-  bool HasPtrauthReturns = llvm::is_contained(CmdArgs, "-fptrauth-returns") ||
-                           Args.hasArgNoClaim(options::OPT_fno_ptrauth_returns,
-                                              options::OPT_fptrauth_returns);
+  bool HasPtrauthReturns =
+      llvm::is_contained(CmdArgs, "-fptrauth-returns") ||
+      Args.hasFlagNoClaim(options::OPT_fptrauth_returns,
+                          options::OPT_fno_ptrauth_returns, false);
 
   if (HardenPACRetArg) {
     if (!isAArch64) {
@@ -1399,6 +1400,13 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
           << HardenPACRetArg->getSpelling() << ArgValue;
       return;
     }
+    if (ArgValue != "none" &&
+        Args.hasFlagNoClaim(options::OPT_mexecute_only,
+                            options::OPT_mno_execute_only, false)) {
+      D.Diag(diag::err_drv_incompatible_options)
+          << HardenPACRetArg->getAsString(Args) << "-mexecute-only";
+      return;
+    }
   }
 
   if (!A && Triple.isOSOpenBSD() && isAArch64) {
diff --git a/clang/test/Driver/aarch64-security-options.c b/clang/test/Driver/aarch64-security-options.c
index 321205abd8e1a..dbac73d1f9769 100644
--- a/clang/test/Driver/aarch64-security-options.c
+++ b/clang/test/Driver/aarch64-security-options.c
@@ -13,6 +13,9 @@
 // RUN: %clang --target=aarch64 -c %s -### -mbranch-protection=standard                                2>&1 | \
 // RUN: FileCheck %s --check-prefix=RA-NON-LEAF --check-prefix=KEY-A --check-prefix=BTE-ON --check-prefix=GCS-ON --check-prefix=WARN
 
+// RUN: not %clang -target arm64-apple-darwin -c %s -### -mbranch-protection=standard -fptrauth-returns 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-INCOMPATIBLE-PTRAUTHRETURNS
+
 // If the -msign-return-address and -mbranch-protection are both used, the
 // right-most one controls return address signing.
 // RUN: %clang --target=aarch64 -c %s -### -msign-return-address=non-leaf -mbranch-protection=none     2>&1 | \
@@ -43,15 +46,23 @@
 
 // RUN: %clang -target aarch64 -c %s -### -mbranch-protection=none -mharden-pac-ret=none 2>&1 | \
 // RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
+// RUN: %clang -target arm64-apple-darwin -c %s -### -fno-ptrauth-returns -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
 
 // RUN: %clang -target aarch64 -c %s -### -mbranch-protection=none -mharden-pac-ret=load-return-address 2>&1 | \
 // RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
+// RUN: %clang -target arm64-apple-darwin -c %s -### -fno-ptrauth-returns -mharden-pac-ret=load-return-address 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
 
 // RUN: %clang -target aarch64 -c %s -### -mbranch-protection=pac-ret -mharden-pac-ret=none 2>&1 | \
 // RUN: FileCheck %s --check-prefixes=RA-HARDEN-NONE
+// RUN: %clang -target arm64-apple-darwin -c %s -### -fptrauth-returns -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-NONE
 
 // RUN: %clang -target aarch64 -c %s -### -mbranch-protection=pac-ret -mharden-pac-ret=load-return-address 2>&1 | \
 // RUN: FileCheck %s --check-prefixes=RA-HARDEN-LRA
+// RUN: %clang -target arm64-apple-darwin -c %s -### -fptrauth-returns -mharden-pac-ret=load-return-address 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-LRA
 
 // RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=none 2>&1 | \
 // RUN: FileCheck %s --check-prefixes=RA-HARDEN-NONE
@@ -61,6 +72,12 @@
 
 // RUN: not %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=foo 2>&1 | \
 // RUN: FileCheck %s --check-prefixes=BAD-HARDEN-PROTECTION
+// RUN: not %clang -target arm64-apple-darwin -c %s -### -fptrauth-returns -mharden-pac-ret=foo 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=BAD-HARDEN-PROTECTION
+
+// RUN: not %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=load-return-address -mexecute-only 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-INCOMPATIBLE-EXEC-ONLY
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=none -mexecute-only
 
 // ABSENT-RA-HARDEN-NOT: "-mharden-pac-ret"
 // NO-RA-HARDEN:         ignoring '-mharden-pac-ret' as it requires return address signing
@@ -68,6 +85,7 @@
 // RA-HARDEN-NONE:       "-mharden-pac-ret=none"
 // RA-HARDEN-LRA:        "-mharden-pac-ret=load-return-address"
 // BAD-HARDEN-PROTECTION: unsupported argument 'foo' to option '-mharden-pac-ret='
+// RA-HARDEN-INCOMPATIBLE-EXEC-ONLY: the combination of '-mharden-pac-ret=load-return-address' and '-mexecute-only' is incompatible
 
 // RA-OFF: "-msign-return-address=none"
 // RA-NON-LEAF: "-msign-return-address=non-leaf"
@@ -76,6 +94,8 @@
 // KEY-A: "-msign-return-address-key=a_key"
 // KEY-NOT: "-msign-return-address-key"
 
+// RA-INCOMPATIBLE-PTRAUTHRETURNS: the combination of '-mbranch-protection=standard' and '-fptrauth-returns' is incompatible
+
 // BTE-OFF-NOT: "-mbranch-target-enforce"
 // BTE-ON: "-mbranch-target-enforce"
 

>From 84a619b17b8475232390c24451f06f2fb8e5036d Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Wed, 22 Jul 2026 11:59:28 +0100
Subject: [PATCH 4/9] Add the new feature to the Release Notes

---
 clang/docs/ReleaseNotes.md | 8 ++++++++
 llvm/docs/ReleaseNotes.md  | 5 +++++
 2 files changed, 13 insertions(+)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 44ae8ca102100..f72728d7ee0b7 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -123,6 +123,14 @@ in a future version of Clang.
 - New option `-fdefined-pointer-subtraction` added to preserve stable semantics
   when subtracting pointers to unrelated objects.
 
+- Added the AArch64 option `-mharden-pac-ret=load-return-address` to harden
+  return address signing against PACMAN attacks. The option requires return
+  address signing to be enabled and emits a load of the return address before
+  returning, reducing the cache side channel used to guess pointer
+  authentication codes. See
+  {doc}`Return Address Signing Hardening <ReturnAddressSigningHardening>` for
+  more information.
+
 ### Deprecated Compiler Flags
 
 ### Modified Compiler Flags
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 5f47ab6dd925f..60637af256c74 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -73,6 +73,11 @@ Makes programs 10x faster by doing Special New Thing.
 * On AArch64 Windows targets, return address signing now uses the B-key by
   default because Windows unwind information only supports B-key signing.
 
+* Added support for hardening return address signing against PACMAN attacks.
+  Functions with the `"sign-return-address-harden"="load-return-address"`
+  attribute perform a load of the return address before returning, reducing the
+  cache side channel used to guess pointer authentication codes.
+
 ### Changes to the AMDGPU Backend
 
 * Replaced `xnack` and `sramecc` target features with `amdgpu.xnack`

>From 22ffc981b19ce68ecf10fedea77df11c9a03b281 Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Wed, 22 Jul 2026 12:08:32 +0100
Subject: [PATCH 5/9] rst: fix typo and ran formatter

---
 clang/docs/ReturnAddressSigningHardening.rst | 125 +++++++++----------
 1 file changed, 61 insertions(+), 64 deletions(-)

diff --git a/clang/docs/ReturnAddressSigningHardening.rst b/clang/docs/ReturnAddressSigningHardening.rst
index 063132d6e6f0b..fd34addb01396 100644
--- a/clang/docs/ReturnAddressSigningHardening.rst
+++ b/clang/docs/ReturnAddressSigningHardening.rst
@@ -9,15 +9,15 @@
  Introduction
 **************
 
-Return Address Signing Hardening is a mitigation against the PACMAN
-attack, which aims to bypass Pointer Authentication on AArch64 targets.
-The hardening mechanism described here is specific to pointer
-authentication of return addresses.
+Return Address Signing Hardening is a mitigation against the PACMAN attack,
+which aims to bypass Pointer Authentication on AArch64 targets. The hardening
+mechanism described here is specific to pointer authentication of return
+addresses.
 
-Return Address Signing, also known as Pointer Authentication Code
-(PAC-RET), is a feature devised to protect programs against Return
-Oriented Programming (ROP), in which attackers may hijack the return
-address of functions in order to direct execution to malicious code.
+Return Address Signing, also known as Pointer Authentication Code (PAC-RET), is
+a feature devised to protect programs against Return Oriented Programming (ROP),
+in which attackers may hijack the return address of functions in order to direct
+execution to malicious code.
 
 PAC-RET can be enabled via different command-line options:
 
@@ -29,23 +29,22 @@ PAC-RET can be enabled via different command-line options:
 More information can be found in :doc:`Pointer Authentication
 <PointerAuthentication>`.
 
-Return Address Signing Hardening is a mechanism to strenghthen Return
-Address Signing against the PACMAN attack in AArch64 targets. It can be
-enabled with ``-mharden-pac-ret=load-return-address``.
+Return Address Signing Hardening is a mechanism to strenghthen Return Address
+Signing against the PACMAN attack in AArch64 targets. It can be enabled with
+``-mharden-pac-ret=load-return-address``.
 
 ***************
  PACMAN attack
 ***************
 
-PACMAN is an attack that aims to extract valuable information about
-pointer authentication codes using side-channels in speculative
-execution.
+PACMAN is an attack that aims to extract valuable information about pointer
+authentication codes using side-channels in speculative execution.
 
-It is performed with the use of gadgets to try and guess PAC codes.
-These guesses raise no faults because they are done in speculation. By
-observing the effects of the guessed PAC code on the processor's cache,
-it might be possible to determine the valid PAC code for the address to
-which the attacker wants the program to return.
+It is performed with the use of gadgets to try and guess PAC codes. These
+guesses raise no faults because they are done in speculation. By observing the
+effects of the guessed PAC code on the processor's cache, it might be possible
+to determine the valid PAC code for the address to which the attacker wants the
+program to return.
 
 A usual PACMAN gadget looks like this:
 
@@ -69,23 +68,22 @@ Such code would be compiled to:
    ret
    ...
 
-This code segment may be used as a gadget. A speculative execution of
-this segment can happen as follows:
+This code segment may be used as a gadget. A speculative execution of this
+segment can happen as follows:
 
-   -  If the Link Register (LR) has the right PAC code, ``autiasp`` will
-      succeed and strip the PAC code out of the it. The processor's
-      instruction fetcher will then bring the code after the return into
-      the cache (that is, the instructions located at the address
-      pointed by LR).
+   -  If the Link Register (LR) has the right PAC code, ``autiasp`` will succeed
+      and strip the PAC code out of it. The processor's instruction fetcher will
+      then bring the code after the return into the cache (that is, the
+      instructions located at the address pointed by LR).
 
-   -  If the LR has the wrong PAC code, ``autiasp`` will not succeed and
-      hence will write a predefined error value to the LR's higher bits.
-      Because of this, the instruction fetcher will not bring the code
-      after the return into the cache.
+   -  If the LR has the wrong PAC code, ``autiasp`` will not succeed and hence
+      will write a predefined error value to the LR's higher bits. Because of
+      this, the instruction fetcher will not bring the code after the return
+      into the cache.
 
-This difference in behavior is what drives the PACMAN attack. An
-attacker can try to guess PAC codes and monitor cache behavior until the
-code after the return is observed to have been brought into the cache.
+This difference in behavior is what drives the PACMAN attack. An attacker can
+try to guess PAC codes and monitor cache behavior until the code after the
+return is observed to have been brought into the cache.
 
 Details can be found in https://pacmanattack.com.
 
@@ -93,8 +91,8 @@ Details can be found in https://pacmanattack.com.
  Hardening
 ***********
 
-In order to mitigate the PACMAN attack, a hardening mechanism can be
-enabled with ``-mharden-pac-ret=load-return-address``.
+In order to mitigate the PACMAN attack, a hardening mechanism can be enabled
+with ``-mharden-pac-ret=load-return-address``.
 
 .. code:: asm
 
@@ -109,25 +107,25 @@ enabled with ``-mharden-pac-ret=load-return-address``.
    ...
 
 The idea is to always bring the code after the return into cache (the
-instructions located at the address pointed by LR), therefore minimizing
-the difference between a speculative execution with a correct PAC code
-and with an incorrect one.
+instructions located at the address pointed by LR), therefore minimizing the
+difference between a speculative execution with a correct PAC code and with an
+incorrect one.
 
    -  ``autiasp`` performs the authentication step.
-   -  ``mov x8, x30`` copies the return address (LR and x30 are
-      synonyms) to a temporary.
+   -  ``mov x8, x30`` copies the return address (LR and x30 are synonyms) to a
+      temporary.
    -  ``xpaclri`` strips the PAC code out of the return address in x30.
    -  ``ldr w30, [x30]`` performs a load of the return address in x30.
    -  ``ret x8`` returns to the authenticated return address.
 
-The load operation brings the code into the cache even if the
-authentication step fails. As a consequence, in either case the code is
-loaded into the cache. Furthermore, the return operation uses the
-original return address before stripping, so the return address
-protection is still kept in place in a normal non-speculative execution.
+The load operation brings the code into the cache even if the authentication
+step fails. As a consequence, in either case the code is loaded into the cache.
+Furthermore, the return operation uses the original return address before
+stripping, so the return address protection is still kept in place in a normal
+non-speculative execution.
 
-If FEAT_PAUTH is present, the code sequence can use instructions only
-available with said feature with no change in semantics:
+If FEAT_PAUTH is present, the code sequence can use instructions only available
+with said feature with no change in semantics:
 
 .. code:: asm
 
@@ -142,17 +140,16 @@ available with said feature with no change in semantics:
 *********************
 
 Return address signing hardening can be enabled at module level with
-``-mharden-pac-ret=load-return-address``. It requires PAC-RET to be
-enabled at module level as well.
+``-mharden-pac-ret=load-return-address``. It requires PAC-RET to be enabled at
+module level as well.
 
 ********************
  Function attribute
 ********************
 
-In addition to the command-line option
-``-mharden-pac-ret=load-return-address``, the developer can enable the
-mitigation at function level with the use of the corresponding function
-attribute.
+In addition to the command-line option ``-mharden-pac-ret=load-return-address``,
+the developer can enable the mitigation at function level with the use of the
+corresponding function attribute.
 
 .. code:: C
 
@@ -165,14 +162,14 @@ attribute.
  Caveats
 *********
 
-The load of return address brings the code into the shared
-instruction/data cache, therefore this cache level can't be used as an
-oracle to find out whether the authentication succeeded or not. However,
-in the case of authentication success, the code is also fetched into the
-instruction cache. An attacker who is able to measure this cache level
-specifically may still be able to carry out the exploit.
-
-Another caveat is if the code at the return address contains a load
-operation within the speculation window. If this is the case, this load
-will only execute speculatively if authentication succeeds, thus opening
-the program up for exploitation despite the mitigation.
+The load of return address brings the code into the shared instruction/data
+cache, therefore this cache level can't be used as an oracle to find out whether
+the authentication succeeded or not. However, in the case of authentication
+success, the code is also fetched into the instruction cache. An attacker who is
+able to measure this cache level specifically may still be able to carry out the
+exploit.
+
+Another caveat is if the code at the return address contains a load operation
+within the speculation window. If this is the case, this load will only execute
+speculatively if authentication succeeds, thus opening the program up for
+exploitation despite the mitigation.

>From aef4aa401443113eafccc62063ce1af04d7569f2 Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Tue, 18 Aug 2026 17:43:12 +0100
Subject: [PATCH 6/9] Address reviews:

 - Emit module flag
 - Changes to the rst file.
 - Nit fixes.
---
 clang/docs/ReleaseNotes.md                    |  4 +-
 ... ReturnAddressAuthenticationHardening.rst} | 34 ++++-----
 clang/docs/index.rst                          |  2 +-
 clang/include/clang/Basic/LangOptions.h       |  7 ++
 clang/lib/CodeGen/CodeGenModule.cpp           |  4 +
 clang/lib/CodeGen/TargetInfo.cpp              |  7 +-
 .../CodeGen/AArch64/sign-return-address.c     | 76 +++++++++++--------
 clang/test/Driver/aarch64-security-options.c  |  9 ++-
 8 files changed, 85 insertions(+), 58 deletions(-)
 rename clang/docs/{ReturnAddressSigningHardening.rst => ReturnAddressAuthenticationHardening.rst} (81%)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 405e723bc3448..abe4a338acf9f 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -214,8 +214,8 @@ features cannot lower the translation-unit ABI level;
   address signing to be enabled and emits a load of the return address before
   returning, reducing the cache side channel used to guess pointer
   authentication codes. See
-  {doc}`Return Address Signing Hardening <ReturnAddressSigningHardening>` for
-  more information.
+  {doc}`Return Address Authentication Hardening <ReturnAddressAuthenticationHardening>`
+  for more information.
 
 ### Deprecated Compiler Flags
 
diff --git a/clang/docs/ReturnAddressSigningHardening.rst b/clang/docs/ReturnAddressAuthenticationHardening.rst
similarity index 81%
rename from clang/docs/ReturnAddressSigningHardening.rst
rename to clang/docs/ReturnAddressAuthenticationHardening.rst
index fd34addb01396..259f8e3c45d43 100644
--- a/clang/docs/ReturnAddressSigningHardening.rst
+++ b/clang/docs/ReturnAddressAuthenticationHardening.rst
@@ -1,6 +1,6 @@
-##################################
- Return Address Signing Hardening
-##################################
+#########################################
+ Return Address Authentication Hardening
+#########################################
 
 .. contents::
    :local:
@@ -9,17 +9,17 @@
  Introduction
 **************
 
-Return Address Signing Hardening is a mitigation against the PACMAN attack,
-which aims to bypass Pointer Authentication on AArch64 targets. The hardening
-mechanism described here is specific to pointer authentication of return
-addresses.
+Return Address Authentication Hardening is a mitigation against the PACMAN
+attack, which aims to bypass Pointer Authentication on AArch64 targets. The
+hardening mechanism described here is specific to pointer authentication of
+return addresses.
 
-Return Address Signing, also known as Pointer Authentication Code (PAC-RET), is
-a feature devised to protect programs against Return Oriented Programming (ROP),
-in which attackers may hijack the return address of functions in order to direct
+Return Address Signing (or Authentication), also known as pac-ret, is a feature
+devised to protect programs against Return Oriented Programming (ROP), in which
+attackers may hijack the return address of functions in order to direct
 execution to malicious code.
 
-PAC-RET can be enabled via different command-line options:
+pac-ret can be enabled via different command-line options:
 
    -  ``-mbranch-protection=`` with ``pac-ret``, ``pac-ret+leaf`` or
       ``standard`` as value.
@@ -29,9 +29,9 @@ PAC-RET can be enabled via different command-line options:
 More information can be found in :doc:`Pointer Authentication
 <PointerAuthentication>`.
 
-Return Address Signing Hardening is a mechanism to strenghthen Return Address
-Signing against the PACMAN attack in AArch64 targets. It can be enabled with
-``-mharden-pac-ret=load-return-address``.
+Return Address Authentication Hardening is a mechanism to strenghthen Return
+Address Signing against the PACMAN attack in AArch64 targets. It can be enabled
+with ``-mharden-pac-ret=load-return-address``.
 
 ***************
  PACMAN attack
@@ -139,9 +139,9 @@ with said feature with no change in semantics:
  Command-line option
 *********************
 
-Return address signing hardening can be enabled at module level with
-``-mharden-pac-ret=load-return-address``. It requires PAC-RET to be enabled at
-module level as well.
+Return address authentication hardening can be enabled at translation unit level
+with ``-mharden-pac-ret=load-return-address``. It requires pac-ret to be enabled
+at translation unit level as well.
 
 ********************
  Function attribute
diff --git a/clang/docs/index.rst b/clang/docs/index.rst
index 90f045ce17e2a..000d765e422a9 100644
--- a/clang/docs/index.rst
+++ b/clang/docs/index.rst
@@ -51,7 +51,7 @@ Using Clang as a Compiler
    ControlFlowIntegrity
    LTOVisibility
    PointerAuthentication
-   ReturnAddressSigningHardening
+   ReturnAddressAuthenticationHardening
    SafeStack
    ShadowCallStack
    StructureProtection
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index cd8ae724dcb6f..7da5baf5ed8b9 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -770,6 +770,13 @@ class LangOptions : public LangOptionsBase {
     return getSignReturnAddressScope() == SignReturnAddressScopeKind::All;
   }
 
+  /// Check if return address signing hardening is enabled and uses load of
+  /// return address.
+  bool isSignReturnAddressHardenWithLoadReturnAddress() const {
+    return getSignReturnAddressHardening() ==
+           SignReturnAddressHardeningKind::LoadReturnAddress;
+  }
+
   bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; }
 
   bool hasDefaultVisibilityExportMapping() const {
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index b171418f5d51d..d939ba01684a2 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1605,6 +1605,10 @@ void CodeGenModule::Release() {
     if (!LangOpts.isSignReturnAddressWithAKey())
       getModule().addModuleFlag(llvm::Module::Min,
                                 "sign-return-address-with-bkey", 2);
+    if (LangOpts.isSignReturnAddressHardenWithLoadReturnAddress())
+      getModule().addModuleFlag(
+          llvm::Module::Min, "sign-return-address-harden-load-return-address",
+          2);
   }
   if (T.isAArch64()) {
     // Emit the following 4 module flags so LLVM can derive corresponding
diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp
index 43f87d3bec1d0..bd37ae114004b 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -220,10 +220,6 @@ void TargetCodeGenInfo::setBranchProtectionFnAttributes(
   if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) {
     F.addFnAttr("sign-return-address", BPI.getSignReturnAddrStr());
     F.addFnAttr("sign-return-address-key", BPI.getSignKeyStr());
-    if (BPI.SignReturnAddressHardening !=
-        LangOptions::SignReturnAddressHardeningKind::None)
-      F.addFnAttr("sign-return-address-harden",
-                  BPI.getSignReturnAddressHardeningStr());
   } else {
     if (F.hasFnAttribute("sign-return-address"))
       F.removeFnAttr("sign-return-address");
@@ -234,7 +230,8 @@ void TargetCodeGenInfo::setBranchProtectionFnAttributes(
   if (BPI.SignReturnAddressHardening ==
       LangOptions::SignReturnAddressHardeningKind::None) {
     F.removeFnAttr("sign-return-address-harden");
-  } else {
+  } else if (BPI.SignReturnAddr !=
+             LangOptions::SignReturnAddressScopeKind::None) {
     F.addFnAttr("sign-return-address-harden",
                 BPI.getSignReturnAddressHardeningStr());
   }
diff --git a/clang/test/CodeGen/AArch64/sign-return-address.c b/clang/test/CodeGen/AArch64/sign-return-address.c
index c617b45c21317..16150d1a30112 100644
--- a/clang/test/CodeGen/AArch64/sign-return-address.c
+++ b/clang/test/CodeGen/AArch64/sign-return-address.c
@@ -6,6 +6,8 @@
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret+leaf  %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret+b-key %s | FileCheck %s --check-prefix=CHECK --check-prefix=B-KEY
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=bti %s           | FileCheck %s --check-prefix=CHECK --check-prefix=BTE
+// RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret -mharden-pac-ret=load-return-address %s | \
+//      FileCheck %s --check-prefix=CHECK --check-prefixes=HARDEN
 // RUN: %clang -target aarch64-windows-msvc -S -emit-llvm -o - -mbranch-protection=pac-ret %s | FileCheck %s --check-prefix=CHECK --check-prefix=WIN-PAC
 // RUN: %clang -target aarch64-windows-msvc -S -emit-llvm -o - -mbranch-protection=standard %s | FileCheck %s --check-prefix=CHECK --check-prefix=WIN-STD
 
@@ -19,49 +21,63 @@
 // NONE-NOT:  attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"
 // NONE-NOT:  attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement"
 
-// ALL:   attributes #[[#ATTR]] = { {{.*}} "sign-return-address"
-// PART:  attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="a_key"
-// B-KEY: attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="b_key"
-// BTE:   attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement"
+// ALL:     attributes #[[#ATTR]] = { {{.*}} "sign-return-address"
+// PART:    attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="a_key"
+// B-KEY:   attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="b_key"
+// BTE:     attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement"
+// HARDEN:  attributes #[[#ATTR]] = { {{.*}} "sign-return-address-harden"="load-return-address"
 // WIN-PAC: attributes #[[#ATTR]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
 // WIN-STD: attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement" {{.*}} "guarded-control-stack" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
 
 
 // Check module attributes
 
-// NONE-NOT:  !"branch-target-enforcement"
-// ALL-NOT:   !"branch-target-enforcement"
-// PART-NOT:  !"branch-target-enforcement"
-// BTE:       !{i32 8, !"branch-target-enforcement", i32 2}
-// B-KEY-NOT: !"branch-target-enforcement"
+// NONE-NOT:    !"branch-target-enforcement"
+// ALL-NOT:     !"branch-target-enforcement"
+// PART-NOT:    !"branch-target-enforcement"
+// BTE:         !{i32 8, !"branch-target-enforcement", i32 2}
+// B-KEY-NOT:   !"branch-target-enforcement"
+// HARDEN-NOT:  !"branch-target-enforcement"
 // WIN-PAC-NOT: !"branch-target-enforcement"
-// WIN-STD:   !{i32 8, !"branch-target-enforcement", i32 2}
+// WIN-STD:     !{i32 8, !"branch-target-enforcement", i32 2}
 
 // WIN-PAC-NOT: !"guarded-control-stack"
-// WIN-STD: !{i32 8, !"guarded-control-stack", i32 2}
+// WIN-STD:     !{i32 8, !"guarded-control-stack", i32 2}
 
-// NONE-NOT:  !"sign-return-address"
-// ALL:   !{i32 8, !"sign-return-address", i32 2}
-// PART:  !{i32 8, !"sign-return-address", i32 2}
-// BTE-NOT:   !"sign-return-address"
-// B-KEY: !{i32 8, !"sign-return-address", i32 2}
-// WIN-PAC: !{i32 8, !"sign-return-address", i32 2}
-// WIN-STD: !{i32 8, !"sign-return-address", i32 2}
+// NONE-NOT: !"sign-return-address"
+// ALL:      !{i32 8, !"sign-return-address", i32 2}
+// PART:     !{i32 8, !"sign-return-address", i32 2}
+// BTE-NOT:  !"sign-return-address"
+// B-KEY:    !{i32 8, !"sign-return-address", i32 2}
+// HARDEN:   !{i32 8, !"sign-return-address", i32 2}
+// WIN-PAC:  !{i32 8, !"sign-return-address", i32 2}
+// WIN-STD:  !{i32 8, !"sign-return-address", i32 2}
 
-// NONE-NOT:  !"sign-return-address-all"
-// ALL:   !{i32 8, !"sign-return-address-all", i32 2}
-// PART-NOT:  !"sign-return-address-all"
-// BTE-NOT:   !"sign-return-address-all"
-// B-KEY-NOT: !"sign-return-address-all"
+// NONE-NOT:    !"sign-return-address-all"
+// ALL:         !{i32 8, !"sign-return-address-all", i32 2}
+// PART-NOT:    !"sign-return-address-all"
+// BTE-NOT:     !"sign-return-address-all"
+// B-KEY-NOT:   !"sign-return-address-all"
+// HARDEN-NOT:  !"sign-return-address-all"
 // WIN-PAC-NOT: !"sign-return-address-all"
 // WIN-STD-NOT: !"sign-return-address-all"
 
-// NONE-NOT:  !"sign-return-address-with-bkey"
-// ALL-NOT:   !"sign-return-address-with-bkey"
-// PART-NOT:  !"sign-return-address-with-bkey"
-// BTE-NOT:   !"sign-return-address-with-bkey"
-// B-KEY: !{i32 8, !"sign-return-address-with-bkey", i32 2}
-// WIN-PAC: !{i32 8, !"sign-return-address-with-bkey", i32 2}
-// WIN-STD: !{i32 8, !"sign-return-address-with-bkey", i32 2}
+// NONE-NOT:   !"sign-return-address-with-bkey"
+// ALL-NOT:    !"sign-return-address-with-bkey"
+// PART-NOT:   !"sign-return-address-with-bkey"
+// BTE-NOT:    !"sign-return-address-with-bkey"
+// HARDEN-NOT: !"sign-return-address-with-bkey"
+// B-KEY:      !{i32 8, !"sign-return-address-with-bkey", i32 2}
+// WIN-PAC:    !{i32 8, !"sign-return-address-with-bkey", i32 2}
+// WIN-STD:    !{i32 8, !"sign-return-address-with-bkey", i32 2}
+
+// NONE-NOT:    !"sign-return-address-harden-load-return-address"
+// ALL-NOT:     !"sign-return-address-harden-load-return-address"
+// PART-NOT:    !"sign-return-address-harden-load-return-address"
+// BTE-NOT:     !"sign-return-address-harden-load-return-address"
+// HARDEN:      !{i32 8, !"sign-return-address-harden-load-return-address", i32 2}
+// B-KEY-NOT:   !"sign-return-address-harden-load-return-address"
+// WIN-PAC-NOT: !"sign-return-address-harden-load-return-address"
+// WIN-STD-NOT: !"sign-return-address-harden-load-return-address"
 
 void foo() {}
diff --git a/clang/test/Driver/aarch64-security-options.c b/clang/test/Driver/aarch64-security-options.c
index bc63ea58b46d7..0a4356b278214 100644
--- a/clang/test/Driver/aarch64-security-options.c
+++ b/clang/test/Driver/aarch64-security-options.c
@@ -79,8 +79,10 @@
 // RUN: FileCheck %s --check-prefixes=BAD-HARDEN-PROTECTION
 
 // RUN: not %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=load-return-address -mexecute-only 2>&1 | \
-// RUN: FileCheck %s --check-prefixes=RA-HARDEN-INCOMPATIBLE-EXEC-ONLY
-// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=none -mexecute-only
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-LRA-INCOMPATIBLE-EXEC-ONLY
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=none -mexecute-only 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-NONE-COMPATIBLE-EXEC-ONLY
+
 
 // ABSENT-RA-HARDEN-NOT: "-mharden-pac-ret"
 // NO-RA-HARDEN:         ignoring '-mharden-pac-ret' as it requires return address signing
@@ -88,7 +90,8 @@
 // RA-HARDEN-NONE:       "-mharden-pac-ret=none"
 // RA-HARDEN-LRA:        "-mharden-pac-ret=load-return-address"
 // BAD-HARDEN-PROTECTION: unsupported argument 'foo' to option '-mharden-pac-ret='
-// RA-HARDEN-INCOMPATIBLE-EXEC-ONLY: the combination of '-mharden-pac-ret=load-return-address' and '-mexecute-only' is incompatible
+// RA-HARDEN-LRA-INCOMPATIBLE-EXEC-ONLY: the combination of '-mharden-pac-ret=load-return-address' and '-mexecute-only' is incompatible
+// RA-HARDEN-NONE-COMPATIBLE-EXEC-ONLY-NOT: the combination of '-mharden-pac-ret={{.*}}' and '-mexecute-only' is incompatible
 
 // RA-OFF: "-msign-return-address=none"
 // RA-NON-LEAF: "-msign-return-address=non-leaf"

>From 477508dbb7f621fefc243a28dd9b8d03a160a664 Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Wed, 2 Sep 2026 10:03:41 +0100
Subject: [PATCH 7/9] Fix precedence of function attrs over command line
 options

---
 clang/include/clang/Basic/TargetInfo.h        |  7 +-
 clang/lib/Basic/Targets/AArch64.cpp           | 13 ++-
 clang/lib/Basic/Targets/AArch64.h             |  2 +-
 clang/lib/Basic/Targets/ARM.cpp               |  6 +-
 clang/lib/Basic/Targets/ARM.h                 |  2 +-
 clang/lib/CodeGen/Targets/AArch64.cpp         | 12 +--
 clang/lib/CodeGen/Targets/ARM.cpp             |  2 +-
 clang/lib/Sema/SemaDeclAttr.cpp               |  3 +-
 clang/test/Sema/aarch64-harden-pac-ret-attr.c | 85 +++++++++++++------
 9 files changed, 84 insertions(+), 48 deletions(-)

diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 5c9e947377449..c7e232ed616a7 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1549,9 +1549,10 @@ class TargetInfo : public TransferrableTargetInfo,
     return false;
   }
 
-  /// Determine if this TargetInfo supports the given branch protection
-  /// specification
-  virtual bool validateBranchProtection(StringRef Spec, StringRef Arch,
+  /// Determines whether this TargetInfo supports the given branch protection
+  /// specification. BPI is modified based on the input arguments. Returns
+  /// whether the specification is valid.
+  virtual bool validateBranchProtection(const ParsedTargetAttr &Attr,
                                         BranchProtectionInfo &BPI,
                                         const LangOptions &LO,
                                         StringRef &Err) const {
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index ec29fc656d4d3..c9dec77afa4de 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -249,13 +249,13 @@ bool AArch64TargetInfo::validateGlobalRegisterVariable(
          getTargetOpts().FeatureMap.lookup(("reserve-x" + RegNum).str());
 }
 
-bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
+bool AArch64TargetInfo::validateBranchProtection(const ParsedTargetAttr &Attr,
                                                  BranchProtectionInfo &BPI,
                                                  const LangOptions &LO,
                                                  StringRef &Err) const {
   llvm::ARM::ParsedBranchProtection PBP;
-  if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err, getTriple(),
-                                        HasPAuthLR))
+  if (!llvm::ARM::parseBranchProtection(Attr.BranchProtection, PBP, Err,
+                                        getTriple(), HasPAuthLR))
     return false;
 
   // GCS is currently untested with ptrauth-returns, but enabling this could be
@@ -276,6 +276,13 @@ bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
   else
     BPI.SignKey = LangOptions::SignReturnAddressKeyKind::BKey;
 
+  if (Attr.SignReturnAddrHardening.empty())
+    BPI.SignReturnAddressHardening =
+        LangOptions::SignReturnAddressHardeningKind::None;
+  else if (auto Hardening =
+               parseSignReturnAddressHardening(Attr.SignReturnAddrHardening))
+    BPI.SignReturnAddressHardening = *Hardening;
+
   BPI.BranchTargetEnforcement = PBP.BranchTargetEnforcement;
   BPI.BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
   BPI.GuardedControlStack = PBP.GuardedControlStack;
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index 4500b66d0f945..aeea2ab16ea59 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -140,7 +140,7 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
   StringRef getABI() const override;
   bool setABI(const std::string &Name) override;
 
-  bool validateBranchProtection(StringRef Spec, StringRef Arch,
+  bool validateBranchProtection(const ParsedTargetAttr &Attr,
                                 BranchProtectionInfo &BPI,
                                 const LangOptions &LO,
                                 StringRef &Err) const override;
diff --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp
index 8d6e8d6990e80..dfdb17f8e7715 100644
--- a/clang/lib/Basic/Targets/ARM.cpp
+++ b/clang/lib/Basic/Targets/ARM.cpp
@@ -370,14 +370,16 @@ bool ARMTargetInfo::isBranchProtectionSupportedArch(StringRef Arch) const {
   return a.isArmT32() && (Profile == llvm::ARM::ProfileKind::M);
 }
 
-bool ARMTargetInfo::validateBranchProtection(StringRef Spec, StringRef Arch,
+bool ARMTargetInfo::validateBranchProtection(const ParsedTargetAttr &Attr,
                                              BranchProtectionInfo &BPI,
                                              const LangOptions &LO,
                                              StringRef &Err) const {
   llvm::ARM::ParsedBranchProtection PBP;
-  if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err, getTriple()))
+  if (!llvm::ARM::parseBranchProtection(Attr.BranchProtection, PBP, Err,
+                                        getTriple()))
     return false;
 
+  StringRef Arch = Attr.CPU.empty() ? getTargetOpts().CPU : Attr.CPU;
   if (!isBranchProtectionSupportedArch(Arch))
     return false;
 
diff --git a/clang/lib/Basic/Targets/ARM.h b/clang/lib/Basic/Targets/ARM.h
index 976e1e06ae9b2..a9ab0373a7f5e 100644
--- a/clang/lib/Basic/Targets/ARM.h
+++ b/clang/lib/Basic/Targets/ARM.h
@@ -146,7 +146,7 @@ class LLVM_LIBRARY_VISIBILITY ARMTargetInfo : public TargetInfo {
   bool setABI(const std::string &Name) override;
 
   bool isBranchProtectionSupportedArch(StringRef Arch) const override;
-  bool validateBranchProtection(StringRef Spec, StringRef Arch,
+  bool validateBranchProtection(const ParsedTargetAttr &Attr,
                                 BranchProtectionInfo &BPI,
                                 const LangOptions &LO,
                                 StringRef &Err) const override;
diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp
index 3615face72519..5315e6197c7d3 100644
--- a/clang/lib/CodeGen/Targets/AArch64.cpp
+++ b/clang/lib/CodeGen/Targets/AArch64.cpp
@@ -157,18 +157,8 @@ class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {
       if (!Attr.BranchProtection.empty()) {
         StringRef Error;
         (void)CGM.getTarget().validateBranchProtection(
-            Attr.BranchProtection, Attr.CPU, BPI, CGM.getLangOpts(), Error);
+            Attr, BPI, CGM.getLangOpts(), Error);
         assert(Error.empty());
-
-        // Hardening is only accepted in the target attribute if PAC-RET is also
-        // present there. Invalid combinations are handled in Sema.
-        if (BPI.SignReturnAddr !=
-            LangOptions::SignReturnAddressScopeKind::None) {
-          if (auto Hardening = CGM.getTarget().parseSignReturnAddressHardening(
-                  Attr.SignReturnAddrHardening)) {
-            BPI.SignReturnAddressHardening = *Hardening;
-          }
-        }
       }
     }
     setBranchProtectionFnAttributes(BPI, *Fn);
diff --git a/clang/lib/CodeGen/Targets/ARM.cpp b/clang/lib/CodeGen/Targets/ARM.cpp
index c6435dd6f5a8b..17bae6b7c2021 100644
--- a/clang/lib/CodeGen/Targets/ARM.cpp
+++ b/clang/lib/CodeGen/Targets/ARM.cpp
@@ -149,7 +149,7 @@ class ARMTargetCodeGenInfo : public TargetCodeGenInfo {
         StringRef Arch =
             Attr.CPU.empty() ? CGM.getTarget().getTargetOpts().CPU : Attr.CPU;
         if (!CGM.getTarget().validateBranchProtection(
-                Attr.BranchProtection, Arch, BPI, CGM.getLangOpts(), DiagMsg)) {
+                Attr, BPI, CGM.getLangOpts(), DiagMsg)) {
           CGM.getDiags().Report(
               D->getLocation(),
               diag::warn_target_unsupported_branch_protection_attribute)
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index d729bed81818d..94fa252d6f3be 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3724,8 +3724,7 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
   StringRef DiagMsg;
 
   if (!Context.getTargetInfo().validateBranchProtection(
-          ParsedAttrs.BranchProtection, ParsedAttrs.CPU, BPI,
-          Context.getLangOpts(), DiagMsg)) {
+          ParsedAttrs, BPI, Context.getLangOpts(), DiagMsg)) {
     if (DiagMsg.empty())
       return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
              << Unsupported << None << "branch-protection" << Target;
diff --git a/clang/test/Sema/aarch64-harden-pac-ret-attr.c b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
index 28795003e3a5b..2da3175733b42 100644
--- a/clang/test/Sema/aarch64-harden-pac-ret-attr.c
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
@@ -4,51 +4,88 @@
 // RUN: %clang_cc1 -triple aarch64 -emit-llvm -target-cpu generic -target-feature +v8.5a %s -msign-return-address=all -mharden-pac-ret=none -o - | FileCheck %s
 // RUN: %clang_cc1 -triple aarch64 -emit-llvm -target-cpu generic -target-feature +v8.5a %s -msign-return-address=all -mharden-pac-ret=load-return-address -o - | FileCheck %s
 
-__attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=none")))
+__attribute__ ((target("branch-protection=none")))
 void f1() {}
-// CHECK: define{{.*}} void @f1() #[[#F1:]]
+// CHECK: define{{.*}} void @f1() #[[#A1:]]
 
-__attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=load-return-address")))
+__attribute__ ((target("branch-protection=pac-ret")))
 void f2() {}
-// CHECK: define{{.*}} void @f2() #[[#F2:]]
+// CHECK: define{{.*}} void @f2() #[[#A2:]]
 
-__attribute__ ((target("branch-protection=pac-ret+leaf,harden-pac-ret=none")))
+__attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=none")))
 void f3() {}
-// CHECK: define{{.*}} void @f3() #[[#F3:]]
+// CHECK: define{{.*}} void @f3() #[[#A2]]
 
-__attribute__ ((target("branch-protection=pac-ret+leaf,harden-pac-ret=load-return-address")))
+__attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=load-return-address")))
 void f4() {}
-// CHECK: define{{.*}} void @f4() #[[#F4:]]
+// CHECK: define{{.*}} void @f4() #[[#A3:]]
 
-__attribute__ ((target("branch-protection=pac-ret+b-key,harden-pac-ret=none")))
+__attribute__ ((target("branch-protection=pac-ret+leaf")))
 void f5() {}
-// CHECK: define{{.*}} void @f5() #[[#F5:]]
+// CHECK: define{{.*}} void @f5() #[[#A4:]]
 
-__attribute__ ((target("branch-protection=pac-ret+b-key,harden-pac-ret=load-return-address")))
+__attribute__ ((target("branch-protection=pac-ret+leaf,harden-pac-ret=none")))
 void f6() {}
-// CHECK: define{{.*}} void @f6() #[[#F6:]]
+// CHECK: define{{.*}} void @f6() #[[#A4]]
 
-__attribute__ ((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=none")))
+__attribute__ ((target("branch-protection=pac-ret+leaf,harden-pac-ret=load-return-address")))
 void f7() {}
-// CHECK: define{{.*}} void @f7() #[[#F7:]]
+// CHECK: define{{.*}} void @f7() #[[#A5:]]
 
-__attribute__ ((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=load-return-address")))
+__attribute__ ((target("branch-protection=pac-ret+b-key")))
 void f8() {}
-// CHECK: define{{.*}} void @f8() #[[#F8:]]
+// CHECK: define{{.*}} void @f8() #[[#A6:]]
+
+__attribute__ ((target("branch-protection=pac-ret+b-key,harden-pac-ret=none")))
+void f9() {}
+// CHECK: define{{.*}} void @f9() #[[#A6]]
+
+__attribute__ ((target("branch-protection=pac-ret+b-key,harden-pac-ret=load-return-address")))
+void f10() {}
+// CHECK: define{{.*}} void @f10() #[[#A7:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf+b-key")))
+void f11() {}
+// CHECK: define{{.*}} void @f11() #[[#A8:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=none")))
+void f12() {}
+// CHECK: define{{.*}} void @f12() #[[#A8]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=load-return-address")))
+void f13() {}
+// CHECK: define{{.*}} void @f13() #[[#A9:]]
 
 // These check patterns rely on the fact that "sign-return-address-harden" appears after "sign-return-address"
 
-// CHECK:     attributes #[[#F1]] = { {{.*}} "sign-return-address"="non-leaf"
+// CHECK:     attributes #[[#A1]]
+// CHECK-NOT: "sign-return-address"
 // CHECK-NOT: "sign-return-address-harden"
-// CHECK:     attributes #[[#F2]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address"
-// CHECK:     attributes #[[#F3]] = { {{.*}} "sign-return-address"="all"
+// CHECK:     attributes #[[#A2]]
+// CHECK:     "sign-return-address"="non-leaf"
 // CHECK-NOT: "sign-return-address-harden"
-// CHECK:     attributes #[[#F4]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address"
-// CHECK:     attributes #[[#F5]] = { {{.*}} "sign-return-address"="non-leaf"
+// CHECK:     attributes #[[#A3]]
+// CHECK:     "sign-return-address"="non-leaf"
+// CHECK:     "sign-return-address-harden"="load-return-address"
+// CHECK:     attributes #[[#A4]]
+// CHECK:     "sign-return-address"="all"
 // CHECK-NOT: "sign-return-address-harden"
+// CHECK:     attributes #[[#A5]]
+// CHECK:     "sign-return-address"="all"
+// CHECK:     "sign-return-address-harden"="load-return-address"
+// CHECK:     attributes #[[#A6]]
+// CHECK:     "sign-return-address"="non-leaf"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#A7]]
+// CHECK:     "sign-return-address"="non-leaf"
+// CHECK:     "sign-return-address-harden"="load-return-address"
 // CHECK:     "sign-return-address-key"="b_key"
-// CHECK:     attributes #[[#F6]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"
-// CHECK:     attributes #[[#F7]] = { {{.*}} "sign-return-address"="all"
+// CHECK:     attributes #[[#A8]]
+// CHECK:     "sign-return-address"="all"
 // CHECK-NOT: "sign-return-address-harden"
 // CHECK:     "sign-return-address-key"="b_key"
-// CHECK:     attributes #[[#F8]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#A9]]
+// CHECK:     "sign-return-address"="all"
+// CHECK:     "sign-return-address-harden"="load-return-address"
+// CHECK:     "sign-return-address-key"="b_key"

>From 4ef6021797e9a9d72c160ee8043075a895c1c0f7 Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Wed, 2 Sep 2026 17:55:53 +0100
Subject: [PATCH 8/9] Convert from rst to md

---
 .../ReturnAddressAuthenticationHardening.md   | 300 +++++++++---------
 1 file changed, 145 insertions(+), 155 deletions(-)

diff --git a/clang/docs/ReturnAddressAuthenticationHardening.md b/clang/docs/ReturnAddressAuthenticationHardening.md
index 259f8e3c45d43..4c9172a54ccff 100644
--- a/clang/docs/ReturnAddressAuthenticationHardening.md
+++ b/clang/docs/ReturnAddressAuthenticationHardening.md
@@ -1,175 +1,165 @@
-#########################################
- Return Address Authentication Hardening
-#########################################
+# Return Address Authentication Hardening
 
-.. contents::
-   :local:
+```{contents}
+:local:
+```
 
-**************
- Introduction
-**************
+## Introduction
 
-Return Address Authentication Hardening is a mitigation against the PACMAN
-attack, which aims to bypass Pointer Authentication on AArch64 targets. The
-hardening mechanism described here is specific to pointer authentication of
-return addresses.
+Return Address Authentication Hardening is a mitigation against the
+PACMAN attack, which aims to bypass Pointer Authentication on AArch64
+targets. The hardening mechanism described here is specific to pointer
+authentication of return addresses.
 
-Return Address Signing (or Authentication), also known as pac-ret, is a feature
-devised to protect programs against Return Oriented Programming (ROP), in which
-attackers may hijack the return address of functions in order to direct
-execution to malicious code.
+Return Address Signing (or Authentication), also known as pac-ret, is a
+feature devised to protect programs against Return Oriented Programming
+(ROP), in which attackers may hijack the return address of functions in
+order to direct execution to malicious code.
 
 pac-ret can be enabled via different command-line options:
 
-   -  ``-mbranch-protection=`` with ``pac-ret``, ``pac-ret+leaf`` or
-      ``standard`` as value.
-   -  ``-msign-return-address=`` with ``non-leaf`` or ``all`` as value.
-   -  ``-fptrauth-returns``.
+- `-mbranch-protection=` with `pac-ret`, `pac-ret+leaf` or `standard` as
+  value.
+- `-msign-return-address=` with `non-leaf` or `all` as value.
+- `-fptrauth-returns`.
 
-More information can be found in :doc:`Pointer Authentication
-<PointerAuthentication>`.
+More information can be found in
+[Pointer Authentication](PointerAuthentication.md).
 
-Return Address Authentication Hardening is a mechanism to strenghthen Return
-Address Signing against the PACMAN attack in AArch64 targets. It can be enabled
-with ``-mharden-pac-ret=load-return-address``.
+Return Address Authentication Hardening is a mechanism to strenghthen
+Return Address Signing against the PACMAN attack in AArch64 targets. It
+can be enabled with `-mharden-pac-ret=load-return-address`.
 
-***************
- PACMAN attack
-***************
+## PACMAN attack
 
-PACMAN is an attack that aims to extract valuable information about pointer
-authentication codes using side-channels in speculative execution.
+PACMAN is an attack that aims to extract valuable information about
+pointer authentication codes using side-channels in speculative
+execution.
 
-It is performed with the use of gadgets to try and guess PAC codes. These
-guesses raise no faults because they are done in speculation. By observing the
-effects of the guessed PAC code on the processor's cache, it might be possible
-to determine the valid PAC code for the address to which the attacker wants the
-program to return.
+It is performed with the use of gadgets to try and guess PAC codes.
+These guesses raise no faults because they are done in speculation. By
+observing the effects of the guessed PAC code on the processor's cache,
+it might be possible to determine the valid PAC code for the address to
+which the attacker wants the program to return.
 
 A usual PACMAN gadget looks like this:
 
-.. code:: C
-
-   void function() {
-     ...
-     if (condition)
-       return;
-     ...
-   }
+```c
+void function() {
+  ...
+  if (condition)
+    return;
+  ...
+}
+```
 
 Such code would be compiled to:
 
-.. code:: asm
-
-   paciasp
-   ...
-   cbz w0, .LBB0_2
-   autiasp
-   ret
-   ...
-
-This code segment may be used as a gadget. A speculative execution of this
-segment can happen as follows:
-
-   -  If the Link Register (LR) has the right PAC code, ``autiasp`` will succeed
-      and strip the PAC code out of it. The processor's instruction fetcher will
-      then bring the code after the return into the cache (that is, the
-      instructions located at the address pointed by LR).
-
-   -  If the LR has the wrong PAC code, ``autiasp`` will not succeed and hence
-      will write a predefined error value to the LR's higher bits. Because of
-      this, the instruction fetcher will not bring the code after the return
-      into the cache.
-
-This difference in behavior is what drives the PACMAN attack. An attacker can
-try to guess PAC codes and monitor cache behavior until the code after the
-return is observed to have been brought into the cache.
-
-Details can be found in https://pacmanattack.com.
-
-***********
- Hardening
-***********
-
-In order to mitigate the PACMAN attack, a hardening mechanism can be enabled
-with ``-mharden-pac-ret=load-return-address``.
-
-.. code:: asm
-
-   paciasp
-   ...
-   cbz w0, .LBB0_2
-   autiasp
-   mov     x8, x30
-   xpaclri
-   ldr     w30, [x30]
-   ret     x8
-   ...
+```asm
+paciasp
+...
+cbz w0, .LBB0_2
+autiasp
+ret
+...
+```
+
+This code segment may be used as a gadget. A speculative execution of
+this segment can happen as follows:
+
+- If the Link Register (LR) has the right PAC code, `autiasp` will
+  succeed and strip the PAC code out of it. The processor's instruction
+  fetcher will then bring the code after the return into the cache (that
+  is, the instructions located at the address pointed by LR).
+
+- If the LR has the wrong PAC code, `autiasp` will not succeed and
+  hence will write a predefined error value to the LR's higher bits.
+  Because of this, the instruction fetcher will not bring the code
+  after the return into the cache.
+
+This difference in behavior is what drives the PACMAN attack. An
+attacker can try to guess PAC codes and monitor cache behavior until the
+code after the return is observed to have been brought into the cache.
+
+Details can be found at [pacmanattack.com](https://pacmanattack.com).
+
+## Hardening
+
+In order to mitigate the PACMAN attack, a hardening mechanism can be
+enabled with `-mharden-pac-ret=load-return-address`.
+
+```asm
+paciasp
+...
+cbz w0, .LBB0_2
+autiasp
+mov     x8, x30
+xpaclri
+ldr     w30, [x30]
+ret     x8
+...
+```
 
 The idea is to always bring the code after the return into cache (the
-instructions located at the address pointed by LR), therefore minimizing the
-difference between a speculative execution with a correct PAC code and with an
-incorrect one.
-
-   -  ``autiasp`` performs the authentication step.
-   -  ``mov x8, x30`` copies the return address (LR and x30 are synonyms) to a
-      temporary.
-   -  ``xpaclri`` strips the PAC code out of the return address in x30.
-   -  ``ldr w30, [x30]`` performs a load of the return address in x30.
-   -  ``ret x8`` returns to the authenticated return address.
-
-The load operation brings the code into the cache even if the authentication
-step fails. As a consequence, in either case the code is loaded into the cache.
-Furthermore, the return operation uses the original return address before
-stripping, so the return address protection is still kept in place in a normal
-non-speculative execution.
-
-If FEAT_PAUTH is present, the code sequence can use instructions only available
-with said feature with no change in semantics:
-
-.. code:: asm
-
-   autiasp
-   mov     x8, x30
-   xpaci   x8
-   ldr     w8, [x8]
-   ret
-
-*********************
- Command-line option
-*********************
-
-Return address authentication hardening can be enabled at translation unit level
-with ``-mharden-pac-ret=load-return-address``. It requires pac-ret to be enabled
-at translation unit level as well.
-
-********************
- Function attribute
-********************
-
-In addition to the command-line option ``-mharden-pac-ret=load-return-address``,
-the developer can enable the mitigation at function level with the use of the
-corresponding function attribute.
-
-.. code:: C
-
-   __attribute__((target("branch-protection=pac-ret,harden-pac-ret=load-return-address")))
-   void function()  {
-     ...
-   }
-
-*********
- Caveats
-*********
-
-The load of return address brings the code into the shared instruction/data
-cache, therefore this cache level can't be used as an oracle to find out whether
-the authentication succeeded or not. However, in the case of authentication
-success, the code is also fetched into the instruction cache. An attacker who is
-able to measure this cache level specifically may still be able to carry out the
-exploit.
-
-Another caveat is if the code at the return address contains a load operation
-within the speculation window. If this is the case, this load will only execute
-speculatively if authentication succeeds, thus opening the program up for
-exploitation despite the mitigation.
+instructions located at the address pointed by LR), therefore minimizing
+the difference between a speculative execution with a correct PAC code
+and with an incorrect one.
+
+- `autiasp` performs the authentication step.
+- `mov x8, x30` copies the return address (LR and x30 are synonyms) to a
+  temporary.
+- `xpaclri` strips the PAC code out of the return address in x30.
+- `ldr w30, [x30]` performs a load of the return address in x30.
+- `ret x8` returns to the authenticated return address.
+
+The load operation brings the code into the cache even if the
+authentication step fails. As a consequence, in either case the code is
+loaded into the cache. Furthermore, the return operation uses the
+original return address before stripping, so the return address
+protection is still kept in place in a normal non-speculative execution.
+
+If FEAT_PAUTH is present, the code sequence can use instructions only
+available with said feature with no change in semantics:
+
+```asm
+autiasp
+mov     x8, x30
+xpaci   x8
+ldr     w8, [x8]
+ret
+```
+
+## Command-line option
+
+Return address authentication hardening can be enabled at translation
+unit level with `-mharden-pac-ret=load-return-address`. It requires
+pac-ret to be enabled at translation unit level as well.
+
+## Function attribute
+
+In addition to the command-line option
+`-mharden-pac-ret=load-return-address`, the developer can enable the
+mitigation at function level with the use of the corresponding function
+attribute.
+
+```c
+__attribute__((target(
+    "branch-protection=pac-ret,harden-pac-ret=load-return-address")))
+void function() {
+  ...
+}
+```
+
+## Caveats
+
+The load of return address brings the code into the shared
+instruction/data cache, therefore this cache level can't be used as an
+oracle to find out whether the authentication succeeded or not. However,
+in the case of authentication success, the code is also fetched into the
+instruction cache. An attacker who is able to measure this cache level
+specifically may still be able to carry out the exploit.
+
+Another caveat is if the code at the return address contains a load
+operation within the speculation window. If this is the case, this load
+will only execute speculatively if authentication succeeds, thus opening
+the program up for exploitation despite the mitigation.

>From 97b8b9ec493ffd66e20ed24e761e4141ed3cfea0 Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Mon, 7 Sep 2026 10:47:46 +0100
Subject: [PATCH 9/9] Changes:

 - Renamed feature in remaining places.
 - Restored the early exit path for when return address signing options
   are absent from the command-line.
 - The hardening command-line option is now accepted in case where no
   pac-ret option is present in the command line, but the target
   configuration still enables it by default.
 - Changed the module flag to take a string value.
---
 clang/docs/ReleaseNotes.md                    |   2 +-
 .../ReturnAddressAuthenticationHardening.md   |   6 +-
 clang/include/clang/Basic/LangOptions.h       |   6 +-
 clang/include/clang/Basic/TargetInfo.h        |   2 +-
 clang/include/clang/Options/Options.td        |   2 +-
 clang/lib/CodeGen/CodeGenModule.cpp           |   9 +-
 clang/lib/Driver/ToolChains/Clang.cpp         | 167 +++++++++---------
 .../CodeGen/AArch64/sign-return-address.c     |  19 +-
 .../aarch64-sign-return-address-harden.c      |   4 +
 9 files changed, 113 insertions(+), 104 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index bfde402f066ee..8d7ea0629e0b8 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -255,7 +255,7 @@ features cannot lower the translation-unit ABI level;
 
 - Added the AArch64 option `-mharden-pac-ret=load-return-address` to harden
   return address signing against PACMAN attacks. The option requires return
-  address signing to be enabled and emits a load of the return address before
+  address signing to be enabled and emits a load from the return address before
   returning, reducing the cache side channel used to guess pointer
   authentication codes. See
   {doc}`Return Address Authentication Hardening <ReturnAddressAuthenticationHardening>`
diff --git a/clang/docs/ReturnAddressAuthenticationHardening.md b/clang/docs/ReturnAddressAuthenticationHardening.md
index 4c9172a54ccff..98dd8b7a4b628 100644
--- a/clang/docs/ReturnAddressAuthenticationHardening.md
+++ b/clang/docs/ReturnAddressAuthenticationHardening.md
@@ -109,7 +109,7 @@ and with an incorrect one.
 - `mov x8, x30` copies the return address (LR and x30 are synonyms) to a
   temporary.
 - `xpaclri` strips the PAC code out of the return address in x30.
-- `ldr w30, [x30]` performs a load of the return address in x30.
+- `ldr w30, [x30]` performs a load from the return address in x30.
 - `ret x8` returns to the authenticated return address.
 
 The load operation brings the code into the cache even if the
@@ -118,7 +118,7 @@ loaded into the cache. Furthermore, the return operation uses the
 original return address before stripping, so the return address
 protection is still kept in place in a normal non-speculative execution.
 
-If FEAT_PAUTH is present, the code sequence can use instructions only
+If FEAT_PAuth is present, the code sequence can make use of instructions
 available with said feature with no change in semantics:
 
 ```asm
@@ -152,7 +152,7 @@ void function() {
 
 ## Caveats
 
-The load of return address brings the code into the shared
+The load from the return address brings the code into the shared
 instruction/data cache, therefore this cache level can't be used as an
 oracle to find out whether the authentication succeeded or not. However,
 in the case of authentication success, the code is also fetched into the
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 64f7f9a7d70bd..73ed9f62e122d 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -323,7 +323,7 @@ class LangOptionsBase {
   enum class SignReturnAddressHardeningKind {
     /// Regular return address signing.
     None,
-    /// Hardened return address signing with load of return address.
+    /// Hardened return address signing with load from the return address.
     LoadReturnAddress
   };
 
@@ -771,8 +771,8 @@ class LangOptions : public LangOptionsBase {
     return getSignReturnAddressScope() == SignReturnAddressScopeKind::All;
   }
 
-  /// Check if return address signing hardening is enabled and uses load of
-  /// return address.
+  /// Check if return address signing hardening is enabled and uses load from
+  /// the return address.
   bool isSignReturnAddressHardenWithLoadReturnAddress() const {
     return getSignReturnAddressHardening() ==
            SignReturnAddressHardeningKind::LoadReturnAddress;
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index b9905515c9ea3..6a3fded69bdfb 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1547,7 +1547,7 @@ class TargetInfo : public TransferrableTargetInfo,
     return false;
   }
 
-  /// Parse the Return Address Signing Hardening specification.
+  /// Parse the Return Address Authentication Hardening specification.
   virtual std::optional<LangOptions::SignReturnAddressHardeningKind>
   parseSignReturnAddressHardening(StringRef Spec) const {
     return std::nullopt;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 9b733c0897ff4..b1aa4faf51493 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5992,7 +5992,7 @@ def msign_return_address_EQ : Joined<["-"], "msign-return-address=">,
 def mharden_pac_ret_EQ : Joined<["-"], "mharden-pac-ret=">,
   Visibility<[ClangOption, CC1Option]>,
   Flags<[TargetSpecific]>, Group<m_Group>,
-  HelpText<"Select the return address signing hardening scheme">,
+  HelpText<"Select the return address authentication hardening scheme">,
   Values<"none,load-return-address">, NormalizedValues<["None", "LoadReturnAddress"]>,
   NormalizedValuesScope<"LangOptions::SignReturnAddressHardeningKind">,
   MarshallingInfoEnum<LangOpts<"SignReturnAddressHardening">, "None">;
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index b489bfcddbd17..90b5125441307 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1619,10 +1619,6 @@ void CodeGenModule::Release() {
     if (!LangOpts.isSignReturnAddressWithAKey())
       getModule().addModuleFlag(llvm::Module::Min,
                                 "sign-return-address-with-bkey", 2);
-    if (LangOpts.isSignReturnAddressHardenWithLoadReturnAddress())
-      getModule().addModuleFlag(
-          llvm::Module::Min, "sign-return-address-harden-load-return-address",
-          2);
   }
   if (T.isAArch64()) {
     // Emit the following 4 module flags so LLVM can derive corresponding
@@ -1664,6 +1660,11 @@ void CodeGenModule::Release() {
               LangOpts.PointerAuthInitFiniAddressDiscrimination);
     }
 
+    if (LangOpts.isSignReturnAddressHardenWithLoadReturnAddress())
+      getModule().addModuleFlag(
+          llvm::Module::Error, "sign-return-address-harden",
+          llvm::MDString::get(getLLVMContext(), "load-return-address"));
+
     if (getTriple().isOSLinux()) {
       getModule().addModuleFlag(llvm::Module::Error, "ptrauth-sign-personality",
                                 LangOpts.PointerAuthCalls);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index a7e32dee933c7..253e8ffd05229 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -1394,13 +1394,6 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
   const Arg *HardenPACRetArg = Args.getLastArg(options::OPT_mharden_pac_ret_EQ);
   const Driver &D = TC.getDriver();
 
-  // Check CmdArgs because some toolchains bypass the driver args and add to
-  // the frontend args directly.
-  bool HasPtrauthReturns =
-      llvm::is_contained(CmdArgs, "-fptrauth-returns") ||
-      Args.hasFlagNoClaim(options::OPT_fptrauth_returns,
-                          options::OPT_fno_ptrauth_returns, false);
-
   if (HardenPACRetArg) {
     if (!isAArch64) {
       D.Diag(diag::err_drv_unsupported_opt_for_target)
@@ -1422,83 +1415,101 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
     }
   }
 
-  if (!A && (Triple.isOSOpenBSD() || Triple.isAndroid()) && isAArch64) {
-    CmdArgs.push_back("-msign-return-address=non-leaf");
-    CmdArgs.push_back("-msign-return-address-key=a_key");
-    CmdArgs.push_back("-mbranch-target-enforce");
+  // Check CmdArgs because some toolchains bypass the driver args and add to
+  // the frontend args directly.
+  bool HasPtrauthReturns =
+      llvm::is_contained(CmdArgs, "-fptrauth-returns") ||
+      Args.hasFlagNoClaim(options::OPT_fptrauth_returns,
+                          options::OPT_fno_ptrauth_returns, false);
+
+  auto RenderHardenPACRet = [&](StringRef Scope) {
+    if (!HardenPACRetArg)
+      return;
+    if (Scope == "none" && !HasPtrauthReturns)
+      D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
+    else
+      CmdArgs.push_back(Args.MakeArgString(Twine("-mharden-pac-ret=") +
+                                           HardenPACRetArg->getValue()));
+  };
+
+  if (!A) {
+    if ((Triple.isOSOpenBSD() || Triple.isAndroid()) && isAArch64) {
+      CmdArgs.push_back("-msign-return-address=non-leaf");
+      CmdArgs.push_back("-msign-return-address-key=a_key");
+      CmdArgs.push_back("-mbranch-target-enforce");
+      RenderHardenPACRet("non-leaf");
+    } else {
+      RenderHardenPACRet("none");
+    }
     return;
   }
 
-  if (A && !(isAArch64 || (Triple.isArmT32() && Triple.isArmMClass())))
+  if (!(isAArch64 || (Triple.isArmT32() && Triple.isArmMClass())))
     D.Diag(diag::warn_incompatible_branch_protection_option)
         << Triple.getArchName();
 
-  StringRef Scope = "none", Key;
-  bool IndirectBranches = false, BranchProtectionPAuthLR = false,
-       GuardedControlStack = false;
+  StringRef Scope, Key;
+  bool IndirectBranches, BranchProtectionPAuthLR, GuardedControlStack;
 
-  if (A) {
-    if (A->getOption().matches(options::OPT_msign_return_address_EQ)) {
-      Scope = A->getValue();
-      if (Scope != "none" && Scope != "non-leaf" && Scope != "all")
-        D.Diag(diag::err_drv_unsupported_option_argument)
-            << A->getSpelling() << Scope;
-      // This spelling cannot express a key, and AArch64 Windows only supports
-      // B-key, so default to it there as parseBranchProtection() does.
-      Key = isAArch64 && Triple.isOSWindows() ? "b_key" : "a_key";
-      IndirectBranches =
-          (Triple.isOSOpenBSD() || Triple.isAndroid()) && isAArch64;
-      BranchProtectionPAuthLR = false;
-      GuardedControlStack = false;
-    } else {
-      StringRef DiagMsg;
-      llvm::ARM::ParsedBranchProtection PBP;
-      bool EnablePAuthLR = false;
-
-      // To know if we need to enable PAuth-LR As part of the standard branch
-      // protection option, it needs to be determined if the feature has been
-      // activated in the `march` argument. This information is stored within
-      // the CmdArgs variable and can be found using a search.
-      if (isAArch64) {
-        auto isPAuthLR = [](const char *member) {
-          llvm::AArch64::ExtensionInfo pauthlr_extension =
-              llvm::AArch64::getExtensionByID(llvm::AArch64::AEK_PAUTHLR);
-          return llvm::AArch64::StrTab[pauthlr_extension.PosTargetFeature] ==
-                 member;
-        };
-
-        if (llvm::any_of(CmdArgs, isPAuthLR))
-          EnablePAuthLR = true;
-      }
-      if (!llvm::ARM::parseBranchProtection(A->getValue(), PBP, DiagMsg, Triple,
-                                            EnablePAuthLR))
-        D.Diag(diag::err_drv_unsupported_option_argument)
-            << A->getSpelling() << DiagMsg;
-      if (!isAArch64 && PBP.Key == "b_key")
-        D.Diag(diag::warn_unsupported_branch_protection)
-            << "b-key" << A->getAsString(Args);
-      Scope = PBP.Scope;
-      Key = PBP.Key;
-      BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
-      IndirectBranches = PBP.BranchTargetEnforcement;
-      GuardedControlStack = PBP.GuardedControlStack;
-    }
-
-    // GCS is currently untested with ptrauth-returns, but enabling this could
-    // be allowed in future after testing with a suitable system.
-    if (Scope != "none" || BranchProtectionPAuthLR || GuardedControlStack) {
-      if (Triple.getEnvironment() == llvm::Triple::PAuthTest)
-        D.Diag(diag::err_drv_unsupported_opt_for_target)
-            << A->getAsString(Args) << Triple.getTriple();
-      else if (HasPtrauthReturns)
-        D.Diag(diag::err_drv_incompatible_options)
-            << A->getAsString(Args) << "-fptrauth-returns";
+  if (A->getOption().matches(options::OPT_msign_return_address_EQ)) {
+    Scope = A->getValue();
+    if (Scope != "none" && Scope != "non-leaf" && Scope != "all")
+      D.Diag(diag::err_drv_unsupported_option_argument)
+          << A->getSpelling() << Scope;
+    // This spelling cannot express a key, and AArch64 Windows only supports
+    // B-key, so default to it there as parseBranchProtection() does.
+    Key = isAArch64 && Triple.isOSWindows() ? "b_key" : "a_key";
+    IndirectBranches =
+        (Triple.isOSOpenBSD() || Triple.isAndroid()) && isAArch64;
+    BranchProtectionPAuthLR = false;
+    GuardedControlStack = false;
+  } else {
+    StringRef DiagMsg;
+    llvm::ARM::ParsedBranchProtection PBP;
+    bool EnablePAuthLR = false;
+
+    // To know if we need to enable PAuth-LR As part of the standard branch
+    // protection option, it needs to be determined if the feature has been
+    // activated in the `march` argument. This information is stored within the
+    // CmdArgs variable and can be found using a search.
+    if (isAArch64) {
+      auto isPAuthLR = [](const char *member) {
+        llvm::AArch64::ExtensionInfo pauthlr_extension =
+            llvm::AArch64::getExtensionByID(llvm::AArch64::AEK_PAUTHLR);
+        return llvm::AArch64::StrTab[pauthlr_extension.PosTargetFeature] ==
+               member;
+      };
+
+      if (llvm::any_of(CmdArgs, isPAuthLR))
+        EnablePAuthLR = true;
     }
+    if (!llvm::ARM::parseBranchProtection(A->getValue(), PBP, DiagMsg, Triple,
+                                          EnablePAuthLR))
+      D.Diag(diag::err_drv_unsupported_option_argument)
+          << A->getSpelling() << DiagMsg;
+    if (!isAArch64 && PBP.Key == "b_key")
+      D.Diag(diag::warn_unsupported_branch_protection)
+          << "b-key" << A->getAsString(Args);
+    Scope = PBP.Scope;
+    Key = PBP.Key;
+    BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
+    IndirectBranches = PBP.BranchTargetEnforcement;
+    GuardedControlStack = PBP.GuardedControlStack;
+  }
+
+  // GCS is currently untested with ptrauth-returns, but enabling this could be
+  // allowed in future after testing with a suitable system.
+  if (Scope != "none" || BranchProtectionPAuthLR || GuardedControlStack) {
+    if (Triple.getEnvironment() == llvm::Triple::PAuthTest)
+      D.Diag(diag::err_drv_unsupported_opt_for_target)
+          << A->getAsString(Args) << Triple.getTriple();
+    else if (HasPtrauthReturns)
+      D.Diag(diag::err_drv_incompatible_options)
+          << A->getAsString(Args) << "-fptrauth-returns";
   }
 
-  if (A)
-    CmdArgs.push_back(
-        Args.MakeArgString(Twine("-msign-return-address=") + Scope));
+  CmdArgs.push_back(
+      Args.MakeArgString(Twine("-msign-return-address=") + Scope));
   if (Scope != "none")
     CmdArgs.push_back(
         Args.MakeArgString(Twine("-msign-return-address-key=") + Key));
@@ -1511,13 +1522,7 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
   if (GuardedControlStack)
     CmdArgs.push_back("-mguarded-control-stack");
 
-  if (HardenPACRetArg) {
-    if (Scope == "none" && !HasPtrauthReturns)
-      D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
-    else
-      CmdArgs.push_back(Args.MakeArgString(Twine("-mharden-pac-ret=") +
-                                           HardenPACRetArg->getValue()));
-  }
+  RenderHardenPACRet(Scope);
 }
 
 void Clang::AddARMTargetArgs(const llvm::Triple &Triple, const ArgList &Args,
diff --git a/clang/test/CodeGen/AArch64/sign-return-address.c b/clang/test/CodeGen/AArch64/sign-return-address.c
index 16150d1a30112..f5dea76a6a267 100644
--- a/clang/test/CodeGen/AArch64/sign-return-address.c
+++ b/clang/test/CodeGen/AArch64/sign-return-address.c
@@ -6,8 +6,7 @@
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret+leaf  %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret+b-key %s | FileCheck %s --check-prefix=CHECK --check-prefix=B-KEY
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=bti %s           | FileCheck %s --check-prefix=CHECK --check-prefix=BTE
-// RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret -mharden-pac-ret=load-return-address %s | \
-//      FileCheck %s --check-prefix=CHECK --check-prefixes=HARDEN
+// RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret -mharden-pac-ret=load-return-address %s | FileCheck %s --check-prefix=CHECK --check-prefix=HARDEN
 // RUN: %clang -target aarch64-windows-msvc -S -emit-llvm -o - -mbranch-protection=pac-ret %s | FileCheck %s --check-prefix=CHECK --check-prefix=WIN-PAC
 // RUN: %clang -target aarch64-windows-msvc -S -emit-llvm -o - -mbranch-protection=standard %s | FileCheck %s --check-prefix=CHECK --check-prefix=WIN-STD
 
@@ -71,13 +70,13 @@
 // WIN-PAC:    !{i32 8, !"sign-return-address-with-bkey", i32 2}
 // WIN-STD:    !{i32 8, !"sign-return-address-with-bkey", i32 2}
 
-// NONE-NOT:    !"sign-return-address-harden-load-return-address"
-// ALL-NOT:     !"sign-return-address-harden-load-return-address"
-// PART-NOT:    !"sign-return-address-harden-load-return-address"
-// BTE-NOT:     !"sign-return-address-harden-load-return-address"
-// HARDEN:      !{i32 8, !"sign-return-address-harden-load-return-address", i32 2}
-// B-KEY-NOT:   !"sign-return-address-harden-load-return-address"
-// WIN-PAC-NOT: !"sign-return-address-harden-load-return-address"
-// WIN-STD-NOT: !"sign-return-address-harden-load-return-address"
+// NONE-NOT:    !"sign-return-address-harden"
+// ALL-NOT:     !"sign-return-address-harden"
+// PART-NOT:    !"sign-return-address-harden"
+// BTE-NOT:     !"sign-return-address-harden"
+// HARDEN:      !{i32 1, !"sign-return-address-harden", !"load-return-address"}
+// B-KEY-NOT:   !"sign-return-address-harden"
+// WIN-PAC-NOT: !"sign-return-address-harden"
+// WIN-STD-NOT: !"sign-return-address-harden"
 
 void foo() {}
diff --git a/clang/test/CodeGen/aarch64-sign-return-address-harden.c b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
index 4b078bbc367b3..44bd536d780a6 100644
--- a/clang/test/CodeGen/aarch64-sign-return-address-harden.c
+++ b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
@@ -14,6 +14,10 @@
 // RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
 // RUN: %clang -target aarch64-linux-pauthtest -S -emit-llvm -o - %s -fptrauth-returns -mharden-pac-ret=load-return-address \
 // RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
+// RUN: %clang -target aarch64-openbsd -S -emit-llvm -o - %s -mharden-pac-ret=load-return-address \
+// RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
+// RUN: %clang -target aarch64-android -S -emit-llvm -o - %s -mharden-pac-ret=load-return-address \
+// RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
 
 void foo() {}
 



More information about the cfe-commits mailing list