Skip to content

[BOLT] Overwrite .eh_frame and .gcc_except_table #116755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions bolt/include/bolt/Core/BinarySection.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class BinarySection {
// been renamed)
uint64_t OutputAddress{0}; // Section address for the rewritten binary.
uint64_t OutputSize{0}; // Section size in the rewritten binary.
// Can exceed OutputContents with padding.
uint64_t OutputFileOffset{0}; // File offset in the rewritten binary file.
StringRef OutputContents; // Rewritten section contents.
const uint64_t SectionNumber; // Order in which the section was created.
Expand Down Expand Up @@ -474,6 +475,11 @@ class BinarySection {
/// Use name \p SectionName for the section during the emission.
void emitAsData(MCStreamer &Streamer, const Twine &SectionName) const;

/// Write finalized contents of the section. If OutputSize exceeds the size of
/// the OutputContents, append zero padding to the stream and return the
/// number of byte written which should match the OutputSize.
uint64_t write(raw_ostream &OS) const;

using SymbolResolverFuncTy = llvm::function_ref<uint64_t(const MCSymbol *)>;

/// Flush all pending relocations to patch original contents of sections
Expand All @@ -497,6 +503,9 @@ class BinarySection {
IsFinalized = true;
}

/// When writing section contents, add \p PaddingSize zero bytes at the end.
void addPadding(uint64_t PaddingSize) { OutputSize += PaddingSize; }

/// Reorder the contents of this section according to /p Order. If
/// /p Inplace is true, the entire contents of the section is reordered,
/// otherwise the new contents contain only the reordered data.
Expand Down
9 changes: 9 additions & 0 deletions bolt/lib/Core/BinarySection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ void BinarySection::emitAsData(MCStreamer &Streamer,
Streamer.emitLabel(BC.Ctx->getOrCreateSymbol("__hot_data_end"));
}

uint64_t BinarySection::write(raw_ostream &OS) const {
const uint64_t NumValidContentBytes =
std::min<uint64_t>(getOutputContents().size(), getOutputSize());
OS.write(getOutputContents().data(), NumValidContentBytes);
if (getOutputSize() > NumValidContentBytes)
OS.write_zeros(getOutputSize() - NumValidContentBytes);
return getOutputSize();
}

void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
SymbolResolverFuncTy Resolver) {
if (PendingRelocations.empty() && Patches.empty())
Expand Down
56 changes: 45 additions & 11 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3887,6 +3887,43 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {

void RewriteInstance::mapAllocatableSections(
BOLTLinker::SectionMapper MapSection) {

if (opts::UseOldText || opts::StrictMode) {
auto tryRewriteSection = [&](BinarySection &OldSection,
BinarySection &NewSection) {
if (OldSection.getSize() < NewSection.getOutputSize())
return;

BC->outs() << "BOLT-INFO: rewriting " << OldSection.getName()
<< " in-place\n";

NewSection.setOutputAddress(OldSection.getAddress());
NewSection.setOutputFileOffset(OldSection.getInputFileOffset());
MapSection(NewSection, OldSection.getAddress());

// Pad contents with zeros.
NewSection.addPadding(OldSection.getSize() - NewSection.getOutputSize());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the padding is required to make sure the size match? We can add that in a comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we overwrite the original contents, if the new section is smaller, there will be leftovers from the original section left in the tail. Hence, we write zeros to remove "junk" bytes.


// Prevent the original section name from appearing in the section header
// table.
OldSection.setAnonymous(true);
};

if (EHFrameSection) {
BinarySection *NewEHFrameSection =
getSection(getNewSecPrefix() + getEHFrameSectionName());
assert(NewEHFrameSection && "New contents expected for .eh_frame");
tryRewriteSection(*EHFrameSection, *NewEHFrameSection);
}
BinarySection *EHSection = getSection(".gcc_except_table");
BinarySection *NewEHSection =
getSection(getNewSecPrefix() + ".gcc_except_table");
if (EHSection) {
assert(NewEHSection && "New contents expected for .gcc_except_table");
tryRewriteSection(*EHSection, *NewEHSection);
}
}

// Allocate read-only sections first, then writable sections.
enum : uint8_t { ST_READONLY, ST_READWRITE };
for (uint8_t SType = ST_READONLY; SType <= ST_READWRITE; ++SType) {
Expand Down Expand Up @@ -4164,7 +4201,6 @@ void RewriteInstance::rewriteNoteSections() {
// New section size.
uint64_t Size = 0;
bool DataWritten = false;
uint8_t *SectionData = nullptr;
// Copy over section contents unless it's one of the sections we overwrite.
if (!willOverwriteSection(SectionName)) {
Size = Section.sh_size;
Expand Down Expand Up @@ -4196,12 +4232,7 @@ void RewriteInstance::rewriteNoteSections() {
if (BSec->getAllocAddress()) {
assert(!DataWritten && "Writing section twice.");
(void)DataWritten;
SectionData = BSec->getOutputData();

LLVM_DEBUG(dbgs() << "BOLT-DEBUG: " << (Size ? "appending" : "writing")
<< " contents to section " << SectionName << '\n');
OS.write(reinterpret_cast<char *>(SectionData), BSec->getOutputSize());
Size += BSec->getOutputSize();
Size += BSec->write(OS);
}

BSec->setOutputFileOffset(NextAvailableOffset);
Expand Down Expand Up @@ -4232,8 +4263,7 @@ void RewriteInstance::rewriteNoteSections() {
<< " of size " << Section.getOutputSize() << " at offset 0x"
<< Twine::utohexstr(Section.getOutputFileOffset()) << '\n');

OS.write(Section.getOutputContents().data(), Section.getOutputSize());
NextAvailableOffset += Section.getOutputSize();
NextAvailableOffset += Section.write(OS);
}
}

Expand Down Expand Up @@ -4347,6 +4377,10 @@ RewriteInstance::getOutputSections(ELFObjectFile<ELFT> *File,
BinarySection *BinSec = BC->getSectionForSectionRef(SecRef);
assert(BinSec && "Matching BinarySection should exist.");

// Exclude anonymous sections.
if (BinSec->isAnonymous())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is the matching bit of

      // Prevent the original section name from appearing in the section header
      // table.

in the rewriter. Can you add a comment? Do we have other anonymous sections outside of this one in the patch? Looks like this is a larger functional change (OK to have it here, but I want to understand)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently use anonymous sections to emit jump tables.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG

continue;

addSection(Section, *BinSec);
}

Expand Down Expand Up @@ -5699,8 +5733,8 @@ void RewriteInstance::rewriteFile() {
<< Twine::utohexstr(Section.getAllocAddress()) << "\n of size "
<< Section.getOutputSize() << "\n at offset "
<< Section.getOutputFileOffset() << '\n';
OS.pwrite(reinterpret_cast<const char *>(Section.getOutputData()),
Section.getOutputSize(), Section.getOutputFileOffset());
OS.seek(Section.getOutputFileOffset());
Section.write(OS);
}

for (BinarySection &Section : BC->allocatableSections())
Expand Down
8 changes: 8 additions & 0 deletions bolt/test/eh-frame-overwrite.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Check that llvm-bolt can overwrite .eh_frame section in-place.

REQUIRES: system-linux

RUN: %clang %cflags %p/Inputs/hello.c -o %t -Wl,-q
RUN: llvm-bolt %t -o %t.bolt --strict | FileCheck %s

CHECK: rewriting .eh_frame in-place
Loading