[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