Skip to content
This repository has been archived by the owner on Apr 23, 2020. It is now read-only.

Commit

Permalink
Discard debuginfo for object files empty after GC
Browse files Browse the repository at this point in the history
Patch by Robert O'Callahan.

Rust projects tend to link in all object files from all dependent
libraries and rely on --gc-sections to strip unused code and data.
Unfortunately --gc-sections doesn't currently strip any debuginfo
associated with GC'ed sections, so lld links in the full debuginfo from
all dependencies even if almost all that code has been discarded. See
rust-lang/rust#56068 for some details.

Properly stripping debuginfo for discarded sections would be difficult,
but a simple approach that helps significantly is to mark debuginfo
sections as live only if their associated object file has at least one
live code/data section. This patch does that. In a (contrived but not
totally artificial) Rust testcase linked above, it reduces the final
binary size from 46MB to 5.1MB.

Differential Revision: https://reviews.llvm.org/D54747

git-svn-id: https://llvm.org/svn/llvm-project/lld/trunk@358069 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
rui314 committed Apr 10, 2019
1 parent fca6cb6 commit 0c17952
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 19 deletions.
7 changes: 2 additions & 5 deletions ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1613,11 +1613,8 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {

// We do not want to emit debug sections if --strip-all
// or -strip-debug are given.
if (Config->Strip != StripPolicy::None) {
llvm::erase_if(InputSections, [](InputSectionBase *S) {
return S->Name.startswith(".debug") || S->Name.startswith(".zdebug");
});
}
if (Config->Strip != StripPolicy::None)
llvm::erase_if(InputSections, [](InputSectionBase *S) { return S->Debug; });

// The Target instance handles target-specific stuff, such as applying
// relocations or writing a PLT section. It also contains target-dependent
Expand Down
4 changes: 4 additions & 0 deletions ELF/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ template <class ELFT> class ObjFile : public ELFFileBase {
// but had one or more functions with the no_split_stack attribute.
bool SomeNoSplitStack = false;

// True if the file has any live Regular or Merge sections that aren't
// the LDSA section.
bool HasLiveCodeOrData = false;

// Pointer to this input file's .llvm_addrsig section, if it has one.
const Elf_Shdr *AddrsigSec = nullptr;

Expand Down
1 change: 1 addition & 0 deletions ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,

NumRelocations = 0;
AreRelocsRela = false;
Debug = Name.startswith(".debug") || Name.startswith(".zdebug");

// The ELF spec states that a value of 0 means the section has
// no alignment constraits.
Expand Down
10 changes: 7 additions & 3 deletions ELF/InputSection.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class SectionBase {

unsigned SectionKind : 3;

// The next three bit fields are only used by InputSectionBase, but we
// The next four bit fields are only used by InputSectionBase, but we
// put them here so the struct packs better.

// The garbage collector sets sections' Live bits.
Expand All @@ -73,6 +73,9 @@ class SectionBase {

unsigned Bss : 1;

// True if this is a debuginfo section.
unsigned Debug : 1;

// Set for sections that should not be folded by ICF.
unsigned KeepUnique : 1;

Expand Down Expand Up @@ -100,8 +103,9 @@ class SectionBase {
uint64_t Entsize, uint64_t Alignment, uint32_t Type,
uint32_t Info, uint32_t Link)
: Name(Name), Repl(this), SectionKind(SectionKind), Live(false),
Assigned(false), Bss(false), KeepUnique(false), Alignment(Alignment),
Flags(Flags), Entsize(Entsize), Type(Type), Link(Link), Info(Info) {}
Assigned(false), Bss(false), Debug(false), KeepUnique(false),
Alignment(Alignment), Flags(Flags), Entsize(Entsize), Type(Type),
Link(Link), Info(Info) {}
};

// This corresponds to a section of an input file.
Expand Down
36 changes: 25 additions & 11 deletions ELF/MarkLive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ template <class ELFT> class MarkLive {
void run();

private:
void enqueue(InputSectionBase *Sec, uint64_t Offset);
void enqueue(InputSectionBase *Sec, uint64_t Offset, bool IsLSDA);
void markSymbol(Symbol *Sym);

template <class RelTy>
Expand Down Expand Up @@ -97,7 +97,7 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &Sec, RelTy &Rel,
Offset += getAddend<ELFT>(Sec, Rel);

if (!IsLSDA || !(RelSec->Flags & SHF_EXECINSTR))
enqueue(RelSec, Offset);
enqueue(RelSec, Offset, IsLSDA);
return;
}

Expand All @@ -106,7 +106,7 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &Sec, RelTy &Rel,
SS->getFile().IsNeeded = true;

for (InputSectionBase *Sec : CNamedSections.lookup(Sym.getName()))
enqueue(Sec, 0);
enqueue(Sec, 0, IsLSDA);
}

// The .eh_frame section is an unfortunate special case.
Expand Down Expand Up @@ -169,7 +169,8 @@ static bool isReserved(InputSectionBase *Sec) {
}

template <class ELFT>
void MarkLive<ELFT>::enqueue(InputSectionBase *Sec, uint64_t Offset) {
void MarkLive<ELFT>::enqueue(InputSectionBase *Sec, uint64_t Offset,
bool IsLSDA) {
// Skip over discarded sections. This in theory shouldn't happen, because
// the ELF spec doesn't allow a relocation to point to a deduplicated
// COMDAT section directly. Unfortunately this happens in practice (e.g.
Expand All @@ -183,19 +184,26 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *Sec, uint64_t Offset) {
if (auto *MS = dyn_cast<MergeInputSection>(Sec))
MS->getSectionPiece(Offset)->Live = true;

InputSection *S = dyn_cast<InputSection>(Sec);
// LSDA does not count as "live code or data" in the object file.
// The section may already have been marked live for LSDA in which
// case we still need to mark the file.
if (S && !IsLSDA && Sec->File)
Sec->getFile<ELFT>()->HasLiveCodeOrData = true;

if (Sec->Live)
return;
Sec->Live = true;

Sec->Live = true;
// Add input section to the queue.
if (InputSection *S = dyn_cast<InputSection>(Sec))
if (S)
Queue.push_back(S);
}

template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *Sym) {
if (auto *D = dyn_cast_or_null<Defined>(Sym))
if (auto *IS = dyn_cast_or_null<InputSectionBase>(D->Section))
enqueue(IS, D->Value);
enqueue(IS, D->Value, false);
}

// This is the main function of the garbage collector.
Expand Down Expand Up @@ -239,7 +247,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
continue;

if (isReserved(Sec) || Script->shouldKeep(Sec)) {
enqueue(Sec, 0);
enqueue(Sec, 0, false);
} else if (isValidCIdentifier(Sec->Name)) {
CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec);
CNamedSections[Saver.save("__stop_" + Sec->Name)].push_back(Sec);
Expand All @@ -259,7 +267,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
}

for (InputSectionBase *IS : Sec.DependentSections)
enqueue(IS, 0);
enqueue(IS, 0, false);
}
}

Expand All @@ -285,7 +293,7 @@ template <class ELFT> void elf::markLive() {
// The -gc-sections option works only for SHF_ALLOC sections
// (sections that are memory-mapped at runtime). So we can
// unconditionally make non-SHF_ALLOC sections alive except
// SHF_LINK_ORDER and SHT_REL/SHT_RELA sections.
// SHF_LINK_ORDER and SHT_REL/SHT_RELA sections and .debug sections.
//
// Usually, SHF_ALLOC sections are not removed even if they are
// unreachable through relocations because reachability is not
Expand All @@ -306,13 +314,19 @@ template <class ELFT> void elf::markLive() {
bool IsLinkOrder = (Sec->Flags & SHF_LINK_ORDER);
bool IsRel = (Sec->Type == SHT_REL || Sec->Type == SHT_RELA);

if (!IsAlloc && !IsLinkOrder && !IsRel)
if (!IsAlloc && !IsLinkOrder && !IsRel && !Sec->Debug)
Sec->Live = true;
}

// Follow the graph to mark all live sections.
MarkLive<ELFT>().run();

// Mark debug sections as live in any object file that has a live
// Regular or Merge section.
for (InputSectionBase *Sec : InputSections)
if (Sec->Debug && Sec->getFile<ELFT>()->HasLiveCodeOrData)
Sec->Live = true;

// Report garbage-collected sections.
if (Config->PrintGcSections)
for (InputSectionBase *Sec : InputSections)
Expand Down
3 changes: 3 additions & 0 deletions test/ELF/linkerscript/comdat-gc.s
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

# GC1: Name: .debug_line

# Add .ctors section so all debuginfo isn't GCed
.section .ctors,"ax",@progbits

.file 1 "test/ELF/linkerscript/comdat_gc.s"
.section .text._Z3fooIiEvv,"axG",@progbits,_Z3fooIiEvv,comdat
.loc 1 14
Expand Down
14 changes: 14 additions & 0 deletions test/ELF/linkerscript/debuginfo-gc.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# REQUIRES: x86

# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/comdat-gc.s -o %t1
# RUN: echo "SECTIONS { .text : { *(.text*) } }" > %t.script
# RUN: ld.lld --gc-sections --script %t.script %t %t1 -o %t2
# RUN: llvm-readobj -sections -symbols %t2 | FileCheck %s

# CHECK-NOT: Name: .debug_line

.file 1 "test/ELF/linkerscript/comdat_gc.s"
.section .text._Z3fooIiEvv,"axG",@progbits,_Z3fooIiEvv,comdat
.loc 1 14
ret

0 comments on commit 0c17952

Please sign in to comment.