diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index ea9b87952cd8..1e9d44fa37be 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -540,43 +540,6 @@ void EhFrameSection::finalizeContents() { this->size = off; } -// Returns data for .eh_frame_hdr. .eh_frame_hdr is a binary search table -// to get an FDE from an address to which FDE is applied. This function -// returns a list of such pairs. -SmallVector EhFrameSection::getFdeData() const { - uint8_t *buf = ctx.bufferStart + getParent()->offset + outSecOff; - SmallVector ret; - - uint64_t va = getPartition(ctx).ehFrameHdr->getVA(); - for (CieRecord *rec : cieRecords) { - uint8_t enc = getFdeEncoding(rec->cie); - for (EhSectionPiece *fde : rec->fdes) { - uint64_t pc = getFdePc(buf, fde->outputOff, enc); - uint64_t fdeVA = getParent()->addr + fde->outputOff; - if (!isInt<32>(pc - va)) { - Err(ctx) << fde->sec << ": PC offset is too large: 0x" - << Twine::utohexstr(pc - va); - continue; - } - ret.push_back({uint32_t(pc - va), uint32_t(fdeVA - va)}); - } - } - - // Sort the FDE list by their PC and uniqueify. Usually there is only - // one FDE for a PC (i.e. function), but if ICF merges two functions - // into one, there can be more than one FDEs pointing to the address. - auto less = [](const FdeData &a, const FdeData &b) { - return a.pcRel < b.pcRel; - }; - llvm::stable_sort(ret, less); - auto eq = [](const FdeData &a, const FdeData &b) { - return a.pcRel == b.pcRel; - }; - ret.erase(llvm::unique(ret, eq), ret.end()); - - return ret; -} - static uint64_t readFdeAddr(Ctx &ctx, uint8_t *buf, int size) { switch (size) { case DW_EH_PE_udata2: @@ -630,14 +593,79 @@ void EhFrameSection::writeTo(uint8_t *buf) { } } - // Apply relocations. .eh_frame section contents are not contiguous - // in the output buffer, but relocateAlloc() still works because - // getOffset() takes care of discontiguous section pieces. + // Apply relocations to .eh_frame entries. This includes CIE personality + // pointers, FDE initial_location fields, and LSDA pointers. for (EhInputSection *s : sections) ctx.target->relocateEh(*s, buf); - if (getPartition(ctx).ehFrameHdr && getPartition(ctx).ehFrameHdr->getParent()) - getPartition(ctx).ehFrameHdr->write(); + EhFrameHeader *hdr = getPartition(ctx).ehFrameHdr.get(); + if (!hdr || !hdr->getParent()) + return; + + // Write the .eh_frame_hdr section, which contains a binary search table of + // pointers to FDEs. This must be written after .eh_frame relocation since + // the content depends on relocated initial_location fields in FDEs. + using FdeData = EhFrameSection::FdeData; + SmallVector fdes; + uint64_t va = hdr->getVA(); + for (CieRecord *rec : cieRecords) { + uint8_t enc = getFdeEncoding(rec->cie); + for (EhSectionPiece *fde : rec->fdes) { + uint64_t pc = getFdePc(buf, fde->outputOff, enc); + uint64_t fdeVA = getParent()->addr + fde->outputOff; + if (!isInt<32>(pc - va)) { + Err(ctx) << fde->sec << ": PC offset is too large: 0x" + << Twine::utohexstr(pc - va); + continue; + } + fdes.push_back({uint32_t(pc - va), uint32_t(fdeVA - va)}); + } + } + + // Sort the FDE list by their PC and uniqueify. Usually there is only + // one FDE for a PC (i.e. function), but if ICF merges two functions + // into one, there can be more than one FDEs pointing to the address. + llvm::stable_sort(fdes, [](const FdeData &a, const FdeData &b) { + return a.pcRel < b.pcRel; + }); + fdes.erase( + llvm::unique(fdes, [](auto &a, auto &b) { return a.pcRel == b.pcRel; }), + fdes.end()); + + // Write header. + uint8_t *hdrBuf = ctx.bufferStart + hdr->getParent()->offset + hdr->outSecOff; + hdrBuf[0] = 1; // version + hdrBuf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; // eh_frame_ptr_enc + hdrBuf[2] = DW_EH_PE_udata4; // fde_count_enc + hdrBuf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; // table_enc + write32(ctx, hdrBuf + 4, + getParent()->addr - hdr->getVA() - 4); // eh_frame_ptr + write32(ctx, hdrBuf + 8, fdes.size()); // fde_count + hdrBuf += 12; + + // Write binary search table. Each entry describes the starting PC and the FDE + // address. + for (FdeData &fde : fdes) { + write32(ctx, hdrBuf, fde.pcRel); + write32(ctx, hdrBuf + 4, fde.fdeVARel); + hdrBuf += 8; + } +} + +EhFrameHeader::EhFrameHeader(Ctx &ctx) + : SyntheticSection(ctx, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 4) {} + +void EhFrameHeader::writeTo(uint8_t *buf) { + // The section content is written during EhFrameSection::writeTo. +} + +size_t EhFrameHeader::getSize() const { + // .eh_frame_hdr has a 12 bytes header followed by an array of FDEs. + return 12 + getPartition(ctx).ehFrame->numFdes * 8; +} + +bool EhFrameHeader::isNeeded() const { + return isLive() && getPartition(ctx).ehFrame->isNeeded(); } GotSection::GotSection(Ctx &ctx) @@ -3658,51 +3686,6 @@ void GdbIndexSection::writeTo(uint8_t *buf) { bool GdbIndexSection::isNeeded() const { return !chunks.empty(); } -EhFrameHeader::EhFrameHeader(Ctx &ctx) - : SyntheticSection(ctx, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 4) {} - -void EhFrameHeader::writeTo(uint8_t *buf) { - // Unlike most sections, the EhFrameHeader section is written while writing - // another section, namely EhFrameSection, which calls the write() function - // below from its writeTo() function. This is necessary because the contents - // of EhFrameHeader depend on the relocated contents of EhFrameSection and we - // don't know which order the sections will be written in. -} - -// .eh_frame_hdr contains a binary search table of pointers to FDEs. -// Each entry of the search table consists of two values, -// the starting PC from where FDEs covers, and the FDE's address. -// It is sorted by PC. -void EhFrameHeader::write() { - uint8_t *buf = ctx.bufferStart + getParent()->offset + outSecOff; - using FdeData = EhFrameSection::FdeData; - SmallVector fdes = getPartition(ctx).ehFrame->getFdeData(); - - buf[0] = 1; - buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; - buf[2] = DW_EH_PE_udata4; - buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; - write32(ctx, buf + 4, - getPartition(ctx).ehFrame->getParent()->addr - this->getVA() - 4); - write32(ctx, buf + 8, fdes.size()); - buf += 12; - - for (FdeData &fde : fdes) { - write32(ctx, buf, fde.pcRel); - write32(ctx, buf + 4, fde.fdeVARel); - buf += 8; - } -} - -size_t EhFrameHeader::getSize() const { - // .eh_frame_hdr has a 12 bytes header followed by an array of FDEs. - return 12 + getPartition(ctx).ehFrame->numFdes * 8; -} - -bool EhFrameHeader::isNeeded() const { - return isLive() && getPartition(ctx).ehFrame->isNeeded(); -} - VersionDefinitionSection::VersionDefinitionSection(Ctx &ctx) : SyntheticSection(ctx, ".gnu.version_d", SHT_GNU_verdef, SHF_ALLOC, sizeof(uint32_t)) {} diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index 66c866d7e8cd..e01a5ad8abc6 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -68,7 +68,6 @@ public: uint32_t fdeVARel; }; - SmallVector getFdeData() const; ArrayRef getCieRecords() const { return cieRecords; } template void iterateFDEWithLSDA(llvm::function_ref fn); @@ -95,6 +94,17 @@ private: llvm::DenseMap, Symbol *>, CieRecord *> cieMap; }; +// .eh_frame_hdr contains a binary search table for .eh_frame FDEs. The section +// is covered by a PT_GNU_EH_FRAME segment, which allows the runtime unwinder to +// locate it via functions like `dl_iterate_phdr`. +class EhFrameHeader final : public SyntheticSection { +public: + EhFrameHeader(Ctx &); + void writeTo(uint8_t *buf) override; + size_t getSize() const override; + bool isNeeded() const override; +}; + class GotSection final : public SyntheticSection { public: GotSection(Ctx &); @@ -967,24 +977,6 @@ private: size_t size; }; -// --eh-frame-hdr option tells linker to construct a header for all the -// .eh_frame sections. This header is placed to a section named .eh_frame_hdr -// and also to a PT_GNU_EH_FRAME segment. -// At runtime the unwinder then can find all the PT_GNU_EH_FRAME segments by -// calling dl_iterate_phdr. -// This section contains a lookup table for quick binary search of FDEs. -// Detailed info about internals can be found in Ian Lance Taylor's blog: -// http://www.airs.com/blog/archives/460 (".eh_frame") -// http://www.airs.com/blog/archives/462 (".eh_frame_hdr") -class EhFrameHeader final : public SyntheticSection { -public: - EhFrameHeader(Ctx &); - void write(); - void writeTo(uint8_t *buf) override; - size_t getSize() const override; - bool isNeeded() const override; -}; - // For more information about .gnu.version and .gnu.version_r see: // https://www.akkadia.org/drepper/symbol-versioning