From 8884784516eb33e5df95bce3e0be64ca07b440df Mon Sep 17 00:00:00 2001 From: Simon Spies Date: Fri, 23 Jan 2026 16:44:26 +0000 Subject: [PATCH] support 8 byte header entries --- lld/ELF/Config.h | 1 + lld/ELF/Driver.cpp | 2 + lld/ELF/Options.td | 4 ++ lld/ELF/SyntheticSections.cpp | 90 ++++++++++++++++++++++++++++------- lld/ELF/SyntheticSections.h | 6 +++ 5 files changed, 85 insertions(+), 18 deletions(-) diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 28726d48e4284..908453857d1d7 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -241,6 +241,7 @@ struct Config { bool dependentLibraries; bool disableVerify; bool ehFrameHdr; + bool ehFrameHdr64 = false; bool emitLLVM; bool emitRelocs; bool enableNewDtags; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index c06308008a411..0b851ce4b23d3 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1281,6 +1281,8 @@ static void readConfigs(opt::InputArgList &args) { config->dynamicLinker = getDynamicLinker(args); config->ehFrameHdr = args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false); + config->ehFrameHdr64 = + args.hasFlag(OPT_eh_frame_hdr_64, OPT_no_eh_frame_hdr_64, false); config->emitLLVM = args.hasArg(OPT_lto_emit_llvm); config->emitRelocs = args.hasArg(OPT_emit_relocs); config->enableNewDtags = diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index a40c12a72afb1..27c66101a31d5 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -193,6 +193,10 @@ defm eh_frame_hdr: B<"eh-frame-hdr", "Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header", "Do not create .eh_frame_hdr section">; +defm eh_frame_hdr_64: BB<"eh-frame-hdr-64", + "Use 64-bit entries in .eh_frame_hdr", + "Use 32-bit entries in .eh_frame_hdr (default)">; + def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">; def enable_new_dtags: F<"enable-new-dtags">, diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 41053c6472751..fe269b1217151 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -586,6 +586,35 @@ SmallVector EhFrameSection::getFdeData() const { return ret; } +// Returns data for .eh_frame_hdr with 64-bit entries. Similar to getFdeData() +// but without the 32-bit overflow check. +SmallVector EhFrameSection::getFdeData64() const { + uint8_t *buf = Out::bufferStart + getParent()->offset + outSecOff; + SmallVector ret; + + uint64_t va = getPartition().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; + ret.push_back({pc - va, fdeVA - va}); + } + } + + // Sort the FDE list by their PC and uniqueify. + auto less = [](const FdeData64 &a, const FdeData64 &b) { + return a.pcRel < b.pcRel; + }; + llvm::stable_sort(ret, less); + auto eq = [](const FdeData64 &a, const FdeData64 &b) { + return a.pcRel == b.pcRel; + }; + ret.erase(std::unique(ret.begin(), ret.end(), eq), ret.end()); + + return ret; +} + static uint64_t readFdeAddr(uint8_t *buf, int size) { switch (size) { case DW_EH_PE_udata2: @@ -3632,28 +3661,53 @@ void EhFrameHeader::writeTo(uint8_t *buf) { // It is sorted by PC. void EhFrameHeader::write() { uint8_t *buf = Out::bufferStart + getParent()->offset + outSecOff; - using FdeData = EhFrameSection::FdeData; - SmallVector fdes = getPartition().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(buf + 4, - getPartition().ehFrame->getParent()->addr - this->getVA() - 4); - write32(buf + 8, fdes.size()); - buf += 12; - - for (FdeData &fde : fdes) { - write32(buf, fde.pcRel); - write32(buf + 4, fde.fdeVARel); - buf += 8; + + if (!config->ehFrameHdr64) { + using FdeData = EhFrameSection::FdeData; + SmallVector fdes = getPartition().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(buf + 4, + getPartition().ehFrame->getParent()->addr - this->getVA() - 4); + write32(buf + 8, fdes.size()); + buf += 12; // 4-byte header + 4-byte eh_frame_ptr + 4-byte fde_count + + for (FdeData &fde : fdes) { + write32(buf, fde.pcRel); + write32(buf + 4, fde.fdeVARel); + buf += 8; // 4-byte pc + 4-byte fde_va + } + } else { + using FdeData64 = EhFrameSection::FdeData64; + SmallVector fdes = getPartition().ehFrame->getFdeData64(); + + buf[0] = 1; + buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata8; + buf[2] = DW_EH_PE_udata8; + buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata8; + write64(buf + 4, + getPartition().ehFrame->getParent()->addr - this->getVA() - 4); + write64(buf + 12, fdes.size()); + buf += 20; // 4-byte header + 8-byte eh_frame_ptr + 8-byte fde_count + + for (FdeData64 &fde : fdes) { + write64(buf, fde.pcRel); + write64(buf + 8, fde.fdeVARel); + buf += 16; // 8-byte pc + 8-byte fde_va + } } } size_t EhFrameHeader::getSize() const { - // .eh_frame_hdr has a 12 bytes header followed by an array of FDEs. - return 12 + getPartition().ehFrame->numFdes * 8; + size_t numFdes = getPartition().ehFrame->numFdes; + // 32-bit: 12-byte header + 8 bytes per entry + // 64-bit: 20-byte header + 16 bytes per entry + if (config->ehFrameHdr64) + return 20 + numFdes * 16; + return 12 + numFdes * 8; } bool EhFrameHeader::isNeeded() const { diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index d4169e1e1acaf..47e46f33aecd1 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -68,7 +68,13 @@ class EhFrameSection final : public SyntheticSection { uint32_t fdeVARel; }; + struct FdeData64 { + uint64_t pcRel; + uint64_t fdeVARel; + }; + SmallVector getFdeData() const; + SmallVector getFdeData64() const; ArrayRef getCieRecords() const { return cieRecords; } template void iterateFDEWithLSDA(llvm::function_ref fn);