Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -271,17 +271,7 @@ switch (Sec.sh_type) { case SHT_GROUP: - Sections[I] = &InputSection::Discarded; - if (ComdatGroups.insert(CachedHashStringRef( - getShtGroupSignature(ObjSections, Sec))) - .second) - continue; - for (uint32_t SecIndex : getShtGroupEntries(Sec)) { - if (SecIndex >= Size) - fatal(toString(this) + ": invalid section index in group: " + - Twine(SecIndex)); - Sections[SecIndex] = &InputSection::Discarded; - } + // Handled later. break; case SHT_SYMTAB: this->initSymtab(ObjSections, &Sec); @@ -306,6 +296,35 @@ IS->DependentSection = Sections[I]; } } + + // Handle SHT_GROUP by giving all members of the selected group a GC link back + // to the group section, and discarding the rest. This is done after the main + // loop because it needs group member sections to be initialized. + I = -1; + for (const Elf_Shdr &Sec : ObjSections) { + ++I; + if (Sec.sh_type != SHT_GROUP) + continue; + + Sections[I] = &InputSection::Discarded; + bool DiscardGroupMembers = !ComdatGroups + .insert(CachedHashStringRef( + getShtGroupSignature(ObjSections, Sec))) + .second; + + auto GroupSections = getShtGroupEntries(Sec); + for (uint32_t SecIndex : GroupSections) { + if (SecIndex >= Size) + fatal(toString(this) + + ": invalid section index in group: " + Twine(SecIndex)); + if (!Sections[SecIndex] || Sections[SecIndex] == &InputSection::Discarded) + continue; + if (DiscardGroupMembers) + Sections[SecIndex] = &InputSection::Discarded; + else + Sections[SecIndex]->GroupSections = GroupSections; + } + } } template Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -92,6 +92,8 @@ uint32_t Link; uint32_t Info; + ArrayRef GroupSections; + InputSectionBase() : InputSectionData(Regular, "", ArrayRef(), false), Repl(this) { NumRelocations = 0; Index: ELF/MarkLive.cpp =================================================================== --- ELF/MarkLive.cpp +++ ELF/MarkLive.cpp @@ -89,6 +89,14 @@ } if (Sec.DependentSection) Fn({Sec.DependentSection, 0}); + for (auto SecIdx : Sec.GroupSections) { + InputSectionBase *SameGroupSection = + Sec.getFile()->getSections()[SecIdx]; + if (SameGroupSection && + SameGroupSection != &InputSection::Discarded && + SameGroupSection != &Sec) + Fn({SameGroupSection, 0}); + } } // The .eh_frame section is an unfortunate special case. @@ -173,7 +181,7 @@ // We do not want to reclaim sections if they can be referred // by __start_* and __stop_* symbols. StringRef S = Sec->Name; - if (isValidCIdentifier(S)) + if (Sec->GroupSections.size() == 0 && isValidCIdentifier(S)) return true; return S.startswith(".ctors") || S.startswith(".dtors") || Index: test/ELF/comdat-gc.s =================================================================== --- /dev/null +++ test/ELF/comdat-gc.s @@ -0,0 +1,31 @@ +// Test that GC respects section groups. +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +// RUN: ld.lld -shared -gc-sections %t.o -o %t +// RUN: llvm-objdump -t %t | FileCheck --check-prefix=SYM %s +// RUN: llvm-objdump -section-headers %t | FileCheck --check-prefix=SEC %s + + .text + .section .bss.x,"aGw",@nobits,x,comdat + .globl x +x: + .quad 0 + .section .bss.y,"aGw",@nobits,x,comdat +y: + .quad 0 +// "x" is a GC root (external global in a +// shared library), "y" is in the same section group as "x" and therefore must +// not be discarded. +// SYM-DAG: .bss{{.*}} y{{$}} + + .section zzz_no_comdat,"aw",@nobits +z1: + .quad 0 +// A section with a name that is a valid C identifier, not part of any section +// group => GC root. +// SYM-DAG: zzz_no_comdat{{.*}} z1{{$}} + + .section zzz_comdat,"aGw",@nobits,z2,comdat +z2: + .quad 0 +// No special treatment if the section is in a section group. +// SEC-NOT: zzz_comdat