From d2df2ea344636f7a35cc3fd419098658a1b3e23a Mon Sep 17 00:00:00 2001 From: Maksim Panchenko Date: Thu, 31 Oct 2024 14:35:28 -0700 Subject: [PATCH] [BOLT] Overwrite .eh_frame_hdr in-place If the new EH frame header can fit into the original .eh_frame_hdr section, overwrite it in-place and pad with zeroes. --- bolt/lib/Rewrite/RewriteInstance.cpp | 80 ++++++++++++++++++---------- bolt/test/eh-frame-hdr.test | 12 +++++ 2 files changed, 63 insertions(+), 29 deletions(-) create mode 100644 bolt/test/eh-frame-hdr.test diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 1fcf2bb959bbb..40769944e3876 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -5791,42 +5791,64 @@ void RewriteInstance::writeEHFrameHeader() { LLVM_DEBUG(dbgs() << "BOLT: writing a new " << getEHFrameHdrSectionName() << '\n'); - NextAvailableAddress = - appendPadding(Out->os(), NextAvailableAddress, EHFrameHdrAlign); + // Try to overwrite the original .eh_frame_hdr if the size permits. + uint64_t EHFrameHdrOutputAddress = 0; + uint64_t EHFrameHdrFileOffset = 0; + std::vector NewEHFrameHdr; + BinarySection *OldEHFrameHdrSection = getSection(getEHFrameHdrSectionName()); + if (OldEHFrameHdrSection) { + NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader( + RelocatedEHFrame, NewEHFrame, OldEHFrameHdrSection->getAddress()); + if (NewEHFrameHdr.size() <= OldEHFrameHdrSection->getSize()) { + BC->outs() << "BOLT-INFO: rewriting " << getEHFrameHdrSectionName() + << " in-place\n"; + EHFrameHdrOutputAddress = OldEHFrameHdrSection->getAddress(); + EHFrameHdrFileOffset = OldEHFrameHdrSection->getInputFileOffset(); + } else { + OldEHFrameHdrSection->setOutputName(getOrgSecPrefix() + + getEHFrameHdrSectionName()); + OldEHFrameHdrSection = nullptr; + } + } - const uint64_t EHFrameHdrOutputAddress = NextAvailableAddress; - const uint64_t EHFrameHdrFileOffset = - getFileOffsetForAddress(NextAvailableAddress); + // If there was not enough space, allocate more memory for .eh_frame_hdr. + if (!OldEHFrameHdrSection) { + NextAvailableAddress = + appendPadding(Out->os(), NextAvailableAddress, EHFrameHdrAlign); - std::vector NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader( - RelocatedEHFrame, NewEHFrame, EHFrameHdrOutputAddress); + EHFrameHdrOutputAddress = NextAvailableAddress; + EHFrameHdrFileOffset = getFileOffsetForAddress(NextAvailableAddress); + + NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader( + RelocatedEHFrame, NewEHFrame, EHFrameHdrOutputAddress); + + NextAvailableAddress += NewEHFrameHdr.size(); + if (!BC->BOLTReserved.empty() && + (NextAvailableAddress > BC->BOLTReserved.end())) { + BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName() + << " into reserved space\n"; + exit(1); + } + + // Create a new entry in the section header table. + const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/true, + /*IsText=*/false, + /*IsAllocatable=*/true); + BinarySection &EHFrameHdrSec = BC->registerOrUpdateSection( + getNewSecPrefix() + getEHFrameHdrSectionName(), ELF::SHT_PROGBITS, + Flags, nullptr, NewEHFrameHdr.size(), /*Alignment=*/1); + EHFrameHdrSec.setOutputFileOffset(EHFrameHdrFileOffset); + EHFrameHdrSec.setOutputAddress(EHFrameHdrOutputAddress); + EHFrameHdrSec.setOutputName(getEHFrameHdrSectionName()); + } Out->os().seek(EHFrameHdrFileOffset); Out->os().write(NewEHFrameHdr.data(), NewEHFrameHdr.size()); - const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/true, - /*IsText=*/false, - /*IsAllocatable=*/true); - BinarySection *OldEHFrameHdrSection = getSection(getEHFrameHdrSectionName()); + // Pad the contents if overwriting in-place. if (OldEHFrameHdrSection) - OldEHFrameHdrSection->setOutputName(getOrgSecPrefix() + - getEHFrameHdrSectionName()); - - BinarySection &EHFrameHdrSec = BC->registerOrUpdateSection( - getNewSecPrefix() + getEHFrameHdrSectionName(), ELF::SHT_PROGBITS, Flags, - nullptr, NewEHFrameHdr.size(), /*Alignment=*/1); - EHFrameHdrSec.setOutputFileOffset(EHFrameHdrFileOffset); - EHFrameHdrSec.setOutputAddress(EHFrameHdrOutputAddress); - EHFrameHdrSec.setOutputName(getEHFrameHdrSectionName()); - - NextAvailableAddress += EHFrameHdrSec.getOutputSize(); - - if (!BC->BOLTReserved.empty() && - (NextAvailableAddress > BC->BOLTReserved.end())) { - BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName() - << " into reserved space\n"; - exit(1); - } + Out->os().write_zeros(OldEHFrameHdrSection->getSize() - + NewEHFrameHdr.size()); // Merge new .eh_frame with the relocated original so that gdb can locate all // FDEs. diff --git a/bolt/test/eh-frame-hdr.test b/bolt/test/eh-frame-hdr.test new file mode 100644 index 0000000000000..4d718c850e2f2 --- /dev/null +++ b/bolt/test/eh-frame-hdr.test @@ -0,0 +1,12 @@ +# Check that llvm-bolt overwrites .eh_frame_hdr in-place. + +REQUIRES: system-linux + +RUN: %clang %cflags %p/Inputs/hello.c -o %t -Wl,-q +RUN: llvm-bolt %t -o %t.bolt --use-old-text \ +RUN: | FileCheck %s --check-prefix=CHECK-BOLT +RUN: llvm-readelf -WS %t.bolt | FileCheck %s + +CHECK-BOLT: rewriting .eh_frame_hdr in-place + +CHECK-NOT: .bolt.org.eh_frame_hdr