Skip to content

Commit 3a8bb7c

Browse files
committed
Discard debuginfo for object files empty after GC
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 llvm-svn: 358069
1 parent 56f70c6 commit 3a8bb7c

File tree

7 files changed

+56
-19
lines changed

7 files changed

+56
-19
lines changed

lld/ELF/Driver.cpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -1613,11 +1613,8 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
16131613

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

16221619
// The Target instance handles target-specific stuff, such as applying
16231620
// relocations or writing a PLT section. It also contains target-dependent

lld/ELF/InputFiles.h

+4
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ template <class ELFT> class ObjFile : public ELFFileBase {
234234
// but had one or more functions with the no_split_stack attribute.
235235
bool SomeNoSplitStack = false;
236236

237+
// True if the file has any live Regular or Merge sections that aren't
238+
// the LDSA section.
239+
bool HasLiveCodeOrData = false;
240+
237241
// Pointer to this input file's .llvm_addrsig section, if it has one.
238242
const Elf_Shdr *AddrsigSec = nullptr;
239243

lld/ELF/InputSection.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
7171

7272
NumRelocations = 0;
7373
AreRelocsRela = false;
74+
Debug = Name.startswith(".debug") || Name.startswith(".zdebug");
7475

7576
// The ELF spec states that a value of 0 means the section has
7677
// no alignment constraits.

lld/ELF/InputSection.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class SectionBase {
5151

5252
unsigned SectionKind : 3;
5353

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

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

7474
unsigned Bss : 1;
7575

76+
// True if this is a debuginfo section.
77+
unsigned Debug : 1;
78+
7679
// Set for sections that should not be folded by ICF.
7780
unsigned KeepUnique : 1;
7881

@@ -100,8 +103,9 @@ class SectionBase {
100103
uint64_t Entsize, uint64_t Alignment, uint32_t Type,
101104
uint32_t Info, uint32_t Link)
102105
: Name(Name), Repl(this), SectionKind(SectionKind), Live(false),
103-
Assigned(false), Bss(false), KeepUnique(false), Alignment(Alignment),
104-
Flags(Flags), Entsize(Entsize), Type(Type), Link(Link), Info(Info) {}
106+
Assigned(false), Bss(false), Debug(false), KeepUnique(false),
107+
Alignment(Alignment), Flags(Flags), Entsize(Entsize), Type(Type),
108+
Link(Link), Info(Info) {}
105109
};
106110

107111
// This corresponds to a section of an input file.

lld/ELF/MarkLive.cpp

+25-11
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ template <class ELFT> class MarkLive {
4747
void run();
4848

4949
private:
50-
void enqueue(InputSectionBase *Sec, uint64_t Offset);
50+
void enqueue(InputSectionBase *Sec, uint64_t Offset, bool IsLSDA);
5151
void markSymbol(Symbol *Sym);
5252

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

9999
if (!IsLSDA || !(RelSec->Flags & SHF_EXECINSTR))
100-
enqueue(RelSec, Offset);
100+
enqueue(RelSec, Offset, IsLSDA);
101101
return;
102102
}
103103

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

108108
for (InputSectionBase *Sec : CNamedSections.lookup(Sym.getName()))
109-
enqueue(Sec, 0);
109+
enqueue(Sec, 0, IsLSDA);
110110
}
111111

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

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

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

197+
Sec->Live = true;
190198
// Add input section to the queue.
191-
if (InputSection *S = dyn_cast<InputSection>(Sec))
199+
if (S)
192200
Queue.push_back(S);
193201
}
194202

195203
template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *Sym) {
196204
if (auto *D = dyn_cast_or_null<Defined>(Sym))
197205
if (auto *IS = dyn_cast_or_null<InputSectionBase>(D->Section))
198-
enqueue(IS, D->Value);
206+
enqueue(IS, D->Value, false);
199207
}
200208

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

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

261269
for (InputSectionBase *IS : Sec.DependentSections)
262-
enqueue(IS, 0);
270+
enqueue(IS, 0, false);
263271
}
264272
}
265273

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

309-
if (!IsAlloc && !IsLinkOrder && !IsRel)
317+
if (!IsAlloc && !IsLinkOrder && !IsRel && !Sec->Debug)
310318
Sec->Live = true;
311319
}
312320

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

324+
// Mark debug sections as live in any object file that has a live
325+
// Regular or Merge section.
326+
for (InputSectionBase *Sec : InputSections)
327+
if (Sec->Debug && Sec->getFile<ELFT>()->HasLiveCodeOrData)
328+
Sec->Live = true;
329+
316330
// Report garbage-collected sections.
317331
if (Config->PrintGcSections)
318332
for (InputSectionBase *Sec : InputSections)

lld/test/ELF/linkerscript/comdat-gc.s

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
# GC1: Name: .debug_line
1010

11+
# Add .ctors section so all debuginfo isn't GCed
12+
.section .ctors,"ax",@progbits
13+
1114
.file 1 "test/ELF/linkerscript/comdat_gc.s"
1215
.section .text._Z3fooIiEvv,"axG",@progbits,_Z3fooIiEvv,comdat
1316
.loc 1 14
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# REQUIRES: x86
2+
3+
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
4+
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/comdat-gc.s -o %t1
5+
# RUN: echo "SECTIONS { .text : { *(.text*) } }" > %t.script
6+
# RUN: ld.lld --gc-sections --script %t.script %t %t1 -o %t2
7+
# RUN: llvm-readobj -sections -symbols %t2 | FileCheck %s
8+
9+
# CHECK-NOT: Name: .debug_line
10+
11+
.file 1 "test/ELF/linkerscript/comdat_gc.s"
12+
.section .text._Z3fooIiEvv,"axG",@progbits,_Z3fooIiEvv,comdat
13+
.loc 1 14
14+
ret

0 commit comments

Comments
 (0)