[llvm] [BOLT] Support relative vtable (PR #135449)

YongKang Zhu via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 12 02:00:55 PDT 2025


https://github.com/yozhu updated https://github.com/llvm/llvm-project/pull/135449

>From 64092130165148f1e7224499177254f0d03bdefb Mon Sep 17 00:00:00 2001
From: YongKang Zhu <yongzhu at fb.com>
Date: Mon, 10 Mar 2025 23:02:24 -0700
Subject: [PATCH 1/2] [BOLT][AArch64] Support relative vftable

Summary:
To handle relative vftable (which is enabled with clang option
`-fexperimental-relative-c++-abi-vtables`), we look for PC relative
relocations whose fixup locations fall in vftable address ranges. For such
relocations actual target is just virtual function itself and the addend
is to record distance between vftable slot for target virtual function and
the first virtual function slot in vftable, which is to match generated code
calling virtual function. So we can skip the logic on handling
"function + offset" and directly record such relocations for future fixup
after new layout is known.
---
 bolt/lib/Core/Relocation.cpp           |  4 ++
 bolt/lib/Rewrite/RewriteInstance.cpp   | 32 +++++++++++++--
 bolt/test/runtime/relative-vftable.cpp | 57 ++++++++++++++++++++++++++
 3 files changed, 90 insertions(+), 3 deletions(-)
 create mode 100644 bolt/test/runtime/relative-vftable.cpp

diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
index 4696a1f1f0402..ff1681f823987 100644
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -96,6 +96,7 @@ static bool isSupportedAArch64(uint32_t Type) {
   case ELF::R_AARCH64_MOVW_UABS_G2:
   case ELF::R_AARCH64_MOVW_UABS_G2_NC:
   case ELF::R_AARCH64_MOVW_UABS_G3:
+  case ELF::R_AARCH64_PLT32:
     return true;
   }
 }
@@ -202,6 +203,7 @@ static size_t getSizeForTypeAArch64(uint32_t Type) {
   case ELF::R_AARCH64_MOVW_UABS_G2_NC:
   case ELF::R_AARCH64_MOVW_UABS_G3:
   case ELF::R_AARCH64_ABS32:
+  case ELF::R_AARCH64_PLT32:
     return 4;
   case ELF::R_AARCH64_ABS64:
   case ELF::R_AARCH64_PREL64:
@@ -354,6 +356,7 @@ static uint64_t extractValueAArch64(uint32_t Type, uint64_t Contents,
   case ELF::R_AARCH64_PREL16:
     return static_cast<int64_t>(PC) + SignExtend64<16>(Contents & 0xffff);
   case ELF::R_AARCH64_PREL32:
+  case ELF::R_AARCH64_PLT32:
     return static_cast<int64_t>(PC) + SignExtend64<32>(Contents & 0xffffffff);
   case ELF::R_AARCH64_PREL64:
     return static_cast<int64_t>(PC) + Contents;
@@ -676,6 +679,7 @@ static bool isPCRelativeAArch64(uint32_t Type) {
   case ELF::R_AARCH64_PREL16:
   case ELF::R_AARCH64_PREL32:
   case ELF::R_AARCH64_PREL64:
+  case ELF::R_AARCH64_PLT32:
     return true;
   }
 }
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 23faa92642d01..70a175eec2900 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -2603,7 +2603,9 @@ void RewriteInstance::readRelocations(const SectionRef &Section) {
 void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
                                        const RelocationRef &Rel) {
   const bool IsAArch64 = BC->isAArch64();
+  const bool IsX86 = BC->isX86();
   const bool IsFromCode = RelocatedSection.isText();
+  const bool IsWritable = BinarySection(*BC, RelocatedSection).isWritable();
 
   SmallString<16> TypeName;
   Rel.getTypeName(TypeName);
@@ -2612,7 +2614,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
     return;
 
   // Adjust the relocation type as the linker might have skewed it.
-  if (BC->isX86() && (RType & ELF::R_X86_64_converted_reloc_bit)) {
+  if (IsX86 && (RType & ELF::R_X86_64_converted_reloc_bit)) {
     if (opts::Verbosity >= 1)
       dbgs() << "BOLT-WARNING: ignoring R_X86_64_converted_reloc_bit\n";
     RType &= ~ELF::R_X86_64_converted_reloc_bit;
@@ -2620,7 +2622,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
 
   if (Relocation::isTLS(RType)) {
     // No special handling required for TLS relocations on X86.
-    if (BC->isX86())
+    if (IsX86)
       return;
 
     // The non-got related TLS relocations on AArch64 and RISC-V also could be
@@ -2661,6 +2663,30 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
     return;
   }
 
+  if (!IsFromCode && !IsWritable && (IsX86 || IsAArch64) &&
+      Relocation::isPCRelative(RType)) {
+    BinaryData *BD = BC->getBinaryDataContainingAddress(Rel.getOffset());
+    if (BD && (BD->nameStartsWith("_ZTV") ||   // vtable
+               BD->nameStartsWith("_ZTCN"))) { // construction vtable
+      BinaryFunction *BF = BC->getBinaryFunctionContainingAddress(
+          SymbolAddress, /*CheckPastEnd*/ false, /*UseMaxSize*/ true);
+      if (!BF || BF->getAddress() != SymbolAddress) {
+        BC->errs()
+            << "BOLT-ERROR: the virtual function table entry at offset 0x"
+            << Twine::utohexstr(Rel.getOffset());
+        if (BF)
+          BC->errs() << " points to the middle of a function @ 0x"
+                     << Twine::utohexstr(BF->getAddress()) << "\n";
+        else
+          BC->errs() << " does not point to any function\n";
+        exit(1);
+      }
+      BC->addRelocation(Rel.getOffset(), BF->getSymbol(), RType, Addend,
+                        ExtractedValue);
+      return;
+    }
+  }
+
   const uint64_t Address = SymbolAddress + Addend;
 
   LLVM_DEBUG({
@@ -2724,7 +2750,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
   const bool IsToCode = ReferencedSection && ReferencedSection->isText();
 
   // Special handling of PC-relative relocations.
-  if (BC->isX86() && Relocation::isPCRelative(RType)) {
+  if (IsX86 && Relocation::isPCRelative(RType)) {
     if (!IsFromCode && IsToCode) {
       // PC-relative relocations from data to code are tricky since the
       // original information is typically lost after linking, even with
diff --git a/bolt/test/runtime/relative-vftable.cpp b/bolt/test/runtime/relative-vftable.cpp
new file mode 100644
index 0000000000000..f4c14c30f971f
--- /dev/null
+++ b/bolt/test/runtime/relative-vftable.cpp
@@ -0,0 +1,57 @@
+// Test bolt instrumentation is able to handle relative virtual function table,
+// i.e., when code is compiled with `-fexperimental-relative-c++-abi-vtables`.
+
+// REQUIRES: system-linux,bolt-runtime
+
+// RUN: split-file %s %t
+// RUN: %clang -fuse-ld=lld -o %t/main.so %t/tt.cpp %t/main.cpp -Wl,-q \
+// RUN:     -fno-rtti -fexperimental-relative-c++-abi-vtables
+// RUN: %t/main.so | FileCheck %s
+
+// CHECK: derived_foo
+// CHECK-NEXT: derived_bar
+// CHECK-NEXT: derived_goo
+
+// RUN: llvm-bolt --instrument %t/main.so -o %t/main.instr.so
+// RUN: %t/main.instr.so | FileCheck %s
+
+;--- tt.h
+#include <stdio.h>
+
+class Base {
+public:
+  virtual void foo();
+  virtual void bar();
+  virtual void goo();
+};
+
+class Derived : public Base {
+public:
+  virtual void foo() override;
+  virtual void bar() override;
+  virtual void goo() override;
+};
+
+;--- tt.cpp
+#include "tt.h"
+void Derived::goo() { printf("derived_goo\n"); }
+
+;--- main.cpp
+#include "tt.h"
+// #pragma clang optimize off
+
+void Base::foo() { printf("base_foo\n"); }
+void Base::bar() { printf("base_bar\n"); }
+void Base::goo() { printf("base_goo\n"); }
+
+void Derived::foo() { printf("derived_foo\n"); }
+void Derived::bar() { printf("derived_bar\n"); }
+
+int main() {
+  Derived D;
+  Base *ptr = &D;
+  ptr->foo();
+  ptr->bar();
+  ptr->goo();
+  return 0;
+}

>From badacecde0be2f30e1b0bd18c39cfac7cfaed30b Mon Sep 17 00:00:00 2001
From: YongKang Zhu <yongzhu at fb.com>
Date: Sat, 12 Apr 2025 01:59:50 -0700
Subject: [PATCH 2/2] A small tweak to the added test

---
 bolt/test/runtime/relative-vftable.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/bolt/test/runtime/relative-vftable.cpp b/bolt/test/runtime/relative-vftable.cpp
index f4c14c30f971f..55465cb52aa7b 100644
--- a/bolt/test/runtime/relative-vftable.cpp
+++ b/bolt/test/runtime/relative-vftable.cpp
@@ -12,8 +12,8 @@
 // CHECK-NEXT: derived_bar
 // CHECK-NEXT: derived_goo
 
-// RUN: llvm-bolt --instrument %t/main.so -o %t/main.instr.so
-// RUN: %t/main.instr.so | FileCheck %s
+// RUN: llvm-bolt %t/main.so -o %t/main.bolted.so --trap-old-code
+// RUN: %t/main.bolted.so | FileCheck %s
 
 ;--- tt.h
 #include <stdio.h>
@@ -38,7 +38,7 @@ void Derived::goo() { printf("derived_goo\n"); }
 
 ;--- main.cpp
 #include "tt.h"
-// #pragma clang optimize off
+#pragma clang optimize off
 
 void Base::foo() { printf("base_foo\n"); }
 void Base::bar() { printf("base_bar\n"); }



More information about the llvm-commits mailing list