Index: lld/ELF/InputFiles.cpp =================================================================== --- lld/ELF/InputFiles.cpp +++ lld/ELF/InputFiles.cpp @@ -316,6 +316,12 @@ Sections[I] = createInputSection(Sec, SectionStringTable); } + InputSectionBase *Base = Sections[I]; + if (Sec.sh_flags & SHF_ASSOCIATED && + (!Base || !isa>(Base))) + error("SHF_ASSOCIATED only supported with regular sections " + + toString(Base)); + // .ARM.exidx sections have a reverse dependency on the InputSection they // have a SHF_LINK_ORDER dependency, this is identified by the sh_link. if (Sec.sh_flags & SHF_LINK_ORDER) { @@ -323,7 +329,7 @@ fatal(toString(this) + ": invalid sh_link index: " + Twine(Sec.sh_link)); auto *IS = cast>(Sections[Sec.sh_link]); - IS->DependentSection = Sections[I]; + IS->DependentSections.push_back(Base); } } } Index: lld/ELF/InputSection.h =================================================================== --- lld/ELF/InputSection.h +++ lld/ELF/InputSection.h @@ -273,8 +273,8 @@ // to. The writer sets a value. uint64_t OutSecOff = 0; - // InputSection that is dependent on us (reverse dependency for GC) - InputSectionBase *DependentSection = nullptr; + // InputSections that are dependent on us (reverse dependency for GC) + std::vector *> DependentSections; static bool classof(const InputSectionData *S); Index: lld/ELF/MarkLive.cpp =================================================================== --- lld/ELF/MarkLive.cpp +++ lld/ELF/MarkLive.cpp @@ -87,8 +87,8 @@ for (const typename ELFT::Rel &Rel : Sec.rels()) Fn(resolveReloc(Sec, Rel)); } - if (Sec.DependentSection) - Fn({Sec.DependentSection, 0}); + for (InputSectionBase *D : Sec.DependentSections) + Fn({D, 0}); } // The .eh_frame section is an unfortunate special case. @@ -235,6 +235,18 @@ // Preserve special sections and those which are specified in linker // script KEEP command. for (InputSectionBase *Sec : Symtab::X->Sections) { + if (Sec->Flags & SHF_ASSOCIATED) { + forEachSuccessor( + cast>(*Sec), [=](ResolvedReloc R) { + // We only consider references from SHF_ASSOCIATED to regular + // sections. This allows it to refer to, for example, a string table + // and still be gced. + if (auto *S = dyn_cast>(R.Sec)) + S->DependentSections.push_back(Sec); + }); + continue; + } + // .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. Index: lld/test/ELF/gc-sections-invert-edge-error.s =================================================================== --- /dev/null +++ lld/test/ELF/gc-sections-invert-edge-error.s @@ -0,0 +1,9 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t +# RUN: not ld.lld --gc-sections -shared %t -o %t2 2>&1 | FileCheck %s + +# CHECK: error: SHF_ASSOCIATED only supported with regular sections {{.*}}{{/|\\}}gc-sections-invert-edge-error.s.tmp:(foo1) + +.section foo1,"aoM",@progbits,8 +.quad 0 Index: lld/test/ELF/gc-sections-invert-edge.s =================================================================== --- /dev/null +++ lld/test/ELF/gc-sections-invert-edge.s @@ -0,0 +1,34 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t +# RUN: ld.lld --gc-sections %t -o %t2 +# RUN: llvm-objdump -section-headers %t2 | FileCheck %s + +# CHECK: 1 .merge +# CHECK-NEXT: foo1 +# CHECK-NEXT: .bar1 +# CHECK-NEXT: .text +# CHECK-NEXT: .comment +# CHECK-NEXT: .symtab +# CHECK-NEXT: .shstrtab +# CHECK-NEXT: .strtab + +.global _start +_start: +.quad .bar1 - . + +.section .merge, "aM", @progbits, 8 +.quad 0 + +.section foo1,"ao" +.quad .bar1 - . +.quad .merge + +.section foo2,"ao" +.quad .bar2 - . + +.section .bar1,"a" +.quad 0 +.section .bar2,"a" +.quad 0 + Index: llvm/docs/Extensions.rst =================================================================== --- llvm/docs/Extensions.rst +++ llvm/docs/Extensions.rst @@ -204,6 +204,8 @@ The unique number is not present in the resulting object at all. It is just used in the assembler to differentiate the sections. +The section flags can include an 'o' and it maps to SHF_ASSOCIATIVE. + Target Specific Behaviour ========================= Index: llvm/include/llvm/Support/ELF.h =================================================================== --- llvm/include/llvm/Support/ELF.h +++ llvm/include/llvm/Support/ELF.h @@ -746,6 +746,11 @@ // Identifies a section containing compressed data. SHF_COMPRESSED = 0x800U, + // Relocations from this section should not keep the destination alive. It is + // the destination that should keep this section alive. + // FIXME: Tentative value. + SHF_ASSOCIATED = 0x1000U, + // This section is excluded from the final executable or shared library. SHF_EXCLUDE = 0x80000000U, Index: llvm/lib/MC/MCParser/ELFAsmParser.cpp =================================================================== --- llvm/lib/MC/MCParser/ELFAsmParser.cpp +++ llvm/lib/MC/MCParser/ELFAsmParser.cpp @@ -303,6 +303,9 @@ case 'G': flags |= ELF::SHF_GROUP; break; + case 'o': + flags |= ELF::SHF_ASSOCIATED; + break; case '?': *UseLastGroup = true; break; Index: llvm/test/MC/ELF/section.s =================================================================== --- llvm/test/MC/ELF/section.s +++ llvm/test/MC/ELF/section.s @@ -149,3 +149,18 @@ // CHECK: Name: bar-"foo" // CHECK: Section { // CHECK: Name: foo + +// Test SHF_LINK_ORDER + +.section .shf_link_order,"ao" +// CHECK: Section { +// CHECK: Name: .shf_link_order +// CHECK-NEXT: Type: SHT_PROGBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_ASSOCIATED +// CHECK-NEXT: ] +// CHECK-NEXT: Address: +// CHECK-NEXT: Offset: 0x40 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Link: 0 Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -1055,6 +1055,7 @@ ENUM_ENT(SHF_MASKOS, "o"), ENUM_ENT(SHF_MASKPROC, "p"), ENUM_ENT_1(SHF_COMPRESSED), + ENUM_ENT_1(SHF_ASSOCIATED), }; static const EnumEntry ElfXCoreSectionFlags[] = {