Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -82,6 +82,8 @@ template void relocate(uint8_t *Buf, uint8_t *BufEnd, RelIteratorRange Rels); + StringRef getOutputSectionName(); + private: template uint8_t *findMipsPairedReloc(uint8_t *Buf, uint32_t SymIndex, uint32_t Type, Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -11,6 +11,7 @@ #include "Config.h" #include "Error.h" #include "InputFiles.h" +#include "LinkerScript.h" #include "OutputSections.h" #include "Target.h" @@ -243,6 +244,20 @@ } } +template StringRef InputSectionBase::getOutputSectionName() { + StringRef Dest = Script->getOutputSection(this); + if (!Dest.empty()) + return Dest; + + StringRef Name = getSectionName(); + for (StringRef V : {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.", + ".init_array.", ".fini_array.", ".ctors.", ".dtors.", + ".tbss.", ".gcc_except_table.", ".tdata."}) + if (Name.startswith(V)) + return V.drop_back(); + return Name; +} + template void InputSection::writeTo(uint8_t *Buf) { if (this->Header->sh_type == SHT_NOBITS) return; Index: ELF/MarkLive.cpp =================================================================== --- ELF/MarkLive.cpp +++ ELF/MarkLive.cpp @@ -76,6 +76,20 @@ } } +// Reference to __start or __stop is sufficient to prevent the section from +// being garbage collected. Function returns if section should be kept. +template +static bool isRetained(SymbolTable *Symtab, InputSectionBase *Sec) { + StringRef Name = Sec->getOutputSectionName(); + if (!isValidCIdentifier(Name)) + return false; + if (SymbolBody *B = Symtab->find(("__start_" + Name).str())) + if (B->isUndefined()) + return true; + SymbolBody *B = Symtab->find(("__stop_" + Name).str()); + return B && B->isUndefined(); +} + // This is the main function of the garbage collector. // Starting from GC-root sections, this function visits all reachable // sections to set their "Live" bits. @@ -116,8 +130,9 @@ // Preserve special sections. for (const std::unique_ptr> &F : Symtab->getObjectFiles()) for (InputSectionBase *Sec : F->getSections()) - if (Sec && Sec != &InputSection::Discarded && isReserved(Sec)) - Enqueue(Sec); + if (Sec && Sec != &InputSection::Discarded) + if (isReserved(Sec) || isRetained(Symtab, Sec)) + Enqueue(Sec); // Mark all reachable sections. while (!Q.empty()) Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -55,6 +55,8 @@ bool canBePreempted(const SymbolBody *Body, bool NeedsGot); +bool isValidCIdentifier(StringRef S); + // This represents a section in an output file. // Different sub classes represent different types of sections. Some contain // input sections, others are created by the linker. Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -24,6 +24,19 @@ using namespace lld; using namespace lld::elf2; +static bool isAlpha(char C) { + return ('a' <= C && C <= 'z') || ('A' <= C && C <= 'Z') || C == '_'; +} + +static bool isAlnum(char C) { return isAlpha(C) || ('0' <= C && C <= '9'); } + +// Returns true if S is valid as a C language identifier. +bool elf2::isValidCIdentifier(StringRef S) { + if (S.empty() || !isAlpha(S[0])) + return false; + return std::all_of(S.begin() + 1, S.end(), isAlnum); +} + template OutputSectionBase::OutputSectionBase(StringRef Name, uint32_t Type, uintX_t Flags) Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -74,7 +74,6 @@ void writeHeader(); void writeSections(); bool isDiscarded(InputSectionBase *IS) const; - StringRef getOutputSectionName(InputSectionBase *S) const; bool needsInterpSection() const { return !Symtab.getSharedFiles().empty() && !Config->DynamicLinker.empty(); } @@ -726,21 +725,6 @@ } template -StringRef Writer::getOutputSectionName(InputSectionBase *S) const { - StringRef Dest = Script->getOutputSection(S); - if (!Dest.empty()) - return Dest; - - StringRef Name = S->getSectionName(); - for (StringRef V : {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.", - ".init_array.", ".fini_array.", ".ctors.", ".dtors.", - ".tbss.", ".gcc_except_table.", ".tdata."}) - if (Name.startswith(V)) - return V.drop_back(); - return Name; -} - -template void reportDiscarded(InputSectionBase *IS, const std::unique_ptr> &File) { if (!Config->PrintGcSections || !IS || IS->isLive()) @@ -945,7 +929,7 @@ } OutputSectionBase *Sec; bool IsNew; - std::tie(Sec, IsNew) = Factory.create(C, getOutputSectionName(C)); + std::tie(Sec, IsNew) = Factory.create(C, C->getOutputSectionName()); if (IsNew) { OwningSections.emplace_back(Sec); OutputSections.push_back(Sec); @@ -1149,19 +1133,6 @@ Out::Dynamic->FiniArraySec); } -static bool isAlpha(char C) { - return ('a' <= C && C <= 'z') || ('A' <= C && C <= 'Z') || C == '_'; -} - -static bool isAlnum(char C) { return isAlpha(C) || ('0' <= C && C <= '9'); } - -// Returns true if S is valid as a C language identifier. -static bool isValidCIdentifier(StringRef S) { - if (S.empty() || !isAlpha(S[0])) - return false; - return std::all_of(S.begin() + 1, S.end(), isAlnum); -} - // If a section name is valid as a C identifier (which is rare because of // the leading '.'), linkers are expected to define __start_ and // __stop_ symbols. They are at beginning and end of the section, Index: test/ELF/startstop-gccollect.s =================================================================== --- test/ELF/startstop-gccollect.s +++ test/ELF/startstop-gccollect.s @@ -0,0 +1,43 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t + +## Default run: sections .foo and .bar exist in output +# RUN: ld.lld %t -o %tout +# RUN: llvm-objdump -d %tout | FileCheck -check-prefix=DISASM %s + +## Check that to have a reference to __start or __stop is +## sufficient condition to prevent the section from being garbage collected. +# RUN: ld.lld %t --gc-sections -o %tout +# RUN: llvm-objdump -d %tout | FileCheck -check-prefix=DISASM %s + +# DISASM: _start: +# DISASM-NEXT: 11000: {{.*}} callq 8 +# DISASM-NEXT: 11005: {{.*}} callq 3 +# DISASM-NEXT: Disassembly of section foo: +# DISASM-NEXT: foo: +# DISASM-NEXT: 1100a: 90 nop +# DISASM-NEXT: 1100b: 90 nop +# DISASM-NEXT: 1100c: 90 nop +# DISASM-NEXT: Disassembly of section bar: +# DISASM-NEXT: __start_bar: +# DISASM-NEXT: 1100d: 90 nop +# DISASM-NEXT: 1100e: 90 nop +# DISASM-NEXT: 1100f: 90 nop + +.hidden __stop_foo +.hidden __start_bar +.global _start +.text +_start: + call __stop_foo + call __start_bar + +.section foo,"ax" + nop + nop + nop + +.section bar,"ax" + nop + nop + nop