mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-14 15:46:32 +00:00
[BOLT] Skip out-of-range pending relocations (#116964)
When a pending relocation is created it is also marked whether it is optional or not. It can be optional when such relocation is added as part of an optimization (i.e., `scanExternalRefs`). When bolt tries to `flushPendingRelocations`, it safely skips any optional relocations that cannot be encoded due to being out of range. A pre-requisite to that is the usage of the `-force-patch` flag. Alternatrively, BOLT will bail out with a relevant message. Background: BOLT, as part of scanExternalRefs, identifies external references from calls and creates some pending relocations for them. Those when flushed will update references to point to the optimized functions. This optimization can be disabled using `--no-scan`. BOLT can assert if any of these pending relocations cannot be encoded. This patch does not disable this optimization but instead selectively applies it given that a pending relocation is optional and `-force-patch` was enabled.
This commit is contained in:
parent
e8d5009784
commit
3d24046b33
@ -86,6 +86,9 @@ public:
|
||||
/// Adjust value depending on relocation type (make it PC relative or not).
|
||||
static uint64_t encodeValue(uint32_t Type, uint64_t Value, uint64_t PC);
|
||||
|
||||
/// Return true if there are enough bits to encode the relocation value.
|
||||
static bool canEncodeValue(uint32_t Type, uint64_t Value, uint64_t PC);
|
||||
|
||||
/// Extract current relocated value from binary contents. This is used for
|
||||
/// RISC architectures where values are encoded in specific bits depending
|
||||
/// on the relocation value. For X86, we limit to sign extending the value
|
||||
|
@ -1795,6 +1795,8 @@ bool BinaryFunction::scanExternalRefs() {
|
||||
// Create relocation for every fixup.
|
||||
for (const MCFixup &Fixup : Fixups) {
|
||||
std::optional<Relocation> Rel = BC.MIB->createRelocation(Fixup, *BC.MAB);
|
||||
// Can be skipped in case of overlow during relocation value encoding.
|
||||
Rel->setOptional();
|
||||
if (!Rel) {
|
||||
Success = false;
|
||||
continue;
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "bolt/Core/BinarySection.h"
|
||||
#include "bolt/Core/BinaryContext.h"
|
||||
#include "bolt/Utils/CommandLineOpts.h"
|
||||
#include "bolt/Utils/Utils.h"
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
@ -22,8 +23,8 @@ using namespace llvm;
|
||||
using namespace bolt;
|
||||
|
||||
namespace opts {
|
||||
extern cl::opt<bool> PrintRelocations;
|
||||
extern cl::opt<bool> HotData;
|
||||
extern cl::opt<bool> PrintRelocations;
|
||||
} // namespace opts
|
||||
|
||||
uint64_t BinarySection::Count = 0;
|
||||
@ -174,11 +175,30 @@ void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
|
||||
OS.pwrite(Patch.Bytes.data(), Patch.Bytes.size(),
|
||||
SectionFileOffset + Patch.Offset);
|
||||
|
||||
uint64_t SkippedPendingRelocations = 0;
|
||||
for (Relocation &Reloc : PendingRelocations) {
|
||||
uint64_t Value = Reloc.Addend;
|
||||
if (Reloc.Symbol)
|
||||
Value += Resolver(Reloc.Symbol);
|
||||
|
||||
// Safely skip any optional pending relocation that cannot be encoded.
|
||||
if (Reloc.isOptional() &&
|
||||
!Relocation::canEncodeValue(Reloc.Type, Value,
|
||||
SectionAddress + Reloc.Offset)) {
|
||||
|
||||
// A successful run of 'scanExternalRefs' means that all pending
|
||||
// relocations are flushed. Otherwise, PatchEntries should run.
|
||||
if (!opts::ForcePatch) {
|
||||
BC.errs()
|
||||
<< "BOLT-ERROR: cannot encode relocation for symbol "
|
||||
<< Reloc.Symbol->getName()
|
||||
<< " as it is out-of-range. To proceed must use -force-patch\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
++SkippedPendingRelocations;
|
||||
continue;
|
||||
}
|
||||
Value = Relocation::encodeValue(Reloc.Type, Value,
|
||||
SectionAddress + Reloc.Offset);
|
||||
|
||||
@ -197,6 +217,11 @@ void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
|
||||
}
|
||||
|
||||
clearList(PendingRelocations);
|
||||
|
||||
if (SkippedPendingRelocations > 0 && opts::Verbosity >= 1) {
|
||||
BC.outs() << "BOLT-INFO: skipped " << SkippedPendingRelocations
|
||||
<< " out-of-range optional relocations\n";
|
||||
}
|
||||
}
|
||||
|
||||
BinarySection::~BinarySection() { updateContents(nullptr, 0); }
|
||||
|
@ -271,6 +271,16 @@ static uint64_t encodeValueX86(uint32_t Type, uint64_t Value, uint64_t PC) {
|
||||
return Value;
|
||||
}
|
||||
|
||||
static bool canEncodeValueAArch64(uint32_t Type, uint64_t Value, uint64_t PC) {
|
||||
switch (Type) {
|
||||
default:
|
||||
llvm_unreachable("unsupported relocation");
|
||||
case ELF::R_AARCH64_CALL26:
|
||||
case ELF::R_AARCH64_JUMP26:
|
||||
return isInt<28>(Value - PC);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t encodeValueAArch64(uint32_t Type, uint64_t Value, uint64_t PC) {
|
||||
switch (Type) {
|
||||
default:
|
||||
@ -303,6 +313,16 @@ static uint64_t encodeValueAArch64(uint32_t Type, uint64_t Value, uint64_t PC) {
|
||||
return Value;
|
||||
}
|
||||
|
||||
static uint64_t canEncodeValueRISCV(uint32_t Type, uint64_t Value,
|
||||
uint64_t PC) {
|
||||
switch (Type) {
|
||||
default:
|
||||
llvm_unreachable("unsupported relocation");
|
||||
case ELF::R_RISCV_64:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t encodeValueRISCV(uint32_t Type, uint64_t Value, uint64_t PC) {
|
||||
switch (Type) {
|
||||
default:
|
||||
@ -739,6 +759,19 @@ uint64_t Relocation::encodeValue(uint32_t Type, uint64_t Value, uint64_t PC) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Relocation::canEncodeValue(uint32_t Type, uint64_t Value, uint64_t PC) {
|
||||
switch (Arch) {
|
||||
default:
|
||||
llvm_unreachable("Unsupported architecture");
|
||||
case Triple::aarch64:
|
||||
return canEncodeValueAArch64(Type, Value, PC);
|
||||
case Triple::riscv64:
|
||||
return canEncodeValueRISCV(Type, Value, PC);
|
||||
case Triple::x86_64:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Relocation::extractValue(uint32_t Type, uint64_t Contents,
|
||||
uint64_t PC) {
|
||||
switch (Arch) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "bolt/Core/BinaryContext.h"
|
||||
#include "bolt/Utils/CommandLineOpts.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
@ -161,6 +162,67 @@ TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
|
||||
<< "Wrong forward branch value\n";
|
||||
}
|
||||
|
||||
TEST_P(BinaryContextTester,
|
||||
FlushOptionalOutOfRangePendingRelocCALL26_ForcePatchOff) {
|
||||
if (GetParam() != Triple::aarch64)
|
||||
GTEST_SKIP();
|
||||
|
||||
// Tests that flushPendingRelocations exits if any pending relocation is out
|
||||
// of range and PatchEntries hasn't run. Pending relocations are added by
|
||||
// scanExternalRefs, so this ensures that either all scanExternalRefs
|
||||
// relocations were flushed or PatchEntries ran.
|
||||
|
||||
BinarySection &BS = BC->registerOrUpdateSection(
|
||||
".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
|
||||
// Create symbol 'Func0x4'
|
||||
MCSymbol *RelSymbol = BC->getOrCreateGlobalSymbol(4, "Func");
|
||||
ASSERT_TRUE(RelSymbol);
|
||||
Relocation Reloc{8, RelSymbol, ELF::R_AARCH64_CALL26, 0, 0};
|
||||
Reloc.setOptional();
|
||||
BS.addPendingRelocation(Reloc);
|
||||
|
||||
SmallVector<char> Vect;
|
||||
raw_svector_ostream OS(Vect);
|
||||
|
||||
// Resolve relocation symbol to a high value so encoding will be out of range.
|
||||
EXPECT_EXIT(BS.flushPendingRelocations(
|
||||
OS, [&](const MCSymbol *S) { return 0x800000F; }),
|
||||
::testing::ExitedWithCode(1),
|
||||
"BOLT-ERROR: cannot encode relocation for symbol Func0x4 as it is"
|
||||
" out-of-range. To proceed must use -force-patch");
|
||||
}
|
||||
|
||||
TEST_P(BinaryContextTester,
|
||||
FlushOptionalOutOfRangePendingRelocCALL26_ForcePatchOn) {
|
||||
if (GetParam() != Triple::aarch64)
|
||||
GTEST_SKIP();
|
||||
|
||||
// Tests that flushPendingRelocations can skip flushing any optional pending
|
||||
// relocations that cannot be encoded, given that PatchEntries runs.
|
||||
opts::ForcePatch = true;
|
||||
|
||||
opts::Verbosity = 1;
|
||||
testing::internal::CaptureStdout();
|
||||
|
||||
BinarySection &BS = BC->registerOrUpdateSection(
|
||||
".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
|
||||
MCSymbol *RelSymbol = BC->getOrCreateGlobalSymbol(4, "Func");
|
||||
ASSERT_TRUE(RelSymbol);
|
||||
Relocation Reloc{8, RelSymbol, ELF::R_AARCH64_CALL26, 0, 0};
|
||||
Reloc.setOptional();
|
||||
BS.addPendingRelocation(Reloc);
|
||||
|
||||
SmallVector<char> Vect;
|
||||
raw_svector_ostream OS(Vect);
|
||||
|
||||
// Resolve relocation symbol to a high value so encoding will be out of range.
|
||||
BS.flushPendingRelocations(OS, [&](const MCSymbol *S) { return 0x800000F; });
|
||||
outs().flush();
|
||||
std::string CapturedStdOut = testing::internal::GetCapturedStdout();
|
||||
EXPECT_EQ(CapturedStdOut,
|
||||
"BOLT-INFO: skipped 1 out-of-range optional relocations\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST_P(BinaryContextTester, BaseAddress) {
|
||||
|
@ -19,6 +19,7 @@ target_link_libraries(CoreTests
|
||||
LLVMBOLTCore
|
||||
LLVMBOLTRewrite
|
||||
LLVMBOLTProfile
|
||||
LLVMBOLTUtils
|
||||
LLVMTestingSupport
|
||||
)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user