Skip to content

Commit 3a0d894

Browse files
authored
[BOLT] Add support for BOLT-reserved space in a binary (llvm#90300)
Allow the user to allocate space in a binary that could be used by BOLT for allocating new sections. The reservation is specified by two special symbols recognizable by BOLT: __bolt_reserved_{start,end}. The reserved space will be useful for optimizing the Linux kernel where we cannot allocate a new executable segment. However, the support is not limited to kernel binaries as some user-space application may find it useful too.
1 parent 5bbf1ea commit 3a0d894

File tree

2 files changed

+70
-22
lines changed

2 files changed

+70
-22
lines changed

bolt/include/bolt/Rewrite/RewriteInstance.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,10 @@ class RewriteInstance {
422422
/// Section name used for extra BOLT code in addition to .text.
423423
static StringRef getBOLTTextSectionName() { return ".bolt.text"; }
424424

425+
/// Symbol markers for BOLT reserved area.
426+
static StringRef getBOLTReservedStart() { return "__bolt_reserved_start"; }
427+
static StringRef getBOLTReservedEnd() { return "__bolt_reserved_end"; }
428+
425429
/// Common section names.
426430
static StringRef getEHFrameSectionName() { return ".eh_frame"; }
427431
static StringRef getEHFrameHdrSectionName() { return ".eh_frame_hdr"; }

bolt/lib/Rewrite/RewriteInstance.cpp

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,11 @@ void RewriteInstance::discoverFileObjects() {
10631063
continue;
10641064
}
10651065

1066+
if (SymName == getBOLTReservedStart() || SymName == getBOLTReservedEnd()) {
1067+
registerName(SymbolSize);
1068+
continue;
1069+
}
1070+
10661071
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: considering symbol " << UniqueName
10671072
<< " for function\n");
10681073

@@ -3594,6 +3599,26 @@ void RewriteInstance::updateMetadata() {
35943599
void RewriteInstance::mapFileSections(BOLTLinker::SectionMapper MapSection) {
35953600
BC->deregisterUnusedSections();
35963601

3602+
// Check if the input has a space reserved for BOLT.
3603+
BinaryData *StartBD = BC->getBinaryDataByName(getBOLTReservedStart());
3604+
BinaryData *EndBD = BC->getBinaryDataByName(getBOLTReservedEnd());
3605+
if (!StartBD != !EndBD) {
3606+
BC->errs() << "BOLT-ERROR: one of the symbols is missing from the binary: "
3607+
<< getBOLTReservedStart() << ", " << getBOLTReservedEnd()
3608+
<< '\n';
3609+
exit(1);
3610+
}
3611+
3612+
if (StartBD) {
3613+
PHDRTableOffset = 0;
3614+
PHDRTableAddress = 0;
3615+
NewTextSegmentAddress = 0;
3616+
NewTextSegmentOffset = 0;
3617+
NextAvailableAddress = StartBD->getAddress();
3618+
BC->outs()
3619+
<< "BOLT-INFO: using reserved space for allocating new sections\n";
3620+
}
3621+
35973622
// If no new .eh_frame was written, remove relocated original .eh_frame.
35983623
BinarySection *RelocatedEHFrameSection =
35993624
getSection(".relocated" + getEHFrameSectionName());
@@ -3613,6 +3638,18 @@ void RewriteInstance::mapFileSections(BOLTLinker::SectionMapper MapSection) {
36133638

36143639
// Map the rest of the sections.
36153640
mapAllocatableSections(MapSection);
3641+
3642+
if (StartBD) {
3643+
const uint64_t ReservedSpace = EndBD->getAddress() - StartBD->getAddress();
3644+
const uint64_t AllocatedSize = NextAvailableAddress - StartBD->getAddress();
3645+
if (ReservedSpace < AllocatedSize) {
3646+
BC->errs() << "BOLT-ERROR: reserved space (" << ReservedSpace << " byte"
3647+
<< (ReservedSpace == 1 ? "" : "s")
3648+
<< ") is smaller than required for new allocations ("
3649+
<< AllocatedSize << " bytes)\n";
3650+
exit(1);
3651+
}
3652+
}
36163653
}
36173654

36183655
std::vector<BinarySection *> RewriteInstance::getCodeSections() {
@@ -3854,7 +3891,7 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {
38543891
// Add the new text section aggregating all existing code sections.
38553892
// This is pseudo-section that serves a purpose of creating a corresponding
38563893
// entry in section header table.
3857-
int64_t NewTextSectionSize =
3894+
const uint64_t NewTextSectionSize =
38583895
NextAvailableAddress - NewTextSectionStartAddress;
38593896
if (NewTextSectionSize) {
38603897
const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/true,
@@ -3937,7 +3974,7 @@ void RewriteInstance::mapAllocatableSections(
39373974
if (PHDRTableAddress) {
39383975
// Segment size includes the size of the PHDR area.
39393976
NewTextSegmentSize = NextAvailableAddress - PHDRTableAddress;
3940-
} else {
3977+
} else if (NewTextSegmentAddress) {
39413978
// Existing PHDR table would be updated.
39423979
NewTextSegmentSize = NextAvailableAddress - NewTextSegmentAddress;
39433980
}
@@ -3976,7 +4013,7 @@ void RewriteInstance::patchELFPHDRTable() {
39764013
assert(!PHDRTableAddress && "unexpected address for program header table");
39774014
PHDRTableOffset = Obj.getHeader().e_phoff;
39784015
if (NewWritableSegmentSize) {
3979-
BC->errs() << "Unable to add writable segment with UseGnuStack option\n";
4016+
BC->errs() << "BOLT-ERROR: unable to add writable segment\n";
39804017
exit(1);
39814018
}
39824019
}
@@ -3986,7 +4023,7 @@ void RewriteInstance::patchELFPHDRTable() {
39864023
if (!NewWritableSegmentSize) {
39874024
if (PHDRTableAddress)
39884025
NewTextSegmentSize = NextAvailableAddress - PHDRTableAddress;
3989-
else
4026+
else if (NewTextSegmentAddress)
39904027
NewTextSegmentSize = NextAvailableAddress - NewTextSegmentAddress;
39914028
} else {
39924029
NewWritableSegmentSize = NextAvailableAddress - NewWritableSegmentAddress;
@@ -4020,8 +4057,10 @@ void RewriteInstance::patchELFPHDRTable() {
40204057
};
40214058

40224059
auto writeNewSegmentPhdrs = [&]() {
4023-
ELF64LE::Phdr NewTextPhdr = createNewTextPhdr();
4024-
OS.write(reinterpret_cast<const char *>(&NewTextPhdr), sizeof(NewTextPhdr));
4060+
if (PHDRTableAddress || NewTextSegmentSize) {
4061+
ELF64LE::Phdr NewPhdr = createNewTextPhdr();
4062+
OS.write(reinterpret_cast<const char *>(&NewPhdr), sizeof(NewPhdr));
4063+
}
40254064

40264065
if (NewWritableSegmentSize) {
40274066
ELF64LEPhdrTy NewPhdr;
@@ -4119,9 +4158,8 @@ void RewriteInstance::rewriteNoteSections() {
41194158
const ELFFile<ELF64LE> &Obj = ELF64LEFile->getELFFile();
41204159
raw_fd_ostream &OS = Out->os();
41214160

4122-
uint64_t NextAvailableOffset = getFileOffsetForAddress(NextAvailableAddress);
4123-
assert(NextAvailableOffset >= FirstNonAllocatableOffset &&
4124-
"next available offset calculation failure");
4161+
uint64_t NextAvailableOffset = std::max(
4162+
getFileOffsetForAddress(NextAvailableAddress), FirstNonAllocatableOffset);
41254163
OS.seek(NextAvailableOffset);
41264164

41274165
// Copy over non-allocatable section contents and update file offsets.
@@ -4860,7 +4898,7 @@ void RewriteInstance::updateELFSymbolTable(
48604898
++NumHotDataSymsUpdated;
48614899
}
48624900

4863-
if (*SymbolName == "_end")
4901+
if (*SymbolName == "_end" && NextAvailableAddress > Symbol.st_value)
48644902
updateSymbolValue(*SymbolName, NextAvailableAddress);
48654903

48664904
if (IsDynSym)
@@ -4974,13 +5012,6 @@ void RewriteInstance::patchELFSymTabs(ELFObjectFile<ELFT> *File) {
49745012
std::vector<uint32_t> NewSectionIndex;
49755013
getOutputSections(File, NewSectionIndex);
49765014

4977-
// Set pointer at the end of the output file, so we can pwrite old symbol
4978-
// tables if we need to.
4979-
uint64_t NextAvailableOffset = getFileOffsetForAddress(NextAvailableAddress);
4980-
assert(NextAvailableOffset >= FirstNonAllocatableOffset &&
4981-
"next available offset calculation failure");
4982-
Out->os().seek(NextAvailableOffset);
4983-
49845015
// Update dynamic symbol table.
49855016
const ELFShdrTy *DynSymSection = nullptr;
49865017
for (const ELFShdrTy &Section : cantFail(Obj.sections())) {
@@ -4992,6 +5023,10 @@ void RewriteInstance::patchELFSymTabs(ELFObjectFile<ELFT> *File) {
49925023
assert((DynSymSection || BC->IsStaticExecutable) &&
49935024
"dynamic symbol table expected");
49945025
if (DynSymSection) {
5026+
// Set pointer to the end of the section, so we can use pwrite to update
5027+
// the dynamic symbol table.
5028+
Out->os().seek(DynSymSection->sh_offset + DynSymSection->sh_size);
5029+
49955030
updateELFSymbolTable(
49965031
File,
49975032
/*IsDynSym=*/true,
@@ -5545,10 +5580,10 @@ void RewriteInstance::rewriteFile() {
55455580
auto Streamer = BC->createStreamer(OS);
55465581
// Make sure output stream has enough reserved space, otherwise
55475582
// pwrite() will fail.
5548-
uint64_t Offset = OS.seek(getFileOffsetForAddress(NextAvailableAddress));
5549-
(void)Offset;
5550-
assert(Offset == getFileOffsetForAddress(NextAvailableAddress) &&
5551-
"error resizing output file");
5583+
uint64_t Offset = std::max(getFileOffsetForAddress(NextAvailableAddress),
5584+
FirstNonAllocatableOffset);
5585+
Offset = OS.seek(Offset);
5586+
assert((Offset != (uint64_t)-1) && "Error resizing output file");
55525587

55535588
// Overwrite functions with fixed output address. This is mostly used by
55545589
// non-relocation mode, with one exception: injected functions are covered
@@ -5780,7 +5815,7 @@ void RewriteInstance::writeEHFrameHeader() {
57805815
std::vector<char> NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader(
57815816
RelocatedEHFrame, NewEHFrame, EHFrameHdrOutputAddress, FailedAddresses);
57825817

5783-
assert(Out->os().tell() == EHFrameHdrFileOffset && "offset mismatch");
5818+
Out->os().seek(EHFrameHdrFileOffset);
57845819
Out->os().write(NewEHFrameHdr.data(), NewEHFrameHdr.size());
57855820

57865821
const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/true,
@@ -5800,6 +5835,15 @@ void RewriteInstance::writeEHFrameHeader() {
58005835

58015836
NextAvailableAddress += EHFrameHdrSec.getOutputSize();
58025837

5838+
if (const BinaryData *ReservedEnd =
5839+
BC->getBinaryDataByName(getBOLTReservedEnd())) {
5840+
if (NextAvailableAddress > ReservedEnd->getAddress()) {
5841+
BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName()
5842+
<< " into reserved space\n";
5843+
exit(1);
5844+
}
5845+
}
5846+
58035847
// Merge new .eh_frame with the relocated original so that gdb can locate all
58045848
// FDEs.
58055849
if (RelocatedEHFrameSection) {

0 commit comments

Comments
 (0)