[lld] r251043 - ELF2: Implement --gc-sections.

Rafael EspĂ­ndola via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 22 14:32:23 PDT 2015


BTW, I just noticed one problem: .eh_frame.

It is a magical section that needs to be kept alive when data it
refers to is kept alive. I.E.: we have to invert the edges.

For now I would suggest just keeping all .eh_frame sections.

Cheers,
Rafael


On 22 October 2015 at 14:49, Rui Ueyama via llvm-commits
<llvm-commits at lists.llvm.org> wrote:
> Author: ruiu
> Date: Thu Oct 22 13:49:53 2015
> New Revision: 251043
>
> URL: http://llvm.org/viewvc/llvm-project?rev=251043&view=rev
> Log:
> ELF2: Implement --gc-sections.
>
> 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
>
> Added:
>     lld/trunk/ELF/MarkLive.cpp
>     lld/trunk/test/elf2/gc-sections.s
> Modified:
>     lld/trunk/ELF/CMakeLists.txt
>     lld/trunk/ELF/Config.h
>     lld/trunk/ELF/Driver.cpp
>     lld/trunk/ELF/InputFiles.cpp
>     lld/trunk/ELF/InputFiles.h
>     lld/trunk/ELF/InputSection.h
>     lld/trunk/ELF/Options.td
>     lld/trunk/ELF/OutputSections.cpp
>     lld/trunk/ELF/SymbolTable.h
>     lld/trunk/ELF/Symbols.h
>     lld/trunk/ELF/Writer.cpp
>     lld/trunk/ELF/Writer.h
>
> Modified: lld/trunk/ELF/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/CMakeLists.txt?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/CMakeLists.txt (original)
> +++ lld/trunk/ELF/CMakeLists.txt Thu Oct 22 13:49:53 2015
> @@ -9,6 +9,7 @@ add_llvm_library(lldELF2
>    InputFiles.cpp
>    InputSection.cpp
>    LinkerScript.cpp
> +  MarkLive.cpp
>    OutputSections.cpp
>    SymbolTable.cpp
>    Symbols.cpp
>
> Modified: lld/trunk/ELF/Config.h
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Config.h?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/Config.h (original)
> +++ lld/trunk/ELF/Config.h Thu Oct 22 13:49:53 2015
> @@ -51,6 +51,7 @@ struct Configuration {
>    bool DiscardNone;
>    bool EnableNewDtags;
>    bool ExportDynamic;
> +  bool GcSections;
>    bool GnuHash = false;
>    bool Mips64EL = false;
>    bool NoInhibitExec;
>
> Modified: lld/trunk/ELF/Driver.cpp
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Driver.cpp?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/Driver.cpp (original)
> +++ lld/trunk/ELF/Driver.cpp Thu Oct 22 13:49:53 2015
> @@ -142,6 +142,7 @@ void LinkerDriver::createFiles(opt::Inpu
>    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:
>
>    // Write the result to the file.
>    Symtab.scanShlibUndefined();
> +  if (Config->GcSections)
> +    markLive<ELFT>(&Symtab);
>    writeResult<ELFT>(&Symtab);
>  }
>
> Modified: lld/trunk/ELF/InputFiles.cpp
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputFiles.cpp?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/InputFiles.cpp (original)
> +++ lld/trunk/ELF/InputFiles.cpp Thu Oct 22 13:49:53 2015
> @@ -75,6 +75,16 @@ typename ObjectFile<ELFT>::Elf_Sym_Range
>  }
>
>  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);
>
> Modified: lld/trunk/ELF/InputFiles.h
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputFiles.h?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/InputFiles.h (original)
> +++ lld/trunk/ELF/InputFiles.h Thu Oct 22 13:49:53 2015
> @@ -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; };
>
> Modified: lld/trunk/ELF/InputSection.h
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputSection.h?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/InputSection.h (original)
> +++ lld/trunk/ELF/InputSection.h Thu Oct 22 13:49:53 2015
> @@ -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; }
>
>
> Added: lld/trunk/ELF/MarkLive.cpp
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/MarkLive.cpp?rev=251043&view=auto
> ==============================================================================
> --- lld/trunk/ELF/MarkLive.cpp (added)
> +++ lld/trunk/ELF/MarkLive.cpp Thu Oct 22 13:49:53 2015
> @@ -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> *);
>
> Modified: lld/trunk/ELF/Options.td
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Options.td?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/Options.td (original)
> +++ lld/trunk/ELF/Options.td Thu Oct 22 13:49:53 2015
> @@ -49,6 +49,9 @@ def fini : Separate<["-"], "fini">, Meta
>  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">;
>
> Modified: lld/trunk/ELF/OutputSections.cpp
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/OutputSections.cpp?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/OutputSections.cpp (original)
> +++ lld/trunk/ELF/OutputSections.cpp Thu Oct 22 13:49:53 2015
> @@ -898,21 +898,23 @@ void SymbolTableSection<ELFT>::writeLoca
>          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>::writeGlob
>    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>::writeGlob
>        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>::writeGlob
>      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)
>
> Modified: lld/trunk/ELF/SymbolTable.h
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/SymbolTable.h?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/SymbolTable.h (original)
> +++ lld/trunk/ELF/SymbolTable.h Thu Oct 22 13:49:53 2015
> @@ -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);
>
>
> Modified: lld/trunk/ELF/Symbols.h
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.h?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/Symbols.h (original)
> +++ lld/trunk/ELF/Symbols.h Thu Oct 22 13:49:53 2015
> @@ -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> {
>
> Modified: lld/trunk/ELF/Writer.cpp
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.cpp?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/Writer.cpp (original)
> +++ lld/trunk/ELF/Writer.cpp Thu Oct 22 13:49:53 2015
> @@ -426,7 +426,7 @@ template <class ELFT> void Writer<ELFT>:
>
>    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>:
>      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;
>
> Modified: lld/trunk/ELF/Writer.h
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.h?rev=251043&r1=251042&r2=251043&view=diff
> ==============================================================================
> --- lld/trunk/ELF/Writer.h (original)
> +++ lld/trunk/ELF/Writer.h Thu Oct 22 13:49:53 2015
> @@ -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);
>  }
>  }
>
>
> Added: lld/trunk/test/elf2/gc-sections.s
> URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/elf2/gc-sections.s?rev=251043&view=auto
> ==============================================================================
> --- lld/trunk/test/elf2/gc-sections.s (added)
> +++ lld/trunk/test/elf2/gc-sections.s Thu Oct 22 13:49:53 2015
> @@ -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", at progbits
> +a:
> +  call _start
> +  call b
> +
> +.section .text.b,"ax", at progbits
> +b:
> +  call c
> +
> +.section .text.c,"ax", at progbits
> +c:
> +  nop
> +
> +.section .text.d,"ax", at progbits
> +d:
> +  nop
> +
> +.section .text.x,"ax", at progbits
> +x:
> +  call y
> +
> +.section .text.y,"ax", at progbits
> +y:
> +  call x
> +
> +.section .init,"aw", at init_array
> +  .quad 0
> +
> +.section .fini,"aw", at fini_array
> +  .quad 0
> +
> +.section .preinit_array,"aw", at preinit_array
> +  .quad 0
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits


More information about the llvm-commits mailing list