[llvm] [BOLT][AArch64] Handle IFUNCS properly (PR #71104)

Vladislav Khmelevsky via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 2 13:45:46 PDT 2023


https://github.com/yota9 updated https://github.com/llvm/llvm-project/pull/71104

>From 54e2931f00c56badff81109762124d123dbb7fd5 Mon Sep 17 00:00:00 2001
From: Vladislav Khmelevsky <och95 at yandex.ru>
Date: Wed, 1 Nov 2023 22:48:31 +0400
Subject: [PATCH] [BOLT][AArch64] Handle IFUNCS properly

Currently we were testing only the binaries compiled with O0, which
results in indirect call to the IFUNC trampoline and the trampoline has
associated IFUNC symbol with it. Compile with O3 results in direct
calling the IFUNC trampoline and no symbols are associated with it, the
IFUNC symbol address becomes the same as IFUNC resolver address. Since
no symbol was associated the BF was not created before PLT analyze and
be the algorithm we're going to analyze target relocation. As we're
expecting the JUMP relocation we're also expecting the associated symbol
with it to be presented. But for IFUNC relocation the IRELATIVE
relocation is used and no symbol is associated with it, the addend value
is pointing on the target symbol, so we need to find BF using it and use
it's symbol in this situation. Currently this is checked only for
AArch64 platform, so I've limited it in code to use this logic only for
this platform, although I wouldn't be surprised if other platforms needs
to activate this logic too.
---
 bolt/include/bolt/Core/Relocation.h  |  4 +++
 bolt/lib/Rewrite/RewriteInstance.cpp | 29 +++++++++++++++-----
 bolt/test/AArch64/Inputs/iplt.ld     |  3 +++
 bolt/test/AArch64/ifunc.c            | 40 ++++++++++++++++++++++++++++
 bolt/test/runtime/iplt.c             |  8 +++++-
 5 files changed, 77 insertions(+), 7 deletions(-)
 create mode 100644 bolt/test/AArch64/Inputs/iplt.ld
 create mode 100644 bolt/test/AArch64/ifunc.c

diff --git a/bolt/include/bolt/Core/Relocation.h b/bolt/include/bolt/Core/Relocation.h
index bc16d952e7a291e..36bf12fddd514e7 100644
--- a/bolt/include/bolt/Core/Relocation.h
+++ b/bolt/include/bolt/Core/Relocation.h
@@ -124,6 +124,10 @@ struct Relocation {
   /// otherwise.
   bool isRelative() const { return isRelative(Type); }
 
+  /// Return true if this relocation is R_*_IRELATIVE type. Return false
+  /// otherwise.
+  bool isIRelative() const { return isIRelative(Type); }
+
   /// Emit relocation at a current \p Streamer' position. The caller is
   /// responsible for setting the position correctly.
   size_t emit(MCStreamer *Streamer) const;
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 0d78c9b75e03d32..abdbb79e8eb60ef 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -1344,24 +1344,41 @@ void RewriteInstance::createPLTBinaryFunction(uint64_t TargetAddress,
 
   BinaryFunction *BF = BC->getBinaryFunctionAtAddress(EntryAddress);
   if (BF && BC->isAArch64()) {
-    // Handle IFUNC trampoline
+    // Handle IFUNC trampoline with symbol
     setPLTSymbol(BF, BF->getOneName());
     return;
   }
 
   const Relocation *Rel = BC->getDynamicRelocationAt(TargetAddress);
-  if (!Rel || !Rel->Symbol)
+  if (!Rel)
     return;
 
+  MCSymbol *Symbol = Rel->Symbol;
+  if (!Symbol) {
+    if (!BC->isAArch64() || !Rel->Addend || !Rel->isIRelative())
+      return;
+
+    // IFUNC trampoline without symbol
+    BinaryFunction *TargetBF = BC->getBinaryFunctionAtAddress(Rel->Addend);
+    if (!TargetBF) {
+      errs()
+          << "BOLT-WARNING: Expected BF to be presented as IFUNC resolver at "
+          << Twine::utohexstr(Rel->Addend) << ", skipping\n";
+      return;
+    }
+
+    Symbol = TargetBF->getSymbol();
+  }
+
   ErrorOr<BinarySection &> Section = BC->getSectionForAddress(EntryAddress);
   assert(Section && "cannot get section for address");
   if (!BF)
-    BF = BC->createBinaryFunction(Rel->Symbol->getName().str() + "@PLT",
-                                  *Section, EntryAddress, 0, EntrySize,
+    BF = BC->createBinaryFunction(Symbol->getName().str() + "@PLT", *Section,
+                                  EntryAddress, 0, EntrySize,
                                   Section->getAlignment());
   else
-    BF->addAlternativeName(Rel->Symbol->getName().str() + "@PLT");
-  setPLTSymbol(BF, Rel->Symbol->getName());
+    BF->addAlternativeName(Symbol->getName().str() + "@PLT");
+  setPLTSymbol(BF, Symbol->getName());
 }
 
 void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) {
diff --git a/bolt/test/AArch64/Inputs/iplt.ld b/bolt/test/AArch64/Inputs/iplt.ld
new file mode 100644
index 000000000000000..1e54a249b2182e0
--- /dev/null
+++ b/bolt/test/AArch64/Inputs/iplt.ld
@@ -0,0 +1,3 @@
+SECTIONS {
+  .plt : ALIGN(16) { *(.plt) *(.iplt) }
+}
diff --git a/bolt/test/AArch64/ifunc.c b/bolt/test/AArch64/ifunc.c
new file mode 100644
index 000000000000000..dea2cf6bd543f0a
--- /dev/null
+++ b/bolt/test/AArch64/ifunc.c
@@ -0,0 +1,40 @@
+// This test checks that IFUNC trampoline is properly recognised by BOLT
+
+// With -O0 indirect call is performed on IPLT trampoline. IPLT trampoline
+// has IFUNC symbol.
+// RUN: %clang %cflags -nostdlib -O0 -no-pie %s -fuse-ld=lld \
+// RUN:    -o %t.O0.exe -Wl,-q
+// RUN: llvm-bolt %t.O0.exe -o %t.O0.bolt.exe \
+// RUN:   --print-disasm --print-only=_start | \
+// RUN:   FileCheck --check-prefix=O0_CHECK %s
+
+// With -O3 direct call is performed on IPLT trampoline. IPLT trampoline
+// doesn't have associated symbol. The ifunc symbol has the same address as
+// IFUNC resolver function.
+// RUN: %clang %cflags -nostdlib -O3 %s -fuse-ld=lld -fPIC -pie \
+// RUN:   -o %t.O3_pie.exe -Wl,-q
+// RUN: llvm-bolt %t.O3_pie.exe -o %t.O3_pie.bolt.exe  \
+// RUN:   --print-disasm --print-only=_start | \
+// RUN:   FileCheck --check-prefix=O3_CHECK %s
+
+// Check that IPLT trampoline located in .plt section are normally handled by
+// BOLT. The gnu-ld linker doesn't use separate .iplt section.
+// RUN: %clang %cflags -nostdlib -O3 %s -fuse-ld=lld -fPIC -pie \
+// RUN:   -T %p/Inputs/iplt.ld -o %t.iplt_O3_pie.exe -Wl,-q
+// RUN: llvm-bolt %t.iplt_O3_pie.exe -o %t.iplt_O3_pie.bolt.exe  \
+// RUN:   --print-disasm --print-only=_start  | \
+// RUN:   FileCheck --check-prefix=O3_CHECK %s
+
+// O0_CHECK: adr x{{[0-9]+}}, ifoo
+// O3_CHECK: b "{{resolver_foo|ifoo}}{{.*}}@PLT"
+
+#include <stdio.h>
+#include <string.h>
+
+static void foo() {}
+
+static void *resolver_foo(void) { return foo; }
+
+__attribute__((ifunc("resolver_foo"))) void ifoo();
+
+void _start() { ifoo(); }
diff --git a/bolt/test/runtime/iplt.c b/bolt/test/runtime/iplt.c
index b0e2e6d250700c9..d5b56d901e6227d 100644
--- a/bolt/test/runtime/iplt.c
+++ b/bolt/test/runtime/iplt.c
@@ -1,10 +1,16 @@
 // This test checks that the ifuncs works after bolt.
+// Compiling with 00 results in IFUNC indirect calling.
 
-// RUN: %clang %cflags -no-pie %s -fuse-ld=lld \
+// RUN: %clang %cflags -O0 -no-pie %s -fuse-ld=lld \
 // RUN:    -o %t.exe -Wl,-q
 // RUN: llvm-bolt %t.exe -o %t.bolt.exe --use-old-text=0 --lite=0
 // RUN: %t.bolt.exe  | FileCheck %s
 
+// RUN: %clang %cflags -O3 -no-pie %s -fuse-ld=lld \
+// RUN:    -o %t.O3.exe -Wl,-q
+// RUN: llvm-bolt %t.O3.exe -o %t.O3.bolt.exe --use-old-text=0 --lite=0
+// RUN: %t.O3.bolt.exe  | FileCheck %s
+
 // CHECK: foo
 
 #include <stdio.h>



More information about the llvm-commits mailing list