[llvm] [BOLT][RISCV] Handle static IFUNC calls through .iplt (PR #207733)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 19 06:10:22 PDT 2026


https://github.com/Thrrreeee updated https://github.com/llvm/llvm-project/pull/207733

>From a64264916cd15c2c7d6c0101180754cf7c2aea73 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Wed, 19 Aug 2026 21:09:57 +0800
Subject: [PATCH] [BOLT][RISCV] Handle static IFUNC calls through .iplt

---
 bolt/include/bolt/Rewrite/RewriteInstance.h |  12 +-
 bolt/lib/Core/Relocation.cpp                |   5 +
 bolt/lib/Rewrite/RewriteInstance.cpp        | 122 +++++++++++++++++---
 bolt/test/RISCV/ifunc.s                     |  44 +++++++
 4 files changed, 163 insertions(+), 20 deletions(-)
 create mode 100644 bolt/test/RISCV/ifunc.s

diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index a624c056ada14..aacc9dc41a359 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -307,6 +307,14 @@ class RewriteInstance {
   /// Disassemble and create function entries for PLT.
   void disassemblePLT();
 
+  /// Disassemble and create function entries for a deferred RISC-V .iplt.
+  void disassembleRISCVIPLT();
+
+  /// Mark the function at the start of \p Section as pseudo, creating it with
+  /// \p EntrySize if it was not registered while disassembling PLT entries.
+  void registerPLTSectionPseudoFunction(BinarySection &Section,
+                                        uint64_t EntrySize);
+
   /// Auxiliary function to create .plt BinaryFunction on \p EntryAddres
   /// with the \p EntrySize size. \p TargetAddress is the .got entry
   /// associated address.
@@ -564,8 +572,8 @@ class RewriteInstance {
       {".plt"}, {".plt.got"}, {".iplt"}, {nullptr}};
 
   /// RISCV PLT sections.
-  const PLTSectionInfo RISCV_PLTSections[2] = {{".plt"}, {nullptr}};
-
+  const PLTSectionInfo RISCV_PLTSections[3] = {
+      {".plt", 16}, {".iplt", 16}, {nullptr}};
   /// Return PLT information for a section with \p SectionName or nullptr
   /// if the section is not PLT.
   const PLTSectionInfo *getPLTSectionInfo(StringRef SectionName) {
diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index 6663abcffc7e8..0862ba0e578bc 100644
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -130,6 +130,7 @@ static bool isSupportedRISCV(uint32_t Type) {
   case ELF::R_RISCV_TPREL_LO12_S:
   case ELFReserved::R_RISCV_TPREL_I:
   case ELFReserved::R_RISCV_TPREL_S:
+  case ELF::R_RISCV_IRELATIVE:
     return true;
   }
 }
@@ -240,6 +241,9 @@ static size_t getSizeForTypeRISCV(uint32_t Type) {
   case ELF::R_RISCV_TLS_GD_HI20:
     // See extractValueRISCV for why this is necessary.
     return 8;
+  case ELF::R_RISCV_IRELATIVE:
+    // Unsupported on RISCV32.
+    return 8;
   }
 }
 
@@ -858,6 +862,7 @@ bool Relocation::isIRelative(uint32_t Type) {
   case Triple::aarch64:
     return Type == ELF::R_AARCH64_IRELATIVE;
   case Triple::riscv64:
+    return Type == ELF::R_RISCV_IRELATIVE;
   case Triple::riscv32:
     llvm_unreachable("not implemented");
   case Triple::x86_64:
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 05ea606bdad7b..facf73a8b8252 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -1385,6 +1385,34 @@ void RewriteInstance::discoverFileObjects() {
   adjustFunctionBoundaries(MarkerSymbols);
   splitUnmarkedTailFunctions(MarkerSymbols);
 
+  // This is deliberately RISC-V-only. LLD may canonicalize the only IFUNC
+  // symbol to the IPLT entry, leaving the resolver named only by an
+  // R_RISCV_IRELATIVE addend at a non-zero offset in another function.
+  // Function sizes are not final when dynamic relocations are first read, so
+  // record exact secondary entries after boundary adjustment and only then
+  // process .iplt. x86 and AArch64 .iplt sections were already processed by
+  // disassemblePLT() above and retain their original ordering.
+  if (BC->isRISCV()) {
+    for (const BinarySection &Section : BC->allocatableSections()) {
+      for (const Relocation &Rel : Section.dynamicRelocations()) {
+        if (!Rel.isIRelative() || !Rel.Addend)
+          continue;
+        BinaryFunction *BF = BC->getBinaryFunctionContainingAddress(Rel.Addend);
+        if (!BF || BF->getAddress() == Rel.Addend)
+          continue;
+        if (BF->isInConstantIsland(Rel.Addend)) {
+          BC->errs() << "BOLT-ERROR: IFUNC resolver at 0x"
+                     << Twine::utohexstr(Rel.Addend)
+                     << " is in constant island of function " << *BF << '\n';
+          exit(1);
+        }
+        BF->addEntryPointAtOffset(Rel.Addend - BF->getAddress());
+      }
+    }
+
+    disassembleRISCVIPLT();
+  }
+
   // Annotate functions with code/data markers in AArch64
   for (auto &[Address, Type] : MarkerSymbols) {
     auto *BF = BC->getBinaryFunctionContainingAddress(Address,
@@ -1876,11 +1904,15 @@ void RewriteInstance::createPLTBinaryFunction(uint64_t TargetAddress,
 
   MCSymbol *Symbol = Rel->Symbol;
   if (!Symbol) {
-    if (BC->isRISCV() || !Rel->Addend || !Rel->isIRelative())
+    if (!Rel->Addend || !Rel->isIRelative())
       return;
 
-    // IFUNC trampoline without symbol
+    // IFUNC trampoline without symbol. For RISC-V, deferred .iplt processing
+    // also permits the exact secondary entry registered above, which
+    // getBinaryFunctionAtAddress() does not return.
     BinaryFunction *TargetBF = BC->getBinaryFunctionAtAddress(Rel->Addend);
+    if (!TargetBF && BC->isRISCV())
+      TargetBF = BC->getBinaryFunctionContainingAddress(Rel->Addend);
     if (!TargetBF) {
       BC->errs()
           << "BOLT-WARNING: Expected BF to be presented as IFUNC resolver at "
@@ -1889,6 +1921,11 @@ void RewriteInstance::createPLTBinaryFunction(uint64_t TargetAddress,
     }
 
     Symbol = TargetBF->getSymbol();
+    if (BC->isRISCV()) {
+      const uint64_t ResolverOffset = Rel->Addend - TargetBF->getAddress();
+      if (ResolverOffset)
+        Symbol = TargetBF->addEntryPointAtOffset(ResolverOffset);
+    }
   }
 
   ErrorOr<BinarySection &> Section = BC->getSectionForAddress(EntryAddress);
@@ -1900,6 +1937,25 @@ void RewriteInstance::createPLTBinaryFunction(uint64_t TargetAddress,
   else
     BF->addAlternativeName(Symbol->getName().str() + "@PLT");
   setPLTSymbol(BF, Symbol->getName());
+
+  // Keep RISC-V alias recovery local to the new path so the established x86
+  // and AArch64 PLT naming behavior remains unchanged.
+  if (BC->isRISCV() && Rel->isIRelative()) {
+    auto ResolverSyms = FileSymRefs.equal_range(Rel->Addend);
+    for (const SymbolRef &AliasSymbol : llvm::make_second_range(
+             llvm::make_range(ResolverSyms.first, ResolverSyms.second))) {
+      if (ELFSymbolRef(AliasSymbol).getELFType() != ELF::STT_GNU_IFUNC)
+        continue;
+      StringRef AliasName = cantFail(AliasSymbol.getName());
+      const std::string PLTName = AliasName.str() + "@PLT";
+      if (!BC->getBinaryDataByName(PLTName)) {
+        BF->addAlternativeName(PLTName);
+        BC->registerNameAtAddress(PLTName, EntryAddress, 0, EntrySize,
+                                  Section->getAlignment());
+      }
+      setPLTSymbol(BF, AliasName);
+    }
+  }
 }
 
 void RewriteInstance::disassemblePLTInstruction(const BinarySection &Section,
@@ -1988,8 +2044,9 @@ void RewriteInstance::disassemblePLTSectionRISCV(BinarySection &Section) {
     }
   };
 
-  // Skip the first special entry since no relocation points to it.
-  uint64_t InstrOffset = 32;
+  // A regular RISC-V .plt starts with a 32-byte PLT0 header, while a
+  // standalone .iplt contains only 16-byte non-preemptible IFUNC entries.
+  uint64_t InstrOffset = Section.getName() == ".iplt" ? 0 : 32;
 
   while (InstrOffset < SectionSize) {
     InstructionListType Instructions;
@@ -2051,6 +2108,35 @@ void RewriteInstance::disassemblePLTSectionX86(BinarySection &Section,
   }
 }
 
+void RewriteInstance::registerPLTSectionPseudoFunction(BinarySection &Section,
+                                                       uint64_t EntrySize) {
+  BinaryFunction *PltBF;
+  auto BFIter = BC->getBinaryFunctions().find(Section.getAddress());
+  if (BFIter != BC->getBinaryFunctions().end()) {
+    PltBF = &BFIter->second;
+  } else {
+    // If we did not register any function at the start of the section, then it
+    // must be a general PLT entry. Add a function at the location.
+    PltBF = BC->createBinaryFunction("__BOLT_PSEUDO_" + Section.getName().str(),
+                                     Section, Section.getAddress(), 0,
+                                     EntrySize, Section.getAlignment());
+  }
+  PltBF->setPseudo(true);
+}
+
+void RewriteInstance::disassembleRISCVIPLT() {
+  assert(BC->isRISCV() && "expected RISC-V target");
+
+  for (BinarySection &Section : BC->allocatableSections()) {
+    if (Section.getName() != ".iplt")
+      continue;
+    const PLTSectionInfo *PLTSI = getPLTSectionInfo(Section.getName());
+    assert(PLTSI && "missing RISC-V .iplt section information");
+    disassemblePLTSectionRISCV(Section);
+    registerPLTSectionPseudoFunction(Section, PLTSI->EntrySize);
+  }
+}
+
 void RewriteInstance::disassemblePLT() {
   auto analyzeOnePLTSection = [&](BinarySection &Section, uint64_t EntrySize) {
     if (BC->isAArch64())
@@ -2067,20 +2153,14 @@ void RewriteInstance::disassemblePLT() {
     if (!PLTSI)
       continue;
 
-    analyzeOnePLTSection(Section, PLTSI->EntrySize);
+    // The LLD-generated RISC-V .iplt form handled here may depend on an
+    // unnamed resolver inside another function. x86 and AArch64 .iplt
+    // sections continue through this path.
+    if (BC->isRISCV() && Section.getName() == ".iplt")
+      continue;
 
-    BinaryFunction *PltBF;
-    auto BFIter = BC->getBinaryFunctions().find(Section.getAddress());
-    if (BFIter != BC->getBinaryFunctions().end()) {
-      PltBF = &BFIter->second;
-    } else {
-      // If we did not register any function at the start of the section,
-      // then it must be a general PLT entry. Add a function at the location.
-      PltBF = BC->createBinaryFunction(
-          "__BOLT_PSEUDO_" + Section.getName().str(), Section,
-          Section.getAddress(), 0, PLTSI->EntrySize, Section.getAlignment());
-    }
-    PltBF->setPseudo(true);
+    analyzeOnePLTSection(Section, PLTSI->EntrySize);
+    registerPLTSectionPseudoFunction(Section, PLTSI->EntrySize);
   }
 }
 
@@ -2774,7 +2854,13 @@ bool RewriteInstance::analyzeRelocation(
     // Section symbols are marked as ST_Debug.
     IsSectionRelocation = (cantFail(Symbol.getType()) == SymbolRef::ST_Debug);
     // Check for PLT entry registered with symbol name
-    if (!SymbolAddress && !IsWeakReference(Symbol) &&
+    // LLD may give a defined RISC-V IFUNC symbol the .iplt entry address.
+    // R_RISCV_CALL_PLT must still resolve it through the registered @PLT
+    // BinaryData instead of treating that symbol value as a normal function.
+    const bool IsRISCVIFuncPLT =
+        BC->isRISCV() && RType == ELF::R_RISCV_CALL_PLT &&
+        ELFSymbolRef(Symbol).getELFType() == ELF::STT_GNU_IFUNC;
+    if ((!SymbolAddress || IsRISCVIFuncPLT) && !IsWeakReference(Symbol) &&
         (IsAArch64 || BC->isRISCV())) {
       const BinaryData *BD = BC->getPLTBinaryDataByName(SymbolName);
       SymbolAddress = BD ? BD->getAddress() : 0;
diff --git a/bolt/test/RISCV/ifunc.s b/bolt/test/RISCV/ifunc.s
new file mode 100644
index 0000000000000..db1c841119626
--- /dev/null
+++ b/bolt/test/RISCV/ifunc.s
@@ -0,0 +1,44 @@
+## Check that BOLT recognizes a non-preemptible IFUNC IPLT entry and tracks an
+## otherwise unnamed resolver after the linker canonicalizes the IFUNC symbol
+## to the IPLT entry. Moving the containing function also verifies that the
+## IRELATIVE addend is updated to the resolver secondary entry, not the primary
+## function entry.
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax -o %t.64.o %s
+# RUN: ld.lld -q -o %t.64 %t.64.o
+# RUN: llvm-bolt %t.64 -o %t.64.bolt --use-old-text=0 --lite=0 \
+# RUN:   --print-disasm --print-only=_start 2>&1 | FileCheck %s \
+# RUN:   --check-prefix=BOLT \
+# RUN:   --implicit-check-not="Expected BF to be presented as IFUNC resolver"
+# RUN: llvm-readelf -Wr -Ws %t.64.bolt | FileCheck %s --check-prefix=ELF
+# RUN: llvm-objdump -d --no-show-raw-insn %t.64.bolt \
+# RUN:   | FileCheck %s --check-prefix=Check
+
+# BOLT: Binary Function "_start
+# BOLT: auipc a0, %pcrel_hi(__ENTRY_func at 0x{{[0-9a-f]+}}@PLT)
+
+# ELF: R_RISCV_IRELATIVE {{ *}}[[#%x,RESOLVER:]]
+# ELF: {{0*}}[[#%x,RESOLVER-4]] 8 FUNC {{.*}} func
+
+# Check: Disassembly of section .iplt:
+# Check: <ifunc0>:
+# Check-NEXT: {{.*}} auipc t3,
+# Check-NEXT: {{.*}} ld t3,
+
+  .text
+  .globl _start
+  .type _start, @function
+_start:
+1:
+  auipc a0, %pcrel_hi(ifunc0)
+  addi a0, a0, %pcrel_lo(1b)
+
+  .globl func
+  .type func, @function
+func:
+  ret
+
+  .globl ifunc0
+  .type ifunc0, @gnu_indirect_function
+ifunc0:
+  ret



More information about the llvm-commits mailing list