Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -149,6 +149,12 @@ llvm::DenseSet LiveOffsets; }; +struct EhSectionPiece : public SectionPiece { + EhSectionPiece(size_t Off, ArrayRef Data, unsigned FirstRelocation) + : SectionPiece(Off, Data), FirstRelocation(FirstRelocation) {} + unsigned FirstRelocation; +}; + // This corresponds to a .eh_frame section of an input file. template class EhInputSection : public InputSectionBase { public: @@ -157,10 +163,11 @@ EhInputSection(ObjectFile *F, const Elf_Shdr *Header); static bool classof(const InputSectionBase *S); void split(); + template void split(ArrayRef Rels); // Splittable sections are handled as a sequence of data // rather than a single large blob of data. - std::vector Pieces; + std::vector Pieces; // Relocation section that refer to this one. const Elf_Shdr *RelocSection = nullptr; Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -443,14 +443,53 @@ return S->SectionKind == InputSectionBase::EHFrame; } +// Returns the index of the first relocation that points to a region between +// Begin and Begin+Size. +template +static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef &Rels, + unsigned &RelocI) { + // Start search from RelocI for fast access. That works because the + // relocations are sorted in .eh_frame. + for (unsigned N = Rels.size(); RelocI < N; ++RelocI) { + const RelTy &Rel = Rels[RelocI]; + if (Rel.r_offset < Begin) + continue; + + if (Rel.r_offset < Begin + Size) + return RelocI; + return -1; + } + return -1; +} + // .eh_frame is a sequence of CIE or FDE records. // This function splits an input section into records and returns them. template void EhInputSection::split() { + // Early exit if already split. + if (!this->Pieces.empty()) + return; + + if (RelocSection) { + ELFFile &Obj = this->File->getObj(); + if (RelocSection->sh_type == SHT_RELA) + split(Obj.relas(RelocSection)); + else + split(Obj.rels(RelocSection)); + return; + } + split(makeArrayRef(nullptr, nullptr)); +} + +template +template +void EhInputSection::split(ArrayRef Rels) { ArrayRef Data = this->getSectionData(); + unsigned RelI = 0; for (size_t Off = 0, End = Data.size(); Off != End;) { size_t Size = readEhRecordSize(Data.slice(Off)); - this->Pieces.emplace_back(Off, Data.slice(Off, Size)); + this->Pieces.emplace_back(Off, Data.slice(Off, Size), + getReloc(Off, Size, Rels, RelI)); // The empty record is the end marker. if (Size == 4) break; Index: ELF/MarkLive.cpp =================================================================== --- ELF/MarkLive.cpp +++ ELF/MarkLive.cpp @@ -36,6 +36,7 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; +using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; @@ -95,19 +96,70 @@ run(Obj, Sec, RelSec, Fn); } +// The .eh_frame section is an unfortunate special case. +// The section is divided in CIEs and FDEs and the relocations it can have are +// * CIEs can refer to a personality function. +// * FDEs can refer to a LSDA +// * FDEs refer to the function they contain information about +// The last kind of relocation cannot keep the referred section alive, or they +// would keep everything alive in a common object file. In fact, each FDE is +// alive if the section it refers to is alive. +// To keep things simple, in here we just ignore the last relocation kind. The +// other two keep the referred section alive. +// +// A possible improvement would be to fully process .eh_frame in the middle of +// the gc pass. With that we would be able to also gc some sections holding +// LSDAs and personality functions if we found that they were unused. +template +static void +scanEhFrameSection(EhInputSection &EH, ArrayRef Rels, + std::function)> Enqueue) { + const endianness E = ELFT::TargetEndianness; + for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) { + EhSectionPiece &Piece = EH.Pieces[I]; + unsigned FirstRelI = Piece.FirstRelocation; + if (FirstRelI == (unsigned)-1) + continue; + if (read32(Piece.data().data() + 4) == 0) { + // This is a CIE, we only need to worry about the first relocation. It is + // known to point to the personality function. + Enqueue(resolveReloc(EH, Rels[FirstRelI])); + continue; + } + // This is a FDE. The relocations point to the described function or to + // a LSDA. We only need to keep the LSDA alive, so ignore anything that + // points to executable sections. + typename ELFT::uint PieceEnd = Piece.InputOff + Piece.size(); + for (unsigned I2 = FirstRelI, N2 = Rels.size(); I2 < N2; ++I2) { + const RelTy &Rel = Rels[I2]; + if (Rel.r_offset >= PieceEnd) + break; + ResolvedReloc R = resolveReloc(EH, Rels[I2]); + if (!R.Sec || R.Sec == &InputSection::Discarded) + continue; + if (R.Sec->getSectionHdr()->sh_flags & SHF_EXECINSTR) + continue; + Enqueue({R.Sec, 0}); + } + } +} + template -static void scanEhFrameSection(EhInputSection &EH, - std::function)> Fn) { +static void +scanEhFrameSection(EhInputSection &EH, + std::function)> Enqueue) { if (!EH.RelocSection) return; + + // Unfortunately we need to split .eh_frame early since some relocations in + // .eh_frame keep other section alive and some don't. + EH.split(); + ELFFile &EObj = EH.getFile()->getObj(); - run(EObj, EH, EH.RelocSection, [&](ResolvedReloc R) { - if (!R.Sec || R.Sec == &InputSection::Discarded) - return; - if (R.Sec->getSectionHdr()->sh_flags & SHF_EXECINSTR) - return; - Fn({R.Sec, 0}); - }); + if (EH.RelocSection->sh_type == SHT_RELA) + scanEhFrameSection(EH, EObj.relas(EH.RelocSection), Enqueue); + else + scanEhFrameSection(EH, EObj.rels(EH.RelocSection), Enqueue); } // Sections listed below are special because they are used by the loader Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -24,7 +24,7 @@ namespace elf { class SymbolBody; -struct SectionPiece; +struct EhSectionPiece; template class SymbolTable; template class SymbolTableSection; template class StringTableSection; @@ -372,8 +372,8 @@ }; struct CieRecord { - SectionPiece *Piece = nullptr; - std::vector FdePieces; + EhSectionPiece *Piece = nullptr; + std::vector FdePieces; }; // Output section for .eh_frame. @@ -399,12 +399,12 @@ void addSectionAux(EhInputSection *S, llvm::ArrayRef Rels); template - CieRecord *addCie(SectionPiece &Piece, EhInputSection *Sec, - ArrayRef &Rels); + CieRecord *addCie(EhSectionPiece &Piece, EhInputSection *Sec, + ArrayRef Rels); template - bool isFdeLive(SectionPiece &Piece, EhInputSection *Sec, - ArrayRef &Rels); + bool isFdeLive(EhSectionPiece &Piece, EhInputSection *Sec, + ArrayRef Rels); uintX_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc); Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -963,41 +963,22 @@ EhOutputSection::EhOutputSection() : OutputSectionBase(".eh_frame", SHT_PROGBITS, SHF_ALLOC) {} -// Returns the first relocation that points to a region -// between Begin and Begin+Size. -template -static const RelTy *getReloc(IntTy Begin, IntTy Size, ArrayRef &Rels) { - for (auto I = Rels.begin(), E = Rels.end(); I != E; ++I) { - if (I->r_offset < Begin) - continue; - - // Truncate Rels for fast access. That means we expect that the - // relocations are sorted and we are looking up symbols in - // sequential order. It is naturally satisfied for .eh_frame. - Rels = Rels.slice(I - Rels.begin()); - if (I->r_offset < Begin + Size) - return I; - return nullptr; - } - Rels = ArrayRef(); - return nullptr; -} - // Search for an existing CIE record or create a new one. // CIE records from input object files are uniquified by their contents // and where their relocations point to. template template -CieRecord *EhOutputSection::addCie(SectionPiece &Piece, +CieRecord *EhOutputSection::addCie(EhSectionPiece &Piece, EhInputSection *Sec, - ArrayRef &Rels) { + ArrayRef Rels) { const endianness E = ELFT::TargetEndianness; if (read32(Piece.data().data() + 4) != 0) fatal("CIE expected at beginning of .eh_frame: " + Sec->getSectionName()); SymbolBody *Personality = nullptr; - if (const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels)) - Personality = &Sec->getFile()->getRelocTargetSym(*Rel); + unsigned FirstRelI = Piece.FirstRelocation; + if (FirstRelI != (unsigned)-1) + Personality = &Sec->getFile()->getRelocTargetSym(Rels[FirstRelI]); // Search for an existing CIE by CIE contents/relocation target pair. CieRecord *Cie = &CieMap[{Piece.data(), Personality}]; @@ -1014,13 +995,14 @@ // points to a live function. template template -bool EhOutputSection::isFdeLive(SectionPiece &Piece, +bool EhOutputSection::isFdeLive(EhSectionPiece &Piece, EhInputSection *Sec, - ArrayRef &Rels) { - const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels); - if (!Rel) + ArrayRef Rels) { + unsigned FirstRelI = Piece.FirstRelocation; + if (FirstRelI == (unsigned)-1) fatal("FDE doesn't reference another section"); - SymbolBody &B = Sec->getFile()->getRelocTargetSym(*Rel); + const RelTy &Rel = Rels[FirstRelI]; + SymbolBody &B = Sec->getFile()->getRelocTargetSym(Rel); auto *D = dyn_cast>(&B); if (!D || !D->Section) return false; @@ -1039,7 +1021,7 @@ const endianness E = ELFT::TargetEndianness; DenseMap OffsetToCie; - for (SectionPiece &Piece : Sec->Pieces) { + for (EhSectionPiece &Piece : Sec->Pieces) { // The empty record is the end marker. if (Piece.size() == 4) return; Index: test/ELF/eh-frame-gc2.s =================================================================== --- /dev/null +++ test/ELF/eh-frame-gc2.s @@ -0,0 +1,15 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o +// RUN: ld.lld --gc-sections %t.o -o %t +// RUN: llvm-readobj -s %t | FileCheck %s + +// Test that the we don't gc the personality function. +// CHECK: Name: .foobar + + .globl _start +_start: + .cfi_startproc + .cfi_personality 3, foobar + .cfi_endproc + .section .foobar,"ax" +foobar: