[BOLT] Overwrite .eh_frame and .gcc_except_table (#116755)

Under --use-old-text or --strict, we completely rewrite contents of EH
frames and exception tables sections. If new contents of either section
do not exceed the size of the original section, rewrite the section
in-place.
This commit is contained in:
Maksim Panchenko 2024-11-19 12:59:05 -08:00 committed by GitHub
parent 565a9ac7df
commit 996553228f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 71 additions and 11 deletions

View File

@ -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.
@ -474,6 +475,11 @@ public:
/// 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
@ -497,6 +503,9 @@ public:
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.

View File

@ -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())

View File

@ -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());
// 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) {
@ -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;
@ -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);
@ -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);
}
}
@ -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())
continue;
addSection(Section, *BinSec);
}
@ -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())

View File

@ -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