summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/CMakeLists.txt1
-rw-r--r--lld/ELF/Config.h1
-rw-r--r--lld/ELF/Driver.cpp3
-rw-r--r--lld/ELF/InputFiles.cpp10
-rw-r--r--lld/ELF/InputFiles.h1
-rw-r--r--lld/ELF/InputSection.h6
-rw-r--r--lld/ELF/MarkLive.cpp141
-rw-r--r--lld/ELF/Options.td4
-rw-r--r--lld/ELF/OutputSections.cpp29
-rw-r--r--lld/ELF/SymbolTable.h2
-rw-r--r--lld/ELF/Symbols.h2
-rw-r--r--lld/ELF/Writer.cpp5
-rw-r--r--lld/ELF/Writer.h2
-rw-r--r--lld/test/elf2/gc-sections.s84
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
OpenPOWER on IntegriCloud