diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -506,6 +506,8 @@ this->sectionStringTable = CHECK(obj.getSectionStringTable(objSections), this); + std::vector> selectedGroups; + for (size_t i = 0, e = objSections.size(); i < e; ++i) { if (this->sections[i] == &InputSection::discarded) continue; @@ -563,6 +565,9 @@ if (isNew) { if (config->relocatable) this->sections[i] = createInputSection(sec); + + // Save group entries for some post-processing. + selectedGroups.push_back(entries); continue; } @@ -609,6 +614,41 @@ " with SHF_LINK_ORDER should not refer a non-regular section: " + toString(linkSec)); } + + // The ELF specification states "If the linker decides to remove the section + // group, it must remove all members of the group." The annobin tool places + // SHT_NOTES sections into groups, relying on the linker garbage collection to + // remove them if the group is removed. As LLD does not garbage collect non + // SHF_ALLOC sections we make the notes dependent on the non-SHT_NOTE group + // sections so that we can garbage collect them. + + for (ArrayRef entries : selectedGroups) { + auto const &entriesSlice = entries.slice(1); + bool hasNote = std::any_of( + entriesSlice.begin(), entriesSlice.end(), [this](uint32_t secIndex) { + return this->sections[secIndex] && + this->sections[secIndex]->type == SHT_NOTE; + }); + + if (hasNote) { + // Use circular dependencies to represent the group semantic. + InputSectionBase *head = nullptr; + InputSectionBase *prev = nullptr; + for (uint32_t secIndex : entriesSlice) { + if (!this->sections[secIndex] || + this->sections[secIndex] == &InputSection::discarded) + continue; + if (prev) { + this->sections[secIndex]->dependentSections.push_back( + cast(prev)); + prev = this->sections[secIndex]; + } else { + head = prev = this->sections[secIndex]; + } + } + head->dependentSections.push_back(cast(prev)); + } + } } // For ARM only, to set the EF_ARM_ABI_FLOAT_SOFT or EF_ARM_ABI_FLOAT_HARD diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp --- a/lld/ELF/MarkLive.cpp +++ b/lld/ELF/MarkLive.cpp @@ -165,9 +165,10 @@ switch (sec->type) { case SHT_FINI_ARRAY: case SHT_INIT_ARRAY: - case SHT_NOTE: case SHT_PREINIT_ARRAY: return true; + case SHT_NOTE: + return sec->dependentSections.empty(); default: StringRef s = sec->name; return s.startswith(".ctors") || s.startswith(".dtors") || @@ -357,8 +358,9 @@ bool isAlloc = (sec->flags & SHF_ALLOC); bool isLinkOrder = (sec->flags & SHF_LINK_ORDER); bool isRel = (sec->type == SHT_REL || sec->type == SHT_RELA); + bool isDependent = !sec->dependentSections.empty(); - if (!isAlloc && !isLinkOrder && !isRel) + if (!isAlloc && !isLinkOrder && !isRel && !isDependent) sec->markLive(); } diff --git a/lld/test/ELF/sht-gc-section-group.test b/lld/test/ELF/sht-gc-section-group.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/sht-gc-section-group.test @@ -0,0 +1,73 @@ +# Check that notes attached to group are discarded / kept depending on the +# liveness of the other code sections in the group. + +# RUN: yaml2obj %s -o %t.o +# RUN: ld.lld %t.o -o %t0 --gc-sections +# RUN: llvm-readobj -S %t0 | FileCheck --check-prefix=CHECK-DEAD %s +# +# RUN: ld.lld %t.o -o %t1 --gc-sections --export-dynamic-symbol=foo +# RUN: llvm-readobj -S %t1 | FileCheck --check-prefix=CHECK-LIVE %s +# +# RUN: ld.lld %t.o -o %t2 --gc-sections --export-dynamic-symbol=foo.note +# RUN: llvm-readobj -S %t2 | FileCheck --check-prefix=CHECK-LIVE %s +# +# RUN: ld.lld %t.o -o %t3 --gc-sections --export-dynamic-symbol=foo.nobits +# RUN: llvm-readobj -S %t3 | FileCheck --check-prefix=CHECK-LIVE %s + +# CHECK-DEAD-NOT: Name: .note.text.foo +# CHECK-DEAD-NOT: Name: .nobits.text.foo +# CHECK-DEAD-NOT: Name: .text.foo +# CHECK-DEAD: Name: .note.bar + +# CHECK-LIVE: Name: .text +# CHECK-LIVE: Name: .nobits.text.foo +# CHECK-LIVE: Name: .note.text.foo +# CHECK-LIVE: Name: .note.bar + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + Info: foo + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .text.foo + - SectionOrType: .note.text.foo + - SectionOrType: .nobits.text.foo + - Name: .note.text.foo + Type: SHT_NOTE + Flags: [ SHF_GROUP ] + Content: "DEAD" + - Name: .text.foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + Content: "00" + - Name: .nobits.text.foo + Type: SHT_NOBITS + Flags: [ SHF_ALLOC, SHF_WRITE ] + - Name: .note.bar + Type: SHT_NOTE + Flags: [ ] + Content: "BEEF" +Symbols: + - Name: foo + Binding: STB_GLOBAL + Type: STT_FUNC + Section: .text.foo + Size: 0x08 + - Name: foo.note + Binding: STB_GLOBAL + Type: STT_FUNC + Section: .note.text.foo + Size: 0x08 + - Name: foo.nobits + Binding: STB_GLOBAL + Type: STT_FUNC + Section: .nobits.text.foo + Size: 0x08