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(PC) + SignExtend64<16>(Contents & 0xffff); case ELF::R_AARCH64_PREL32: + case ELF::R_AARCH64_PLT32: return static_cast(PC) + SignExtend64<32>(Contents & 0xffffffff); case ELF::R_AARCH64_PREL64: return static_cast(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..10de94a2e2282 --- /dev/null +++ b/bolt/test/runtime/relative-vftable.cpp @@ -0,0 +1,57 @@ +// Test BOLT is able to handle relative virtual function table, i.e., when +// code is compiled with `-fexperimental-relative-c++-abi-vtables`. + +// REQUIRES: system-linux + +// 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 %t/main.so -o %t/main.bolted.so --trap-old-code +// RUN: %t/main.bolted.so | FileCheck %s + +;--- tt.h +#include + +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; +}