Skip to content

Commit b3902db

Browse files
[BOLT] Skip flushing optional out-of-range pending relocs
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. 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.
1 parent d34cb52 commit b3902db

File tree

6 files changed

+74
-18
lines changed

6 files changed

+74
-18
lines changed

bolt/include/bolt/Core/BinarySection.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,10 @@ class BinarySection {
6666
// from the original section address.
6767
RelocationSetType DynamicRelocations;
6868

69-
// Pending relocations for this section.
70-
std::vector<Relocation> PendingRelocations;
69+
/// Pending relocations for this section and whether they are optional, i.e.,
70+
/// added as part of an optimization. In that case they can be safely omitted
71+
/// if flushPendingRelocations discovers they cannot be encoded.
72+
std::vector<std::pair<Relocation, bool>> PendingRelocations;
7173

7274
struct BinaryPatch {
7375
uint64_t Offset;
@@ -374,9 +376,10 @@ class BinarySection {
374376
DynamicRelocations.emplace(Reloc);
375377
}
376378

377-
/// Add relocation against the original contents of this section.
378-
void addPendingRelocation(const Relocation &Rel) {
379-
PendingRelocations.push_back(Rel);
379+
/// Add relocation against the original contents of this section. When added
380+
/// as part of an optimization it is marked as \p Optional.
381+
void addPendingRelocation(const Relocation &Rel, bool Optional = false) {
382+
PendingRelocations.push_back({Rel, Optional});
380383
}
381384

382385
/// Add patch to the input contents of this section.

bolt/include/bolt/Core/Relocation.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,12 @@ struct Relocation {
6464
/// and \P Type mismatch occurred.
6565
static bool skipRelocationProcess(uint64_t &Type, uint64_t Contents);
6666

67-
// Adjust value depending on relocation type (make it PC relative or not)
67+
/// Adjust value depending on relocation type (make it PC relative or not).
6868
static uint64_t encodeValue(uint64_t Type, uint64_t Value, uint64_t PC);
6969

70+
/// Return true if there are enough bits to encode the relocation value.
71+
static bool canEncodeValue(uint64_t Type, uint64_t Value, uint64_t PC);
72+
7073
/// Extract current relocated value from binary contents. This is used for
7174
/// RISC architectures where values are encoded in specific bits depending
7275
/// on the relocation value. For X86, we limit to sign extending the value

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1672,7 +1672,7 @@ bool BinaryFunction::scanExternalRefs() {
16721672
// Add relocations unless disassembly failed for this function.
16731673
if (!DisassemblyFailed)
16741674
for (Relocation &Rel : FunctionRelocations)
1675-
getOriginSection()->addPendingRelocation(Rel);
1675+
getOriginSection()->addPendingRelocation(Rel, /*Optional*/ true);
16761676

16771677
// Inform BinaryContext that this function symbols will not be defined and
16781678
// relocations should not be created against them.

bolt/lib/Core/BinarySection.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,19 @@ void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
165165
OS.pwrite(Patch.Bytes.data(), Patch.Bytes.size(),
166166
SectionFileOffset + Patch.Offset);
167167

168-
for (Relocation &Reloc : PendingRelocations) {
168+
uint64_t SkippedPendingRelocations = 0;
169+
for (auto &[Reloc, Optional] : PendingRelocations) {
169170
uint64_t Value = Reloc.Addend;
170171
if (Reloc.Symbol)
171172
Value += Resolver(Reloc.Symbol);
172173

174+
// Safely skip any pending relocation that cannot be encoded and was added
175+
// as part of an optimization.
176+
if (Optional && !Relocation::canEncodeValue(
177+
Reloc.Type, Value, SectionAddress + Reloc.Offset)) {
178+
++SkippedPendingRelocations;
179+
continue;
180+
}
173181
Value = Relocation::encodeValue(Reloc.Type, Value,
174182
SectionAddress + Reloc.Offset);
175183

@@ -188,6 +196,10 @@ void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
188196
}
189197

190198
clearList(PendingRelocations);
199+
200+
if (SkippedPendingRelocations > 0)
201+
LLVM_DEBUG(dbgs() << "BOLT-INFO: Skipped " << SkippedPendingRelocations
202+
<< " pending relocations as they were out of range\n");
191203
}
192204

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

bolt/lib/Core/Relocation.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,16 @@ static uint64_t encodeValueX86(uint64_t Type, uint64_t Value, uint64_t PC) {
361361
return Value;
362362
}
363363

364+
static bool canEncodeValueAArch64(uint64_t Type, uint64_t Value, uint64_t PC) {
365+
switch (Type) {
366+
default:
367+
llvm_unreachable("unsupported relocation");
368+
case ELF::R_AARCH64_CALL26:
369+
case ELF::R_AARCH64_JUMP26:
370+
return isInt<28>(Value - PC);
371+
}
372+
}
373+
364374
static uint64_t encodeValueAArch64(uint64_t Type, uint64_t Value, uint64_t PC) {
365375
switch (Type) {
366376
default:
@@ -393,6 +403,16 @@ static uint64_t encodeValueAArch64(uint64_t Type, uint64_t Value, uint64_t PC) {
393403
return Value;
394404
}
395405

406+
static uint64_t canEncodeValueRISCV(uint64_t Type, uint64_t Value,
407+
uint64_t PC) {
408+
switch (Type) {
409+
default:
410+
llvm_unreachable("unsupported relocation");
411+
case ELF::R_RISCV_64:
412+
return true;
413+
}
414+
}
415+
396416
static uint64_t encodeValueRISCV(uint64_t Type, uint64_t Value, uint64_t PC) {
397417
switch (Type) {
398418
default:
@@ -838,6 +858,19 @@ uint64_t Relocation::encodeValue(uint64_t Type, uint64_t Value, uint64_t PC) {
838858
}
839859
}
840860

861+
bool Relocation::canEncodeValue(uint64_t Type, uint64_t Value, uint64_t PC) {
862+
switch (Arch) {
863+
default:
864+
llvm_unreachable("Unsupported architecture");
865+
case Triple::aarch64:
866+
return canEncodeValueAArch64(Type, Value, PC);
867+
case Triple::riscv64:
868+
return canEncodeValueRISCV(Type, Value, PC);
869+
case Triple::x86_64:
870+
return true;
871+
}
872+
}
873+
841874
uint64_t Relocation::extractValue(uint64_t Type, uint64_t Contents,
842875
uint64_t PC) {
843876
switch (Arch) {

bolt/unittests/Core/BinaryContext.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -157,31 +157,36 @@ TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
157157
<< "Wrong forward branch value\n";
158158
}
159159

160-
// TODO: BOLT currently asserts when a relocation is out of range.
161160
TEST_P(BinaryContextTester, FlushOptionalOutOfRangePendingRelocCALL26) {
162161
if (GetParam() != Triple::aarch64)
163162
GTEST_SKIP();
164163

165164
// This test checks that flushPendingRelocations skips flushing any optional
166165
// pending relocations that cannot be encoded.
167166

167+
bool DebugFlagPrev = ::llvm::DebugFlag;
168+
::llvm::DebugFlag = true;
169+
testing::internal::CaptureStderr();
170+
168171
BinarySection &BS = BC->registerOrUpdateSection(
169172
".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
170173
MCSymbol *RelSymbol = BC->getOrCreateGlobalSymbol(4, "Func");
171174
ASSERT_TRUE(RelSymbol);
172-
BS.addPendingRelocation(
173-
Relocation{8, RelSymbol, ELF::R_AARCH64_CALL26, 0, 0});
175+
BS.addPendingRelocation(Relocation{8, RelSymbol, ELF::R_AARCH64_CALL26, 0, 0},
176+
/*Optional*/ true);
174177

175178
SmallVector<char> Vect;
176179
raw_svector_ostream OS(Vect);
177180

178-
// FIXME: bolt must be able to skip pending relocs that are out of range.
179-
EXPECT_DEBUG_DEATH(
180-
// Resolve relocation symbol to a high value so encoding will be out of
181-
// range.
182-
BS.flushPendingRelocations(OS,
183-
[&](const MCSymbol *S) { return 0x800000F; }),
184-
".*only PC \\+/- 128MB is allowed for direct call");
181+
// Resolve relocation symbol to a high value so encoding will be out of range.
182+
BS.flushPendingRelocations(OS, [&](const MCSymbol *S) { return 0x800000F; });
183+
184+
std::string CapturedStdErr = testing::internal::GetCapturedStderr();
185+
EXPECT_TRUE(CapturedStdErr.find("BOLT-INFO: Skipped 1 pending relocations as "
186+
"they were out of range") !=
187+
std::string::npos);
188+
189+
::llvm::DebugFlag = DebugFlagPrev;
185190
}
186191

187192
#endif

0 commit comments

Comments
 (0)