[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