Skip to content

Commit 2a83c0c

Browse files
authored
[BOLT] Support relative vtable (#135449)
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 vtable address ranges. For such relocations, actual target is just virtual function itself, and the addend is to record the distance between vtable slot for target virtual function and the first virtual function slot in vtable, which is to match generated code that calls virtual function. So we can skip the logic of handling "function + offset" and directly save such relocations for future fixup after new layout is known.
1 parent aad09da commit 2a83c0c

File tree

3 files changed

+90
-3
lines changed

3 files changed

+90
-3
lines changed

bolt/lib/Core/Relocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ static bool isSupportedAArch64(uint32_t Type) {
9696
case ELF::R_AARCH64_MOVW_UABS_G2:
9797
case ELF::R_AARCH64_MOVW_UABS_G2_NC:
9898
case ELF::R_AARCH64_MOVW_UABS_G3:
99+
case ELF::R_AARCH64_PLT32:
99100
return true;
100101
}
101102
}
@@ -202,6 +203,7 @@ static size_t getSizeForTypeAArch64(uint32_t Type) {
202203
case ELF::R_AARCH64_MOVW_UABS_G2_NC:
203204
case ELF::R_AARCH64_MOVW_UABS_G3:
204205
case ELF::R_AARCH64_ABS32:
206+
case ELF::R_AARCH64_PLT32:
205207
return 4;
206208
case ELF::R_AARCH64_ABS64:
207209
case ELF::R_AARCH64_PREL64:
@@ -354,6 +356,7 @@ static uint64_t extractValueAArch64(uint32_t Type, uint64_t Contents,
354356
case ELF::R_AARCH64_PREL16:
355357
return static_cast<int64_t>(PC) + SignExtend64<16>(Contents & 0xffff);
356358
case ELF::R_AARCH64_PREL32:
359+
case ELF::R_AARCH64_PLT32:
357360
return static_cast<int64_t>(PC) + SignExtend64<32>(Contents & 0xffffffff);
358361
case ELF::R_AARCH64_PREL64:
359362
return static_cast<int64_t>(PC) + Contents;
@@ -676,6 +679,7 @@ static bool isPCRelativeAArch64(uint32_t Type) {
676679
case ELF::R_AARCH64_PREL16:
677680
case ELF::R_AARCH64_PREL32:
678681
case ELF::R_AARCH64_PREL64:
682+
case ELF::R_AARCH64_PLT32:
679683
return true;
680684
}
681685
}

bolt/lib/Rewrite/RewriteInstance.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2603,7 +2603,9 @@ void RewriteInstance::readRelocations(const SectionRef &Section) {
26032603
void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
26042604
const RelocationRef &Rel) {
26052605
const bool IsAArch64 = BC->isAArch64();
2606+
const bool IsX86 = BC->isX86();
26062607
const bool IsFromCode = RelocatedSection.isText();
2608+
const bool IsWritable = BinarySection(*BC, RelocatedSection).isWritable();
26072609

26082610
SmallString<16> TypeName;
26092611
Rel.getTypeName(TypeName);
@@ -2612,15 +2614,15 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
26122614
return;
26132615

26142616
// Adjust the relocation type as the linker might have skewed it.
2615-
if (BC->isX86() && (RType & ELF::R_X86_64_converted_reloc_bit)) {
2617+
if (IsX86 && (RType & ELF::R_X86_64_converted_reloc_bit)) {
26162618
if (opts::Verbosity >= 1)
26172619
dbgs() << "BOLT-WARNING: ignoring R_X86_64_converted_reloc_bit\n";
26182620
RType &= ~ELF::R_X86_64_converted_reloc_bit;
26192621
}
26202622

26212623
if (Relocation::isTLS(RType)) {
26222624
// No special handling required for TLS relocations on X86.
2623-
if (BC->isX86())
2625+
if (IsX86)
26242626
return;
26252627

26262628
// The non-got related TLS relocations on AArch64 and RISC-V also could be
@@ -2661,6 +2663,30 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
26612663
return;
26622664
}
26632665

2666+
if (!IsFromCode && !IsWritable && (IsX86 || IsAArch64) &&
2667+
Relocation::isPCRelative(RType)) {
2668+
BinaryData *BD = BC->getBinaryDataContainingAddress(Rel.getOffset());
2669+
if (BD && (BD->nameStartsWith("_ZTV") || // vtable
2670+
BD->nameStartsWith("_ZTCN"))) { // construction vtable
2671+
BinaryFunction *BF = BC->getBinaryFunctionContainingAddress(
2672+
SymbolAddress, /*CheckPastEnd*/ false, /*UseMaxSize*/ true);
2673+
if (!BF || BF->getAddress() != SymbolAddress) {
2674+
BC->errs()
2675+
<< "BOLT-ERROR: the virtual function table entry at offset 0x"
2676+
<< Twine::utohexstr(Rel.getOffset());
2677+
if (BF)
2678+
BC->errs() << " points to the middle of a function @ 0x"
2679+
<< Twine::utohexstr(BF->getAddress()) << "\n";
2680+
else
2681+
BC->errs() << " does not point to any function\n";
2682+
exit(1);
2683+
}
2684+
BC->addRelocation(Rel.getOffset(), BF->getSymbol(), RType, Addend,
2685+
ExtractedValue);
2686+
return;
2687+
}
2688+
}
2689+
26642690
const uint64_t Address = SymbolAddress + Addend;
26652691

26662692
LLVM_DEBUG({
@@ -2724,7 +2750,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
27242750
const bool IsToCode = ReferencedSection && ReferencedSection->isText();
27252751

27262752
// Special handling of PC-relative relocations.
2727-
if (BC->isX86() && Relocation::isPCRelative(RType)) {
2753+
if (IsX86 && Relocation::isPCRelative(RType)) {
27282754
if (!IsFromCode && IsToCode) {
27292755
// PC-relative relocations from data to code are tricky since the
27302756
// original information is typically lost after linking, even with
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Test BOLT is able to handle relative virtual function table, i.e., when
2+
// code is compiled with `-fexperimental-relative-c++-abi-vtables`.
3+
4+
// REQUIRES: system-linux
5+
6+
// RUN: split-file %s %t
7+
// RUN: %clang -fuse-ld=lld -o %t/main.so %t/tt.cpp %t/main.cpp -Wl,-q \
8+
// RUN: -fno-rtti -fexperimental-relative-c++-abi-vtables
9+
// RUN: %t/main.so | FileCheck %s
10+
11+
// CHECK: derived_foo
12+
// CHECK-NEXT: derived_bar
13+
// CHECK-NEXT: derived_goo
14+
15+
// RUN: llvm-bolt %t/main.so -o %t/main.bolted.so --trap-old-code
16+
// RUN: %t/main.bolted.so | FileCheck %s
17+
18+
;--- tt.h
19+
#include <stdio.h>
20+
21+
class Base {
22+
public:
23+
virtual void foo();
24+
virtual void bar();
25+
virtual void goo();
26+
};
27+
28+
class Derived : public Base {
29+
public:
30+
virtual void foo() override;
31+
virtual void bar() override;
32+
virtual void goo() override;
33+
};
34+
35+
;--- tt.cpp
36+
#include "tt.h"
37+
void Derived::goo() { printf("derived_goo\n"); }
38+
39+
;--- main.cpp
40+
#include "tt.h"
41+
#pragma clang optimize off
42+
43+
void Base::foo() { printf("base_foo\n"); }
44+
void Base::bar() { printf("base_bar\n"); }
45+
void Base::goo() { printf("base_goo\n"); }
46+
47+
void Derived::foo() { printf("derived_foo\n"); }
48+
void Derived::bar() { printf("derived_bar\n"); }
49+
50+
int main() {
51+
Derived D;
52+
Base *ptr = &D;
53+
ptr->foo();
54+
ptr->bar();
55+
ptr->goo();
56+
return 0;
57+
}

0 commit comments

Comments
 (0)