@@ -45,8 +45,16 @@ using namespace lld::elf;
45
45
46
46
namespace {
47
47
48
- // Something that can be the most proximate reason that something else is alive.
49
- typedef std::variant<InputSectionBase *, Symbol *> LiveReason;
48
+ using SecOffset = std::pair<InputSectionBase*, unsigned >;
49
+
50
+ // Something that can have an independent reason for being live.
51
+ using LiveObject = std::variant<InputSectionBase *, Symbol *, SecOffset>;
52
+
53
+ // The most proximate reason that an object is live.
54
+ struct LiveReason {
55
+ std::optional<LiveObject> obj;
56
+ StringRef desc;
57
+ };
50
58
51
59
template <class ELFT , bool TrackWhyLive> class MarkLive {
52
60
public:
@@ -57,10 +65,9 @@ template <class ELFT, bool TrackWhyLive> class MarkLive {
57
65
void printWhyLive (Symbol *s) const ;
58
66
59
67
private:
60
- void enqueue (InputSectionBase *sec, uint64_t offset = 0 ,
61
- Symbol *sym = nullptr ,
62
- std::optional<LiveReason> reason = std::nullopt);
63
- void markSymbol (Symbol *sym);
68
+ void enqueue (InputSectionBase *sec, uint64_t offset, Symbol *sym,
69
+ LiveReason reason);
70
+ void markSymbol (Symbol *sym, StringRef reason);
64
71
void mark ();
65
72
66
73
template <class RelTy >
@@ -80,11 +87,8 @@ template <class ELFT, bool TrackWhyLive> class MarkLive {
80
87
// identifiers, so we just store a SmallVector instead of a multimap.
81
88
DenseMap<StringRef, SmallVector<InputSectionBase *, 0 >> cNamedSections;
82
89
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;
90
+ // The most proximate reason that something is live.
91
+ DenseMap<LiveObject, LiveReason> whyLive;
88
92
};
89
93
} // namespace
90
94
@@ -116,11 +120,9 @@ void MarkLive<ELFT, TrackWhyLive>::resolveReloc(InputSectionBase &sec,
116
120
Symbol &sym = sec.file ->getRelocTargetSym (rel);
117
121
sym.used = true ;
118
122
119
- std::optional<LiveReason> reason;
120
- if (TrackWhyLive) {
121
- Defined *reasonSym = sec.getEnclosingSymbol (rel.r_offset );
122
- reason = reasonSym ? LiveReason (reasonSym) : LiveReason (&sec);
123
- }
123
+ LiveReason reason;
124
+ if (TrackWhyLive)
125
+ reason = {SecOffset (&sec, rel.r_offset ), " referenced by" };
124
126
125
127
if (auto *d = dyn_cast<Defined>(&sym)) {
126
128
auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section );
@@ -222,7 +224,7 @@ static bool isReserved(InputSectionBase *sec) {
222
224
template <class ELFT , bool TrackWhyLive>
223
225
void MarkLive<ELFT, TrackWhyLive>::enqueue(InputSectionBase *sec,
224
226
uint64_t offset, Symbol *sym,
225
- std::optional< LiveReason> reason) {
227
+ LiveReason reason) {
226
228
// Usually, a whole section is marked as live or dead, but in mergeable
227
229
// (splittable) sections, each piece of data has independent liveness bit.
228
230
// So we explicitly tell it which offset is in use.
@@ -241,7 +243,7 @@ void MarkLive<ELFT, TrackWhyLive>::enqueue(InputSectionBase *sec,
241
243
// If a specific symbol is referenced, that makes it alive. It may in turn
242
244
// make its section alive.
243
245
whyLive.try_emplace (sym, reason);
244
- whyLive.try_emplace (sec, sym);
246
+ whyLive.try_emplace (sec, LiveReason{ sym, " contained live symbol " } );
245
247
} else {
246
248
// Otherwise, the reference generically makes the section live.
247
249
whyLive.try_emplace (sec, reason);
@@ -258,59 +260,73 @@ template <class ELFT, bool TrackWhyLive>
258
260
void MarkLive<ELFT, TrackWhyLive>::printWhyLive(Symbol *s) const {
259
261
// Skip dead symbols. A symbol is dead if it belongs to a dead section.
260
262
if (auto *d = dyn_cast<Defined>(s)) {
261
- auto *reason = dyn_cast_or_null<InputSectionBase>(d->section );
262
- if (reason && !reason ->isLive ())
263
+ auto *sec = dyn_cast_or_null<InputSectionBase>(d->section );
264
+ if (sec && !sec ->isLive ())
263
265
return ;
264
266
}
265
267
266
268
auto msg = Msg (ctx);
267
269
268
270
const auto printSymbol = [&](Symbol *s) {
269
271
if (s->isLocal ())
270
- msg << s->file << " :(" << toStr (ctx, *s) << ' )' ;
272
+ msg << s->file << " :(" << s << ' )' ;
271
273
else
272
- msg << toStr (ctx, *s) ;
274
+ msg << s ;
273
275
};
274
276
275
277
msg << " live symbol: " ;
276
278
printSymbol (s);
277
279
278
- std::optional<LiveReason> cur = s;
280
+ LiveObject cur = s;
279
281
while (true ) {
280
- auto it = whyLive.find (*cur);
282
+ auto it = whyLive.find (cur);
283
+ LiveReason reason;
281
284
// If there is a specific reason this object is live...
282
285
if (it != whyLive.end ()) {
283
- cur = it->second ;
286
+ reason = it->second ;
284
287
} else {
285
- // This object is live, but it has no tracked reason. It is either
286
- // intrinsically live or an unreferenced symbol in a live section. Return
287
- // in the first case.
288
- if (!std::holds_alternative<Symbol *>(*cur))
289
- return ;
290
- auto *d = dyn_cast<Defined>(std::get<Symbol *>(*cur));
291
- if (!d)
292
- return ;
293
- auto *reason = dyn_cast_or_null<InputSectionBase>(d->section );
294
- if (!reason)
295
- return ;
296
- cur = LiveReason{reason};
288
+ // This object is live, but it has no tracked reason. It must be an
289
+ // unreferenced symbol in a live section or a symbol with no section.
290
+ const auto getParentSec = [&]() -> InputSectionBase * {
291
+ auto *d = dyn_cast<Defined>(std::get<Symbol *>(cur));
292
+ if (!d)
293
+ return nullptr ;
294
+ return dyn_cast_or_null<InputSectionBase>(d->section );
295
+ };
296
+ InputSectionBase *sec = getParentSec ();
297
+ reason = sec ? LiveReason{sec, " in live section" }
298
+ : LiveReason{std::nullopt, " no section" };
297
299
}
298
- if (!cur)
299
- break ;
300
300
301
- msg << " \n >>> kept live by " ;
302
- if (std::holds_alternative<Symbol *>(*cur))
303
- printSymbol (std::get<Symbol *>(*cur));
304
- else
305
- msg << toStr (ctx, std::get<InputSectionBase *>(*cur));
301
+ if (reason.obj ) {
302
+ msg << " \n >>> " << reason.desc << " : " ;
303
+ // The reason may not yet have been resolved to a symbol; do so now.
304
+ if (std::holds_alternative<SecOffset>(*reason.obj )) {
305
+ const auto &so = std::get<SecOffset>(*reason.obj );
306
+ InputSectionBase *sec = so.first ;
307
+ Defined *sym = sec->getEnclosingSymbol (so.second );
308
+ cur = sym ? LiveObject (sym) : LiveObject (sec);
309
+ } else {
310
+ cur = *reason.obj ;
311
+ }
312
+
313
+ if (std::holds_alternative<Symbol *>(cur))
314
+ printSymbol (std::get<Symbol *>(cur));
315
+ else
316
+ msg << std::get<InputSectionBase *>(cur);
317
+ }
318
+ if (!reason.obj ) {
319
+ msg << " (" << reason.desc << ' )' ;
320
+ break ;
321
+ }
306
322
}
307
323
}
308
324
309
325
template <class ELFT , bool TrackWhyLive>
310
- void MarkLive<ELFT, TrackWhyLive>::markSymbol(Symbol *sym) {
326
+ void MarkLive<ELFT, TrackWhyLive>::markSymbol(Symbol *sym, StringRef reason ) {
311
327
if (auto *d = dyn_cast_or_null<Defined>(sym))
312
328
if (auto *isec = dyn_cast_or_null<InputSectionBase>(d->section ))
313
- enqueue (isec, d->value , sym);
329
+ enqueue (isec, d->value , sym, {std::nullopt, reason} );
314
330
}
315
331
316
332
// This is the main function of the garbage collector.
@@ -324,24 +340,24 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
324
340
// file can interpose other ELF file's symbols at runtime.
325
341
for (Symbol *sym : ctx.symtab ->getSymbols ())
326
342
if (sym->isExported && sym->partition == partition)
327
- markSymbol (sym);
343
+ markSymbol (sym, " externally visible symbol; may interpose " );
328
344
329
345
// If this isn't the main partition, that's all that we need to preserve.
330
346
if (partition != 1 ) {
331
347
mark ();
332
348
return ;
333
349
}
334
350
335
- markSymbol (ctx.symtab ->find (ctx.arg .entry ));
336
- markSymbol (ctx.symtab ->find (ctx.arg .init ));
337
- markSymbol (ctx.symtab ->find (ctx.arg .fini ));
351
+ markSymbol (ctx.symtab ->find (ctx.arg .entry ), " entry point " );
352
+ markSymbol (ctx.symtab ->find (ctx.arg .init ), " initializer function " );
353
+ markSymbol (ctx.symtab ->find (ctx.arg .fini ), " finalizer function " );
338
354
for (StringRef s : ctx.arg .undefined )
339
- markSymbol (ctx.symtab ->find (s));
355
+ markSymbol (ctx.symtab ->find (s), " undefined command line flag " );
340
356
for (StringRef s : ctx.script ->referencedSymbols )
341
- markSymbol (ctx.symtab ->find (s));
357
+ markSymbol (ctx.symtab ->find (s), " referenced by linker script " );
342
358
for (auto [symName, _] : ctx.symtab ->cmseSymMap ) {
343
- markSymbol (ctx.symtab ->cmseSymMap [symName].sym );
344
- markSymbol (ctx.symtab ->cmseSymMap [symName].acleSeSym );
359
+ markSymbol (ctx.symtab ->cmseSymMap [symName].sym , " ARM CMSE symbol " );
360
+ markSymbol (ctx.symtab ->cmseSymMap [symName].acleSeSym , " ARM CMSE symbol " );
345
361
}
346
362
347
363
// Mark .eh_frame sections as live because there are usually no relocations
@@ -358,7 +374,7 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
358
374
}
359
375
for (InputSectionBase *sec : ctx.inputSections ) {
360
376
if (sec->flags & SHF_GNU_RETAIN) {
361
- enqueue (sec);
377
+ enqueue (sec, 0 , nullptr , {std::nullopt, " retained " } );
362
378
continue ;
363
379
}
364
380
if (sec->flags & SHF_LINK_ORDER)
@@ -396,8 +412,10 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
396
412
397
413
// Preserve special sections and those which are specified in linker
398
414
// script KEEP command.
399
- if (isReserved (sec) || ctx.script ->shouldKeep (sec)) {
400
- enqueue (sec);
415
+ if (isReserved (sec)) {
416
+ enqueue (sec, 0 , nullptr , {std::nullopt, " reserved" });
417
+ } else if (ctx.script ->shouldKeep (sec)) {
418
+ enqueue (sec, 0 , nullptr , {std::nullopt, " KEEP in linker script" });
401
419
} else if ((!ctx.arg .zStartStopGC || sec->name .starts_with (" __libc_" )) &&
402
420
isValidCIdentifier (sec->name )) {
403
421
// As a workaround for glibc libc.a before 2.34
@@ -442,11 +460,12 @@ void MarkLive<ELFT, TrackWhyLive>::mark() {
442
460
resolveReloc (sec, rel, false );
443
461
444
462
for (InputSectionBase *isec : sec.dependentSections )
445
- enqueue (isec, 0 , nullptr , &sec);
463
+ enqueue (isec, 0 , nullptr , { &sec, " dependent section " } );
446
464
447
465
// Mark the next group member.
448
466
if (sec.nextInSectionGroup )
449
- enqueue (sec.nextInSectionGroup , 0 , nullptr , &sec);
467
+ enqueue (sec.nextInSectionGroup , 0 , nullptr ,
468
+ {&sec, " next in section group" });
450
469
}
451
470
}
452
471
@@ -466,14 +485,14 @@ void MarkLive<ELFT, TrackWhyLive>::moveToMain() {
466
485
if (auto *d = dyn_cast<Defined>(s))
467
486
if ((d->type == STT_GNU_IFUNC || d->type == STT_TLS) && d->section &&
468
487
d->section ->isLive ())
469
- markSymbol (s);
488
+ markSymbol (s, {} );
470
489
471
490
for (InputSectionBase *sec : ctx.inputSections ) {
472
491
if (!sec->isLive () || !isValidCIdentifier (sec->name ))
473
492
continue ;
474
493
if (ctx.symtab ->find ((" __start_" + sec->name ).str ()) ||
475
494
ctx.symtab ->find ((" __stop_" + sec->name ).str ()))
476
- enqueue (sec);
495
+ enqueue (sec, 0 , nullptr , {} );
477
496
}
478
497
479
498
mark ();
0 commit comments