[BOLT] Register PIE indirect goto relocations (#206819)
PIE binaries use R_*_RELATIVE dynamic relocations for indirect goto jump tables. BOLT creates entry points for their targets but does not mark these addresses as targets from data relocations, so indirect jumps with unknown control flow (which originates from indirect goto) have no CFG successors and are interpreted as potential tail calls, while the jump targets are incorrectly identified as additional entry points. Add these offsets to the function's list of non-entry relocation references instead of marking them as entry points. Extend relocation rewriting to also check whether a block is externally referenced. This mirrors behavior of data-to-code relocations in non-pie binaries.
This commit is contained in:
parent
790fbc8fe5
commit
0bd936eee2
6 changed files with 50 additions and 24 deletions
|
|
@ -681,6 +681,12 @@ private:
|
|||
return !ExternallyReferencedOffsets.empty();
|
||||
}
|
||||
|
||||
/// True if there are references to \p Offset inside this function from data,
|
||||
/// e.g. from indirect goto references.
|
||||
bool hasInternalReferenceAt(uint64_t Offset) const {
|
||||
return ExternallyReferencedOffsets.count(Offset) != 0;
|
||||
}
|
||||
|
||||
/// Return an entry ID corresponding to a symbol known to belong to
|
||||
/// the function.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -2569,7 +2569,10 @@ Error BinaryFunction::buildCFG(MCPlusBuilder::AllocatorIdTy AllocatorId) {
|
|||
setSimple(false);
|
||||
}
|
||||
|
||||
clearList(ExternallyReferencedOffsets);
|
||||
// Clear externally referenced offsets only if there are no relocations
|
||||
// targeting internal references (from indirect goto).
|
||||
if (InternalRefDataRelocations.empty())
|
||||
clearList(ExternallyReferencedOffsets);
|
||||
clearList(UnknownIndirectBranchOffsets);
|
||||
|
||||
return Error::success();
|
||||
|
|
@ -3270,6 +3273,7 @@ bool BinaryFunction::requiresAddressMap() const {
|
|||
return false;
|
||||
|
||||
return opts::UpdateDebugSections || isMultiEntry() ||
|
||||
!getInternalRefDataRelocations().empty() ||
|
||||
requiresAddressTranslation();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2805,6 +2805,8 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
|
|||
if (Symbol)
|
||||
SymbolIndex[Symbol] = getRelocationSymbol(InputFile, Rel);
|
||||
|
||||
// Check if this relocation targets an address within a function. This
|
||||
// happens with indirect goto.
|
||||
const uint64_t ReferencedAddress = SymbolAddress + Addend;
|
||||
BinaryFunction *Func =
|
||||
BC->getBinaryFunctionContainingAddress(ReferencedAddress);
|
||||
|
|
@ -2814,7 +2816,10 @@ void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
|
|||
if (!Func->isInConstantIsland(ReferencedAddress)) {
|
||||
if (const uint64_t ReferenceOffset =
|
||||
ReferencedAddress - Func->getAddress()) {
|
||||
Func->addEntryPointAtOffset(ReferenceOffset);
|
||||
assert(!BC->getBinaryFunctionContainingAddress(Rel.getOffset()) &&
|
||||
"Relative relocation to code only from data");
|
||||
Func->registerInternalRefDataRelocation(ReferenceOffset,
|
||||
Rel.getOffset());
|
||||
}
|
||||
} else {
|
||||
BC->errs() << "BOLT-ERROR: referenced address at 0x"
|
||||
|
|
@ -6239,13 +6244,17 @@ uint64_t RewriteInstance::getNewFunctionOrDataAddress(uint64_t OldAddress) {
|
|||
if (const BinaryFunction *BF =
|
||||
BC->getBinaryFunctionContainingAddress(OldAddress)) {
|
||||
if (BF->isEmitted()) {
|
||||
// If OldAddress is the another entry point of
|
||||
// the function, then BOLT could get the new address.
|
||||
if (BF->isMultiEntry()) {
|
||||
for (const BinaryBasicBlock &BB : *BF)
|
||||
if (BB.isEntryPoint() &&
|
||||
(BF->getAddress() + BB.getOffset()) == OldAddress)
|
||||
// If OldAddress is another entry point of the function or the target of
|
||||
// an indirect goto, then BOLT could get the new address.
|
||||
bool HasInternalRelocationTarget =
|
||||
BF->hasInternalReferenceAt(OldAddress - BF->getAddress());
|
||||
if (HasInternalRelocationTarget || BF->isMultiEntry()) {
|
||||
for (const BinaryBasicBlock &BB : *BF) {
|
||||
const uint64_t BBAddr = BF->getAddress() + BB.getOffset();
|
||||
if ((HasInternalRelocationTarget || BB.isEntryPoint()) &&
|
||||
BBAddr == OldAddress)
|
||||
return BB.getOutputStartAddress();
|
||||
}
|
||||
}
|
||||
BC->errs() << "BOLT-ERROR: unable to get new address corresponding to "
|
||||
"input address 0x"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// This test checks that BOLT creates entry points for addresses
|
||||
// This test checks that BOLT recognizes blocks for addresses
|
||||
// referenced by dynamic relocations.
|
||||
// The test also checks that BOLT can map addresses inside functions.
|
||||
|
||||
// Checks for error and entry points.
|
||||
// Checks for error and cfg.
|
||||
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
|
||||
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
|
||||
# RUN: llvm-bolt %t.exe -o %t.bolt 2>&1 | FileCheck %s
|
||||
# RUN: llvm-bolt %t.exe -o %t.bolt --print-cfg | FileCheck --check-prefix=CHECK-ENTRIES %s
|
||||
# RUN: llvm-bolt %t.exe -o %t.bolt --print-cfg | FileCheck --check-prefix=CHECK-CFG %s
|
||||
|
||||
// Checks for dynamic relocations.
|
||||
# RUN: llvm-readelf -dr %t.bolt > %t.out.txt
|
||||
|
|
@ -30,13 +30,12 @@
|
|||
# CHECK-RELOCS: [[#ADDR]] <unknown>
|
||||
# CHECK-RELOCS: [[#ADDR + 8]] <unknown>
|
||||
|
||||
// Check that BOLT registers extra entry points for dynamic relocations.
|
||||
# CHECK-ENTRIES: Binary Function "main" after building cfg {
|
||||
# CHECK-ENTRIES: IsMultiEntry: 1
|
||||
# CHECK-ENTRIES: .Ltmp0 {{.*}}
|
||||
# CHECK-ENTRIES-NEXT: Secondary Entry Point: {{.*}}
|
||||
# CHECK-ENTRIES: .Ltmp1 {{.*}}
|
||||
# CHECK-ENTRIES-NEXT: Secondary Entry Point: {{.*}}
|
||||
// Check that BOLT recognizes indirect targeted blocks.
|
||||
# CHECK-CFG: Binary Function "main" after building cfg {
|
||||
# CHECK-CFG: IsMultiEntry: 0
|
||||
# CHECK-CFG: BB Count : 3
|
||||
# CHECK-CFG: .Ltmp0 {{.*}}
|
||||
# CHECK-CFG: .Ltmp1 {{.*}}
|
||||
|
||||
.globl main
|
||||
.p2align 2
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@ RUN: llvm-bolt %t -o %t.null --relocs=1 --print-cfg --print-only=main \
|
|||
RUN: --strict \
|
||||
RUN: 2>&1 | FileCheck %s
|
||||
|
||||
## Check indirect goto works in pie
|
||||
RUN: %clang %cflags -fPIE -pie %S/../Inputs/indirect_goto.c -o %t.pie -Wl,-q
|
||||
RUN: llvm-bolt %t.pie -o %t.pie.null --relocs=1 --print-cfg --print-only=main \
|
||||
RUN: --strict \
|
||||
RUN: 2>&1 | FileCheck %s
|
||||
|
||||
## Check that all possible destinations are included as successors.
|
||||
CHECK: jmpq *%rax # UNKNOWN CONTROL FLOW
|
||||
CHECK: Successors: .Ltmp0, .Ltmp1, .Ltmp2
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
// This test checks that BOLT creates entry points from sources
|
||||
// that use indirect goto.
|
||||
// This test checks that BOLT recognizes unknown control flow from sources that
|
||||
// use indirect goto.
|
||||
|
||||
REQUIRES: system-linux
|
||||
|
||||
RUN: %clang %cflags -pie %S/Inputs/indirect_goto.c -o %t.exe -Wl,-q
|
||||
RUN: llvm-bolt %t.exe -o %t.bolt --print-cfg | FileCheck --check-prefix=CHECK-PIE %s
|
||||
RUN: llvm-bolt %t.exe -o %t.bolt --print-cfg --print-only=main \
|
||||
RUN: | FileCheck --check-prefix=CHECK-PIE %s
|
||||
|
||||
// Check that BOLT registers extra entry points for dynamic relocations with PIE.
|
||||
// Check that BOLT discovers basic blocks for dynamic relocations with PIE.
|
||||
CHECK-PIE: Binary Function "main" after building cfg {
|
||||
CHECK-PIE: IsMultiEntry: 1
|
||||
CHECK-PIE: Secondary Entry Points : {{.*}}
|
||||
CHECK-PIE: IsMultiEntry: 0
|
||||
CHECK-PIE: BB Count : {{5|6}}
|
||||
// BB Count can vary across architectures.
|
||||
|
|
|
|||
Loading…
Reference in a new issue