Index: ELF/EhFrame.h =================================================================== --- ELF/EhFrame.h +++ ELF/EhFrame.h @@ -19,6 +19,8 @@ template size_t readEhRecordSize(InputSectionBase *S, size_t Off); template uint8_t getFdeEncoding(EhSectionPiece *P); +template +bool isFdeLive(EhSectionPiece &P, 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" @@ -201,6 +202,24 @@ return DW_EH_PE_absptr; } +// There is one FDE per function. Returns true if a given FDE +// points to a live function. +template +bool elf::isFdeLive(EhSectionPiece &Piece, ArrayRef Rels) { + auto *Sec = cast(Piece.ID); + unsigned FirstRelI = Piece.FirstRelocation; + if (FirstRelI == (unsigned)-1) + return false; + const RelTy &Rel = Rels[FirstRelI]; + SymbolBody &B = Sec->template getFile()->getRelocTargetSym(Rel); + auto *D = dyn_cast(&B); + if (!D || !D->Section) + return false; + auto *Target = + cast(cast(D->Section)->Repl); + return Target && Target->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); @@ -210,3 +229,20 @@ template uint8_t elf::getFdeEncoding(EhSectionPiece *P); template uint8_t elf::getFdeEncoding(EhSectionPiece *P); template uint8_t elf::getFdeEncoding(EhSectionPiece *P); + +template bool elf::isFdeLive(EhSectionPiece &Piece, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Piece, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Piece, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Piece, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Piece, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Piece, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Piece, + ArrayRef Rels); +template bool elf::isFdeLive(EhSectionPiece &Piece, + 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" @@ -108,45 +109,72 @@ // * 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. +// alive if the section it refers to is alive. We mark here such FDEs, +// as well as their dependent LSDAs, CIEs and personality function(s). template static void scanEhFrameSection(EhInputSection &EH, ArrayRef Rels, std::function Fn) { const endianness E = ELFT::TargetEndianness; + DenseMap OffsetToCie; 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); + + // The empty record is the end marker. + if (Piece.size() == 4) + return; + + size_t Offset = Piece.InputOff; + uint32_t ID = read32(Piece.data().data() + 4); + if (ID == 0) { + OffsetToCie[Offset] = &Piece; continue; } - // This is a FDE. The relocations point to the described function or to + + if (Piece.Live // This FDE is already fully processed. + || !elf::isFdeLive(Piece, Rels)) + continue; + + // The FDE should be kept. + Piece.Live = true; + + // In 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) { + for (unsigned I2 = Piece.FirstRelocation + 1, N2 = Rels.size(); I2 < N2; + ++I2) { const RelTy &Rel = Rels[I2]; if (Rel.r_offset >= PieceEnd) break; - resolveReloc(EH, Rels[I2], + resolveReloc(EH, Rel, [&](InputSectionBase *Sec, uint64_t Offset) { if (Sec && Sec != &InputSection::Discarded && !(Sec->Flags & SHF_EXECINSTR)) Fn(Sec, 0); }); } + + // Process the corresponding CIE + uint32_t CieOffset = Offset + 4 - ID; + EhSectionPiece *Cie = OffsetToCie[CieOffset]; + if (!Cie) + fatal(toString(&EH) + ": invalid CIE reference"); + + if (Cie->Live) + // Already processed. + continue; + + Cie->Live = true; + + unsigned CieFirstRelI = Cie->FirstRelocation; + if (CieFirstRelI == (unsigned)-1) + continue; + + // In a CIE, we only need to worry about the first relocation. + // It is known to point to the personality function. + resolveReloc(EH, Rels[CieFirstRelI], Fn); } } @@ -247,14 +275,15 @@ if (S->includeInDynsym()) MarkSymbol(S->body()); + std::vector EhInputSections; + // 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. + // Postpone scanning .eh_frame until we find all the secions reachable + // from the roots. if (auto *EH = dyn_cast_or_null(Sec)) - scanEhFrameSection(*EH, Enqueue); + EhInputSections.push_back(EH); if (Sec->Flags & SHF_LINK_ORDER) continue; if (isReserved(Sec) || Script->shouldKeep(Sec)) @@ -266,8 +295,12 @@ } // Mark all reachable sections. - while (!Q.empty()) - forEachSuccessor(*Q.pop_back_val(), Enqueue); + while (!Q.empty()) { + while (!Q.empty()) + forEachSuccessor(*Q.pop_back_val(), Enqueue); + for (EhInputSection *EH : EhInputSections) + scanEhFrameSection(*EH, Enqueue); + } } template void elf::markLive(); Index: ELF/SyntheticSections.h =================================================================== --- ELF/SyntheticSections.h +++ ELF/SyntheticSections.h @@ -95,9 +95,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 Cies; 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" @@ -438,26 +439,6 @@ return Cie; } -// There is one FDE per function. Returns true if a given FDE -// points to a live function. -template -template -bool EhFrameSection::isFdeLive(EhSectionPiece &Piece, - ArrayRef Rels) { - auto *Sec = cast(Piece.ID); - unsigned FirstRelI = Piece.FirstRelocation; - if (FirstRelI == (unsigned)-1) - return false; - const RelTy &Rel = Rels[FirstRelI]; - SymbolBody &B = Sec->template getFile()->getRelocTargetSym(Rel); - auto *D = dyn_cast(&B); - if (!D || !D->Section) - return false; - auto *Target = - cast(cast(D->Section)->Repl); - return Target && Target->Live; -} - // .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 @@ -474,6 +455,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) { @@ -486,7 +470,7 @@ if (!Cie) fatal(toString(Sec) + ": invalid CIE reference"); - if (!isFdeLive(Piece, Rels)) + if (!Config->GcSections && !elf::isFdeLive(Piece, Rels)) continue; Cie->FdePieces.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: