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

Rui Ueyama via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 22 11:49:54 PDT 2015


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




More information about the llvm-commits mailing list