[llvm] [BOLT] Prevent rela.plt reordering (PR #219447)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 28 04:51:49 PDT 2026
https://github.com/maksimra updated https://github.com/llvm/llvm-project/pull/219447
>From 8e00280b0cbc5650b2e1f9c376864ed90932fb54 Mon Sep 17 00:00:00 2001
From: Maksim <rachinskii.mv at phystech.edu>
Date: Thu, 20 Aug 2026 15:12:48 +0300
Subject: [PATCH] [BOLT] Prevent rela.plt reordering
BOLT reorders .rela.plt entries without also updating the corresponding
PLT stub relocation indices. This patch prevent original order of jump
relative relocations.
---
bolt/include/bolt/Core/BinaryContext.h | 4 +-
bolt/include/bolt/Core/BinarySection.h | 7 +-
bolt/include/bolt/Core/Relocation.h | 25 +++-
bolt/include/bolt/Rewrite/RewriteInstance.h | 4 +-
bolt/lib/Core/BinaryContext.cpp | 5 +-
bolt/lib/Rewrite/RewriteInstance.cpp | 123 ++++++++++++--------
bolt/test/runtime/X86/rela-plt-order.c | 31 +++++
7 files changed, 140 insertions(+), 59 deletions(-)
create mode 100644 bolt/test/runtime/X86/rela-plt-order.c
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 92cd853870cae..7677fb83e1e04 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -1455,7 +1455,9 @@ class BinaryContext {
/// Register dynamic relocation at \p Address.
void addDynamicRelocation(uint64_t Address, MCSymbol *Symbol, uint32_t Type,
uint64_t Addend, uint64_t Value = 0,
- bool IsRELR = false);
+ bool IsRELR = false,
+ uint64_t JmpRelocationIndex =
+ Relocation::NoJmpRelocationIndex);
/// Return a dynamic relocation registered at a given \p Address, or nullptr
/// if there is no dynamic relocation at such address.
diff --git a/bolt/include/bolt/Core/BinarySection.h b/bolt/include/bolt/Core/BinarySection.h
index 4609105d8b5ba..0098933054e3b 100644
--- a/bolt/include/bolt/Core/BinarySection.h
+++ b/bolt/include/bolt/Core/BinarySection.h
@@ -367,9 +367,12 @@ class BinarySection {
/// Add a dynamic relocation at the given /p Offset.
void addDynamicRelocation(uint64_t Offset, MCSymbol *Symbol, uint32_t Type,
uint64_t Addend, uint64_t Value = 0,
- bool IsRELR = false) {
+ bool IsRELR = false,
+ uint64_t JmpRelocationIndex =
+ Relocation::NoJmpRelocationIndex) {
addDynamicRelocation(
- Relocation{Offset, Symbol, Type, Addend, Value, IsRELR});
+ Relocation{Offset, Symbol, Type, Addend, Value, IsRELR,
+ JmpRelocationIndex});
}
void addDynamicRelocation(const Relocation &Reloc) {
diff --git a/bolt/include/bolt/Core/Relocation.h b/bolt/include/bolt/Core/Relocation.h
index 9bc94d6484b70..1237314b994f3 100644
--- a/bolt/include/bolt/Core/Relocation.h
+++ b/bolt/include/bolt/Core/Relocation.h
@@ -37,17 +37,21 @@ namespace bolt {
/// Relocation class.
class Relocation {
public:
+ static constexpr uint64_t NoJmpRelocationIndex = uint64_t(-1);
+
Relocation(uint64_t Offset, MCSymbol *Symbol, uint32_t Type, uint64_t Addend,
- uint64_t Value, bool IsRELR = false)
+ uint64_t Value, bool IsRELR = false,
+ uint64_t JmpRelocationIndex = NoJmpRelocationIndex)
: Offset(Offset), Symbol(Symbol), Type(Type), Optional(false),
- IsRELR(IsRELR), Addend(Addend), Value(Value) {
+ IsRELR(IsRELR), Addend(Addend), Value(Value),
+ JmpRelocationIndex(JmpRelocationIndex) {
assert((isRelative() || !isRELR()) &&
"Only relative relocations can be relr.");
}
Relocation()
: Offset(0), Symbol(0), Type(0), Optional(0), IsRELR(0), Addend(0),
- Value(0) {}
+ Value(0), JmpRelocationIndex(NoJmpRelocationIndex) {}
static Triple::ArchType Arch; /// set by BinaryContext ctor.
@@ -79,6 +83,21 @@ class Relocation {
/// Used to validate relocation correctness.
uint64_t Value;
+private:
+ /// Original index in DT_JMPREL, or NoJmpRelocationIndex for relocations
+ /// originating from other relocation tables.
+ uint64_t JmpRelocationIndex;
+
+public:
+ bool isJmpRelocation() const {
+ return JmpRelocationIndex != NoJmpRelocationIndex;
+ }
+
+ uint64_t getJmpRelocationIndex() const {
+ assert(isJmpRelocation() && "not a DT_JMPREL relocation");
+ return JmpRelocationIndex;
+ }
+
/// Return size in bytes of the given relocation \p Type.
static size_t getSizeForType(uint32_t Type);
diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index a624c056ada14..de253c33520a2 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -522,8 +522,8 @@ class RewriteInstance {
std::optional<uint64_t> PLTRelocationsAddress;
uint64_t PLTRelocationsSize{0};
- /// True if relocation of specified type came from .rela.plt
- DenseMap<uint64_t, bool> IsJmpRelocation;
+ /// Number of relocations read from DT_JMPREL.
+ uint64_t NumJmpRelocations{0};
/// Index of specified symbol in the dynamic symbol table. NOTE Currently it
/// is filled and used only with the relocations-related symbols.
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index d911f8191f791..ca61d70d594c9 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -2663,11 +2663,12 @@ void BinaryContext::addRelocation(uint64_t Address, MCSymbol *Symbol,
void BinaryContext::addDynamicRelocation(uint64_t Address, MCSymbol *Symbol,
uint32_t Type, uint64_t Addend,
- uint64_t Value, bool IsRELR) {
+ uint64_t Value, bool IsRELR,
+ uint64_t JmpRelocationIndex) {
ErrorOr<BinarySection &> Section = getSectionForAddress(Address);
assert(Section && "cannot find section for address");
Section->addDynamicRelocation(Address - Section->getAddress(), Symbol, Type,
- Addend, Value, IsRELR);
+ Addend, Value, IsRELR, JmpRelocationIndex);
}
bool BinaryContext::removeRelocationAt(uint64_t Address) {
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 51b19db0cbd9b..a73cdd85b0988 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -65,6 +65,7 @@
#include <memory>
#include <optional>
#include <system_error>
+#include <tuple>
#undef DEBUG_TYPE
#define DEBUG_TYPE "bolt"
@@ -2976,9 +2977,6 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
<< " : + 0x" << Twine::utohexstr(Addend) << '\n'
);
- if (IsJmpRel)
- IsJmpRelocation[RType] = true;
-
if (Symbol)
SymbolIndex[Symbol] = getRelocationSymbol(InputFile, Rel);
@@ -2994,7 +2992,12 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
handleRelativeDynamicRelocation(Rel.getOffset(), ReferencedAddress);
}
- BC->addDynamicRelocation(Rel.getOffset(), Symbol, RType, Addend);
+ const uint64_t JmpRelocationIndex =
+ IsJmpRel ? NumJmpRelocations++
+ : Relocation::NoJmpRelocationIndex;
+ BC->addDynamicRelocation(Rel.getOffset(), Symbol, RType, Addend,
+ /*Value=*/0, /*IsRELR=*/false,
+ JmpRelocationIndex);
}
}
@@ -6085,65 +6088,87 @@ RewriteInstance::patchELFAllocatableRelaSections(ELFObjectFile<ELFT> *File) {
Offset += sizeof(*RelA);
};
- auto writeRelocations = [&](bool PatchRelative) {
- for (BinarySection &Section : BC->allocatableSections()) {
- const uint64_t SectionInputAddress = Section.getAddress();
- uint64_t SectionAddress = Section.getOutputAddress();
- if (!SectionAddress)
- SectionAddress = SectionInputAddress;
+ auto writeRelocation = [&](BinarySection &Section, const Relocation &Rel) {
+ const uint64_t SectionInputAddress = Section.getAddress();
+ uint64_t SectionAddress = Section.getOutputAddress();
+ if (!SectionAddress)
+ SectionAddress = SectionInputAddress;
- for (const Relocation &Rel : Section.dynamicRelocations()) {
- const bool IsRelative = Rel.isRelative();
- if (PatchRelative != IsRelative || Rel.isRELR())
- continue;
+ Elf_Rela NewRelA;
+ MCSymbol *Symbol = Rel.Symbol;
+ uint32_t SymbolIdx = 0;
+ uint64_t Addend = Rel.Addend;
+ uint64_t RelOffset =
+ getNewFunctionOrDataAddress(SectionInputAddress + Rel.Offset);
- if (IsRelative)
- ++DynamicRelativeRelocationsCount;
+ RelOffset = RelOffset == 0 ? SectionAddress + Rel.Offset : RelOffset;
+ if (Rel.Symbol) {
+ SymbolIdx = getOutputDynamicSymbolIndex(Symbol);
+ } else {
+ // Usually this case is used for R_*_(I)RELATIVE relocations
+ const uint64_t Address = getNewFunctionOrDataAddress(Addend);
+ if (Address)
+ Addend = Address;
+ }
- Elf_Rela NewRelA;
- MCSymbol *Symbol = Rel.Symbol;
- uint32_t SymbolIdx = 0;
- uint64_t Addend = Rel.Addend;
- uint64_t RelOffset =
- getNewFunctionOrDataAddress(SectionInputAddress + Rel.Offset);
+ NewRelA.setSymbolAndType(SymbolIdx, Rel.Type, EF.isMips64EL());
+ NewRelA.r_offset = RelOffset;
+ NewRelA.r_addend = Addend;
- RelOffset = RelOffset == 0 ? SectionAddress + Rel.Offset : RelOffset;
- if (Rel.Symbol) {
- SymbolIdx = getOutputDynamicSymbolIndex(Symbol);
- } else {
- // Usually this case is used for R_*_(I)RELATIVE relocations
- const uint64_t Address = getNewFunctionOrDataAddress(Addend);
- if (Address)
- Addend = Address;
- }
+ const bool IsJmpRel = Rel.isJmpRelocation();
+ uint64_t &Offset = IsJmpRel ? RelPltOffset : RelDynOffset;
+ const uint64_t &EndOffset = IsJmpRel ? RelPltEndOffset : RelDynEndOffset;
+ if (!Offset || !EndOffset) {
+ BC->errs() << "BOLT-ERROR: Invalid offsets for dynamic relocation\n";
+ exit(1);
+ }
- NewRelA.setSymbolAndType(SymbolIdx, Rel.Type, EF.isMips64EL());
- NewRelA.r_offset = RelOffset;
- NewRelA.r_addend = Addend;
+ if (Offset + sizeof(NewRelA) > EndOffset) {
+ BC->errs() << "BOLT-ERROR: Offset overflow for dynamic relocation\n";
+ exit(1);
+ }
- const bool IsJmpRel = IsJmpRelocation.contains(Rel.Type);
- uint64_t &Offset = IsJmpRel ? RelPltOffset : RelDynOffset;
- const uint64_t &EndOffset =
- IsJmpRel ? RelPltEndOffset : RelDynEndOffset;
- if (!Offset || !EndOffset) {
- BC->errs() << "BOLT-ERROR: Invalid offsets for dynamic relocation\n";
- exit(1);
- }
+ writeRela(&NewRelA, Offset);
+ };
- if (Offset + sizeof(NewRelA) > EndOffset) {
- BC->errs() << "BOLT-ERROR: Offset overflow for dynamic relocation\n";
- exit(1);
- }
+ auto writeDynRelocations = [&](bool PatchRelative) {
+ for (BinarySection &Section : BC->allocatableSections()) {
+ for (const Relocation &Rel : Section.dynamicRelocations()) {
+ if (Rel.isJmpRelocation())
+ continue;
+
+ const bool IsRelative = Rel.isRelative();
+ if (PatchRelative != IsRelative || Rel.isRELR())
+ continue;
- writeRela(&NewRelA, Offset);
+ if (IsRelative)
+ ++DynamicRelativeRelocationsCount;
+ writeRelocation(Section, Rel);
}
}
};
// The dynamic linker expects all R_*_RELATIVE relocations in RELA
// to be emitted first.
- writeRelocations(/* PatchRelative */ true);
- writeRelocations(/* PatchRelative */ false);
+ writeDynRelocations(/* PatchRelative */ true);
+ writeDynRelocations(/* PatchRelative */ false);
+
+ using OrderedJmpRel = std::tuple<uint64_t, BinarySection *, const Relocation *>;
+ std::vector<OrderedJmpRel> JmpRelocations;
+ for (BinarySection &Section : BC->allocatableSections()) {
+ for (const Relocation &Rel : Section.dynamicRelocations()) {
+ if (!Rel.isJmpRelocation())
+ continue;
+ assert(!Rel.isRELR() && "RELR relocation cannot belong to DT_JMPREL");
+ JmpRelocations.emplace_back(Rel.getJmpRelocationIndex(), &Section, &Rel);
+ }
+ }
+ llvm::sort(JmpRelocations, [](const OrderedJmpRel &A,
+ const OrderedJmpRel &B) {
+ return std::get<0>(A) < std::get<0>(B);
+ });
+ for (const OrderedJmpRel &Entry : JmpRelocations)
+ writeRelocation(*std::get<1>(Entry), *std::get<2>(Entry));
auto fillNone = [&](uint64_t &Offset, uint64_t EndOffset) {
if (!Offset)
diff --git a/bolt/test/runtime/X86/rela-plt-order.c b/bolt/test/runtime/X86/rela-plt-order.c
new file mode 100644
index 0000000000000..f5acaf00cdf3c
--- /dev/null
+++ b/bolt/test/runtime/X86/rela-plt-order.c
@@ -0,0 +1,31 @@
+// REQUIRES: x86_64-linux, gnu_ld
+//
+// RUN: split-file %s %t
+// RUN: %clang %cflags -fPIC -shared -o %t/libexample.so \
+// RUN: %t/example.c
+// RUN: %clang %cflags -fno-pie -no-pie -fuse-ld=bfd \
+// RUN: -Wl,--emit-relocs -Wl,-rpath,\$ORIGIN -o %t/main %t/main.c \
+// RUN: -L%t -lexample
+// RUN: llvm-bolt %t/main -o %t/main.bolt
+// RUN: llvm-readelf --dyn-relocations %t/main.bolt | FileCheck %s
+// RUN: %t/main.bolt
+
+// CHECK-LABEL: 'PLT' relocation section
+// CHECK: R_X86_64_JUMP_SLOT{{.*}}long_name
+// CHECK-NEXT: R_X86_64_IRELATIVE
+
+//--- main.c
+extern int long_name(void);
+
+__attribute__((target_clones("default,avx2"))) int foo(int x) { return x + 1; }
+
+int main(void) {
+ if (foo(1) != 2)
+ return 1;
+ if (long_name() != 0)
+ return 1;
+ return 0;
+}
+
+//--- example.c
+int long_name(void) { return 0; }
More information about the llvm-commits
mailing list