[llvm] [BOLT] Prevent rela.plt reordering (PR #219447)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 16 02:44:55 PDT 2026
https://github.com/maksimra updated https://github.com/llvm/llvm-project/pull/219447
>From 506e4f494168c0485d31c792da1efea52cd03c2b 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] Preserve the original order of DT_JMPREL relocations.
BOLT reorders .rela.plt entries without also updating the corresponding PLT stub relocation indices. This patch preserves the original order of DT_JMPREL relocations by recording their original indices and using those indices when emitting them. We store the original DT_JMPREL index directly on each relocation.
AI tools (codex) were used to assist with implementation.
Related to https://github.com/llvm/llvm-project/issues/207222
---
bolt/include/bolt/Core/BinaryContext.h | 4 +-
bolt/include/bolt/Core/BinarySection.h | 7 +-
bolt/include/bolt/Core/Relocation.h | 43 ++++--
bolt/include/bolt/Rewrite/RewriteInstance.h | 4 +-
bolt/lib/Core/BinaryContext.cpp | 5 +-
bolt/lib/Rewrite/RewriteInstance.cpp | 139 ++++++++++++--------
bolt/test/runtime/X86/rela-plt-order.c | 37 ++++++
7 files changed, 163 insertions(+), 76 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 214e7d872569f..1d227dbb19c9c 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -1468,7 +1468,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,
+ uint32_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..cc5ffcb8ca414 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,
+ uint32_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..02d18ff97cf98 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 uint32_t NoJmpRelocationIndex = (1u << 30) - 1;
+
Relocation(uint64_t Offset, MCSymbol *Symbol, uint32_t Type, uint64_t Addend,
- uint64_t Value, bool IsRELR = false)
- : Offset(Offset), Symbol(Symbol), Type(Type), Optional(false),
- IsRELR(IsRELR), Addend(Addend), Value(Value) {
+ uint64_t Value, bool IsRELR = false,
+ uint32_t JmpRelocationIndex = NoJmpRelocationIndex)
+ : Offset(Offset), Symbol(Symbol), Addend(Addend), Value(Value),
+ Type(Type), JmpRelocationIndex(JmpRelocationIndex), Optional(false),
+ IsRELR(IsRELR) {
assert((isRelative() || !isRELR()) &&
"Only relative relocations can be relr.");
}
Relocation()
- : Offset(0), Symbol(0), Type(0), Optional(0), IsRELR(0), Addend(0),
- Value(0) {}
+ : Offset(0), Symbol(0), Addend(0), Value(0), Type(0),
+ JmpRelocationIndex(NoJmpRelocationIndex), Optional(0), IsRELR(0) {}
static Triple::ArchType Arch; /// set by BinaryContext ctor.
@@ -57,27 +61,40 @@ class Relocation {
/// The symbol this relocation is referring to.
MCSymbol *Symbol;
+ /// The offset from the \p Symbol base used to compute the final
+ /// value of this relocation.
+ uint64_t Addend;
+
+ /// The computed relocation value extracted from the binary file.
+ /// Used to validate relocation correctness.
+ uint64_t Value;
+
/// Relocation type.
uint32_t Type;
private:
+ /// Original index in DT_JMPREL, or NoJmpRelocationIndex for relocations
+ /// originating from other relocation tables.
+ uint32_t JmpRelocationIndex : 30;
+
/// Relocations added by optimizations can be optional, meaning they can be
/// omitted under certain circumstances.
- bool Optional = false;
+ uint32_t Optional : 1;
/// Track which relocations originate from a relr section. Emit these
/// exclusively into the relr section and do not accidentally promote relative
/// rela entries, because that would require growing the relr section.
- bool IsRELR = false;
+ uint32_t IsRELR : 1;
public:
- /// The offset from the \p Symbol base used to compute the final
- /// value of this relocation.
- uint64_t Addend;
+ bool isJmpRelocation() const {
+ return JmpRelocationIndex != NoJmpRelocationIndex;
+ }
- /// The computed relocation value extracted from the binary file.
- /// Used to validate relocation correctness.
- uint64_t Value;
+ uint32_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 4d4ac70195bc2..da1684e16bdcf 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -543,8 +543,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.
+ uint32_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 928969d5cb7b0..ae320ab695af3 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -2697,11 +2697,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,
+ uint32_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 cede4d2c946b5..e169f6f440ae6 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -3047,6 +3047,13 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
});
for (const RelocationRef &Rel : Section.relocations()) {
+ uint32_t JmpRelocationIndex = Relocation::NoJmpRelocationIndex;
+ if (IsJmpRel) {
+ assert(NumJmpRelocations < Relocation::NoJmpRelocationIndex &&
+ "too many DT_JMPREL relocations");
+ JmpRelocationIndex = NumJmpRelocations++;
+ }
+
const uint32_t RType = Relocation::getType(Rel);
if (Relocation::isNone(RType))
continue;
@@ -3066,18 +3073,16 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
(void)SymbolAddress;
}
+ const uint64_t RelOffset = Rel.getOffset();
LLVM_DEBUG(
SmallString<16> TypeName;
Rel.getTypeName(TypeName);
dbgs() << "BOLT-DEBUG: dynamic relocation at 0x"
- << Twine::utohexstr(Rel.getOffset()) << " : " << TypeName
+ << Twine::utohexstr(RelOffset) << " : " << TypeName
<< " : " << SymbolName << " : " << Twine::utohexstr(SymbolAddress)
<< " : + 0x" << Twine::utohexstr(Addend) << '\n'
);
- if (IsJmpRel)
- IsJmpRelocation[RType] = true;
-
if (Symbol)
SymbolIndex[Symbol] = getRelocationSymbol(InputFile, Rel);
@@ -3090,10 +3095,12 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
"relocation type\n";
exit(1);
}
- handleRelativeDynamicRelocation(Rel.getOffset(), ReferencedAddress);
+ handleRelativeDynamicRelocation(RelOffset, ReferencedAddress);
}
- BC->addDynamicRelocation(Rel.getOffset(), Symbol, RType, Addend);
+ BC->addDynamicRelocation(RelOffset, Symbol, RType, Addend,
+ /*Value=*/0, /*IsRELR=*/false,
+ JmpRelocationIndex);
}
}
@@ -6308,72 +6315,75 @@ RewriteInstance::patchELFAllocatableRelaSections(ELFObjectFile<ELFT> *File) {
DynamicRelativeRelocationsCount = 0;
- auto writeRela = [&OS](const Elf_Rela *RelA, uint64_t &Offset) {
+ auto writeRela = [&OS](const Elf_Rela *RelA, uint64_t Offset) {
safePWrite(OS, reinterpret_cast<const char *>(RelA), sizeof(*RelA), Offset);
- 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,
+ uint64_t Offset, uint64_t EndOffset) {
+ 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;
- }
+ 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 > EndOffset || EndOffset - Offset < sizeof(NewRelA)) {
+ 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, RelDynOffset, RelDynEndOffset);
+ RelDynOffset += sizeof(Elf_Rela);
}
}
};
// 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);
- auto fillNone = [&](uint64_t &Offset, uint64_t EndOffset) {
+ auto fillNone = [&](uint64_t Offset, uint64_t EndOffset) {
if (!Offset)
return;
@@ -6381,15 +6391,32 @@ RewriteInstance::patchELFAllocatableRelaSections(ELFObjectFile<ELFT> *File) {
RelA.setSymbolAndType(0, Relocation::getNone(), EF.isMips64EL());
RelA.r_offset = 0;
RelA.r_addend = 0;
- while (Offset < EndOffset)
+ while (Offset < EndOffset) {
writeRela(&RelA, Offset);
+ Offset += sizeof(Elf_Rela);
+ }
assert(Offset == EndOffset && "Unexpected section overflow");
};
- // Fill the rest of the sections with R_*_NONE relocations
fillNone(RelDynOffset, RelDynEndOffset);
+
+ // Start with an empty DT_JMPREL table and patch relocations
+ // back into their original slots.
fillNone(RelPltOffset, RelPltEndOffset);
+
+ 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");
+
+ const uint64_t Offset =
+ RelPltOffset + Rel.getJmpRelocationIndex() * sizeof(Elf_Rela);
+
+ writeRelocation(Section, Rel, Offset, RelPltEndOffset);
+ }
+ }
}
template <typename ELFT>
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..4a894ba8f6f6b
--- /dev/null
+++ b/bolt/test/runtime/X86/rela-plt-order.c
@@ -0,0 +1,37 @@
+// 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-readelf --dyn-relocations %t/main | \
+// RUN: sed -n "/'PLT' relocation section/,/^$/p" | \
+// RUN: awk '/^0/ { print $3, $5 }' > %t/main.plt
+// RUN: FileCheck %s --check-prefix=PLT-KINDS < %t/main.plt
+// RUN: llvm-bolt %t/main -o %t/main.bolt
+// RUN: llvm-readelf --dyn-relocations %t/main.bolt | \
+// RUN: sed -n "/'PLT' relocation section/,/^$/p" | \
+// RUN: awk '/^0/ { print $3, $5 }' > %t/main.bolt.plt
+// RUN: diff %t/main.plt %t/main.bolt.plt
+// RUN: %t/main.bolt
+
+// PLT-KINDS-DAG: R_X86_64_JUMP_SLOT long_name
+// PLT-KINDS-DAG: 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