Skip to content

Commit e9a0743

Browse files
committed
Address various review comments
- Prefer "using" - Defer lookup of sec+offset liveness reasons to printing time - Indent instructions - Avoid unnecessary toStr() - Provide description strings to print with liveness reasons
1 parent 8765bd3 commit e9a0743

File tree

2 files changed

+111
-92
lines changed

2 files changed

+111
-92
lines changed

lld/ELF/MarkLive.cpp

Lines changed: 80 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,16 @@ using namespace lld::elf;
4545

4646
namespace {
4747

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+
};
5058

5159
template <class ELFT, bool TrackWhyLive> class MarkLive {
5260
public:
@@ -57,10 +65,9 @@ template <class ELFT, bool TrackWhyLive> class MarkLive {
5765
void printWhyLive(Symbol *s) const;
5866

5967
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);
6471
void mark();
6572

6673
template <class RelTy>
@@ -80,11 +87,8 @@ template <class ELFT, bool TrackWhyLive> class MarkLive {
8087
// identifiers, so we just store a SmallVector instead of a multimap.
8188
DenseMap<StringRef, SmallVector<InputSectionBase *, 0>> cNamedSections;
8289

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;
8892
};
8993
} // namespace
9094

@@ -116,11 +120,9 @@ void MarkLive<ELFT, TrackWhyLive>::resolveReloc(InputSectionBase &sec,
116120
Symbol &sym = sec.file->getRelocTargetSym(rel);
117121
sym.used = true;
118122

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"};
124126

125127
if (auto *d = dyn_cast<Defined>(&sym)) {
126128
auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section);
@@ -222,7 +224,7 @@ static bool isReserved(InputSectionBase *sec) {
222224
template <class ELFT, bool TrackWhyLive>
223225
void MarkLive<ELFT, TrackWhyLive>::enqueue(InputSectionBase *sec,
224226
uint64_t offset, Symbol *sym,
225-
std::optional<LiveReason> reason) {
227+
LiveReason reason) {
226228
// Usually, a whole section is marked as live or dead, but in mergeable
227229
// (splittable) sections, each piece of data has independent liveness bit.
228230
// So we explicitly tell it which offset is in use.
@@ -241,7 +243,7 @@ void MarkLive<ELFT, TrackWhyLive>::enqueue(InputSectionBase *sec,
241243
// If a specific symbol is referenced, that makes it alive. It may in turn
242244
// make its section alive.
243245
whyLive.try_emplace(sym, reason);
244-
whyLive.try_emplace(sec, sym);
246+
whyLive.try_emplace(sec, LiveReason{sym, "contained live symbol"});
245247
} else {
246248
// Otherwise, the reference generically makes the section live.
247249
whyLive.try_emplace(sec, reason);
@@ -258,59 +260,73 @@ template <class ELFT, bool TrackWhyLive>
258260
void MarkLive<ELFT, TrackWhyLive>::printWhyLive(Symbol *s) const {
259261
// Skip dead symbols. A symbol is dead if it belongs to a dead section.
260262
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())
263265
return;
264266
}
265267

266268
auto msg = Msg(ctx);
267269

268270
const auto printSymbol = [&](Symbol *s) {
269271
if (s->isLocal())
270-
msg << s->file << ":(" << toStr(ctx, *s) << ')';
272+
msg << s->file << ":(" << s << ')';
271273
else
272-
msg << toStr(ctx, *s);
274+
msg << s;
273275
};
274276

275277
msg << "live symbol: ";
276278
printSymbol(s);
277279

278-
std::optional<LiveReason> cur = s;
280+
LiveObject cur = s;
279281
while (true) {
280-
auto it = whyLive.find(*cur);
282+
auto it = whyLive.find(cur);
283+
LiveReason reason;
281284
// If there is a specific reason this object is live...
282285
if (it != whyLive.end()) {
283-
cur = it->second;
286+
reason = it->second;
284287
} 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"};
297299
}
298-
if (!cur)
299-
break;
300300

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+
}
306322
}
307323
}
308324

309325
template <class ELFT, bool TrackWhyLive>
310-
void MarkLive<ELFT, TrackWhyLive>::markSymbol(Symbol *sym) {
326+
void MarkLive<ELFT, TrackWhyLive>::markSymbol(Symbol *sym, StringRef reason) {
311327
if (auto *d = dyn_cast_or_null<Defined>(sym))
312328
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});
314330
}
315331

316332
// This is the main function of the garbage collector.
@@ -324,24 +340,24 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
324340
// file can interpose other ELF file's symbols at runtime.
325341
for (Symbol *sym : ctx.symtab->getSymbols())
326342
if (sym->isExported && sym->partition == partition)
327-
markSymbol(sym);
343+
markSymbol(sym, "externally visible symbol; may interpose");
328344

329345
// If this isn't the main partition, that's all that we need to preserve.
330346
if (partition != 1) {
331347
mark();
332348
return;
333349
}
334350

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");
338354
for (StringRef s : ctx.arg.undefined)
339-
markSymbol(ctx.symtab->find(s));
355+
markSymbol(ctx.symtab->find(s), "undefined command line flag");
340356
for (StringRef s : ctx.script->referencedSymbols)
341-
markSymbol(ctx.symtab->find(s));
357+
markSymbol(ctx.symtab->find(s), "referenced by linker script");
342358
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");
345361
}
346362

347363
// Mark .eh_frame sections as live because there are usually no relocations
@@ -358,7 +374,7 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
358374
}
359375
for (InputSectionBase *sec : ctx.inputSections) {
360376
if (sec->flags & SHF_GNU_RETAIN) {
361-
enqueue(sec);
377+
enqueue(sec, 0, nullptr, {std::nullopt, "retained"});
362378
continue;
363379
}
364380
if (sec->flags & SHF_LINK_ORDER)
@@ -396,8 +412,10 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
396412

397413
// Preserve special sections and those which are specified in linker
398414
// 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"});
401419
} else if ((!ctx.arg.zStartStopGC || sec->name.starts_with("__libc_")) &&
402420
isValidCIdentifier(sec->name)) {
403421
// As a workaround for glibc libc.a before 2.34
@@ -442,11 +460,12 @@ void MarkLive<ELFT, TrackWhyLive>::mark() {
442460
resolveReloc(sec, rel, false);
443461

444462
for (InputSectionBase *isec : sec.dependentSections)
445-
enqueue(isec, 0, nullptr, &sec);
463+
enqueue(isec, 0, nullptr, {&sec, "dependent section"});
446464

447465
// Mark the next group member.
448466
if (sec.nextInSectionGroup)
449-
enqueue(sec.nextInSectionGroup, 0, nullptr, &sec);
467+
enqueue(sec.nextInSectionGroup, 0, nullptr,
468+
{&sec, "next in section group"});
450469
}
451470
}
452471

@@ -466,14 +485,14 @@ void MarkLive<ELFT, TrackWhyLive>::moveToMain() {
466485
if (auto *d = dyn_cast<Defined>(s))
467486
if ((d->type == STT_GNU_IFUNC || d->type == STT_TLS) && d->section &&
468487
d->section->isLive())
469-
markSymbol(s);
488+
markSymbol(s, {});
470489

471490
for (InputSectionBase *sec : ctx.inputSections) {
472491
if (!sec->isLive() || !isValidCIdentifier(sec->name))
473492
continue;
474493
if (ctx.symtab->find(("__start_" + sec->name).str()) ||
475494
ctx.symtab->find(("__stop_" + sec->name).str()))
476-
enqueue(sec);
495+
enqueue(sec, 0, nullptr, {});
477496
}
478497

479498
mark();

0 commit comments

Comments
 (0)