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

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 26 02:29:33 PDT 2026


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

>From b50aff8fdee202de2f79f163af7518bbbe1fafbf Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Wed, 26 Aug 2026 15:56:14 +0800
Subject: [PATCH 1/3] [BOLT][RISCV] Handle static IFUNC calls through .iplt

---
 bolt/include/bolt/Rewrite/RewriteInstance.h |  3 +-
 bolt/lib/Core/Relocation.cpp                |  1 +
 bolt/lib/Rewrite/RewriteInstance.cpp        | 67 ++++++++++++++++++---
 bolt/test/RISCV/ifunc.s                     | 51 ++++++++++++++++
 4 files changed, 114 insertions(+), 8 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 8299cc392eade..4d4ac70195bc2 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -585,7 +585,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.
diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index cb6aaa552d05f..502f448f50c18 100644
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -870,6 +870,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 4486efe926d01..a56fc34604ce6 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -1353,6 +1353,30 @@ void RewriteInstance::discoverFileObjects() {
   // that is a subject to dynamic relocation processing.
   processDynamicRelocations();
 
+  // LLD may canonicalize the only RISC-V IFUNC symbol to its IPLT entry,
+  // leaving the resolver identifiable only by an R_RISCV_IRELATIVE addend.
+  // Register every such resolver before PLT disassembly so .iplt can use the
+  // normal PLT processing path.
+  if (BC->TheTriple->isRISCV64()) {
+    for (const BinarySection &Section : BC->allocatableSections()) {
+      for (const Relocation &Rel : Section.dynamicRelocations()) {
+        if (!Rel.isIRelative() || !Rel.Addend ||
+            BC->getBinaryFunctionAtAddress(Rel.Addend))
+          continue;
+
+        ErrorOr<BinarySection &> ResolverSection =
+            BC->getSectionForAddress(Rel.Addend);
+        if (!ResolverSection || !ResolverSection->isText() ||
+            ResolverSection->isVirtual())
+          continue;
+
+        const std::string FunctionName =
+            "__BOLT_IFUNC_RESOLVERat" + Twine::utohexstr(Rel.Addend).str();
+        BC->createBinaryFunction(FunctionName, *ResolverSection, Rel.Addend, 0);
+      }
+    }
+  }
+
   // Process PLT section.
   disassemblePLT();
 
@@ -1886,10 +1910,10 @@ 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.
     BinaryFunction *TargetBF = BC->getBinaryFunctionAtAddress(Rel->Addend);
     if (!TargetBF) {
       BC->errs()
@@ -1910,6 +1934,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, EntrySize,
+                                  Section->getAlignment());
+      }
+      setPLTSymbol(BF, AliasName);
+    }
+  }
 }
 
 void RewriteInstance::disassemblePLTInstruction(const BinarySection &Section,
@@ -1998,8 +2041,11 @@ void RewriteInstance::disassemblePLTSectionRISCV(BinarySection &Section) {
     }
   };
 
-  // Skip the first special entry since no relocation points to it.
-  uint64_t InstrOffset = 32;
+  // A regular .plt has a first special entry with no relocations pointing to
+  // it, while all .iplt sections are headerless.
+  const bool IsHeaderless = Section.getName() == ".iplt" ||
+                            (Section.getName() == ".plt" && HasStaticRISCVPLT);
+  uint64_t InstrOffset = IsHeaderless ? 0 : 32;
 
   while (InstrOffset < SectionSize) {
     InstructionListType Instructions;
@@ -2769,6 +2815,7 @@ bool RewriteInstance::analyzeRelocation(
   };
 
   const bool IsAArch64 = BC->isAArch64();
+  const bool IsRISCV = BC->isRISCV();
 
   const size_t RelSize = Relocation::getSizeForType(RType);
 
@@ -2797,8 +2844,14 @@ 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) &&
-        (IsAArch64 || BC->isRISCV())) {
+    // 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->TheTriple->isRISCV64() && RType == ELF::R_RISCV_CALL_PLT &&
+        ELFSymbolRef(Symbol).getELFType() == ELF::STT_GNU_IFUNC;
+    if ((!SymbolAddress || IsRISCVIFuncPLT) && !IsWeakReference(Symbol) &&
+        (IsAArch64 || IsRISCV)) {
       const BinaryData *BD = BC->getPLTBinaryDataByName(SymbolName);
       SymbolAddress = BD ? BD->getAddress() : 0;
     }
@@ -2858,7 +2911,7 @@ bool RewriteInstance::analyzeRelocation(
     if (SkipVerification)
       return true;
 
-    if (IsAArch64 || BC->isRISCV())
+    if (IsAArch64 || IsRISCV)
       return true;
 
     if (SymbolName == "__hot_start" || SymbolName == "__hot_end")
diff --git a/bolt/test/RISCV/ifunc.s b/bolt/test/RISCV/ifunc.s
new file mode 100644
index 0000000000000..bd638659d65cb
--- /dev/null
+++ b/bolt/test/RISCV/ifunc.s
@@ -0,0 +1,51 @@
+## Check that BOLT recognizes a non-preemptible IFUNC IPLT entry and tracks an
+## otherwise unnamed resolver after the linker canonicalizes the exported IFUNC
+## symbol to the IPLT entry. Function sizes model normal compiler output, while
+## discarding local symbols removes the resolver's remaining name. Moving the
+## synthesized resolver also verifies that the IRELATIVE addend is updated.
+
+# 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-objcopy --discard-all %t.64
+# 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 > %t.64.dump
+# RUN: llvm-objdump -d --no-show-raw-insn %t.64.bolt >> %t.64.dump
+# RUN: FileCheck %s --input-file=%t.64.dump \
+# RUN:   --check-prefixes=ELF,IPLT,RESOLVER
+
+# BOLT: Binary Function "_start
+# BOLT: auipc a0, %pcrel_hi(__BOLT_IFUNC_RESOLVERat{{[0-9a-f]+}}@PLT)
+
+# ELF: R_RISCV_IRELATIVE {{ *}}[[#%x,RESOLVER:]]
+# ELF: {{[0-9a-f]+}} 4 FUNC {{.*}} func
+
+# IPLT: Disassembly of section .iplt:
+# IPLT: <ifunc0>:
+# IPLT-NEXT: {{.*}} auipc t3,
+# IPLT-NEXT: {{.*}} ld t3,
+
+# RESOLVER: {{^ *}}[[#%x,RESOLVER]]:{{ *}}ret
+
+  .text
+  .globl _start
+  .type _start, @function
+_start:
+1:
+  auipc a0, %pcrel_hi(ifunc0)
+  addi a0, a0, %pcrel_lo(1b)
+  .size _start, .-_start
+
+  .globl func
+  .type func, @function
+func:
+  ret
+  .size func, .-func
+
+  .globl ifunc0
+  .type ifunc0, @gnu_indirect_function
+ifunc0:
+  ret
+  .size ifunc0, .-ifunc0

>From bb6d02ddbf098d7b364742d59dc020d1035d02ca Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Wed, 26 Aug 2026 17:13:51 +0800
Subject: [PATCH 2/3] fix

---
 bolt/lib/Rewrite/RewriteInstance.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index a56fc34604ce6..955d6de0f017e 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -2043,8 +2043,7 @@ void RewriteInstance::disassemblePLTSectionRISCV(BinarySection &Section) {
 
   // A regular .plt has a first special entry with no relocations pointing to
   // it, while all .iplt sections are headerless.
-  const bool IsHeaderless = Section.getName() == ".iplt" ||
-                            (Section.getName() == ".plt" && HasStaticRISCVPLT);
+  const bool IsHeaderless = Section.getName() == ".iplt"
   uint64_t InstrOffset = IsHeaderless ? 0 : 32;
 
   while (InstrOffset < SectionSize) {

>From c26117eb1dcb7ef8f89f4397535301c15e4e5251 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Wed, 26 Aug 2026 17:19:46 +0800
Subject: [PATCH 3/3] fix error

---
 bolt/lib/Rewrite/RewriteInstance.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 955d6de0f017e..42105c1987f54 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -2043,7 +2043,7 @@ void RewriteInstance::disassemblePLTSectionRISCV(BinarySection &Section) {
 
   // A regular .plt has a first special entry with no relocations pointing to
   // it, while all .iplt sections are headerless.
-  const bool IsHeaderless = Section.getName() == ".iplt"
+  const bool IsHeaderless = Section.getName() == ".iplt";
   uint64_t InstrOffset = IsHeaderless ? 0 : 32;
 
   while (InstrOffset < SectionSize) {



More information about the llvm-commits mailing list