Index: ELF/EhFrame.h =================================================================== --- ELF/EhFrame.h +++ ELF/EhFrame.h @@ -19,6 +19,11 @@ template size_t readEhRecordSize(InputSectionBase *S, size_t Off); template uint8_t getFdeEncoding(EhSectionPiece *P); +template +InputSectionBase *getDescribedSection(EhSectionPiece &Fde, + ArrayRef Rels); +template +bool isFdeLive(EhSectionPiece &Fde, ArrayRef Rels); } // namespace elf } // namespace lld Index: ELF/EhFrame.cpp =================================================================== --- ELF/EhFrame.cpp +++ ELF/EhFrame.cpp @@ -18,6 +18,7 @@ #include "EhFrame.h" #include "Error.h" +#include "InputFiles.h" #include "InputSection.h" #include "Relocations.h" #include "Strings.h" @@ -200,6 +201,37 @@ return DW_EH_PE_absptr; } +// There is one FDE per function. Returns true if a given FDE +// points to a live function. +template +InputSectionBase *elf::getDescribedSection(EhSectionPiece &Fde, + ArrayRef Rels) { + auto *Sec = cast(Fde.Sec); + unsigned FirstRelI = Fde.FirstRelocation; + + // An FDE should point to some function because FDEs are to describe + // functions. That's however not always the case due to an issue of + // ld.gold with -r. ld.gold may discard only functions and leave their + // corresponding FDEs, which results in creating bad .eh_frame sections. + // To deal with that, we ignore such FDEs. + if (FirstRelI == (unsigned)-1) + return nullptr; + + const RelTy &Rel = Rels[FirstRelI]; + SymbolBody &B = Sec->template getFile()->getRelocTargetSym(Rel); + if (auto *D = dyn_cast(&B)) + if (D->Section) + return cast(D->Section)->Repl; + return nullptr; +} + +// There is one FDE per function. Returns true if a given FDE +// points to a live function. +template +bool elf::isFdeLive(EhSectionPiece &Fde, ArrayRef Rels) { + InputSectionBase *Sec = getDescribedSection(Fde, Rels); + return Sec && Sec->Live; +} template size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off); template size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off); template size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off); @@ -209,3 +241,45 @@ template uint8_t elf::getFdeEncoding(EhSectionPiece *P); template uint8_t elf::getFdeEncoding(EhSectionPiece *P); template uint8_t elf::getFdeEncoding(EhSectionPiece *P); + +template InputSectionBase * +elf::getDescribedSection(EhSectionPiece &Fde, + ArrayRef Rels); +template InputSectionBase * +elf::getDescribedSection(EhSectionPiece &Fde, + ArrayRef Rels); +template InputSectionBase * +elf::getDescribedSection(EhSectionPiece &Fde, + ArrayRef Rels); +template InputSectionBase * +elf::getDescribedSection(EhSectionPiece &Fde, + ArrayRef Rels); +template InputSectionBase * +elf::getDescribedSection(EhSectionPiece &Fde, + ArrayRef Rels); +template InputSectionBase * +elf::getDescribedSection(EhSectionPiece &Fde, + ArrayRef Rels); +template InputSectionBase * +elf::getDescribedSection(EhSectionPiece &Fde, + ArrayRef Rels); +template InputSectionBase * +elf::getDescribedSection(EhSectionPiece &Fde, + ArrayRef Rels); + +template bool elf::isFdeLive(EhSectionPiece &Fde, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Fde, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Fde, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Fde, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Fde, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Fde, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Fde, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Fde, + ArrayRef Rels); Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -264,14 +264,16 @@ struct EhSectionPiece { EhSectionPiece(size_t Off, InputSectionBase *Sec, uint32_t Size, unsigned FirstRelocation) - : InputOff(Off), Sec(Sec), Size(Size), FirstRelocation(FirstRelocation) {} + : InputOff(Off), Sec(Sec), Size(Size), Live(!Config->GcSections), + FirstRelocation(FirstRelocation) {} ArrayRef data() { return {Sec->Data.data() + this->InputOff, Size}; } size_t InputOff; ssize_t OutputOff = -1; InputSectionBase *Sec; - uint32_t Size; + uint32_t Size : 31; + uint32_t Live : 1; unsigned FirstRelocation; }; @@ -327,6 +329,8 @@ // Called by ICF to merge two input sections. void replace(InputSection *Other); + std::vector EhSectionPieces; + private: template void copyRelocations(uint8_t *Buf, llvm::ArrayRef Rels); Index: ELF/MarkLive.cpp =================================================================== --- ELF/MarkLive.cpp +++ ELF/MarkLive.cpp @@ -20,6 +20,7 @@ // //===----------------------------------------------------------------------===// +#include "EhFrame.h" #include "InputSection.h" #include "LinkerScript.h" #include "Memory.h" @@ -42,6 +43,16 @@ using namespace lld; using namespace lld::elf; +namespace { +class EhFrameSectionCies { + std::vector Cies; + +public: + void add(EhSectionPiece *Cie); + EhSectionPiece *get(const EhSectionPiece &Fde, uint32_t ID) const; +}; +} + template static typename ELFT::uint getAddend(InputSectionBase &Sec, const typename ELFT::Rel &Rel) { @@ -101,59 +112,110 @@ Fn(IS, 0); } +void EhFrameSectionCies::add(EhSectionPiece *Cie) { + assert(Cies.empty() || Cies.back()->InputOff < Cie->InputOff); + Cies.push_back(Cie); +} + +EhSectionPiece *EhFrameSectionCies::get(const EhSectionPiece &Fde, + uint32_t ID) const { + if (Cies.empty()) + return nullptr; + + uint32_t CieOffset = Fde.InputOff + 4 - ID; + + // In most cases, a FDE refers to the most recent CIE. + if (Cies.back()->InputOff == CieOffset) + return Cies.back(); + + auto It = std::lower_bound(Cies.begin(), Cies.end(), CieOffset, + [](EhSectionPiece *Cie, uint32_t Offset) { + return Cie->InputOff < Offset; + }); + if (It != Cies.end() && (*It)->InputOff == CieOffset) + return *It; + + return nullptr; +} + +template +static void scanFde(EhInputSection &EH, ArrayRef Rels, InputSection *Sec, + const EhSectionPiece &Fde) { + const uint64_t FdeEnd = Fde.InputOff + Fde.Size; + // The first relocation is known to point to the described function, + // so it is safe to skip it when looking for LSDAs. + for (unsigned I = Fde.FirstRelocation + 1, N = Rels.size(); + I < N && Rels[I].r_offset < FdeEnd; ++I) + // In a FDE, the relocations point to the described function or to a LSDA. + // The described function is already marked and calling Fn for it is safe. + // Although the main intent here is to mark LSDAs, we do not need to + // recognize them especially and can process all references the same way. + resolveReloc(EH, Rels[I], [&](InputSectionBase *S, uint64_t) { + if (S && S != &InputSection::Discarded && !(S->Flags & SHF_EXECINSTR)) + Sec->DependentSections.push_back(S); + }); +} + +template +static void scanCie(EhInputSection &EH, ArrayRef Rels, InputSection *Sec, + const EhSectionPiece &Cie) { + if (Cie.FirstRelocation == (unsigned)-1) + return; + + // In a CIE, we only need to worry about the first relocation. + // It is known to point to the personality function. + resolveReloc(EH, Rels[Cie.FirstRelocation], + [&](InputSectionBase *S, uint64_t) { + Sec->DependentSections.push_back(S); + }); +} + // The .eh_frame section is an unfortunate special case. -// The section is divided in CIEs and FDEs and the relocations it can have are +// The section is divided in CIEs (Common Information Entries) and FDEs +// (Frame Description Entries). Each FDE references to a corresponding +// CIE via ID field, where a relative offset is stored. +// The relocations the section 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. +// * FDEs can refer to a LSDA. +// * FDEs refer to the function they contain information about. +// The last kind of relocation is used to keep a FDE alive if the section +// it refers to is alive. In that case, everything which is referenced from +// that FDE should be marked, including a LSDA, a CIE and a personality +// function. These sections might reference to other sections, so they +// are added to the GC queue. template -static void -scanEhFrameSection(EhInputSection &EH, ArrayRef Rels, - std::function Fn) { +static void scanEhFrameSection(EhInputSection &EH, ArrayRef Rels) { 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. - resolveReloc(EH, Rels[FirstRelI], Fn); + EhFrameSectionCies Cies; + for (EhSectionPiece &Piece : EH.Pieces) { + // The empty record is the end marker. + if (Piece.Size == 4) + return; + + const uint32_t ID = read32(Piece.data().data() + 4); + if (ID == 0) { + Cies.add(&Piece); 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; - resolveReloc(EH, Rels[I2], - [&](InputSectionBase *Sec, uint64_t Offset) { - if (Sec && Sec != &InputSection::Discarded && - !(Sec->Flags & SHF_EXECINSTR)) - Fn(Sec, 0); - }); - } + + InputSection *Sec = dyn_cast_or_null( + elf::getDescribedSection(Piece, Rels)); + if (!Sec || Sec == &InputSection::Discarded) + continue; + Sec->EhSectionPieces.push_back(&Piece); + scanFde(EH, Rels, Sec, Piece); + + // Find the corresponding CIE + EhSectionPiece *Cie = Cies.get(Piece, ID); + if (!Cie) + fatal(toString(&EH) + ": invalid CIE reference"); + Sec->EhSectionPieces.push_back(Cie); + scanCie(EH, Rels, Sec, *Cie); } } -template -static void -scanEhFrameSection(EhInputSection &EH, - std::function Fn) { +template static void scanEhFrameSection(EhInputSection &EH) { if (!EH.NumRelocations) return; @@ -162,9 +224,9 @@ EH.split(); if (EH.AreRelocsRela) - scanEhFrameSection(EH, EH.template relas(), Fn); + scanEhFrameSection(EH, EH.template relas()); else - scanEhFrameSection(EH, EH.template rels(), Fn); + scanEhFrameSection(EH, EH.template rels()); } // We do not garbage-collect two types of sections: @@ -218,8 +280,11 @@ Sec->Live = true; // Add input section to the queue. - if (InputSection *S = dyn_cast(Sec)) + if (InputSection *S = dyn_cast(Sec)) { Q.push_back(S); + for (EhSectionPiece *Piece : S->EhSectionPieces) + Piece->Live = true; + } }; auto MarkSymbol = [&](SymbolBody *Sym) { @@ -232,6 +297,11 @@ S->Live = true; }; + for (InputSectionBase *Sec : InputSections) { + if (auto *EH = dyn_cast_or_null(Sec)) + scanEhFrameSection(*EH); + } + // Add GC root symbols. MarkSymbol(Symtab->find(Config->Entry)); MarkSymbol(Symtab->find(Config->Init)); @@ -250,11 +320,6 @@ // Preserve special sections and those which are specified in linker // script KEEP command. for (InputSectionBase *Sec : InputSections) { - // .eh_frame is always marked as live now, but also it can reference to - // sections that contain personality. We preserve all non-text sections - // referred by .eh_frame here. - if (auto *EH = dyn_cast_or_null(Sec)) - scanEhFrameSection(*EH, Enqueue); if (Sec->Flags & SHF_LINK_ORDER) continue; if (isReserved(Sec) || Script->shouldKeep(Sec)) Index: ELF/SyntheticSections.h =================================================================== --- ELF/SyntheticSections.h +++ ELF/SyntheticSections.h @@ -93,9 +93,6 @@ template CieRecord *addCie(EhSectionPiece &Piece, ArrayRef Rels); - template - bool isFdeLive(EhSectionPiece &Piece, ArrayRef Rels); - uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc); std::vector CieRecords; Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -16,6 +16,7 @@ #include "SyntheticSections.h" #include "Config.h" +#include "EhFrame.h" #include "Error.h" #include "InputFiles.h" #include "LinkerScript.h" @@ -428,31 +429,6 @@ return Rec; } -// There is one FDE per function. Returns true if a given FDE -// points to a live function. -template -template -bool EhFrameSection::isFdeLive(EhSectionPiece &Fde, - ArrayRef Rels) { - auto *Sec = cast(Fde.Sec); - unsigned FirstRelI = Fde.FirstRelocation; - - // An FDE should point to some function because FDEs are to describe - // functions. That's however not always the case due to an issue of - // ld.gold with -r. ld.gold may discard only functions and leave their - // corresponding FDEs, which results in creating bad .eh_frame sections. - // To deal with that, we ignore such FDEs. - if (FirstRelI == (unsigned)-1) - return false; - - const RelTy &Rel = Rels[FirstRelI]; - SymbolBody &B = Sec->template getFile()->getRelocTargetSym(Rel); - if (auto *D = dyn_cast(&B)) - if (D->Section) - return cast(D->Section)->Repl->Live; - return false; -} - // .eh_frame is a sequence of CIE or FDE records. In general, there // is one CIE record per input object file which is followed by // a list of FDEs. This function searches an existing CIE or create a new @@ -469,6 +445,9 @@ if (Piece.Size == 4) return; + if (!Piece.Live) + continue; + size_t Offset = Piece.InputOff; uint32_t ID = read32(Piece.data().data() + 4); if (ID == 0) { @@ -481,7 +460,7 @@ if (!Rec) fatal(toString(Sec) + ": invalid CIE reference"); - if (!isFdeLive(Piece, Rels)) + if (!Config->GcSections && !elf::isFdeLive(Piece, Rels)) continue; Rec->Fdes.push_back(&Piece); NumFdes++; Index: test/ELF/eh-frame-gc3.s =================================================================== --- /dev/null +++ test/ELF/eh-frame-gc3.s @@ -0,0 +1,34 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o +// RUN: ld.lld %t.o --gc-sections -o %tout +// RUN: llvm-nm %tout | FileCheck %s + +// CHECK: _start +// CHECK-NOT: foo +// CHECK-NOT: lsda +// CHECK-NOT: personality + + .global _start, foo, lsda, personality + .text +_start: + .cfi_startproc + nop + .cfi_endproc + + .section ".text.foo","ax" +foo: + .cfi_startproc + .cfi_personality 0, personality + .cfi_lsda 0, .Lexception0 + nop + .cfi_endproc + + .section ".lsda","a" + .p2align 2 +.Lexception0: +lsda: + .byte 0 + + .section ".text.personality","ax" +personality: + nop Index: test/ELF/gc-sections-lsda.s =================================================================== --- test/ELF/gc-sections-lsda.s +++ test/ELF/gc-sections-lsda.s @@ -8,9 +8,14 @@ // we are consistent: if we keep .abc, we have to keep .foo // RUN: llvm-readobj -s %t | FileCheck %s -// CHECK: Name: .abc -// CHECK: Name: .foo +// CHECK: Name: .abc +// CHECK-NOT: Name: .abc2 +// CHECK: Name: .foo +// CHECK-NOT: Name: .foo2 + .section .text.func,"ax" + .global func +func: .cfi_startproc .cfi_lsda 0x1b,zed .cfi_endproc @@ -19,3 +24,16 @@ .long bar-. .section .foo,"ax" bar: + +// This section is similar to .text.func except that the symbol is local +// and should be eliminated as such with all dependent sections. + .section .text.func2,"ax" +func2: + .cfi_startproc + .cfi_lsda 0x1b,zed2 + .cfi_endproc + .section .abc2,"a" +zed2: + .long bar2-. + .section .foo2,"ax" +bar2: