Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -292,14 +292,15 @@ switch (Sec.sh_type) { case SHT_GROUP: Sections[I] = &InputSection::Discarded; - if (ComdatGroups.insert(CachedHashStringRef( - getShtGroupSignature(ObjSections, Sec))) + 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)); + fatal(toString(this) + + ": invalid section index in group: " + Twine(SecIndex)); Sections[SecIndex] = &InputSection::Discarded; } break; @@ -326,6 +327,27 @@ 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; + + 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; + 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. @@ -160,6 +168,10 @@ // 1) Sections used by the loader (.init, .fini, .ctors, .dtors or .jcr) // 2) Non-allocatable sections which typically contain debugging information template static bool isReserved(InputSectionBase *Sec) { + // Sections that are part of a group follow the usual GC rules. + if (Sec->GroupSections.size() > 0) + return false; + switch (Sec->Type) { case SHT_FINI_ARRAY: case SHT_INIT_ARRAY: Index: test/ELF/comdat-gc.s =================================================================== --- /dev/null +++ test/ELF/comdat-gc.s @@ -0,0 +1,38 @@ +// 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 + + .section .ctors,"aGw",@progbits,z3,comdat +z3: + .quad 0 +// No special treatment if the section is in a section group, even for magic +// sections like .ctors. +// SEC-NOT: .ctors