Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -807,12 +807,7 @@ EhInputSection::EhInputSection(ObjFile *F, const typename ELFT::Shdr *Header, StringRef Name) - : InputSectionBase(F, Header, Name, InputSectionBase::EHFrame) { - // Mark .eh_frame sections as live by default because there are - // usually no relocations that point to .eh_frames. Otherwise, - // the garbage collector would drop all .eh_frame sections. - this->Live = true; -} + : InputSectionBase(F, Header, Name, InputSectionBase::EHFrame) {} SyntheticSection *EhInputSection::getParent() const { return cast_or_null(Parent); Index: ELF/MarkLive.cpp =================================================================== --- ELF/MarkLive.cpp +++ ELF/MarkLive.cpp @@ -88,7 +88,7 @@ // Calls Fn for each section that Sec refers to via relocations. template static void -forEachSuccessor(InputSection &Sec, +forEachSuccessor(InputSectionBase &Sec, std::function Fn) { if (Sec.AreRelocsRela) { for (const typename ELFT::Rela &Rel : Sec.template relas()) @@ -102,72 +102,6 @@ Fn(IS, 0); } -// 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 Fn) { - 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); - 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); - }); - } - } -} - -template -static void -scanEhFrameSection(EhInputSection &EH, - std::function Fn) { - if (!EH.NumRelocations) - 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(); - - if (EH.AreRelocsRela) - scanEhFrameSection(EH, EH.template relas(), Fn); - else - scanEhFrameSection(EH, EH.template rels(), Fn); -} - // Some sections are used directly by the loader, so they should never be // garbage-collected. This function returns true if a given section is such // section. @@ -190,9 +124,18 @@ // Starting from GC-root sections, this function visits all reachable // sections to set their "Live" bits. template static void doGcSections() { - SmallVector Q; + SmallVector Q; CNamedSections.clear(); + DenseMap EhFrameMap; + for (InputSectionBase *Sec : InputSections) { + EhInputSection *EH = dyn_cast_or_null(Sec); + if (!EH || !EH->Link) + continue; + InputSectionBase *TextSec = Sec->getFile()->getSection(EH->Link); + EhFrameMap.insert({TextSec, EH}); + } + auto Enqueue = [&](InputSectionBase *Sec, uint64_t Offset) { // Skip over discarded sections. This in theory shouldn't happen, because // the ELF spec doesn't allow a relocation to point to a deduplicated @@ -212,9 +155,19 @@ return; Sec->Live = true; + // If text section is live we want to mark as live .eh_frame section it + // linked with. We also want to mark LDSA and pesonality routine and + // possible depenencies live, so adding .eh_frame to proccessing queue. + if (Sec->Flags & SHF_EXECINSTR) { + if (InputSectionBase *EH = EhFrameMap.lookup(Sec)) { + EH->Live = true; + Q.push_back(EH); + } + } + // Add input section to the queue. - if (InputSection *S = dyn_cast(Sec)) - Q.push_back(S); + if (isa(Sec) || isa(Sec)) + Q.push_back(Sec); }; auto MarkSymbol = [&](Symbol *Sym) { @@ -241,11 +194,15 @@ // 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. + // We expect .eh_frame sections are linked with correspondent .text sections + // and handle such case eariler. Though in real world it can be not true. We + // want to keep alive unlinked sections with all dependencies. That is not + // ideal as might revive dead code, but allows to avoid scaning of .eh_frame + // content and makes implementation simpler. if (auto *EH = dyn_cast_or_null(Sec)) - scanEhFrameSection(*EH, Enqueue); + if (!EH->Link) + Enqueue(EH, 0); + if (Sec->Flags & SHF_LINK_ORDER) continue; if (isReserved(Sec) || Script->shouldKeep(Sec)) Index: test/ELF/eh-frame-hdr-icf-fde.s =================================================================== --- test/ELF/eh-frame-hdr-icf-fde.s +++ test/ELF/eh-frame-hdr-icf-fde.s @@ -12,8 +12,10 @@ # OBJ-NEXT: Section {{.*}} .rela.eh_frame { # OBJ-NEXT: 0x20 R_X86_64_PC32 .text.f1 0x0 # OBJ-NEXT: 0x34 R_X86_64_PC32 .text.f1 0x2 -# OBJ-NEXT: 0x48 R_X86_64_PC32 .text.f2 0x0 -# OBJ-NEXT: 0x5C R_X86_64_PC32 .text.f2 0x2 +# OBJ-NEXT: } +# OBJ-NEXT: Section {{.*}} .rela.eh_frame { +# OBJ-NEXT: 0x20 R_X86_64_PC32 .text.f2 0x0 +# OBJ-NEXT: 0x34 R_X86_64_PC32 .text.f2 0x2 # OBJ-NEXT: } # OBJ-NEXT: ] Index: test/ELF/gc-sections-lsda.s =================================================================== --- test/ELF/gc-sections-lsda.s +++ test/ELF/gc-sections-lsda.s @@ -3,19 +3,41 @@ // RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux // RUN: ld.lld -shared --gc-sections %t.o -o %t -// Test that we handle .eh_frame keeping sections alive. We could be more -// precise and gc the entire contents of this file, but test that at least -// we are consistent: if we keep .abc, we have to keep .foo +// Test that we keep used LSDA and its references alive and at the same time +// unused LSDA is dropped and does not keep any sections it uses alive. // RUN: llvm-readobj -s %t | FileCheck %s -// CHECK: Name: .abc -// CHECK: Name: .foo +// CHECK-NOT: baz +// CHECK-NOT: qux +// CHECK: foo +// CHECK: bar +// CHECK-NOT: baz +// CHECK-NOT: qux - .cfi_startproc - .cfi_lsda 0x1b,zed - .cfi_endproc - .section .abc,"a" -zed: - .long bar-. - .section .foo,"ax" +.section .text.foo,"ax" +.globl _start +_start: + .cfi_startproc + .cfi_lsda 0x1b,foo + .cfi_endproc + +.section .foo,"a" +foo: + .long bar-. + +.section .bar,"ax" bar: + nop + +.section .text.baz,"ax" + .cfi_startproc + .cfi_lsda 0x1b,baz + .cfi_endproc + +.section .baz,"a" +baz: + .long qux-. + +.section .qux,"ax" +qux: + nop Index: test/ELF/relocatable-ehframe.s =================================================================== --- test/ELF/relocatable-ehframe.s +++ test/ELF/relocatable-ehframe.s @@ -22,11 +22,11 @@ # CHECK: Relocations [ # CHECK-NEXT: Section {{.*}} .rela.eh_frame { # CHECK-NEXT: 0x20 R_X86_64_PC32 foo 0x0 -# CHECK-NEXT: 0x34 R_X86_64_PC32 bar 0x0 -# CHECK-NEXT: 0x48 R_X86_64_PC32 dah 0x0 -# CHECK-NEXT: 0x78 R_X86_64_PC32 foo1 0x0 -# CHECK-NEXT: 0x8C R_X86_64_PC32 bar1 0x0 -# CHECK-NEXT: 0xA0 R_X86_64_PC32 dah1 0x0 +# CHECK-NEXT: 0x50 R_X86_64_PC32 bar 0x0 +# CHECK-NEXT: 0x80 R_X86_64_PC32 dah 0x0 +# CHECK-NEXT: 0xB0 R_X86_64_PC32 foo1 0x0 +# CHECK-NEXT: 0xE0 R_X86_64_PC32 bar1 0x0 +# CHECK-NEXT: 0x110 R_X86_64_PC32 dah1 0x0 # CHECK-NEXT: } # CHECK-NEXT: ]