ELF2: Implement --gc-sections.
authorRui Ueyama <ruiu@google.com>
Thu, 22 Oct 2015 18:49:53 +0000 (18:49 +0000)
committerRui Ueyama <ruiu@google.com>
Thu, 22 Oct 2015 18:49:53 +0000 (18:49 +0000)
Section garbage collection is a feature to remove unused sections
from outputs. Unused sections are sections that cannot be reachable
from known GC-root symbols or sections. Naturally the feature is
implemented as a mark-sweep garbage collector.

In this patch, I added Live bit to InputSectionBase. If and only
if Live bit is on, the section will be written to the output.
Starting from GC-root symbols or sections, a new function, markLive(),
visits all reachable sections and sets their Live bits. Writer then
ignores sections whose Live bit is off, so that such sections are
excluded from the output.

This change has small negative impact on performance if you use
the feature because making sections means more work. The time to
link Clang changes from 0.356s to 0.386s, or +8%.

It reduces Clang size from 57,764,984 bytes to 55,296,600 bytes.
That is 4.3% reduction.

http://reviews.llvm.org/D13950

llvm-svn: 251043

14 files changed:
lld/ELF/CMakeLists.txt
lld/ELF/Config.h
lld/ELF/Driver.cpp
lld/ELF/InputFiles.cpp
lld/ELF/InputFiles.h
lld/ELF/InputSection.h
lld/ELF/MarkLive.cpp [new file with mode: 0644]
lld/ELF/Options.td
lld/ELF/OutputSections.cpp
lld/ELF/SymbolTable.h
lld/ELF/Symbols.h
lld/ELF/Writer.cpp
lld/ELF/Writer.h
lld/test/elf2/gc-sections.s [new file with mode: 0644]

index 20e039c..763275e 100644 (file)
@@ -9,6 +9,7 @@ add_llvm_library(lldELF2
   InputFiles.cpp
   InputSection.cpp
   LinkerScript.cpp
+  MarkLive.cpp
   OutputSections.cpp
   SymbolTable.cpp
   Symbols.cpp
index 14c7c1f..00de985 100644 (file)
@@ -51,6 +51,7 @@ struct Configuration {
   bool DiscardNone;
   bool EnableNewDtags;
   bool ExportDynamic;
+  bool GcSections;
   bool GnuHash = false;
   bool Mips64EL = false;
   bool NoInhibitExec;
index 4760bb7..acb9d9b 100644 (file)
@@ -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);
 }
index fa4756a..9aa207b 100644 (file)
@@ -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);
index c3f57e3..8cedbd4 100644 (file)
@@ -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; };
index 25330a8..434384e 100644 (file)
@@ -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 (file)
index 0000000..691a9d4
--- /dev/null
@@ -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> *);
index 6e34071..2195472 100644 (file)
@@ -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">;
index ad31593..25b533b 100644 (file)
@@ -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)
index b2fcade..d05be7d 100644 (file)
@@ -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);
 
index 1571335..30f8ec6 100644 (file)
@@ -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> {
index 8c0807d..4bc5b99 100644 (file)
@@ -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;
index ccdade2..40a1711 100644 (file)
@@ -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 (file)
index 0000000..08da885
--- /dev/null
@@ -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