Skip to content

Commit 3d24046

Browse files
[BOLT] Skip out-of-range pending relocations (llvm#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.
1 parent e8d5009 commit 3d24046

File tree

6 files changed

+127
-1
lines changed

6 files changed

+127
-1
lines changed

bolt/include/bolt/Core/Relocation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ class Relocation {
8686
/// Adjust value depending on relocation type (make it PC relative or not).
8787
static uint64_t encodeValue(uint32_t Type, uint64_t Value, uint64_t PC);
8888

89+
/// Return true if there are enough bits to encode the relocation value.
90+
static bool canEncodeValue(uint32_t Type, uint64_t Value, uint64_t PC);
91+
8992
/// Extract current relocated value from binary contents. This is used for
9093
/// RISC architectures where values are encoded in specific bits depending
9194
/// on the relocation value. For X86, we limit to sign extending the value

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,8 @@ bool BinaryFunction::scanExternalRefs() {
17951795
// Create relocation for every fixup.
17961796
for (const MCFixup &Fixup : Fixups) {
17971797
std::optional<Relocation> Rel = BC.MIB->createRelocation(Fixup, *BC.MAB);
1798+
// Can be skipped in case of overlow during relocation value encoding.
1799+
Rel->setOptional();
17981800
if (!Rel) {
17991801
Success = false;
18001802
continue;

bolt/lib/Core/BinarySection.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "bolt/Core/BinarySection.h"
1414
#include "bolt/Core/BinaryContext.h"
15+
#include "bolt/Utils/CommandLineOpts.h"
1516
#include "bolt/Utils/Utils.h"
1617
#include "llvm/MC/MCStreamer.h"
1718
#include "llvm/Support/CommandLine.h"
@@ -22,8 +23,8 @@ using namespace llvm;
2223
using namespace bolt;
2324

2425
namespace opts {
25-
extern cl::opt<bool> PrintRelocations;
2626
extern cl::opt<bool> HotData;
27+
extern cl::opt<bool> PrintRelocations;
2728
} // namespace opts
2829

2930
uint64_t BinarySection::Count = 0;
@@ -174,11 +175,30 @@ void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
174175
OS.pwrite(Patch.Bytes.data(), Patch.Bytes.size(),
175176
SectionFileOffset + Patch.Offset);
176177

178+
uint64_t SkippedPendingRelocations = 0;
177179
for (Relocation &Reloc : PendingRelocations) {
178180
uint64_t Value = Reloc.Addend;
179181
if (Reloc.Symbol)
180182
Value += Resolver(Reloc.Symbol);
181183

184+
// Safely skip any optional pending relocation that cannot be encoded.
185+
if (Reloc.isOptional() &&
186+
!Relocation::canEncodeValue(Reloc.Type, Value,
187+
SectionAddress + Reloc.Offset)) {
188+
189+
// A successful run of 'scanExternalRefs' means that all pending
190+
// relocations are flushed. Otherwise, PatchEntries should run.
191+
if (!opts::ForcePatch) {
192+
BC.errs()
193+
<< "BOLT-ERROR: cannot encode relocation for symbol "
194+
<< Reloc.Symbol->getName()
195+
<< " as it is out-of-range. To proceed must use -force-patch\n";
196+
exit(1);
197+
}
198+
199+
++SkippedPendingRelocations;
200+
continue;
201+
}
182202
Value = Relocation::encodeValue(Reloc.Type, Value,
183203
SectionAddress + Reloc.Offset);
184204

@@ -197,6 +217,11 @@ void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
197217
}
198218

199219
clearList(PendingRelocations);
220+
221+
if (SkippedPendingRelocations > 0 && opts::Verbosity >= 1) {
222+
BC.outs() << "BOLT-INFO: skipped " << SkippedPendingRelocations
223+
<< " out-of-range optional relocations\n";
224+
}
200225
}
201226

202227
BinarySection::~BinarySection() { updateContents(nullptr, 0); }

bolt/lib/Core/Relocation.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,16 @@ static uint64_t encodeValueX86(uint32_t Type, uint64_t Value, uint64_t PC) {
271271
return Value;
272272
}
273273

274+
static bool canEncodeValueAArch64(uint32_t Type, uint64_t Value, uint64_t PC) {
275+
switch (Type) {
276+
default:
277+
llvm_unreachable("unsupported relocation");
278+
case ELF::R_AARCH64_CALL26:
279+
case ELF::R_AARCH64_JUMP26:
280+
return isInt<28>(Value - PC);
281+
}
282+
}
283+
274284
static uint64_t encodeValueAArch64(uint32_t Type, uint64_t Value, uint64_t PC) {
275285
switch (Type) {
276286
default:
@@ -303,6 +313,16 @@ static uint64_t encodeValueAArch64(uint32_t Type, uint64_t Value, uint64_t PC) {
303313
return Value;
304314
}
305315

316+
static uint64_t canEncodeValueRISCV(uint32_t Type, uint64_t Value,
317+
uint64_t PC) {
318+
switch (Type) {
319+
default:
320+
llvm_unreachable("unsupported relocation");
321+
case ELF::R_RISCV_64:
322+
return true;
323+
}
324+
}
325+
306326
static uint64_t encodeValueRISCV(uint32_t Type, uint64_t Value, uint64_t PC) {
307327
switch (Type) {
308328
default:
@@ -739,6 +759,19 @@ uint64_t Relocation::encodeValue(uint32_t Type, uint64_t Value, uint64_t PC) {
739759
}
740760
}
741761

762+
bool Relocation::canEncodeValue(uint32_t Type, uint64_t Value, uint64_t PC) {
763+
switch (Arch) {
764+
default:
765+
llvm_unreachable("Unsupported architecture");
766+
case Triple::aarch64:
767+
return canEncodeValueAArch64(Type, Value, PC);
768+
case Triple::riscv64:
769+
return canEncodeValueRISCV(Type, Value, PC);
770+
case Triple::x86_64:
771+
return true;
772+
}
773+
}
774+
742775
uint64_t Relocation::extractValue(uint32_t Type, uint64_t Contents,
743776
uint64_t PC) {
744777
switch (Arch) {

bolt/unittests/Core/BinaryContext.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "bolt/Core/BinaryContext.h"
10+
#include "bolt/Utils/CommandLineOpts.h"
1011
#include "llvm/BinaryFormat/ELF.h"
1112
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
1213
#include "llvm/Support/TargetSelect.h"
@@ -161,6 +162,67 @@ TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
161162
<< "Wrong forward branch value\n";
162163
}
163164

165+
TEST_P(BinaryContextTester,
166+
FlushOptionalOutOfRangePendingRelocCALL26_ForcePatchOff) {
167+
if (GetParam() != Triple::aarch64)
168+
GTEST_SKIP();
169+
170+
// Tests that flushPendingRelocations exits if any pending relocation is out
171+
// of range and PatchEntries hasn't run. Pending relocations are added by
172+
// scanExternalRefs, so this ensures that either all scanExternalRefs
173+
// relocations were flushed or PatchEntries ran.
174+
175+
BinarySection &BS = BC->registerOrUpdateSection(
176+
".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
177+
// Create symbol 'Func0x4'
178+
MCSymbol *RelSymbol = BC->getOrCreateGlobalSymbol(4, "Func");
179+
ASSERT_TRUE(RelSymbol);
180+
Relocation Reloc{8, RelSymbol, ELF::R_AARCH64_CALL26, 0, 0};
181+
Reloc.setOptional();
182+
BS.addPendingRelocation(Reloc);
183+
184+
SmallVector<char> Vect;
185+
raw_svector_ostream OS(Vect);
186+
187+
// Resolve relocation symbol to a high value so encoding will be out of range.
188+
EXPECT_EXIT(BS.flushPendingRelocations(
189+
OS, [&](const MCSymbol *S) { return 0x800000F; }),
190+
::testing::ExitedWithCode(1),
191+
"BOLT-ERROR: cannot encode relocation for symbol Func0x4 as it is"
192+
" out-of-range. To proceed must use -force-patch");
193+
}
194+
195+
TEST_P(BinaryContextTester,
196+
FlushOptionalOutOfRangePendingRelocCALL26_ForcePatchOn) {
197+
if (GetParam() != Triple::aarch64)
198+
GTEST_SKIP();
199+
200+
// Tests that flushPendingRelocations can skip flushing any optional pending
201+
// relocations that cannot be encoded, given that PatchEntries runs.
202+
opts::ForcePatch = true;
203+
204+
opts::Verbosity = 1;
205+
testing::internal::CaptureStdout();
206+
207+
BinarySection &BS = BC->registerOrUpdateSection(
208+
".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
209+
MCSymbol *RelSymbol = BC->getOrCreateGlobalSymbol(4, "Func");
210+
ASSERT_TRUE(RelSymbol);
211+
Relocation Reloc{8, RelSymbol, ELF::R_AARCH64_CALL26, 0, 0};
212+
Reloc.setOptional();
213+
BS.addPendingRelocation(Reloc);
214+
215+
SmallVector<char> Vect;
216+
raw_svector_ostream OS(Vect);
217+
218+
// Resolve relocation symbol to a high value so encoding will be out of range.
219+
BS.flushPendingRelocations(OS, [&](const MCSymbol *S) { return 0x800000F; });
220+
outs().flush();
221+
std::string CapturedStdOut = testing::internal::GetCapturedStdout();
222+
EXPECT_EQ(CapturedStdOut,
223+
"BOLT-INFO: skipped 1 out-of-range optional relocations\n");
224+
}
225+
164226
#endif
165227

166228
TEST_P(BinaryContextTester, BaseAddress) {

bolt/unittests/Core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ target_link_libraries(CoreTests
1919
LLVMBOLTCore
2020
LLVMBOLTRewrite
2121
LLVMBOLTProfile
22+
LLVMBOLTUtils
2223
LLVMTestingSupport
2324
)
2425

0 commit comments

Comments
 (0)