[llvm] [BOLT] Treat relr relocations like rela (PR #206830)
Fabian Parzefall via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 1 17:02:16 PDT 2026
https://github.com/pzfl updated https://github.com/llvm/llvm-project/pull/206830
>From 38a65d36564c7e6e7c0b7f9f2174de0a327e32e6 Mon Sep 17 00:00:00 2001
From: Fabian Parzefall <parzefall at meta.com>
Date: Fri, 26 Jun 2026 11:40:36 -0700
Subject: [PATCH 1/2] [BOLT] Register PIE indirect goto relocations
PIE binaries use R_*_RELATIVE dynamic relocations for indirect goto jump
tables. BOLT creates entry points for their targets but does not mark
these addresses as targets from data relocations, so indirect jumps with
unknown control flow (which originates from indirect goto) have no CFG
successors and are interpreted as potential tail calls, while the jump
targets are incorrectly identified as additional entry points.
Add these offsets to the function's list of non-entry relocation
references instead of marking them as entry points. Extend relocation
rewriting to also check whether a block is externally referenced. This
mirrors behavior of data-to-code relocations in non-pie binaries.
---
bolt/include/bolt/Core/BinaryFunction.h | 6 ++++++
bolt/lib/Core/BinaryFunction.cpp | 3 ---
bolt/lib/Rewrite/RewriteInstance.cpp | 13 ++++++++-----
bolt/test/AArch64/computed-goto.s | 19 +++++++++----------
bolt/test/X86/indirect-goto.test | 6 ++++++
bolt/test/indirect-goto-relocs.test | 14 ++++++++------
6 files changed, 37 insertions(+), 24 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 84fbd5661fd0a..1d27d9514e230 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -681,6 +681,12 @@ class BinaryFunction {
return !ExternallyReferencedOffsets.empty();
}
+ bool isExternallReferenced(const BinaryBasicBlock &BB) const {
+ return isEntryPoint(BB) ||
+ ExternallyReferencedOffsets.find(BB.getOffset()) !=
+ ExternallyReferencedOffsets.end();
+ }
+
/// Return an entry ID corresponding to a symbol known to belong to
/// the function.
///
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index 200e286d8e80e..b0d18f77e7737 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1617,7 +1617,6 @@ bool BinaryFunction::scanExternalRefs() {
if (opts::NoScan) {
clearList(Relocations);
- clearList(ExternallyReferencedOffsets);
return false;
}
@@ -1914,7 +1913,6 @@ bool BinaryFunction::scanExternalRefs() {
}
clearList(Relocations);
- clearList(ExternallyReferencedOffsets);
if (Success && BC.HasRelocations)
HasExternalRefRelocations = true;
@@ -2569,7 +2567,6 @@ Error BinaryFunction::buildCFG(MCPlusBuilder::AllocatorIdTy AllocatorId) {
setSimple(false);
}
- clearList(ExternallyReferencedOffsets);
clearList(UnknownIndirectBranchOffsets);
return Error::success();
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 9e00d48e03853..8e13793881c66 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -2814,7 +2814,10 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
if (!Func->isInConstantIsland(ReferencedAddress)) {
if (const uint64_t ReferenceOffset =
ReferencedAddress - Func->getAddress()) {
- Func->addEntryPointAtOffset(ReferenceOffset);
+ assert(!BC->getBinaryFunctionContainingAddress(Rel.getOffset()) &&
+ "Relative relocation to code only from data");
+ Func->registerInternalRefDataRelocation(ReferenceOffset,
+ Rel.getOffset());
}
} else {
BC->errs() << "BOLT-ERROR: referenced address at 0x"
@@ -6239,11 +6242,11 @@ uint64_t RewriteInstance::getNewFunctionOrDataAddress(uint64_t OldAddress) {
if (const BinaryFunction *BF =
BC->getBinaryFunctionContainingAddress(OldAddress)) {
if (BF->isEmitted()) {
- // If OldAddress is the another entry point of
- // the function, then BOLT could get the new address.
- if (BF->isMultiEntry()) {
+ // If OldAddress is the another entry point of the function or the target
+ // of an indirect goto, then BOLT could get the new address.
+ if (BF->isMultiEntry() || BF->hasInternalReference()) {
for (const BinaryBasicBlock &BB : *BF)
- if (BB.isEntryPoint() &&
+ if (BF->isExternallReferenced(BB) &&
(BF->getAddress() + BB.getOffset()) == OldAddress)
return BB.getOutputStartAddress();
}
diff --git a/bolt/test/AArch64/computed-goto.s b/bolt/test/AArch64/computed-goto.s
index 5d775b9a6aeae..3a97ddf1804c6 100644
--- a/bolt/test/AArch64/computed-goto.s
+++ b/bolt/test/AArch64/computed-goto.s
@@ -1,12 +1,12 @@
-// This test checks that BOLT creates entry points for addresses
+// This test checks that BOLT recognizes blocks for addresses
// referenced by dynamic relocations.
// The test also checks that BOLT can map addresses inside functions.
-// Checks for error and entry points.
+// Checks for error and cfg.
# 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.bolt 2>&1 | FileCheck %s
-# RUN: llvm-bolt %t.exe -o %t.bolt --print-cfg | FileCheck --check-prefix=CHECK-ENTRIES %s
+# RUN: llvm-bolt %t.exe -o %t.bolt --print-cfg | FileCheck --check-prefix=CHECK-CFG %s
// Checks for dynamic relocations.
# RUN: llvm-readelf -dr %t.bolt > %t.out.txt
@@ -30,13 +30,12 @@
# CHECK-RELOCS: [[#ADDR]] <unknown>
# CHECK-RELOCS: [[#ADDR + 8]] <unknown>
-// Check that BOLT registers extra entry points for dynamic relocations.
-# CHECK-ENTRIES: Binary Function "main" after building cfg {
-# CHECK-ENTRIES: IsMultiEntry: 1
-# CHECK-ENTRIES: .Ltmp0 {{.*}}
-# CHECK-ENTRIES-NEXT: Secondary Entry Point: {{.*}}
-# CHECK-ENTRIES: .Ltmp1 {{.*}}
-# CHECK-ENTRIES-NEXT: Secondary Entry Point: {{.*}}
+// Check that BOLT recognizes indirect targeted blocks.
+# CHECK-CFG: Binary Function "main" after building cfg {
+# CHECK-CFG: IsMultiEntry: 0
+# CHECK-CFG: BB Count : 3
+# CHECK-CFG: .Ltmp0 {{.*}}
+# CHECK-CFG: .Ltmp1 {{.*}}
.globl main
.p2align 2
diff --git a/bolt/test/X86/indirect-goto.test b/bolt/test/X86/indirect-goto.test
index aeb89de3f2fc2..c7dae07446dfd 100644
--- a/bolt/test/X86/indirect-goto.test
+++ b/bolt/test/X86/indirect-goto.test
@@ -4,6 +4,12 @@ RUN: llvm-bolt %t -o %t.null --relocs=1 --print-cfg --print-only=main \
RUN: --strict \
RUN: 2>&1 | FileCheck %s
+## Check indirect goto works in pie
+RUN: %clang %cflags -fPIE -pie %S/../Inputs/indirect_goto.c -o %t.pie -Wl,-q
+RUN: llvm-bolt %t.pie -o %t.pie.null --relocs=1 --print-cfg --print-only=main \
+RUN: --strict \
+RUN: 2>&1 | FileCheck %s
+
## Check that all possible destinations are included as successors.
CHECK: jmpq *%rax # UNKNOWN CONTROL FLOW
CHECK: Successors: .Ltmp0, .Ltmp1, .Ltmp2
diff --git a/bolt/test/indirect-goto-relocs.test b/bolt/test/indirect-goto-relocs.test
index e28d64135b44e..e48c3529192d7 100644
--- a/bolt/test/indirect-goto-relocs.test
+++ b/bolt/test/indirect-goto-relocs.test
@@ -1,12 +1,14 @@
-// This test checks that BOLT creates entry points from sources
-// that use indirect goto.
+// This test checks that BOLT recognizes unknown control flow from sources that
+// use indirect goto.
REQUIRES: system-linux
RUN: %clang %cflags -pie %S/Inputs/indirect_goto.c -o %t.exe -Wl,-q
-RUN: llvm-bolt %t.exe -o %t.bolt --print-cfg | FileCheck --check-prefix=CHECK-PIE %s
+RUN: llvm-bolt %t.exe -o %t.bolt --print-cfg --print-only=main \
+RUN: | FileCheck --check-prefix=CHECK-PIE %s
-// Check that BOLT registers extra entry points for dynamic relocations with PIE.
+// Check that BOLT discovers basic blocks for dynamic relocations with PIE.
CHECK-PIE: Binary Function "main" after building cfg {
-CHECK-PIE: IsMultiEntry: 1
-CHECK-PIE: Secondary Entry Points : {{.*}}
+CHECK-PIE: IsMultiEntry: 0
+CHECK-PIE: BB Count : {{5|6}}
+// BB Count can vary across architectures.
>From 449c89cfce346b09132b8c57845b60487273dce3 Mon Sep 17 00:00:00 2001
From: Fabian Parzefall <parzefall at meta.com>
Date: Fri, 26 Jun 2026 11:40:36 -0700
Subject: [PATCH 2/2] [BOLT] Treat relr relocations like rela
The linker can emit certain relative .dyn.rela relocations in .dyn.relr.
This includes relocations for jumptables emitted for indirect goto.
The current processing recognizes these relocations as jump targets
only for relative relocations from .dyn.rela, but not from .dyn.relr.
Give .dyn.relr the same treatment, so CFG construction does not dismiss
blocks that are only reachable indirectly.
---
bolt/include/bolt/Rewrite/RewriteInstance.h | 4 ++
bolt/lib/Rewrite/RewriteInstance.cpp | 51 +++++++++++----------
bolt/test/X86/indirect-goto.test | 7 +++
3 files changed, 39 insertions(+), 23 deletions(-)
diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index 3873d944e01ba..efa5e20c16b7b 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -136,6 +136,10 @@ class RewriteInstance {
/// Read relocations from a given RELR section.
void readDynamicRelrRelocations(BinarySection &Section);
+ /// Process relative dynamic relocations targeting code, e.g. from jumptables.
+ void handleRelativeDynamicRelocation(uint64_t RelOffset,
+ uint64_t ReferencedAddress);
+
/// Print relocation information.
void printRelocationInfo(const RelocationRef &Rel, StringRef SymbolName,
uint64_t SymbolAddress, uint64_t Addend,
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 8e13793881c66..b574e23c32561 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -2806,30 +2806,13 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
SymbolIndex[Symbol] = getRelocationSymbol(InputFile, Rel);
const uint64_t ReferencedAddress = SymbolAddress + Addend;
- BinaryFunction *Func =
- BC->getBinaryFunctionContainingAddress(ReferencedAddress);
-
- if (Relocation::isRelative(RType) && SymbolAddress == 0) {
- if (Func) {
- if (!Func->isInConstantIsland(ReferencedAddress)) {
- if (const uint64_t ReferenceOffset =
- ReferencedAddress - Func->getAddress()) {
- assert(!BC->getBinaryFunctionContainingAddress(Rel.getOffset()) &&
- "Relative relocation to code only from data");
- Func->registerInternalRefDataRelocation(ReferenceOffset,
- Rel.getOffset());
- }
- } else {
- BC->errs() << "BOLT-ERROR: referenced address at 0x"
- << Twine::utohexstr(ReferencedAddress)
- << " is in constant island of function " << *Func << "\n";
- exit(1);
- }
+ if (Relocation::isRelative(RType)) {
+ if (SymbolAddress != 0) {
+ BC->errs() << "BOLT-ERROR: symbol address non zero for RELATIVE "
+ "relocation type\n";
+ exit(1);
}
- } else if (Relocation::isRelative(RType) && SymbolAddress != 0) {
- BC->errs() << "BOLT-ERROR: symbol address non zero for RELATIVE "
- "relocation type\n";
- exit(1);
+ handleRelativeDynamicRelocation(Rel.getOffset(), ReferencedAddress);
}
BC->addDynamicRelocation(Rel.getOffset(), Symbol, RType, Addend);
@@ -2862,6 +2845,7 @@ void RewriteInstance::readDynamicRelrRelocations(BinarySection &Section) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: R_*_RELATIVE relocation at 0x"
<< Twine::utohexstr(Address) << " to 0x"
<< Twine::utohexstr(Addend) << '\n';);
+ handleRelativeDynamicRelocation(Address, Addend);
BC->addDynamicRelocation(Address, nullptr, RType, Addend, /*Value=*/0,
/*IsRELR=*/true);
};
@@ -2889,6 +2873,27 @@ void RewriteInstance::readDynamicRelrRelocations(BinarySection &Section) {
}
}
+void RewriteInstance::handleRelativeDynamicRelocation(
+ uint64_t RelOffset, uint64_t ReferencedAddress) {
+ BinaryFunction *Func =
+ BC->getBinaryFunctionContainingAddress(ReferencedAddress);
+ if (!Func)
+ return;
+
+ if (Func->isInConstantIsland(ReferencedAddress)) {
+ BC->errs() << "BOLT-ERROR: referenced address at 0x"
+ << Twine::utohexstr(ReferencedAddress)
+ << " is in constant island of function " << *Func << "\n";
+ exit(1);
+ }
+
+ if (const uint64_t ReferenceOffset = ReferencedAddress - Func->getAddress()) {
+ assert(!BC->getBinaryFunctionContainingAddress(RelOffset) &&
+ "Relative relocation to code only from data");
+ Func->registerInternalRefDataRelocation(ReferenceOffset, RelOffset);
+ }
+}
+
void RewriteInstance::printRelocationInfo(const RelocationRef &Rel,
StringRef SymbolName,
uint64_t SymbolAddress,
diff --git a/bolt/test/X86/indirect-goto.test b/bolt/test/X86/indirect-goto.test
index c7dae07446dfd..38e8d9073a956 100644
--- a/bolt/test/X86/indirect-goto.test
+++ b/bolt/test/X86/indirect-goto.test
@@ -10,6 +10,13 @@ RUN: llvm-bolt %t.pie -o %t.pie.null --relocs=1 --print-cfg --print-only=main \
RUN: --strict \
RUN: 2>&1 | FileCheck %s
+## Check indirect goto works in binaries linked with relr
+RUN: %clang %cflags -fPIE -pie %S/../Inputs/indirect_goto.c -o %t.relr \
+RUN: -Wl,-z,pack-relative-relocs -Wl,-q
+RUN: llvm-bolt %t.relr -o %t.relr.null --relocs=1 --print-cfg --print-only=main \
+RUN: --strict \
+RUN: 2>&1 | FileCheck %s
+
## Check that all possible destinations are included as successors.
CHECK: jmpq *%rax # UNKNOWN CONTROL FLOW
CHECK: Successors: .Ltmp0, .Ltmp1, .Ltmp2
More information about the llvm-commits
mailing list