Skip to content

Commit 8765bd3

Browse files
committed
Handle local symbols etc.
- Explicitly track when explicitly live - This prevents e.g. _start from being reported live as part of .start. - Use quad for more control over reloc offsets
1 parent bdc128b commit 8765bd3

File tree

2 files changed

+70
-34
lines changed

2 files changed

+70
-34
lines changed

lld/ELF/MarkLive.cpp

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ template <class ELFT, bool TrackWhyLive> class MarkLive {
8080
// identifiers, so we just store a SmallVector instead of a multimap.
8181
DenseMap<StringRef, SmallVector<InputSectionBase *, 0>> cNamedSections;
8282

83-
// The most proximate reason that something is live. If something doesn't have
84-
// a recorded reason, it is either dead, intrinsically live, or an
85-
// unreferenced symbol in a live section. (These cases are trivially
86-
// detectable and need not be stored.)
87-
DenseMap<LiveReason, LiveReason> whyLive;
83+
// The most proximate reason that something is live. A nullopt means
84+
// "intrinsically live". If something doesn't have a recorded reason, it is
85+
// either dead or an unreferenced symbol in a live section. (These cases are
86+
// trivially detectable and need not be stored.)
87+
DenseMap<LiveReason, std::optional<LiveReason>> whyLive;
8888
};
8989
} // namespace
9090

@@ -157,7 +157,7 @@ void MarkLive<ELFT, TrackWhyLive>::resolveReloc(InputSectionBase &sec,
157157
if (!ss->isWeak()) {
158158
cast<SharedFile>(ss->file)->isNeeded = true;
159159
if (TrackWhyLive)
160-
whyLive.try_emplace(&sym, *reason);
160+
whyLive.try_emplace(&sym, reason);
161161
}
162162
}
163163

@@ -236,15 +236,15 @@ void MarkLive<ELFT, TrackWhyLive>::enqueue(InputSectionBase *sec,
236236
return;
237237
sec->partition = sec->partition ? 1 : partition;
238238

239-
if (TrackWhyLive && reason) {
239+
if (TrackWhyLive) {
240240
if (sym) {
241241
// If a specific symbol is referenced, that makes it alive. It may in turn
242242
// make its section alive.
243-
whyLive.try_emplace(sym, *reason);
243+
whyLive.try_emplace(sym, reason);
244244
whyLive.try_emplace(sec, sym);
245245
} else {
246246
// Otherwise, the reference generically makes the section live.
247-
whyLive.try_emplace(sec, *reason);
247+
whyLive.try_emplace(sec, reason);
248248
}
249249
}
250250

@@ -264,37 +264,45 @@ void MarkLive<ELFT, TrackWhyLive>::printWhyLive(Symbol *s) const {
264264
}
265265

266266
auto msg = Msg(ctx);
267-
msg << "live symbol: " << toStr(ctx, *s);
268267

269-
LiveReason cur = s;
268+
const auto printSymbol = [&](Symbol *s) {
269+
if (s->isLocal())
270+
msg << s->file << ":(" << toStr(ctx, *s) << ')';
271+
else
272+
msg << toStr(ctx, *s);
273+
};
274+
275+
msg << "live symbol: ";
276+
printSymbol(s);
277+
278+
std::optional<LiveReason> cur = s;
270279
while (true) {
271-
auto it = whyLive.find(cur);
280+
auto it = whyLive.find(*cur);
272281
// If there is a specific reason this object is live...
273282
if (it != whyLive.end()) {
274283
cur = it->second;
275284
} else {
276285
// This object is live, but it has no tracked reason. It is either
277286
// intrinsically live or an unreferenced symbol in a live section. Return
278287
// in the first case.
279-
if (!std::holds_alternative<Symbol *>(cur))
288+
if (!std::holds_alternative<Symbol *>(*cur))
280289
return;
281-
auto *d = dyn_cast<Defined>(std::get<Symbol *>(cur));
290+
auto *d = dyn_cast<Defined>(std::get<Symbol *>(*cur));
282291
if (!d)
283292
return;
284293
auto *reason = dyn_cast_or_null<InputSectionBase>(d->section);
285294
if (!reason)
286295
return;
287296
cur = LiveReason{reason};
288297
}
298+
if (!cur)
299+
break;
289300

290301
msg << "\n>>> kept live by ";
291-
if (std::holds_alternative<Symbol *>(cur)) {
292-
auto *s = std::get<Symbol *>(cur);
293-
msg << toStr(ctx, *s);
294-
} else {
295-
auto *s = std::get<InputSectionBase *>(cur);
296-
msg << toStr(ctx, s);
297-
}
302+
if (std::holds_alternative<Symbol *>(*cur))
303+
printSymbol(std::get<Symbol *>(*cur));
304+
else
305+
msg << toStr(ctx, std::get<InputSectionBase *>(*cur));
298306
}
299307
}
300308

@@ -403,12 +411,19 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
403411
mark();
404412

405413
if (TrackWhyLive) {
406-
for (Symbol *sym : ctx.symtab->getSymbols()) {
414+
const auto handleSym = [&](Symbol *sym) {
407415
if (llvm::any_of(ctx.arg.whyLive, [sym](const llvm::GlobPattern &pat) {
408416
return pat.match(sym->getName());
409417
}))
410418
printWhyLive(sym);
411-
}
419+
};
420+
421+
for (Symbol *sym : ctx.symtab->getSymbols())
422+
handleSym(sym);
423+
for (ELFFileBase *file : ctx.objectFiles)
424+
for (Symbol *sym : file->getSymbols())
425+
if (sym->isLocal())
426+
handleSym(sym);
412427
}
413428
}
414429

lld/test/ELF/why-live.s

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
.section ._start,"ax",@progbits
1111
_start:
1212
jmp test_simple
13-
jmp .Llocal
14-
jmp .Llocal_within_symbol
13+
.quad .Lanonymous
14+
.quad .Lanonymous_within_symbol
1515
jmp test_shared
16+
.quad test_local
1617
.size _start, .-_start
1718

1819
.globl test_simple
@@ -25,6 +26,7 @@ jmp test_from_unsized
2526

2627
# SIMPLE: live symbol: test_simple
2728
# SIMPLE-NEXT: >>> kept live by _start
29+
# SIMPLE-NOT: >>>
2830

2931
## Live only by being a member of .test_simple
3032
.globl test_incidental
@@ -37,6 +39,7 @@ jmp test_incidental
3739
# INCIDENTAL-NEXT: >>> kept live by {{.*}}.o:(.test_simple)
3840
# INCIDENTAL-NEXT: >>> kept live by test_simple
3941
# INCIDENTAL-NEXT: >>> kept live by _start
42+
# INCIDENTAL-NOT: >>>
4043

4144
## Reached from a reference in section .test_simple directly, since test_simple is an unsized symbol.
4245
.globl test_from_unsized
@@ -50,6 +53,7 @@ jmp test_from_unsized
5053
# FROM-UNSIZED-NEXT: >>> kept live by {{.*}}.o:(.test_simple)
5154
# FROM-UNSIZED-NEXT: >>> kept live by test_simple
5255
# FROM-UNSIZED-NEXT: >>> kept live by _start
56+
# FROM-UNSIZED-NOT: >>>
5357

5458
## Symbols in dead sections are dead and not reported.
5559
.globl test_dead
@@ -85,43 +89,60 @@ jmp test_retained
8589

8690
# RETAINED: live symbol: test_retained
8791
# RETAINED-NEXT: >>> kept live by {{.*}}:(.test_retained)
92+
# RETAINED-NOT: >>>
8893

89-
## Relocs that reference offsets from sections (e.g., from local symbols) are considered to point to the section if no enclosing symbol exists.
94+
## Relocs that reference offsets from sections (e.g., from anonymous symbols) are considered to point to the section if no enclosing symbol exists.
9095

9196
.globl test_section_offset
9297
.section .test_section_offset,"ax",@progbits
9398
test_section_offset:
9499
jmp test_section_offset
95-
.Llocal:
100+
.Lanonymous:
96101
jmp test_section_offset
97102

98103
# RUN: ld.lld %t.o %t.so -o /dev/null --gc-sections --why-live=test_section_offset | FileCheck %s --check-prefix=SECTION-OFFSET
99104

100-
# SECTION-OFFSET: live symbol: test_section_offset
101-
# SECTION-OFFSET-NEXT: >>> kept live by {{.*}}:(.test_section_offset)
102-
# SECTION-OFFSET-NEXT: >>> kept live by _start
105+
# SECTION-OFFSET: live symbol: test_section_offset
106+
# SECTION-OFFSET-NEXT: >>> kept live by {{.*}}:(.test_section_offset)
107+
# SECTION-OFFSET-NEXT: >>> kept live by _start
108+
# SECTION-OFFSET-NOT: >>>
103109

104-
## Relocs that reference offsets from sections (e.g., from local symbols) are considered to point to the enclosing symbol if one exists.
110+
## Relocs that reference offsets from sections (e.g., from anonymous symbols) are considered to point to the enclosing symbol if one exists.
105111

106112
.globl test_section_offset_within_symbol
107113
.section .test_section_offset_within_symbol,"ax",@progbits
108114
test_section_offset_within_symbol:
109115
jmp test_section_offset_within_symbol
110-
.Llocal_within_symbol:
116+
.Lanonymous_within_symbol:
111117
jmp test_section_offset_within_symbol
112118
.size test_section_offset_within_symbol, .-test_section_offset_within_symbol
113119

114120
# RUN: ld.lld %t.o %t.so -o /dev/null --gc-sections --why-live=test_section_offset_within_symbol | FileCheck %s --check-prefix=SECTION-OFFSET-WITHIN-SYMBOL
115121

116-
# SECTION-OFFSET-WITHIN-SYMBOL: live symbol: test_section_offset_within_symbol
117-
# SECTION-OFFSET-WITHIN-SYMBOL-NEXT: >>> kept live by _start
122+
# SECTION-OFFSET-WITHIN-SYMBOL: live symbol: test_section_offset_within_symbol
123+
# SECTION-OFFSET-WITHIN-SYMBOL-NEXT: >>> kept live by _start
124+
# SECTION-OFFSET-WITHIN-SYMBOL-NOT: >>>
125+
126+
## Local symbols can be queried just like global symbols.
127+
128+
.section .test_local,"ax",@progbits
129+
test_local:
130+
jmp test_local
131+
.size test_local, .-test_local
132+
133+
# RUN: ld.lld %t.o %t.so -o /dev/null --gc-sections --why-live=test_local | FileCheck %s --check-prefix=LOCAL
134+
135+
# LOCAL: live symbol: {{.*}}:(test_local)
136+
# LOCAL-NEXT: >>> kept live by _start
137+
# LOCAL-NOT: >>>
118138

119139
## Shared symbols
120140

121141
# RUN: ld.lld %t.o %t.so -o /dev/null --gc-sections %t.so --why-live=test_shared | FileCheck %s --check-prefix=SHARED
122142

123143
# SHARED: live symbol: test_shared
124144
# SHARED-NEXT: >>> kept live by _start
145+
# SHARED-NOT: >>>
125146

126147
## Globs match multiple cases. Multiple --why-live flags union.
127148

0 commit comments

Comments
 (0)