[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.
This commit is contained in:
parent
aad09da2ad
commit
2a83c0cc13
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
57
bolt/test/runtime/relative-vftable.cpp
Normal file
57
bolt/test/runtime/relative-vftable.cpp
Normal file
@ -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 <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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user