[llvm-branch-commits] [clang] edb9efb - [clang][llvm][AArch64] Set hardening fn attrs on synthetic functions (#211013)

Tobias Hieta via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Aug 2 02:00:29 PDT 2026


Author: Daniil Kovalev
Date: 2026-08-02T11:00:21+02:00
New Revision: edb9efb3e0823d32d6b4baa8f5f798bca3e300a3

URL: https://github.com/llvm/llvm-project/commit/edb9efb3e0823d32d6b4baa8f5f798bca3e300a3
DIFF: https://github.com/llvm/llvm-project/commit/edb9efb3e0823d32d6b4baa8f5f798bca3e300a3.diff

LOG: [clang][llvm][AArch64] Set hardening fn attrs on synthetic functions (#211013)

Compiler-synthesized functions such as `__llvm_gcov_writeout`,
`__llvm_gcov_reset` and `__llvm_gcov_init` were previously never
receiving the AArch64 hardening function attributes (ptrauth-returns,
ptrauth-auth-traps, ptrauth-indirect-gotos and
aarch64-jump-table-hardening) since the attributes were only emitted by
Clang and gated by `PointerAuthOptions` structure's corresponding
fields. See `setPointerAuthFnAttributes` and
`initPointerAuthFnAttributes` member functions of `TargetCodeGenInfo`.

This patch resolves this in the same manner as #83153 does for several
other attributes. Particularly, Clang now emits corresponding 4 module
flags (conditionally on whether the related feature is enabled) with Max
behavior, and LLVM's `Function::createWithDefaultAttr` derives the
matching function attributes from them. Max behavior with conditional
emission is safe because none of these features affect ABI, so promoting
an absent flag on module merge cannot break compatibility.

(cherry picked from commit 2f8eb5b4be3eba5c7f58470d6538d00df53a55fc)

Added: 
    clang/test/CodeGen/AArch64/ptrauth-function-attributes-synthetic.c
    llvm/test/Instrumentation/AddressSanitizer/ptrauth-module-flags-aarch64.ll
    llvm/test/Transforms/GCOVProfiling/ptrauth-module-flags-aarch64.ll

Modified: 
    clang/lib/CodeGen/CodeGenModule.cpp
    llvm/lib/IR/Function.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index ca71458d85134..de060e31b8800 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1539,7 +1539,33 @@ void CodeGenModule::Release() {
                                 "sign-return-address-with-bkey", 2);
   }
   if (T.isAArch64()) {
+    // Emit the following 4 module flags so LLVM can derive corresponding
+    // function attributes for synthetically generated functions (e.g.
+    // __llvm_gcov_writeout). It is safe to only emit the flags conditionally
+    // and set the Max behavior because of two reasons:
+    // 1) all 4 hardening features gated behind the attributes do not break ABI
+    //    compatibility, so we do not need to error on flag mismatch (thus,
+    //    conditional emission);
+    // 2) promoting an absent flag to a present flag enables the corresponding
+    //    hardening feature for newly emitted functions which does not affect
+    //    correctness and is guaranteed to have sufficient target features for
+    //    it, since the module we are merging with already has the flag set.
+    if (LangOpts.PointerAuthReturns)
+      getModule().addModuleFlag(llvm::Module::Max, "ptrauth-returns", 1);
+    if (LangOpts.PointerAuthAuthTraps)
+      getModule().addModuleFlag(llvm::Module::Max, "ptrauth-auth-traps", 1);
+    if (LangOpts.PointerAuthIndirectGotos)
+      getModule().addModuleFlag(llvm::Module::Max, "ptrauth-indirect-gotos", 1);
+    if (LangOpts.AArch64JumpTableHardening)
+      getModule().addModuleFlag(llvm::Module::Max,
+                                "aarch64-jump-table-hardening", 1);
+
     if (getTriple().isOSBinFormatELF()) {
+      // The following ptrauth-* flags are emitted unconditionally: value 1 if
+      // the corresponding feature is set and value 0 otherwise. It is required
+      // for Error behavior to properly detect value mismatch between modules -
+      // modules with 
diff erent values of these flags are incompatible and merge
+      // is not allowed.
       getModule().addModuleFlag(llvm::Module::Error, "ptrauth-elf-got",
                                 LangOpts.PointerAuthELFGOT);
 

diff  --git a/clang/test/CodeGen/AArch64/ptrauth-function-attributes-synthetic.c b/clang/test/CodeGen/AArch64/ptrauth-function-attributes-synthetic.c
new file mode 100644
index 0000000000000..226be57b94fd8
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/ptrauth-function-attributes-synthetic.c
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios   -coverage-data-file=/dev/null                                -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF
+// RUN: %clang_cc1 -triple arm64e-apple-ios  -coverage-data-file=/dev/null                                -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -coverage-data-file=/dev/null                                -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF
+
+// RUN: %clang_cc1 -triple arm64-apple-ios   -coverage-data-file=/dev/null -fptrauth-returns              -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,RETS \
+// RUN:   --implicit-check-not=ptrauth-auth-traps --implicit-check-not=ptrauth-indirect-gotos --implicit-check-not=aarch64-jump-table-hardening
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -coverage-data-file=/dev/null -fptrauth-returns              -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,RETS \
+// RUN:   --implicit-check-not=ptrauth-auth-traps --implicit-check-not=ptrauth-indirect-gotos --implicit-check-not=aarch64-jump-table-hardening
+
+// RUN: %clang_cc1 -triple arm64-apple-ios   -coverage-data-file=/dev/null -fptrauth-auth-traps           -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,TRAPS \
+// RUN:   --implicit-check-not=ptrauth-returns --implicit-check-not=ptrauth-indirect-gotos --implicit-check-not=aarch64-jump-table-hardening
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -coverage-data-file=/dev/null -fptrauth-auth-traps           -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,TRAPS \
+// RUN:   --implicit-check-not=ptrauth-returns --implicit-check-not=ptrauth-indirect-gotos --implicit-check-not=aarch64-jump-table-hardening
+
+// RUN: %clang_cc1 -triple arm64-apple-ios   -coverage-data-file=/dev/null -fptrauth-indirect-gotos       -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,GOTOS \
+// RUN:   --implicit-check-not=ptrauth-returns --implicit-check-not=ptrauth-auth-traps --implicit-check-not=aarch64-jump-table-hardening
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -coverage-data-file=/dev/null -fptrauth-indirect-gotos       -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,GOTOS \
+// RUN:   --implicit-check-not=ptrauth-returns --implicit-check-not=ptrauth-auth-traps --implicit-check-not=aarch64-jump-table-hardening
+
+// RUN: %clang_cc1 -triple arm64e-apple-ios  -coverage-data-file=/dev/null -faarch64-jump-table-hardening -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,JMPTBL \
+// RUN:   --implicit-check-not=ptrauth-returns --implicit-check-not=ptrauth-auth-traps --implicit-check-not=ptrauth-indirect-gotos
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -coverage-data-file=/dev/null -faarch64-jump-table-hardening -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,JMPTBL \
+// RUN:   --implicit-check-not=ptrauth-returns --implicit-check-not=ptrauth-auth-traps --implicit-check-not=ptrauth-indirect-gotos
+
+// ALL: define internal void @__llvm_gcov_writeout() unnamed_addr #[[#T:]] {
+// ALL: define internal void @__llvm_gcov_reset() unnamed_addr #[[#T]] {
+// ALL: define internal void @__llvm_gcov_init() unnamed_addr #[[#T]] {
+
+// RETS:      attributes #[[#T]] =
+// RETS-SAME: "ptrauth-returns"
+// RETS:      !llvm.module.flags = !{{{.*}}!3{{.*}}}
+// RETS:      !3 = !{i32 7, !"ptrauth-returns", i32 1}
+
+// TRAPS:      attributes #[[#T]] =
+// TRAPS-SAME: "ptrauth-auth-traps"
+// TRAPS:      !llvm.module.flags = !{{{.*}}!3{{.*}}}
+// TRAPS:      !3 = !{i32 7, !"ptrauth-auth-traps", i32 1}
+
+// GOTOS:      attributes #[[#T]] =
+// GOTOS-SAME: "ptrauth-indirect-gotos"
+// GOTOS:      !llvm.module.flags = !{{{.*}}!3{{.*}}}
+// GOTOS:      !3 = !{i32 7, !"ptrauth-indirect-gotos", i32 1}
+
+// JMPTBL:      attributes #[[#T]] =
+// JMPTBL-SAME: "aarch64-jump-table-hardening"
+// JMPTBL:      !llvm.module.flags = !{{{.*}}!3{{.*}}}
+// JMPTBL:      !3 = !{i32 7, !"aarch64-jump-table-hardening", i32 1}
+
+// OFF-NOT: attributes {{.*}} "ptrauth-
+// OFF-NOT: !"ptrauth-returns"
+// OFF-NOT: !"ptrauth-auth-traps"
+// OFF-NOT: !"ptrauth-indirect-gotos"
+// OFF-NOT: !"aarch64-jump-table-hardening"

diff  --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index 03e91bc8e2aa4..7c2a151dbefda 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -432,6 +432,10 @@ Function *Function::createWithDefaultAttr(FunctionType *Ty,
   AddAttributeIfSet("branch-target-enforcement");
   AddAttributeIfSet("branch-protection-pauth-lr");
   AddAttributeIfSet("guarded-control-stack");
+  AddAttributeIfSet("ptrauth-returns");
+  AddAttributeIfSet("ptrauth-auth-traps");
+  AddAttributeIfSet("ptrauth-indirect-gotos");
+  AddAttributeIfSet("aarch64-jump-table-hardening");
 
   F->addFnAttrs(B);
   return F;

diff  --git a/llvm/test/Instrumentation/AddressSanitizer/ptrauth-module-flags-aarch64.ll b/llvm/test/Instrumentation/AddressSanitizer/ptrauth-module-flags-aarch64.ll
new file mode 100644
index 0000000000000..ad732f7020060
--- /dev/null
+++ b/llvm/test/Instrumentation/AddressSanitizer/ptrauth-module-flags-aarch64.ll
@@ -0,0 +1,64 @@
+; REQUIRES: aarch64-registered-target
+; RUN: rm -rf %t && split-file %s %t && cd %t
+
+; RUN: opt -mtriple=aarch64-linux-gnu < all.ll -passes=asan -S | FileCheck %s --check-prefix=ALL
+
+; RUN: opt -mtriple=aarch64-linux-gnu < returns.ll -passes=asan -S | FileCheck %s --check-prefix=RET \
+; RUN:   --implicit-check-not=ptrauth-auth-traps --implicit-check-not=ptrauth-indirect-gotos --implicit-check-not=aarch64-jump-table-hardening
+
+; RUN: opt -mtriple=aarch64-linux-gnu < auth-traps.ll -passes=asan -S | FileCheck %s --check-prefix=TRAP \
+; RUN:   --implicit-check-not=ptrauth-returns --implicit-check-not=ptrauth-indirect-gotos --implicit-check-not=aarch64-jump-table-hardening
+
+; RUN: opt -mtriple=aarch64-linux-gnu < indirect-gotos.ll -passes=asan -S | FileCheck %s --check-prefix=GOTO \
+; RUN:   --implicit-check-not=ptrauth-returns --implicit-check-not=ptrauth-auth-traps --implicit-check-not=aarch64-jump-table-hardening
+
+; RUN: opt -mtriple=aarch64-linux-gnu < jump-table-hardening.ll -passes=asan -S | FileCheck %s --check-prefix=JUMP \
+; RUN:   --implicit-check-not=ptrauth-returns --implicit-check-not=ptrauth-auth-traps --implicit-check-not=ptrauth-indirect-gotos
+
+;--- all.ll
+
+!llvm.module.flags = !{!0, !1, !2, !3}
+
+!0 = !{i32 7, !"ptrauth-returns", i32 1}
+!1 = !{i32 7, !"ptrauth-auth-traps", i32 1}
+!2 = !{i32 7, !"ptrauth-indirect-gotos", i32 1}
+!3 = !{i32 7, !"aarch64-jump-table-hardening", i32 1}
+
+; ALL: define internal void @asan.module_ctor() #[[#ATTR:]]
+; ALL: attributes #[[#ATTR]] = { nounwind "aarch64-jump-table-hardening" "ptrauth-auth-traps" "ptrauth-indirect-gotos" "ptrauth-returns" }
+
+;--- returns.ll
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 7, !"ptrauth-returns", i32 1}
+
+; RET: define internal void @asan.module_ctor() #[[#ATTR:]]
+; RET: attributes #[[#ATTR]] = { nounwind "ptrauth-returns" }
+
+;--- auth-traps.ll
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 7, !"ptrauth-auth-traps", i32 1}
+
+; TRAP: define internal void @asan.module_ctor() #[[#ATTR:]]
+; TRAP: attributes #[[#ATTR]] = { nounwind "ptrauth-auth-traps" }
+
+;--- indirect-gotos.ll
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 7, !"ptrauth-indirect-gotos", i32 1}
+
+; GOTO: define internal void @asan.module_ctor() #[[#ATTR:]]
+; GOTO: attributes #[[#ATTR]] = { nounwind "ptrauth-indirect-gotos" }
+
+;--- jump-table-hardening.ll
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 7, !"aarch64-jump-table-hardening", i32 1}
+
+; JUMP: define internal void @asan.module_ctor() #[[#ATTR:]]
+; JUMP: attributes #[[#ATTR]] = { nounwind "aarch64-jump-table-hardening" }

diff  --git a/llvm/test/Transforms/GCOVProfiling/ptrauth-module-flags-aarch64.ll b/llvm/test/Transforms/GCOVProfiling/ptrauth-module-flags-aarch64.ll
new file mode 100644
index 0000000000000..cadacdd7a5b54
--- /dev/null
+++ b/llvm/test/Transforms/GCOVProfiling/ptrauth-module-flags-aarch64.ll
@@ -0,0 +1,39 @@
+; Run in temporary directory so *.gcno file created stays there
+; RUN: rm -rf %t && mkdir -p %t && cd %t
+
+; RUN: opt < %s -S -passes=insert-gcov-profiling | FileCheck %s
+; REQUIRES: aarch64-registered-target
+
+; CHECK:      define internal void @__llvm_gcov_writeout() unnamed_addr #[[#T:]] {
+; CHECK:      define internal void @__llvm_gcov_reset() unnamed_addr #[[#T]] {
+; CHECK:      define internal void @__llvm_gcov_init() unnamed_addr #[[#T]] {
+
+; CHECK:      attributes #[[#T]] =
+; CHECK-SAME: "aarch64-jump-table-hardening"
+; CHECK-SAME: "ptrauth-auth-traps"
+; CHECK-SAME: "ptrauth-indirect-gotos"
+; CHECK-SAME: "ptrauth-returns"
+
+; CHECK:      !llvm.module.flags = !{!0, !1, !2, !3, !4}
+; CHECK:      !1 = !{i32 7, !"ptrauth-returns", i32 1}
+; CHECK:      !2 = !{i32 7, !"ptrauth-auth-traps", i32 1}
+; CHECK:      !3 = !{i32 7, !"ptrauth-indirect-gotos", i32 1}
+; CHECK:      !4 = !{i32 7, !"aarch64-jump-table-hardening", i32 1}
+
+target triple = "aarch64-unknown-linux-gnu"
+
+!llvm.module.flags = !{!0, !1, !2, !3, !4}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = !{i32 7, !"ptrauth-returns", i32 1}
+!2 = !{i32 7, !"ptrauth-auth-traps", i32 1}
+!3 = !{i32 7, !"ptrauth-indirect-gotos", i32 1}
+!4 = !{i32 7, !"aarch64-jump-table-hardening", i32 1}
+
+!llvm.dbg.cu = !{!5}
+!5 = distinct !DICompileUnit(language: DW_LANG_C99, file: !6)
+!6 = !DIFile(filename: "a.c", directory: "")
+
+define void @empty() {
+  ret void
+}


        


More information about the llvm-branch-commits mailing list