diff options
-rw-r--r-- | lld/ELF/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lld/ELF/Config.h | 1 | ||||
-rw-r--r-- | lld/ELF/Driver.cpp | 3 | ||||
-rw-r--r-- | lld/ELF/InputFiles.cpp | 10 | ||||
-rw-r--r-- | lld/ELF/InputFiles.h | 1 | ||||
-rw-r--r-- | lld/ELF/InputSection.h | 6 | ||||
-rw-r--r-- | lld/ELF/MarkLive.cpp | 141 | ||||
-rw-r--r-- | lld/ELF/Options.td | 4 | ||||
-rw-r--r-- | lld/ELF/OutputSections.cpp | 29 | ||||
-rw-r--r-- | lld/ELF/SymbolTable.h | 2 | ||||
-rw-r--r-- | lld/ELF/Symbols.h | 2 | ||||
-rw-r--r-- | lld/ELF/Writer.cpp | 5 | ||||
-rw-r--r-- | lld/ELF/Writer.h | 2 | ||||
-rw-r--r-- | lld/test/elf2/gc-sections.s | 84 |
14 files changed, 272 insertions, 19 deletions
diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index 20e039c77a3..763275e30ca 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -9,6 +9,7 @@ add_llvm_library(lldELF2 InputFiles.cpp InputSection.cpp LinkerScript.cpp + MarkLive.cpp OutputSections.cpp SymbolTable.cpp Symbols.cpp diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 14c7c1f1aa0..00de985c0d5 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -51,6 +51,7 @@ struct Configuration { bool DiscardNone; bool EnableNewDtags; bool ExportDynamic; + bool GcSections; bool GnuHash = false; bool Mips64EL = false; bool NoInhibitExec; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 4760bb715fb..acb9d9b36d4 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -142,6 +142,7 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) { Config->DiscardNone = Args.hasArg(OPT_discard_none); Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags); Config->ExportDynamic = Args.hasArg(OPT_export_dynamic); + Config->GcSections = Args.hasArg(OPT_gc_sections); Config->NoInhibitExec = Args.hasArg(OPT_noinhibit_exec); Config->NoUndefined = Args.hasArg(OPT_no_undefined); Config->Shared = Args.hasArg(OPT_shared); @@ -253,5 +254,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { // Write the result to the file. Symtab.scanShlibUndefined(); + if (Config->GcSections) + markLive<ELFT>(&Symtab); writeResult<ELFT>(&Symtab); } diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index fa4756a86fe..9aa207b6f65 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -75,6 +75,16 @@ typename ObjectFile<ELFT>::Elf_Sym_Range ObjectFile<ELFT>::getLocalSymbols() { } template <class ELFT> +const typename ObjectFile<ELFT>::Elf_Sym * +ObjectFile<ELFT>::getLocalSymbol(uintX_t SymIndex) { + uint32_t FirstNonLocal = this->Symtab->sh_info; + if (SymIndex >= FirstNonLocal) + return nullptr; + Elf_Sym_Range Syms = this->ELFObj.symbols(this->Symtab); + return Syms.begin() + SymIndex; +} + +template <class ELFT> void elf2::ObjectFile<ELFT>::parse(DenseSet<StringRef> &Comdats) { // Read section and symbol tables. initializeSections(Comdats); diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index c3f57e3acb9..8cedbd4a6c2 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -122,6 +122,7 @@ public: } Elf_Sym_Range getLocalSymbols(); + const Elf_Sym *getLocalSymbol(uintX_t SymIndex); const Elf_Shdr *getSymbolTable() const { return this->Symtab; }; ArrayRef<Elf_Word> getSymbolTableShndx() const { return SymtabSHNDX; }; diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index 25330a8a0b2..434384e84f0 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -10,6 +10,7 @@ #ifndef LLD_ELF_INPUT_SECTION_H #define LLD_ELF_INPUT_SECTION_H +#include "Config.h" #include "lld/Core/LLVM.h" #include "llvm/Object/ELF.h" @@ -39,6 +40,11 @@ public: Kind SectionKind); OutputSectionBase<ELFT> *OutSec = nullptr; + // Used for garbage collection. + // Live bit makes sense only when Config->GcSections is true. + bool isLive() const { return !Config->GcSections || Live; } + bool Live = false; + // Returns the size of this section (even if this is a common or BSS.) size_t getSize() const { return Header->sh_size; } diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp new file mode 100644 index 00000000000..691a9d41252 --- /dev/null +++ b/lld/ELF/MarkLive.cpp @@ -0,0 +1,141 @@ +//===- MarkLive.cpp -------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements --gc-sections, which is a feature to remove unused +// sections from output. Unused sections are sections that are not reachable +// from known GC-root symbols or sections. Naturally the feature is +// implemented as a mark-sweep garbage collector. +// +// Here's how it works. Each InputSectionBase has a "Live" bit. The bit is off +// by default. Starting with GC-root symbols or sections, markLive function +// defined in this file visits all reachable sections to set their Live +// bits. Writer will then ignore sections whose Live bits are off, so that +// such sections are removed from output. +// +//===----------------------------------------------------------------------===// + +#include "InputSection.h" +#include "OutputSections.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "Writer.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Object/ELF.h" +#include <functional> +#include <vector> + +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::object; + +using namespace lld; +using namespace lld::elf2; + +template <class ELFT, bool isRela> +static void +doForEachSuccessor(InputSectionBase<ELFT> *Sec, + std::function<void(InputSectionBase<ELFT> *)> Fn, + iterator_range<const Elf_Rel_Impl<ELFT, isRela> *> Rels) { + typedef typename ELFFile<ELFT>::Elf_Sym Elf_Sym; + typedef Elf_Rel_Impl<ELFT, isRela> RelType; + + ObjectFile<ELFT> *File = Sec->getFile(); + for (const RelType &RI : Rels) { + // Global symbol + uint32_t SymIndex = RI.getSymbol(Config->Mips64EL); + if (SymbolBody *B = File->getSymbolBody(SymIndex)) { + if (auto *D = dyn_cast<DefinedRegular<ELFT>>(B->repl())) + Fn(&D->Section); + continue; + } + // Local symbol + if (const Elf_Sym *Sym = File->getLocalSymbol(SymIndex)) + if (InputSectionBase<ELFT> *Sec = File->getSection(*Sym)) + Fn(Sec); + } +} + +// Calls Fn for each section that Sec refers to. +template <class ELFT> +static void forEachSuccessor(InputSection<ELFT> *Sec, + std::function<void(InputSectionBase<ELFT> *)> Fn) { + typedef typename ELFFile<ELFT>::Elf_Shdr Elf_Shdr; + for (const Elf_Shdr *RelSec : Sec->RelocSections) { + if (RelSec->sh_type == SHT_RELA) + doForEachSuccessor(Sec, Fn, Sec->getFile()->getObj().relas(RelSec)); + else + doForEachSuccessor(Sec, Fn, Sec->getFile()->getObj().rels(RelSec)); + } +} + +// Sections listed below are special because they are used by the loader +// just by being in an ELF file. They should not be garbage-collected. +template <class ELFT> static bool isReserved(InputSectionBase<ELFT> *Sec) { + switch (Sec->getSectionHdr()->sh_type) { + case SHT_FINI_ARRAY: + case SHT_INIT_ARRAY: + case SHT_NOTE: + case SHT_PREINIT_ARRAY: + return true; + default: + StringRef S = Sec->getSectionName(); + return S.startswith(".init") || S.startswith(".fini"); + } +} + +template <class ELFT> void lld::elf2::markLive(SymbolTable<ELFT> *Symtab) { + SmallVector<InputSectionBase<ELFT> *, 256> Q; + + auto Enqueue = [&](InputSectionBase<ELFT> *Sec) { + if (!Sec || Sec->Live) + return; + Sec->Live = true; + Q.push_back(Sec); + }; + + auto MarkSymbol = [&](SymbolBody *Sym) { + if (Sym) + if (auto *D = dyn_cast_or_null<DefinedRegular<ELFT>>(Sym->repl())) + Enqueue(&D->Section); + }; + + // Add GC root symbols. + MarkSymbol(Config->EntrySym); + MarkSymbol(Symtab->find(Config->Init)); + MarkSymbol(Symtab->find(Config->Fini)); + for (StringRef S : Config->Undefined) + MarkSymbol(Symtab->find(S)); + + // Preserve externally-visible symbols if the symbols defined by this + // file could override other ELF file's symbols at runtime. + if (Config->Shared || Config->ExportDynamic) { + for (const std::pair<StringRef, Symbol *> &P : Symtab->getSymbols()) { + SymbolBody *B = P.second->Body; + if (B->getVisibility() == STV_DEFAULT) + MarkSymbol(B); + } + } + + // Preserve special sections. + for (const std::unique_ptr<ObjectFile<ELFT>> &F : Symtab->getObjectFiles()) + for (InputSectionBase<ELFT> *Sec : F->getSections()) + if (Sec && Sec != &InputSection<ELFT>::Discarded) + if (isReserved(Sec)) + Enqueue(Sec); + + // Mark all reachable sections. + while (!Q.empty()) + if (auto *Sec = dyn_cast<InputSection<ELFT>>(Q.pop_back_val())) + forEachSuccessor<ELFT>(Sec, Enqueue); +} + +template void lld::elf2::markLive<ELF32LE>(SymbolTable<ELF32LE> *); +template void lld::elf2::markLive<ELF32BE>(SymbolTable<ELF32BE> *); +template void lld::elf2::markLive<ELF64LE>(SymbolTable<ELF64LE> *); +template void lld::elf2::markLive<ELF64BE>(SymbolTable<ELF64BE> *); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 6e34071f6e2..21954724807 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -49,6 +49,9 @@ def fini : Separate<["-"], "fini">, MetaVarName<"<symbol>">, def hash_style : Separate<["--", "-"], "hash-style">, HelpText<"Specify hash style (sysv, gnu or both)">; +def gc_sections : Flag<["--"], "gc-sections">, + HelpText<"Enable garbage collection of unused sections">; + def init : Separate<["-"], "init">, MetaVarName<"<symbol>">, HelpText<"Specify an initializer function">; @@ -127,7 +130,6 @@ def build_id : Flag<["--"], "build-id">; def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">; def end_group : Flag<["--"], "end-group">; def fatal_warnings : Flag<["--"], "fatal-warnings">; -def gc_sections : Flag<["--"], "gc-sections">; def no_add_needed : Flag<["--"], "no-add-needed">; def no_fatal_warnings : Flag<["--"], "no-fatal-warnings">; def no_warn_mismatch : Flag<["--"], "no-warn-mismatch">; diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index ad315932aee..25b533b2070 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -898,21 +898,23 @@ void SymbolTableSection<ELFT>::writeLocalSymbols(uint8_t *&Buf) { continue; auto *ESym = reinterpret_cast<Elf_Sym *>(Buf); - Buf += sizeof(*ESym); - ESym->st_name = StrTabSec.getFileOff(SymName); - ESym->st_size = Sym.st_size; - ESym->setBindingAndType(Sym.getBinding(), Sym.getType()); uintX_t VA = 0; if (Sym.st_shndx == SHN_ABS) { ESym->st_shndx = SHN_ABS; VA = Sym.st_value; } else { const InputSectionBase<ELFT> *Section = File->getSection(Sym); + if (!Section->isLive()) + continue; const OutputSectionBase<ELFT> *OutSec = Section->OutSec; ESym->st_shndx = OutSec->SectionIndex; VA += OutSec->getVA() + Section->getOffset(Sym); } + ESym->st_name = StrTabSec.getFileOff(SymName); + ESym->st_size = Sym.st_size; + ESym->setBindingAndType(Sym.getBinding(), Sym.getType()); ESym->st_value = VA; + Buf += sizeof(*ESym); } } } @@ -924,20 +926,19 @@ void SymbolTableSection<ELFT>::writeGlobalSymbols(uint8_t *Buf) { auto *ESym = reinterpret_cast<Elf_Sym *>(Buf); for (const SymbolData &Item : Symbols) { SymbolBody *Body = Item.Body; - StringRef Name = Body->getName(); - - ESym->st_name = StrTabSec.getFileOff(Name); - const OutputSectionBase<ELFT> *OutSec = nullptr; - const InputSectionBase<ELFT> *Section = nullptr; switch (Body->kind()) { case SymbolBody::DefinedSyntheticKind: OutSec = &cast<DefinedSynthetic<ELFT>>(Body)->Section; break; - case SymbolBody::DefinedRegularKind: - Section = &cast<DefinedRegular<ELFT>>(Body)->Section; + case SymbolBody::DefinedRegularKind: { + auto *Sym = cast<DefinedRegular<ELFT>>(Body->repl()); + if (!Sym->Section.isLive()) + continue; + OutSec = Sym->Section.OutSec; break; + } case SymbolBody::DefinedCommonKind: OutSec = Out<ELFT>::Bss; break; @@ -948,6 +949,9 @@ void SymbolTableSection<ELFT>::writeGlobalSymbols(uint8_t *Buf) { break; } + StringRef Name = Body->getName(); + ESym->st_name = StrTabSec.getFileOff(Name); + unsigned char Type = STT_NOTYPE; uintX_t Size = 0; if (const auto *EBody = dyn_cast<ELFSymbolBody<ELFT>>(Body)) { @@ -961,9 +965,6 @@ void SymbolTableSection<ELFT>::writeGlobalSymbols(uint8_t *Buf) { ESym->setVisibility(Body->getVisibility()); ESym->st_value = getSymVA<ELFT>(*Body); - if (Section) - OutSec = Section->OutSec; - if (isa<DefinedAbsolute<ELFT>>(Body)) ESym->st_shndx = SHN_ABS; else if (OutSec) diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h index b2fcade68e1..d05be7d83e1 100644 --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -55,6 +55,7 @@ public: void addIgnoredSym(StringRef Name); bool isUndefined(StringRef Name); void scanShlibUndefined(); + SymbolBody *find(StringRef Name); private: Symbol *insert(SymbolBody *New); @@ -63,7 +64,6 @@ private: void addMemberFile(Lazy *Body); void checkCompatibility(std::unique_ptr<InputFile> &File); void resolve(SymbolBody *Body); - SymbolBody *find(StringRef Name); void reportConflict(const Twine &Message, const SymbolBody &Old, const SymbolBody &New, bool Warning); diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 1571335701c..30f8ec6a5bd 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -225,7 +225,7 @@ public: return S->kind() == Base::DefinedRegularKind; } - const InputSectionBase<ELFT> &Section; + InputSectionBase<ELFT> &Section; }; template <class ELFT> class DefinedSynthetic : public Defined<ELFT> { diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 8c0807d13e5..4bc5b99f74e 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -426,7 +426,7 @@ template <class ELFT> void Writer<ELFT>::createSections() { for (const std::unique_ptr<ObjectFile<ELFT>> &F : Symtab.getObjectFiles()) { for (InputSectionBase<ELFT> *C : F->getSections()) { - if (!C || C == &InputSection<ELFT>::Discarded) + if (!C || !C->isLive() || C == &InputSection<ELFT>::Discarded) continue; const Elf_Shdr *H = C->getSectionHdr(); uintX_t OutFlags = H->sh_flags & ~SHF_GROUP; @@ -497,7 +497,8 @@ template <class ELFT> void Writer<ELFT>::createSections() { for (InputSectionBase<ELFT> *B : F->getSections()) if (auto *S = dyn_cast_or_null<InputSection<ELFT>>(B)) if (S != &InputSection<ELFT>::Discarded) - scanRelocs(*S); + if (S->isLive()) + scanRelocs(*S); // FIXME: Try to avoid the extra walk over all global symbols. std::vector<DefinedCommon<ELFT> *> CommonSymbols; diff --git a/lld/ELF/Writer.h b/lld/ELF/Writer.h index ccdade25810..40a1711e2bd 100644 --- a/lld/ELF/Writer.h +++ b/lld/ELF/Writer.h @@ -16,6 +16,8 @@ namespace elf2 { template <class ELFT> class SymbolTable; template <class ELFT> void writeResult(SymbolTable<ELFT> *Symtab); + +template <class ELFT> void markLive(SymbolTable<ELFT> *Symtab); } } diff --git a/lld/test/elf2/gc-sections.s b/lld/test/elf2/gc-sections.s new file mode 100644 index 00000000000..08da885e7b3 --- /dev/null +++ b/lld/test/elf2/gc-sections.s @@ -0,0 +1,84 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld2 %t -o %t2 +# RUN: llvm-readobj -sections -symbols %t2 | FileCheck -check-prefix=NOGC %s +# RUN: ld.lld2 --gc-sections %t -o %t2 +# RUN: llvm-readobj -sections -symbols %t2 | FileCheck -check-prefix=GC1 %s +# RUN: ld.lld2 --export-dynamic --gc-sections %t -o %t2 +# RUN: llvm-readobj -sections -symbols %t2 | FileCheck -check-prefix=GC2 %s + +# NOGC: Name: .text +# NOGC: Name: .init +# NOGC: Name: .fini +# NOGC: Name: a +# NOGC: Name: b +# NOGC: Name: c +# NOGC: Name: x +# NOGC: Name: y +# NOGC: Name: __preinit_array_start +# NOGC: Name: __preinit_array_end +# NOGC: Name: d + +# GC1: Name: .text +# GC1: Name: .init +# GC1: Name: .fini +# GC1: Name: a +# GC1: Name: b +# GC1: Name: c +# GC1-NOT: Name: x +# GC1-NOT: Name: y +# GC1: Name: __preinit_array_start +# GC1: Name: __preinit_array_end +# GC1-NOT: Name: d + +# GC2: Name: .text +# GC2: Name: .init +# GC2: Name: .fini +# GC2: Name: a +# GC2: Name: b +# GC2: Name: c +# GC2-NOT: Name: x +# GC2-NOT: Name: y +# GC2: Name: __preinit_array_start +# GC2: Name: __preinit_array_end +# GC2: Name: d + +.globl _start, d +.protected a, b, c, x, y +_start: + call a + +.section .text.a,"ax",@progbits +a: + call _start + call b + +.section .text.b,"ax",@progbits +b: + call c + +.section .text.c,"ax",@progbits +c: + nop + +.section .text.d,"ax",@progbits +d: + nop + +.section .text.x,"ax",@progbits +x: + call y + +.section .text.y,"ax",@progbits +y: + call x + +.section .init,"aw",@init_array + .quad 0 + +.section .fini,"aw",@fini_array + .quad 0 + +.section .preinit_array,"aw",@preinit_array + .quad 0 |