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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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"); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
