[llvm] [BOLT][binary-analysis] Fix pac-ret scanner's "major limitation" (PR #136664)
Gergely Bálint via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 22 00:56:11 PDT 2025
https://github.com/bgergely0 created https://github.com/llvm/llvm-project/pull/136664
Quoting `bolt/docs/BinaryAnalysis.md`:
> BOLT cannot currently handle functions with cfi_negate_ra_state correctly, i.e. any binaries built with -mbranch-protection=pac-ret. The scanner is meant to be used on specifically such binaries, so this is a major limitation! Work is going on in PR https://github.com/llvm/llvm-project/pull/120064 to fix this.
For pac-ret analysis to work, we don't actually need the whole work in #120064 to be merged.
## Some context
The BOLT codebase holds different tools: BOLT itself is used to optimize the layout of a binary, while other tools only need to generate the same internal representation to aid in that process, for example `perf2bolt` only processes profile information, and does not output an optimized binary.
The `llvm-bolt-binary-analysis` tool is also such a tool.
This is important, because the `.cfi_negate_ra_state` Call Frame Instruction needs to be read, tracked, and eventually emitted by tools that produce a binary: this CFI signals to the unwinder if it needs to strip the PAC bits from pointers or not.
This means, that for all other tools, we don't actually have to read it from the binary!
## Solution
The solution is fairly simple: we need to pass the `ToolName` to `CFIReaderWriter::fillCFIInfoFor`, and branch based on this `ToolName`.
```C++
if (getToolName() == "llvm-bolt") {
BC.errs() << "BOLT-ERROR: pointer authentication is not supported "
"yet. Please compile "
"your target binary using '-mbranch-protection=none'.\n";
exit(1);
}
```
This is a two-bird-one-stone solution: it also adds a user-friendly error message about the lack of PAC support. Previously, users got a fairly arcane error about a `unsupported CFI opcode`. The new error is actionable, so hopefully it would lead to less confusion while support for PAC is added.
>From 1deada421efad6d9587b906773bf36b1bca38631 Mon Sep 17 00:00:00 2001
From: Gergely Balint <gergely.balint at arm.com>
Date: Tue, 22 Apr 2025 09:31:47 +0200
Subject: [PATCH] [BOLT] Fix pac-ret binary analysis tool's CFI issue
- llvm-bolt-binary-analysis, and other tools which don't produce a
binary as output can be run without NegateRAState CFIs.
- during FillCFIInfo, added a better error message about NegateRAState
CFIs when running llvm-bolt.
- updated docs.
- updated a unit test about NegateRAState CFI handling.
---
bolt/docs/BinaryAnalysis.md | 6 ------
bolt/include/bolt/Core/Exceptions.h | 6 +++++-
bolt/lib/Core/Exceptions.cpp | 15 ++++++++++++---
bolt/lib/Rewrite/RewriteInstance.cpp | 3 ++-
bolt/test/AArch64/dw_cfa_gnu_window_save.test | 6 +++---
5 files changed, 22 insertions(+), 14 deletions(-)
diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 9f0f018980517..b13410cd96355 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -180,12 +180,6 @@ The following are current known cases of false negatives:
[prototype branch](
https://github.com/llvm/llvm-project/compare/main...kbeyls:llvm-project:bolt-gadget-scanner-prototype).
-BOLT cannot currently handle functions with `cfi_negate_ra_state` correctly,
-i.e. any binaries built with `-mbranch-protection=pac-ret`. The scanner is meant
-to be used on specifically such binaries, so this is a major limitation! Work is
-going on in PR [#120064](https://github.com/llvm/llvm-project/pull/120064) to
-fix this.
-
## How to add your own binary analysis
_TODO: this section needs to be written. Ideally, we should have a simple
diff --git a/bolt/include/bolt/Core/Exceptions.h b/bolt/include/bolt/Core/Exceptions.h
index f10cf776f0943..54e7fc50078da 100644
--- a/bolt/include/bolt/Core/Exceptions.h
+++ b/bolt/include/bolt/Core/Exceptions.h
@@ -37,7 +37,8 @@ class BinaryFunction;
/// BinaryFunction, as well as rewriting CFI sections.
class CFIReaderWriter {
public:
- explicit CFIReaderWriter(BinaryContext &BC, const DWARFDebugFrame &EHFrame);
+ explicit CFIReaderWriter(BinaryContext &BC, const DWARFDebugFrame &EHFrame,
+ StringRef ToolName);
bool fillCFIInfoFor(BinaryFunction &Function) const;
@@ -56,9 +57,12 @@ class CFIReaderWriter {
const FDEsMap &getFDEs() const { return FDEs; }
+ StringRef getToolName() const { return ToolName; }
+
private:
BinaryContext &BC;
FDEsMap FDEs;
+ StringRef ToolName;
};
/// Parse an existing .eh_frame and invoke the callback for each
diff --git a/bolt/lib/Core/Exceptions.cpp b/bolt/lib/Core/Exceptions.cpp
index 0b2e63b8ca6a7..cbb29a05bee20 100644
--- a/bolt/lib/Core/Exceptions.cpp
+++ b/bolt/lib/Core/Exceptions.cpp
@@ -463,8 +463,10 @@ void BinaryFunction::updateEHRanges() {
const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0;
CFIReaderWriter::CFIReaderWriter(BinaryContext &BC,
- const DWARFDebugFrame &EHFrame)
+ const DWARFDebugFrame &EHFrame,
+ StringRef ToolName)
: BC(BC) {
+ this->ToolName = ToolName;
// Prepare FDEs for fast lookup
for (const dwarf::FrameEntry &Entry : EHFrame.entries()) {
const auto *CurFDE = dyn_cast<dwarf::FDE>(&Entry);
@@ -632,8 +634,15 @@ bool CFIReaderWriter::fillCFIInfoFor(BinaryFunction &Function) const {
// DW_CFA_GNU_window_save and DW_CFA_GNU_NegateRAState just use the same
// id but mean different things. The latter is used in AArch64.
if (Function.getBinaryContext().isAArch64()) {
- Function.addCFIInstruction(
- Offset, MCCFIInstruction::createNegateRAState(nullptr));
+ // .cfi_negate_ra_state is only needed for tools producing binaries (so
+ // BOLT itself). Other BOLT-based tools (perf2bolt, merge-fdata,
+ // llvm-bolt-binary-analysis, etc.) can safely drop this CFI.
+ if (getToolName() == "llvm-bolt") {
+ BC.errs() << "BOLT-ERROR: pointer authentication is not supported "
+ "yet. Please compile "
+ "your target binary using '-mbranch-protection=none'.\n";
+ exit(1);
+ }
break;
}
if (opts::Verbosity >= 1)
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 69fb736d7bde0..6dc7cad4f538c 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -2048,7 +2048,8 @@ Error RewriteInstance::readSpecialSections() {
Expected<const DWARFDebugFrame *> EHFrameOrError = BC->DwCtx->getEHFrame();
if (!EHFrameOrError)
report_error("expected valid eh_frame section", EHFrameOrError.takeError());
- CFIRdWrt.reset(new CFIReaderWriter(*BC, *EHFrameOrError.get()));
+ StringRef ToolName = llvm::sys::path::stem(Argv[0]);
+ CFIRdWrt.reset(new CFIReaderWriter(*BC, *EHFrameOrError.get(), ToolName));
processSectionMetadata();
diff --git a/bolt/test/AArch64/dw_cfa_gnu_window_save.test b/bolt/test/AArch64/dw_cfa_gnu_window_save.test
index 2e044b399720a..97b257ee04666 100644
--- a/bolt/test/AArch64/dw_cfa_gnu_window_save.test
+++ b/bolt/test/AArch64/dw_cfa_gnu_window_save.test
@@ -1,8 +1,8 @@
-# Check that llvm-bolt can handle DW_CFA_GNU_window_save on AArch64.
+# Check that llvm-bolt refuses binaries with DW_CFA_GNU_window_save on AArch64.
RUN: yaml2obj %p/Inputs/dw_cfa_gnu_window_save.yaml &> %t.exe
-RUN: llvm-bolt %t.exe -o %t.bolt 2>&1 | FileCheck %s
+RUN not: llvm-bolt %t.exe -o %t.bolt 2>&1 | FileCheck %s
CHECK-NOT: paciasp
CHECK-NOT: autiasp
-CHECK-NOT: ERROR: unable to fill CFI.
+CHECK: BOLT-ERROR: pointer authentication is not supported yet.
More information about the llvm-commits
mailing list