[llvm-branch-commits] [llvm] [BOLT][PAC] Warn about synchronous unwind tables (PR #165227)
Gergely Bálint via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Nov 17 02:15:25 PST 2025
https://github.com/bgergely0 updated https://github.com/llvm/llvm-project/pull/165227
>From 61e03b5abf74bd5a61f2aa4d21219c67cfbfce24 Mon Sep 17 00:00:00 2001
From: Gergely Balint <gergely.balint at arm.com>
Date: Mon, 27 Oct 2025 09:29:54 +0000
Subject: [PATCH 1/4] [BOLT][PAC] Warn about synchronous unwind tables
BOLT currently ignores functions with synchronous PAuth DWARF info.
When more than 10% of functions get ignored for inconsistencies, we
should emit a warning to only use asynchronous unwind tables.
See also: #165215
---
bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp | 9 ++++-
.../AArch64/pacret-synchronous-unwind.cpp | 33 +++++++++++++++++++
2 files changed, 41 insertions(+), 1 deletion(-)
create mode 100644 bolt/test/runtime/AArch64/pacret-synchronous-unwind.cpp
diff --git a/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp b/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
index 91030544d2b88..01af88818a21d 100644
--- a/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
+++ b/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
@@ -133,11 +133,18 @@ Error PointerAuthCFIAnalyzer::runOnFunctions(BinaryContext &BC) {
ParallelUtilities::runOnEachFunction(
BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun,
SkipPredicate, "PointerAuthCFIAnalyzer");
+
+ float IgnoredPercent = (100.0 * FunctionsIgnored) / Total;
BC.outs() << "BOLT-INFO: PointerAuthCFIAnalyzer ran on " << Total
<< " functions. Ignored " << FunctionsIgnored << " functions "
- << format("(%.2lf%%)", (100.0 * FunctionsIgnored) / Total)
+ << format("(%.2lf%%)", IgnoredPercent)
<< " because of CFI inconsistencies\n";
+ if (IgnoredPercent >= 10.0)
+ BC.outs() << "BOLT-WARNING: PointerAuthCFIAnalyzer only supports "
+ "asynchronous unwind tables. For C compilers, see "
+ "-fasynchronous-unwind-tables.\n";
+
return Error::success();
}
diff --git a/bolt/test/runtime/AArch64/pacret-synchronous-unwind.cpp b/bolt/test/runtime/AArch64/pacret-synchronous-unwind.cpp
new file mode 100644
index 0000000000000..1bfeeaed3715a
--- /dev/null
+++ b/bolt/test/runtime/AArch64/pacret-synchronous-unwind.cpp
@@ -0,0 +1,33 @@
+// Test to demonstrate that functions compiled with synchronous unwind tables
+// are ignored by the PointerAuthCFIAnalyzer.
+// Exception handling is needed to have _any_ unwind tables, otherwise the
+// PointerAuthCFIAnalyzer does not run on these functions, so it does not ignore
+// any function.
+//
+// REQUIRES: system-linux,bolt-runtime
+//
+// RUN: %clangxx --target=aarch64-unknown-linux-gnu \
+// RUN: -mbranch-protection=pac-ret \
+// RUN: -fno-asynchronous-unwind-tables \
+// RUN: %s -o %t.exe -Wl,-q
+// RUN: llvm-bolt %t.exe -o %t.bolt | FileCheck %s --check-prefix=CHECK
+//
+// CHECK: PointerAuthCFIAnalyzer ran on 3 functions. Ignored
+// CHECK-NOT: 0 functions (0.00%) because of CFI inconsistencies
+// CHECK-SAME: 1 functions (33.33%) because of CFI inconsistencies
+// CHECK-NEXT: BOLT-WARNING: PointerAuthCFIAnalyzer only supports asynchronous
+// CHECK-SAME: unwind tables. For C compilers, see -fasynchronous-unwind-tables.
+
+#include <cstdio>
+#include <stdexcept>
+
+void foo() { throw std::runtime_error("Exception from foo()."); }
+
+int main() {
+ try {
+ foo();
+ } catch (const std::exception &e) {
+ printf("Exception caught: %s\n", e.what());
+ }
+ return 0;
+}
>From 7fc8acdbf4cef2aa7f4f5ca9d136d4cb1bce9fe6 Mon Sep 17 00:00:00 2001
From: Gergely Balint <gergely.balint at arm.com>
Date: Tue, 28 Oct 2025 09:23:08 +0000
Subject: [PATCH 2/4] [BOLT] Use opts::Verbosity in PointerAuthCFIAnalyzer
---
bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp | 27 ++++++++++++++--------
bolt/test/AArch64/pacret-cfi-incorrect.s | 2 +-
2 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp b/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
index 01af88818a21d..5979d5fb01818 100644
--- a/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
+++ b/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
@@ -28,6 +28,10 @@
using namespace llvm;
+namespace opts {
+extern llvm::cl::opt<unsigned> Verbosity;
+} // namespace opts
+
namespace llvm {
namespace bolt {
@@ -43,9 +47,10 @@ bool PointerAuthCFIAnalyzer::runOnFunction(BinaryFunction &BF) {
// Not all functions have .cfi_negate_ra_state in them. But if one does,
// we expect psign/pauth instructions to have the hasNegateRAState
// annotation.
- BC.outs() << "BOLT-INFO: inconsistent RAStates in function "
- << BF.getPrintName()
- << ": ptr sign/auth inst without .cfi_negate_ra_state\n";
+ if (opts::Verbosity >= 1)
+ BC.outs() << "BOLT-INFO: inconsistent RAStates in function "
+ << BF.getPrintName()
+ << ": ptr sign/auth inst without .cfi_negate_ra_state\n";
std::lock_guard<std::mutex> Lock(IgnoreMutex);
BF.setIgnored();
return false;
@@ -65,9 +70,10 @@ bool PointerAuthCFIAnalyzer::runOnFunction(BinaryFunction &BF) {
if (BC.MIB->isPSignOnLR(Inst)) {
if (RAState) {
// RA signing instructions should only follow unsigned RA state.
- BC.outs() << "BOLT-INFO: inconsistent RAStates in function "
- << BF.getPrintName()
- << ": ptr signing inst encountered in Signed RA state\n";
+ if (opts::Verbosity >= 1)
+ BC.outs() << "BOLT-INFO: inconsistent RAStates in function "
+ << BF.getPrintName()
+ << ": ptr signing inst encountered in Signed RA state\n";
std::lock_guard<std::mutex> Lock(IgnoreMutex);
BF.setIgnored();
return false;
@@ -75,10 +81,11 @@ bool PointerAuthCFIAnalyzer::runOnFunction(BinaryFunction &BF) {
} else if (BC.MIB->isPAuthOnLR(Inst)) {
if (!RAState) {
// RA authenticating instructions should only follow signed RA state.
- BC.outs() << "BOLT-INFO: inconsistent RAStates in function "
- << BF.getPrintName()
- << ": ptr authenticating inst encountered in Unsigned RA "
- "state\n";
+ if (opts::Verbosity >= 1)
+ BC.outs() << "BOLT-INFO: inconsistent RAStates in function "
+ << BF.getPrintName()
+ << ": ptr authenticating inst encountered in Unsigned RA "
+ "state\n";
std::lock_guard<std::mutex> Lock(IgnoreMutex);
BF.setIgnored();
return false;
diff --git a/bolt/test/AArch64/pacret-cfi-incorrect.s b/bolt/test/AArch64/pacret-cfi-incorrect.s
index 390b3b824d6bc..68a6fc008ab06 100644
--- a/bolt/test/AArch64/pacret-cfi-incorrect.s
+++ b/bolt/test/AArch64/pacret-cfi-incorrect.s
@@ -8,7 +8,7 @@
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
-# RUN: llvm-bolt %t.exe -o %t.exe.bolt --no-threads | FileCheck %s --check-prefix=CHECK-BOLT
+# RUN: llvm-bolt %t.exe -o %t.exe.bolt -v=1 --no-threads | FileCheck %s --check-prefix=CHECK-BOLT
# CHECK-BOLT: BOLT-INFO: inconsistent RAStates in function foo: ptr authenticating inst encountered in Unsigned RA state
# CHECK-BOLT: BOLT-INFO: inconsistent RAStates in function bar: ptr signing inst encountered in Signed RA state
>From 9920ac6d265a833a2fc691f3dc7005a51a6f9c05 Mon Sep 17 00:00:00 2001
From: Gergely Balint <gergely.balint at arm.com>
Date: Tue, 11 Nov 2025 10:10:32 +0000
Subject: [PATCH 3/4] [BOLT] Add comment about the chosen threshold
---
bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp b/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
index 5979d5fb01818..06ddae96515c0 100644
--- a/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
+++ b/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
@@ -147,6 +147,16 @@ Error PointerAuthCFIAnalyzer::runOnFunctions(BinaryContext &BC) {
<< format("(%.2lf%%)", IgnoredPercent)
<< " because of CFI inconsistencies\n";
+ // Errors in the input are expected from two sources:
+ // - compilers emitting incorrect CFIs. This happens more frequently with
+ // older compiler versions, but it should not account for a large percentage.
+ // - input binary is using synchronous unwind tables. This means that after
+ // call sites, the unwind CFIs are dropped: the pass sees missing
+ // .cfi_negate_ra_state from autiasp instructions. If this is the case, a
+ // larger percentage of functions will be ignored.
+ //
+ // This is why the 10% threshold was chosen: we should not warn about
+ // synchronous unwind tables if only a few % is ignored.
if (IgnoredPercent >= 10.0)
BC.outs() << "BOLT-WARNING: PointerAuthCFIAnalyzer only supports "
"asynchronous unwind tables. For C compilers, see "
>From 352b942e72cf0ad9be4ce90be48f21269f94f4c1 Mon Sep 17 00:00:00 2001
From: Gergely Balint <gergely.balint at arm.com>
Date: Tue, 11 Nov 2025 10:16:31 +0000
Subject: [PATCH 4/4] [BOLT] PointerAuthCFIAnalyzer: return early if there is
no work
- makes sure we do not divide by zero, to calculate the % of ignored
functions.
---
bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp b/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
index 06ddae96515c0..c125c5f916141 100644
--- a/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
+++ b/bolt/lib/Passes/PointerAuthCFIAnalyzer.cpp
@@ -137,6 +137,9 @@ Error PointerAuthCFIAnalyzer::runOnFunctions(BinaryContext &BC) {
return P.second.containedNegateRAState() && !P.second.isIgnored();
});
+ if (Total == 0)
+ return Error::success();
+
ParallelUtilities::runOnEachFunction(
BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun,
SkipPredicate, "PointerAuthCFIAnalyzer");
More information about the llvm-branch-commits
mailing list