[lld] [LLD][ELF] Add --why-live flag to report GC liveness reason (PR #119279)

Daniel Thornburgh via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 5 12:03:40 PST 2025


https://github.com/mysterymath updated https://github.com/llvm/llvm-project/pull/119279

>From 802fc57ddd4291404b009aa7e99d3e7bf83ecd74 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 13 Nov 2024 15:12:55 -0800
Subject: [PATCH 01/50] Pass through parent enqueue section and offset

---
 lld/ELF/MarkLive.cpp | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index b6c22884d917698..04caf531720e4a4 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -42,6 +42,11 @@ using namespace lld;
 using namespace lld::elf;
 
 namespace {
+struct LiveParent {
+  InputSectionBase *sec;
+  std::optional<uint64_t> offset;
+};
+
 template <class ELFT> class MarkLive {
 public:
   MarkLive(Ctx &ctx, unsigned partition) : ctx(ctx), partition(partition) {}
@@ -50,7 +55,7 @@ template <class ELFT> class MarkLive {
   void moveToMain();
 
 private:
-  void enqueue(InputSectionBase *sec, uint64_t offset);
+  void enqueue(InputSectionBase *sec, uint64_t offset, std::optional<LiveParent> parent);
   void markSymbol(Symbol *sym);
   void mark();
 
@@ -101,6 +106,8 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
   Symbol &sym = sec.file->getRelocTargetSym(rel);
   sym.used = true;
 
+  LiveParent parent = {&sec, rel.r_offset};
+
   if (auto *d = dyn_cast<Defined>(&sym)) {
     auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section);
     if (!relSec)
@@ -120,7 +127,7 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
     // discarded, marking the LSDA will unnecessarily retain the text section.
     if (!(fromFDE && ((relSec->flags & (SHF_EXECINSTR | SHF_LINK_ORDER)) ||
                       relSec->nextInSectionGroup)))
-      enqueue(relSec, offset);
+      enqueue(relSec, offset, parent);
     return;
   }
 
@@ -129,7 +136,7 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
       cast<SharedFile>(ss->file)->isNeeded = true;
 
   for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
-    enqueue(sec, 0);
+    enqueue(sec, 0, parent);
 }
 
 // The .eh_frame section is an unfortunate special case.
@@ -187,7 +194,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,
+                             std::optional<LiveParent> parent) {
   // Usually, a whole section is marked as live or dead, but in mergeable
   // (splittable) sections, each piece of data has independent liveness bit.
   // So we explicitly tell it which offset is in use.
@@ -209,7 +217,7 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset) {
 template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *sym) {
   if (auto *d = dyn_cast_or_null<Defined>(sym))
     if (auto *isec = dyn_cast_or_null<InputSectionBase>(d->section))
-      enqueue(isec, d->value);
+      enqueue(isec, d->value, std::nullopt);
 }
 
 // This is the main function of the garbage collector.
@@ -256,7 +264,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
   }
   for (InputSectionBase *sec : ctx.inputSections) {
     if (sec->flags & SHF_GNU_RETAIN) {
-      enqueue(sec, 0);
+      enqueue(sec, 0, std::nullopt);
       continue;
     }
     if (sec->flags & SHF_LINK_ORDER)
@@ -295,7 +303,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
     // Preserve special sections and those which are specified in linker
     // script KEEP command.
     if (isReserved(sec) || ctx.script->shouldKeep(sec)) {
-      enqueue(sec, 0);
+      enqueue(sec, 0, std::nullopt);
     } else if ((!ctx.arg.zStartStopGC || sec->name.starts_with("__libc_")) &&
                isValidCIdentifier(sec->name)) {
       // As a workaround for glibc libc.a before 2.34
@@ -323,11 +331,11 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
       resolveReloc(sec, rel, false);
 
     for (InputSectionBase *isec : sec.dependentSections)
-      enqueue(isec, 0);
+      enqueue(isec, 0, LiveParent{&sec, std::nullopt});
 
     // Mark the next group member.
     if (sec.nextInSectionGroup)
-      enqueue(sec.nextInSectionGroup, 0);
+      enqueue(sec.nextInSectionGroup, 0, LiveParent{&sec, std::nullopt});
   }
 }
 
@@ -353,7 +361,7 @@ template <class ELFT> void MarkLive<ELFT>::moveToMain() {
       continue;
     if (ctx.symtab->find(("__start_" + sec->name).str()) ||
         ctx.symtab->find(("__stop_" + sec->name).str()))
-      enqueue(sec, 0);
+      enqueue(sec, 0, std::nullopt);
   }
 
   mark();

>From 6d376d5d528ea1151ee3235f403249f45adb0b24 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 27 Nov 2024 16:07:13 -0800
Subject: [PATCH 02/50] Track parents

---
 lld/ELF/MarkLive.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 04caf531720e4a4..59603e61f4f3cba 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -75,6 +75,8 @@ template <class ELFT> class MarkLive {
   // There are normally few input sections whose names are valid C
   // identifiers, so we just store a SmallVector instead of a multimap.
   DenseMap<StringRef, SmallVector<InputSectionBase *, 0>> cNamedSections;
+
+  DenseMap<InputSectionBase*, LiveParent> parents;
 };
 } // namespace
 
@@ -209,6 +211,9 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
     return;
   sec->partition = sec->partition ? 1 : partition;
 
+  if (parent)
+    parents.try_emplace(sec, *parent);
+
   // Add input section to the queue.
   if (InputSection *s = dyn_cast<InputSection>(sec))
     queue.push_back(s);

>From 5239cdf819ac98cbf879b1c487c2e6ec4e2e59a8 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 2 Dec 2024 15:51:10 -0800
Subject: [PATCH 03/50] Recast as LiveOffset

---
 lld/ELF/MarkLive.cpp | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 59603e61f4f3cba..cd2bc456ea20374 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -42,9 +42,13 @@ using namespace lld;
 using namespace lld::elf;
 
 namespace {
-struct LiveParent {
+struct LiveOffset {
   InputSectionBase *sec;
   std::optional<uint64_t> offset;
+
+  LiveOffset(InputSectionBase *sec,
+             std::optional<uint64_t> offset = std::nullopt)
+      : sec(sec), offset(offset) {}
 };
 
 template <class ELFT> class MarkLive {
@@ -55,7 +59,8 @@ template <class ELFT> class MarkLive {
   void moveToMain();
 
 private:
-  void enqueue(InputSectionBase *sec, uint64_t offset, std::optional<LiveParent> parent);
+  void enqueue(InputSectionBase *sec, uint64_t offset,
+               std::optional<LiveOffset> parent);
   void markSymbol(Symbol *sym);
   void mark();
 
@@ -76,7 +81,7 @@ template <class ELFT> class MarkLive {
   // identifiers, so we just store a SmallVector instead of a multimap.
   DenseMap<StringRef, SmallVector<InputSectionBase *, 0>> cNamedSections;
 
-  DenseMap<InputSectionBase*, LiveParent> parents;
+  DenseMap<LiveOffset, LiveOffset> whyLive;
 };
 } // namespace
 
@@ -108,7 +113,7 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
   Symbol &sym = sec.file->getRelocTargetSym(rel);
   sym.used = true;
 
-  LiveParent parent = {&sec, rel.r_offset};
+  LiveOffset parent = {&sec, rel.r_offset};
 
   if (auto *d = dyn_cast<Defined>(&sym)) {
     auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section);
@@ -197,7 +202,7 @@ static bool isReserved(InputSectionBase *sec) {
 
 template <class ELFT>
 void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
-                             std::optional<LiveParent> parent) {
+                             std::optional<LiveOffset> parent) {
   // Usually, a whole section is marked as live or dead, but in mergeable
   // (splittable) sections, each piece of data has independent liveness bit.
   // So we explicitly tell it which offset is in use.
@@ -212,7 +217,7 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
   sec->partition = sec->partition ? 1 : partition;
 
   if (parent)
-    parents.try_emplace(sec, *parent);
+    whyLive.try_emplace(LiveOffset{sec, offset}, *parent);
 
   // Add input section to the queue.
   if (InputSection *s = dyn_cast<InputSection>(sec))
@@ -336,11 +341,11 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
       resolveReloc(sec, rel, false);
 
     for (InputSectionBase *isec : sec.dependentSections)
-      enqueue(isec, 0, LiveParent{&sec, std::nullopt});
+      enqueue(isec, 0, &sec);
 
     // Mark the next group member.
     if (sec.nextInSectionGroup)
-      enqueue(sec.nextInSectionGroup, 0, LiveParent{&sec, std::nullopt});
+      enqueue(sec.nextInSectionGroup, 0, &sec);
   }
 }
 

>From d025c6976ff3d23cead2fe051770a7f9c3497258 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 2 Dec 2024 16:00:58 -0800
Subject: [PATCH 04/50] Use a pair

---
 lld/ELF/MarkLive.cpp | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index cd2bc456ea20374..90947fc1da22238 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -42,14 +42,7 @@ using namespace lld;
 using namespace lld::elf;
 
 namespace {
-struct LiveOffset {
-  InputSectionBase *sec;
-  std::optional<uint64_t> offset;
-
-  LiveOffset(InputSectionBase *sec,
-             std::optional<uint64_t> offset = std::nullopt)
-      : sec(sec), offset(offset) {}
-};
+typedef std::pair<InputSectionBase *, uint64_t> LiveOffset;
 
 template <class ELFT> class MarkLive {
 public:
@@ -341,11 +334,11 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
       resolveReloc(sec, rel, false);
 
     for (InputSectionBase *isec : sec.dependentSections)
-      enqueue(isec, 0, &sec);
+      enqueue(isec, 0, {{&sec, 0}});
 
     // Mark the next group member.
     if (sec.nextInSectionGroup)
-      enqueue(sec.nextInSectionGroup, 0, &sec);
+      enqueue(sec.nextInSectionGroup, 0, {{&sec, 0}});
   }
 }
 

>From 329c65d1489312726bb6ba966319fe8f531a76c0 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 3 Dec 2024 11:00:50 -0800
Subject: [PATCH 05/50] Also record against the zero offset

---
 lld/ELF/MarkLive.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 90947fc1da22238..869eb87a2c02191 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -209,8 +209,13 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
     return;
   sec->partition = sec->partition ? 1 : partition;
 
-  if (parent)
+  if (parent) {
     whyLive.try_emplace(LiveOffset{sec, offset}, *parent);
+    // Offset zero is treated as a stand-in for the section itself. The parent
+    // is both a specific reason that an offset within this section is alive and
+    // a generic reason the section itself is alive.
+    whyLive.try_emplace(LiveOffset{sec, 0}, *parent);
+  }
 
   // Add input section to the queue.
   if (InputSection *s = dyn_cast<InputSection>(sec))

>From 30be9ebf3ad148bc7805125f082605ba293fba9a Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 3 Dec 2024 11:10:44 -0800
Subject: [PATCH 06/50] Track live objects as either symbols or sections

---
 lld/ELF/MarkLive.cpp | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 869eb87a2c02191..f8cbdd62bf15457 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -29,9 +29,11 @@
 #include "Target.h"
 #include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Strings.h"
+#include "llvm/ADT/DenseMapInfoVariant.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Object/ELF.h"
 #include "llvm/Support/TimeProfiler.h"
+#include <variant>
 #include <vector>
 
 using namespace llvm;
@@ -42,7 +44,7 @@ using namespace lld;
 using namespace lld::elf;
 
 namespace {
-typedef std::pair<InputSectionBase *, uint64_t> LiveOffset;
+typedef std::variant<InputSectionBase *, Defined *> LiveObject;
 
 template <class ELFT> class MarkLive {
 public:
@@ -53,7 +55,7 @@ template <class ELFT> class MarkLive {
 
 private:
   void enqueue(InputSectionBase *sec, uint64_t offset,
-               std::optional<LiveOffset> parent);
+               std::optional<LiveObject> parent);
   void markSymbol(Symbol *sym);
   void mark();
 
@@ -74,7 +76,7 @@ template <class ELFT> class MarkLive {
   // identifiers, so we just store a SmallVector instead of a multimap.
   DenseMap<StringRef, SmallVector<InputSectionBase *, 0>> cNamedSections;
 
-  DenseMap<LiveOffset, LiveOffset> whyLive;
+  DenseMap<LiveObject, LiveObject> whyLive;
 };
 } // namespace
 
@@ -106,7 +108,8 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
   Symbol &sym = sec.file->getRelocTargetSym(rel);
   sym.used = true;
 
-  LiveOffset parent = {&sec, rel.r_offset};
+  Defined *parentSym = sec.getEnclosingSymbol(rel.r_offset);
+  auto parent = parentSym ? LiveObject(parentSym) : LiveObject(&sec);
 
   if (auto *d = dyn_cast<Defined>(&sym)) {
     auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section);
@@ -195,7 +198,7 @@ static bool isReserved(InputSectionBase *sec) {
 
 template <class ELFT>
 void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
-                             std::optional<LiveOffset> parent) {
+                             std::optional<LiveObject> parent) {
   // Usually, a whole section is marked as live or dead, but in mergeable
   // (splittable) sections, each piece of data has independent liveness bit.
   // So we explicitly tell it which offset is in use.
@@ -210,11 +213,10 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
   sec->partition = sec->partition ? 1 : partition;
 
   if (parent) {
-    whyLive.try_emplace(LiveOffset{sec, offset}, *parent);
-    // Offset zero is treated as a stand-in for the section itself. The parent
-    // is both a specific reason that an offset within this section is alive and
-    // a generic reason the section itself is alive.
-    whyLive.try_emplace(LiveOffset{sec, 0}, *parent);
+    whyLive.try_emplace(sec, *parent);
+    Defined *sym = sec->getEnclosingSymbol(offset);
+    if (sym)
+      whyLive.try_emplace(sym, *parent);
   }
 
   // Add input section to the queue.
@@ -339,11 +341,11 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
       resolveReloc(sec, rel, false);
 
     for (InputSectionBase *isec : sec.dependentSections)
-      enqueue(isec, 0, {{&sec, 0}});
+      enqueue(isec, 0, &sec);
 
     // Mark the next group member.
     if (sec.nextInSectionGroup)
-      enqueue(sec.nextInSectionGroup, 0, {{&sec, 0}});
+      enqueue(sec.nextInSectionGroup, 0, &sec);
   }
 }
 

>From 427f6f5a0f737de6a65d44b5e9d1df44565eca5e Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 3 Dec 2024 11:26:04 -0800
Subject: [PATCH 07/50] Be clear about the zero offset vs section distinction

---
 lld/ELF/MarkLive.cpp | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index f8cbdd62bf15457..4c67efe74334f57 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -54,7 +54,7 @@ template <class ELFT> class MarkLive {
   void moveToMain();
 
 private:
-  void enqueue(InputSectionBase *sec, uint64_t offset,
+  void enqueue(InputSectionBase *sec, std::optional<uint64_t> offset,
                std::optional<LiveObject> parent);
   void markSymbol(Symbol *sym);
   void mark();
@@ -139,7 +139,7 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
       cast<SharedFile>(ss->file)->isNeeded = true;
 
   for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
-    enqueue(sec, 0, parent);
+    enqueue(sec, std::nullopt, parent);
 }
 
 // The .eh_frame section is an unfortunate special case.
@@ -197,13 +197,14 @@ static bool isReserved(InputSectionBase *sec) {
 }
 
 template <class ELFT>
-void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
+void MarkLive<ELFT>::enqueue(InputSectionBase *sec,
+                             std::optional<uint64_t> offset,
                              std::optional<LiveObject> parent) {
   // Usually, a whole section is marked as live or dead, but in mergeable
   // (splittable) sections, each piece of data has independent liveness bit.
   // So we explicitly tell it which offset is in use.
   if (auto *ms = dyn_cast<MergeInputSection>(sec))
-    ms->getSectionPiece(offset).live = true;
+    ms->getSectionPiece(offset.value_or(0)).live = true;
 
   // Set Sec->Partition to the meet (i.e. the "minimum") of Partition and
   // Sec->Partition in the following lattice: 1 < other < 0. If Sec->Partition
@@ -214,9 +215,11 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 
   if (parent) {
     whyLive.try_emplace(sec, *parent);
-    Defined *sym = sec->getEnclosingSymbol(offset);
-    if (sym)
-      whyLive.try_emplace(sym, *parent);
+    if (offset) {
+      Defined *sym = sec->getEnclosingSymbol(*offset);
+      if (sym)
+        whyLive.try_emplace(sym, *parent);
+    }
   }
 
   // Add input section to the queue.
@@ -274,7 +277,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
   }
   for (InputSectionBase *sec : ctx.inputSections) {
     if (sec->flags & SHF_GNU_RETAIN) {
-      enqueue(sec, 0, std::nullopt);
+      enqueue(sec, std::nullopt, std::nullopt);
       continue;
     }
     if (sec->flags & SHF_LINK_ORDER)
@@ -313,7 +316,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
     // Preserve special sections and those which are specified in linker
     // script KEEP command.
     if (isReserved(sec) || ctx.script->shouldKeep(sec)) {
-      enqueue(sec, 0, std::nullopt);
+      enqueue(sec, std::nullopt, std::nullopt);
     } else if ((!ctx.arg.zStartStopGC || sec->name.starts_with("__libc_")) &&
                isValidCIdentifier(sec->name)) {
       // As a workaround for glibc libc.a before 2.34
@@ -341,11 +344,11 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
       resolveReloc(sec, rel, false);
 
     for (InputSectionBase *isec : sec.dependentSections)
-      enqueue(isec, 0, &sec);
+      enqueue(isec, std::nullopt, &sec);
 
     // Mark the next group member.
     if (sec.nextInSectionGroup)
-      enqueue(sec.nextInSectionGroup, 0, &sec);
+      enqueue(sec.nextInSectionGroup, std::nullopt, &sec);
   }
 }
 
@@ -371,7 +374,7 @@ template <class ELFT> void MarkLive<ELFT>::moveToMain() {
       continue;
     if (ctx.symtab->find(("__start_" + sec->name).str()) ||
         ctx.symtab->find(("__stop_" + sec->name).str()))
-      enqueue(sec, 0, std::nullopt);
+      enqueue(sec, std::nullopt, std::nullopt);
   }
 
   mark();

>From b9378f5a7e176926f49e44ef684a0218e77cc4e8 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 3 Dec 2024 11:37:59 -0800
Subject: [PATCH 08/50] Track shared symbols too

---
 lld/ELF/MarkLive.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 4c67efe74334f57..c0c6b69aab1d3d2 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -44,7 +44,7 @@ using namespace lld;
 using namespace lld::elf;
 
 namespace {
-typedef std::variant<InputSectionBase *, Defined *> LiveObject;
+typedef std::variant<InputSectionBase *, Symbol *> LiveObject;
 
 template <class ELFT> class MarkLive {
 public:
@@ -134,9 +134,12 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
     return;
   }
 
-  if (auto *ss = dyn_cast<SharedSymbol>(&sym))
-    if (!ss->isWeak())
+  if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
+    if (!ss->isWeak()) {
       cast<SharedFile>(ss->file)->isNeeded = true;
+      whyLive.try_emplace(&sym, parent);
+    }
+  }
 
   for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
     enqueue(sec, std::nullopt, parent);

>From 372760c321e6e751d698683dc6d09801edd86d8a Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 3 Dec 2024 14:49:45 -0800
Subject: [PATCH 09/50] Hax

---
 lld/ELF/MarkLive.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index c0c6b69aab1d3d2..a0f7deec45166b0 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -56,6 +56,7 @@ template <class ELFT> class MarkLive {
 private:
   void enqueue(InputSectionBase *sec, std::optional<uint64_t> offset,
                std::optional<LiveObject> parent);
+  void printWhyLive(const Symbol *s) const;
   void markSymbol(Symbol *sym);
   void mark();
 
@@ -230,6 +231,10 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec,
     queue.push_back(s);
 }
 
+template <class ELFT>
+void MarkLive<ELFT>::printWhyLive(const Symbol *s) const {
+}
+
 template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *sym) {
   if (auto *d = dyn_cast_or_null<Defined>(sym))
     if (auto *isec = dyn_cast_or_null<InputSectionBase>(d->section))
@@ -353,6 +358,8 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
     if (sec.nextInSectionGroup)
       enqueue(sec.nextInSectionGroup, std::nullopt, &sec);
   }
+
+  printWhyLive(ctx.symtab->find("foo"));
 }
 
 // Move the sections for some symbols to the main partition, specifically ifuncs

>From 33d45dabec5e48a9812435ac5da6ed72f9bba0e3 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 4 Dec 2024 14:11:31 -0800
Subject: [PATCH 10/50] Add simple why-live printing fn

---
 lld/ELF/MarkLive.cpp | 27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index a0f7deec45166b0..a5549e93f6a7b7d 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -56,7 +56,7 @@ template <class ELFT> class MarkLive {
 private:
   void enqueue(InputSectionBase *sec, std::optional<uint64_t> offset,
                std::optional<LiveObject> parent);
-  void printWhyLive(const Symbol *s) const;
+  void printWhyLive(Symbol *s) const;
   void markSymbol(Symbol *sym);
   void mark();
 
@@ -231,8 +231,29 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec,
     queue.push_back(s);
 }
 
-template <class ELFT>
-void MarkLive<ELFT>::printWhyLive(const Symbol *s) const {
+template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
+  std::string out = toString(*s) + " from " + toString(s->file);
+  int indent = 2;
+  LiveObject cur = s;
+  while (true) {
+    auto it = whyLive.find(cur);
+    if (it == whyLive.end())
+      if (auto *d = dyn_cast<Defined>(s))
+        it = whyLive.find(LiveObject{d->section});
+    if (it == whyLive.end())
+      break;
+    cur = it->second;
+    out += "\n" + std::string(indent, ' ');
+    if (std::holds_alternative<Symbol *>(cur)) {
+      auto *s = std::get<Symbol *>(cur);
+      out += toString(*s) + " from " + toString(s->file);
+    } else {
+      auto *s = std::get<InputSectionBase *>(cur);
+      // TODO: Fancy formatting
+      out += toString(s);
+    }
+  }
+  message(out);
 }
 
 template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *sym) {

>From b0829a0243c34c42aa8ed26a636bec6e8007ba94 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 4 Dec 2024 14:13:23 -0800
Subject: [PATCH 11/50] Add missing cast

---
 lld/ELF/MarkLive.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index a5549e93f6a7b7d..a7510a361ed4587 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -239,7 +239,8 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
     auto it = whyLive.find(cur);
     if (it == whyLive.end())
       if (auto *d = dyn_cast<Defined>(s))
-        it = whyLive.find(LiveObject{d->section});
+        if (auto *s = dyn_cast<InputSectionBase>(d->section))
+          it = whyLive.find(LiveObject{s});
     if (it == whyLive.end())
       break;
     cur = it->second;

>From c9599fa60ef58d1daebebb2c14c791d192af2965 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 4 Dec 2024 15:14:39 -0800
Subject: [PATCH 12/50] Explicitly mark roots as roots to break cycles

---
 lld/ELF/MarkLive.cpp | 35 ++++++++++++++++++-----------------
 1 file changed, 18 insertions(+), 17 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index a7510a361ed4587..5a57765dcfca021 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -54,8 +54,9 @@ template <class ELFT> class MarkLive {
   void moveToMain();
 
 private:
-  void enqueue(InputSectionBase *sec, std::optional<uint64_t> offset,
-               std::optional<LiveObject> parent);
+  void enqueue(InputSectionBase *sec,
+               std::optional<uint64_t> offset = std::nullopt,
+               std::optional<LiveObject> parent = std::nullopt);
   void printWhyLive(Symbol *s) const;
   void markSymbol(Symbol *sym);
   void mark();
@@ -77,7 +78,7 @@ template <class ELFT> class MarkLive {
   // identifiers, so we just store a SmallVector instead of a multimap.
   DenseMap<StringRef, SmallVector<InputSectionBase *, 0>> cNamedSections;
 
-  DenseMap<LiveObject, LiveObject> whyLive;
+  DenseMap<LiveObject, std::optional<LiveObject>> whyLive;
 };
 } // namespace
 
@@ -143,7 +144,7 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
   }
 
   for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
-    enqueue(sec, std::nullopt, parent);
+    enqueue(sec);
 }
 
 // The .eh_frame section is an unfortunate special case.
@@ -217,13 +218,11 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec,
     return;
   sec->partition = sec->partition ? 1 : partition;
 
-  if (parent) {
-    whyLive.try_emplace(sec, *parent);
-    if (offset) {
-      Defined *sym = sec->getEnclosingSymbol(*offset);
-      if (sym)
-        whyLive.try_emplace(sym, *parent);
-    }
+  whyLive.try_emplace(sec, parent);
+  if (offset) {
+    Defined *sym = sec->getEnclosingSymbol(*offset);
+    if (sym)
+      whyLive.try_emplace(sym, parent);
   }
 
   // Add input section to the queue.
@@ -241,9 +240,11 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
       if (auto *d = dyn_cast<Defined>(s))
         if (auto *s = dyn_cast<InputSectionBase>(d->section))
           it = whyLive.find(LiveObject{s});
-    if (it == whyLive.end())
+    assert(it != whyLive.end() &&
+           "all live objects should have a tracked reason for being live");
+    if (!it->second)
       break;
-    cur = it->second;
+    cur = *it->second;
     out += "\n" + std::string(indent, ' ');
     if (std::holds_alternative<Symbol *>(cur)) {
       auto *s = std::get<Symbol *>(cur);
@@ -260,7 +261,7 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
 template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *sym) {
   if (auto *d = dyn_cast_or_null<Defined>(sym))
     if (auto *isec = dyn_cast_or_null<InputSectionBase>(d->section))
-      enqueue(isec, d->value, std::nullopt);
+      enqueue(isec, d->value);
 }
 
 // This is the main function of the garbage collector.
@@ -307,7 +308,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
   }
   for (InputSectionBase *sec : ctx.inputSections) {
     if (sec->flags & SHF_GNU_RETAIN) {
-      enqueue(sec, std::nullopt, std::nullopt);
+      enqueue(sec);
       continue;
     }
     if (sec->flags & SHF_LINK_ORDER)
@@ -346,7 +347,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
     // Preserve special sections and those which are specified in linker
     // script KEEP command.
     if (isReserved(sec) || ctx.script->shouldKeep(sec)) {
-      enqueue(sec, std::nullopt, std::nullopt);
+      enqueue(sec);
     } else if ((!ctx.arg.zStartStopGC || sec->name.starts_with("__libc_")) &&
                isValidCIdentifier(sec->name)) {
       // As a workaround for glibc libc.a before 2.34
@@ -406,7 +407,7 @@ template <class ELFT> void MarkLive<ELFT>::moveToMain() {
       continue;
     if (ctx.symtab->find(("__start_" + sec->name).str()) ||
         ctx.symtab->find(("__stop_" + sec->name).str()))
-      enqueue(sec, std::nullopt, std::nullopt);
+      enqueue(sec);
   }
 
   mark();

>From b127efb83b3e25204efd7b65d7cb9fad08896d42 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 4 Dec 2024 15:26:49 -0800
Subject: [PATCH 13/50] Find bar actually, not foo

---
 lld/ELF/MarkLive.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 5a57765dcfca021..10c572060da108c 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -382,7 +382,7 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
       enqueue(sec.nextInSectionGroup, std::nullopt, &sec);
   }
 
-  printWhyLive(ctx.symtab->find("foo"));
+  printWhyLive(ctx.symtab->find("bar"));
 }
 
 // Move the sections for some symbols to the main partition, specifically ifuncs

>From 584dfe8b98afc29a45a3cc0b65b97db8d9fa6473 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 4 Dec 2024 15:38:53 -0800
Subject: [PATCH 14/50] Report section membership as a reason for being alive

---
 lld/ELF/MarkLive.cpp | 43 +++++++++++++++++++++++++------------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 10c572060da108c..8287ce3f51af8ff 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -231,29 +231,36 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec,
 }
 
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
-  std::string out = toString(*s) + " from " + toString(s->file);
-  int indent = 2;
-  LiveObject cur = s;
-  while (true) {
-    auto it = whyLive.find(cur);
-    if (it == whyLive.end())
-      if (auto *d = dyn_cast<Defined>(s))
-        if (auto *s = dyn_cast<InputSectionBase>(d->section))
-          it = whyLive.find(LiveObject{s});
-    assert(it != whyLive.end() &&
-           "all live objects should have a tracked reason for being live");
-    if (!it->second)
-      break;
-    cur = *it->second;
-    out += "\n" + std::string(indent, ' ');
-    if (std::holds_alternative<Symbol *>(cur)) {
-      auto *s = std::get<Symbol *>(cur);
+  std::string out;
+  int indent = 0;
+  for (std::optional<LiveObject> cur = s; cur; indent += 2) {
+    if (indent)
+      out += "\n" + std::string(indent, ' ');
+    if (std::holds_alternative<Symbol *>(*cur)) {
+      auto *s = std::get<Symbol *>(*cur);
       out += toString(*s) + " from " + toString(s->file);
     } else {
-      auto *s = std::get<InputSectionBase *>(cur);
+      auto *s = std::get<InputSectionBase *>(*cur);
       // TODO: Fancy formatting
       out += toString(s);
     }
+
+    auto it = whyLive.find(*cur);
+    if (it != whyLive.end()) {
+      // If there is a specific reason this object is live, report it.
+      if (!it->second)
+        break;
+      cur = *it->second;
+    } else {
+      // This object is live merely by being a member of its parent section, so
+      // report the parent.
+      InputSectionBase *parent = nullptr;
+      if (auto *d = dyn_cast<Defined>(s))
+        parent = dyn_cast<InputSectionBase>(d->section);
+      assert(parent &&
+             "all live objects should have a tracked reason for being live");
+      cur = LiveObject{parent};
+    }
   }
   message(out);
 }

>From 3bcd86789bf57b59a8e19c40af4a8d1eac3063ab Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 4 Dec 2024 16:03:17 -0800
Subject: [PATCH 15/50] If a specific symbol is referenced, it's the reason its
 section is alive

---
 lld/ELF/MarkLive.cpp | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 8287ce3f51af8ff..17447330941004e 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -218,11 +218,17 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec,
     return;
   sec->partition = sec->partition ? 1 : partition;
 
-  whyLive.try_emplace(sec, parent);
-  if (offset) {
-    Defined *sym = sec->getEnclosingSymbol(*offset);
-    if (sym)
-      whyLive.try_emplace(sym, parent);
+  Defined *sym = nullptr;
+  if (offset)
+    sym = sec->getEnclosingSymbol(*offset);
+  if (sym) {
+    // If a specific symbol is referenced, the parent makes it alive, and it
+    // (may) makes its section alive.
+    whyLive.try_emplace(sym, parent);
+    whyLive.try_emplace(sec, sym);
+  } else {
+    // Otherwise, the parent generically makes the section itself live.
+    whyLive.try_emplace(sec, parent);
   }
 
   // Add input section to the queue.

>From 94a480843697a9905e020e7fbc6d96b73b74ee6b Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <mysterymath at gmail.com>
Date: Thu, 5 Dec 2024 12:21:51 -0800
Subject: [PATCH 16/50] Determine canonical symbol for a reference

---
 lld/ELF/MarkLive.cpp | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 17447330941004e..14f4694f69772a7 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -54,8 +54,8 @@ template <class ELFT> class MarkLive {
   void moveToMain();
 
 private:
-  void enqueue(InputSectionBase *sec,
-               std::optional<uint64_t> offset = std::nullopt,
+  void enqueue(InputSectionBase *sec, uint64_t offset = 0,
+               Symbol *sym = nullptr,
                std::optional<LiveObject> parent = std::nullopt);
   void printWhyLive(Symbol *s) const;
   void markSymbol(Symbol *sym);
@@ -131,8 +131,13 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
     // group/SHF_LINK_ORDER rules (b) if the associated text section should be
     // discarded, marking the LSDA will unnecessarily retain the text section.
     if (!(fromFDE && ((relSec->flags & (SHF_EXECINSTR | SHF_LINK_ORDER)) ||
-                      relSec->nextInSectionGroup)))
-      enqueue(relSec, offset, parent);
+                      relSec->nextInSectionGroup))) {
+      Symbol *canonicalSym = d;
+      if (offset >= d->value + d->size)
+        if (Symbol *s = relSec->getEnclosingSymbol(offset))
+          canonicalSym = s;
+      enqueue(relSec, offset, canonicalSym, parent);
+    }
     return;
   }
 
@@ -202,14 +207,13 @@ static bool isReserved(InputSectionBase *sec) {
 }
 
 template <class ELFT>
-void MarkLive<ELFT>::enqueue(InputSectionBase *sec,
-                             std::optional<uint64_t> offset,
-                             std::optional<LiveObject> parent) {
+void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
+                             Symbol *sym, std::optional<LiveObject> parent) {
   // Usually, a whole section is marked as live or dead, but in mergeable
   // (splittable) sections, each piece of data has independent liveness bit.
   // So we explicitly tell it which offset is in use.
   if (auto *ms = dyn_cast<MergeInputSection>(sec))
-    ms->getSectionPiece(offset.value_or(0)).live = true;
+    ms->getSectionPiece(offset).live = true;
 
   // Set Sec->Partition to the meet (i.e. the "minimum") of Partition and
   // Sec->Partition in the following lattice: 1 < other < 0. If Sec->Partition
@@ -218,9 +222,6 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec,
     return;
   sec->partition = sec->partition ? 1 : partition;
 
-  Defined *sym = nullptr;
-  if (offset)
-    sym = sec->getEnclosingSymbol(*offset);
   if (sym) {
     // If a specific symbol is referenced, the parent makes it alive, and it
     // (may) makes its section alive.
@@ -388,11 +389,11 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
       resolveReloc(sec, rel, false);
 
     for (InputSectionBase *isec : sec.dependentSections)
-      enqueue(isec, std::nullopt, &sec);
+      enqueue(isec, 0, nullptr, &sec);
 
     // Mark the next group member.
     if (sec.nextInSectionGroup)
-      enqueue(sec.nextInSectionGroup, std::nullopt, &sec);
+      enqueue(sec.nextInSectionGroup, 0, nullptr, &sec);
   }
 
   printWhyLive(ctx.symtab->find("bar"));

>From 22d47b722e1ad57c592b238fe9bde40fb50699a8 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <mysterymath at gmail.com>
Date: Thu, 5 Dec 2024 12:33:45 -0800
Subject: [PATCH 17/50] Refer to sections rather than STT_SECTION symbols

---
 lld/ELF/MarkLive.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 14f4694f69772a7..4ff4616a1103194 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -136,6 +136,8 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
       if (offset >= d->value + d->size)
         if (Symbol *s = relSec->getEnclosingSymbol(offset))
           canonicalSym = s;
+      if (canonicalSym->isSection())
+        canonicalSym = nullptr;
       enqueue(relSec, offset, canonicalSym, parent);
     }
     return;

>From e956dd56dced83d90a6b8ac0ae93c5ce8f748e42 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <mysterymath at gmail.com>
Date: Thu, 5 Dec 2024 12:43:35 -0800
Subject: [PATCH 18/50] Encode parent for named sections

---
 lld/ELF/MarkLive.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 4ff4616a1103194..f3af48216111d91 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -151,7 +151,7 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
   }
 
   for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
-    enqueue(sec);
+    enqueue(sec, 0, nullptr, parent);
 }
 
 // The .eh_frame section is an unfortunate special case.

>From b7fecebe09c35fe03b5a0b38503c2d57a6d6bbe6 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <mysterymath at gmail.com>
Date: Thu, 5 Dec 2024 12:47:18 -0800
Subject: [PATCH 19/50] No defaults; was missing things

---
 lld/ELF/MarkLive.cpp | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index f3af48216111d91..79834da67f5bcec 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -54,9 +54,8 @@ template <class ELFT> class MarkLive {
   void moveToMain();
 
 private:
-  void enqueue(InputSectionBase *sec, uint64_t offset = 0,
-               Symbol *sym = nullptr,
-               std::optional<LiveObject> parent = std::nullopt);
+  void enqueue(InputSectionBase *sec, uint64_t offset, Symbol *sym,
+               std::optional<LiveObject> parent);
   void printWhyLive(Symbol *s) const;
   void markSymbol(Symbol *sym);
   void mark();
@@ -277,7 +276,7 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
 template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *sym) {
   if (auto *d = dyn_cast_or_null<Defined>(sym))
     if (auto *isec = dyn_cast_or_null<InputSectionBase>(d->section))
-      enqueue(isec, d->value);
+      enqueue(isec, d->value, sym, std::nullopt);
 }
 
 // This is the main function of the garbage collector.
@@ -324,7 +323,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
   }
   for (InputSectionBase *sec : ctx.inputSections) {
     if (sec->flags & SHF_GNU_RETAIN) {
-      enqueue(sec);
+      enqueue(sec, 0, nullptr, std::nullopt);
       continue;
     }
     if (sec->flags & SHF_LINK_ORDER)
@@ -363,7 +362,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
     // Preserve special sections and those which are specified in linker
     // script KEEP command.
     if (isReserved(sec) || ctx.script->shouldKeep(sec)) {
-      enqueue(sec);
+      enqueue(sec, 0, nullptr, std::nullopt);
     } else if ((!ctx.arg.zStartStopGC || sec->name.starts_with("__libc_")) &&
                isValidCIdentifier(sec->name)) {
       // As a workaround for glibc libc.a before 2.34
@@ -423,7 +422,7 @@ template <class ELFT> void MarkLive<ELFT>::moveToMain() {
       continue;
     if (ctx.symtab->find(("__start_" + sec->name).str()) ||
         ctx.symtab->find(("__stop_" + sec->name).str()))
-      enqueue(sec);
+      enqueue(sec, 0, nullptr, std::nullopt);
   }
 
   mark();

>From 715e0683ded64cdf58cf5b0d1979aabfe691c08b Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Fri, 6 Dec 2024 15:31:52 -0800
Subject: [PATCH 20/50] Add why-live option

---
 lld/ELF/Options.td | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index c31875305952fb2..93c7293c2ee1512 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -559,6 +559,10 @@ defm wrap : Eq<"wrap", "Redirect symbol references to __wrap_symbol and "
                        "__real_symbol references to symbol">,
             MetaVarName<"<symbol>">;
 
+defm why_live : EEq<"why-live", "Report a chain of references to <symbol-glob> that keeps it from "
+                                "being garbage collected">,
+                MetaVarName<"<symbol-glob>">;
+
 def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
   HelpText<"Linker option extensions">;
 

>From 6484056b14232501510ffc66aeac70ee2d98a06f Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Fri, 6 Dec 2024 15:34:06 -0800
Subject: [PATCH 21/50] toStr

---
 lld/ELF/MarkLive.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 79834da67f5bcec..025ff9b728ab62a 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -246,11 +246,11 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
       out += "\n" + std::string(indent, ' ');
     if (std::holds_alternative<Symbol *>(*cur)) {
       auto *s = std::get<Symbol *>(*cur);
-      out += toString(*s) + " from " + toString(s->file);
+      out += toStr(ctx, *s) + " from " + toStr(ctx, s->file);
     } else {
       auto *s = std::get<InputSectionBase *>(*cur);
       // TODO: Fancy formatting
-      out += toString(s);
+      out += toStr(ctx, s);
     }
 
     auto it = whyLive.find(*cur);

>From b42bd9474f263180f652fcbb939b95078c539462 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Fri, 6 Dec 2024 15:36:35 -0800
Subject: [PATCH 22/50] Better wording

---
 lld/ELF/Options.td | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 93c7293c2ee1512..2e7ee35e762f2d7 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -559,9 +559,9 @@ defm wrap : Eq<"wrap", "Redirect symbol references to __wrap_symbol and "
                        "__real_symbol references to symbol">,
             MetaVarName<"<symbol>">;
 
-defm why_live : EEq<"why-live", "Report a chain of references to <symbol-glob> that keeps it from "
-                                "being garbage collected">,
-                MetaVarName<"<symbol-glob>">;
+defm why_live : EEq<"why-live", "Report a chain of references preventing garbage collection for "
+                                "each symbol matching <glob>">,
+                MetaVarName<"<glob>">;
 
 def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
   HelpText<"Linker option extensions">;

>From 7bca5d534c56e5b2c08a8886e5dcadc2b6d87193 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 9 Dec 2024 11:51:12 -0800
Subject: [PATCH 23/50] Parse whylive arg

---
 lld/ELF/Config.h   | 1 +
 lld/ELF/Driver.cpp | 9 +++++++++
 2 files changed, 10 insertions(+)

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index b2859486d58e93a..12164f599934363 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -223,6 +223,7 @@ struct Config {
   llvm::StringRef thinLTOCacheDir;
   llvm::StringRef thinLTOIndexOnlyArg;
   llvm::StringRef whyExtract;
+  llvm::SmallVector<llvm::GlobPattern, 0> whyLive;
   llvm::StringRef cmseInputLib;
   llvm::StringRef cmseOutputLib;
   StringRef zBtiReport = "none";
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 13e8f8ce6df2077..db0b2ea8afcf0a8 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1472,6 +1472,15 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
   ctx.arg.warnSymbolOrdering =
       args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true);
   ctx.arg.whyExtract = args.getLastArgValue(OPT_why_extract);
+  for (opt::Arg *arg : args.filtered(OPT_why_live)) {
+    StringRef value(arg->getValue());
+    if (Expected<GlobPattern> pat = GlobPattern::create(arg->getValue())) {
+      ctx.arg.whyLive.emplace_back(std::move(*pat));
+    } else {
+      ErrAlways(ctx) << arg->getSpelling() << ": " << pat.takeError();
+      continue;
+    }
+  }
   ctx.arg.zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true);
   ctx.arg.zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true);
   ctx.arg.zForceBti = hasZOption(args, "force-bti");

>From e26bbf21ae29f2502d2b87b4aa616a8d8eaf93c0 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 9 Dec 2024 12:18:28 -0800
Subject: [PATCH 24/50] Connect whylive pattern matching

---
 lld/ELF/MarkLive.cpp | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 025ff9b728ab62a..952fa7b9fe2996f 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -397,8 +397,13 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
       enqueue(sec.nextInSectionGroup, 0, nullptr, &sec);
   }
 
-  printWhyLive(ctx.symtab->find("bar"));
-}
+  for (Symbol *sym : ctx.symtab->getSymbols()) {
+    if (llvm::any_of(ctx.arg.whyLive, [sym](const llvm::GlobPattern &pat) {
+          return pat.match(sym->getName());
+        }))
+      printWhyLive(sym);
+  }
+  }
 
 // Move the sections for some symbols to the main partition, specifically ifuncs
 // (because they can result in an IRELATIVE being added to the main partition's

>From 932cb8cefd0772fc3f703ffb895d28ba5471beec Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 9 Dec 2024 14:30:19 -0800
Subject: [PATCH 25/50] Don't trigger assertion for dead symbols

---
 lld/ELF/MarkLive.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 952fa7b9fe2996f..27131d043780938 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -241,6 +241,8 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
   std::string out;
   int indent = 0;
+  if (!whyLive.contains(s))
+    return;
   for (std::optional<LiveObject> cur = s; cur; indent += 2) {
     if (indent)
       out += "\n" + std::string(indent, ' ');
@@ -403,7 +405,7 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
         }))
       printWhyLive(sym);
   }
-  }
+}
 
 // Move the sections for some symbols to the main partition, specifically ifuncs
 // (because they can result in an IRELATIVE being added to the main partition's

>From cf794154fc7db9b9b61ab36d508dddbc736f0797 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 19 Dec 2024 14:14:09 -0800
Subject: [PATCH 26/50] Optional args

---
 lld/ELF/MarkLive.cpp | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 27131d043780938..1b05b5845a29e6b 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -54,8 +54,9 @@ template <class ELFT> class MarkLive {
   void moveToMain();
 
 private:
-  void enqueue(InputSectionBase *sec, uint64_t offset, Symbol *sym,
-               std::optional<LiveObject> parent);
+  void enqueue(InputSectionBase *sec, uint64_t offset = 0,
+               Symbol *sym = nullptr,
+               std::optional<LiveObject> parent = std::nullopt);
   void printWhyLive(Symbol *s) const;
   void markSymbol(Symbol *sym);
   void mark();
@@ -278,7 +279,7 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
 template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *sym) {
   if (auto *d = dyn_cast_or_null<Defined>(sym))
     if (auto *isec = dyn_cast_or_null<InputSectionBase>(d->section))
-      enqueue(isec, d->value, sym, std::nullopt);
+      enqueue(isec, d->value, sym);
 }
 
 // This is the main function of the garbage collector.
@@ -364,7 +365,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
     // Preserve special sections and those which are specified in linker
     // script KEEP command.
     if (isReserved(sec) || ctx.script->shouldKeep(sec)) {
-      enqueue(sec, 0, nullptr, std::nullopt);
+      enqueue(sec);
     } else if ((!ctx.arg.zStartStopGC || sec->name.starts_with("__libc_")) &&
                isValidCIdentifier(sec->name)) {
       // As a workaround for glibc libc.a before 2.34
@@ -429,7 +430,7 @@ template <class ELFT> void MarkLive<ELFT>::moveToMain() {
       continue;
     if (ctx.symtab->find(("__start_" + sec->name).str()) ||
         ctx.symtab->find(("__stop_" + sec->name).str()))
-      enqueue(sec, 0, nullptr, std::nullopt);
+      enqueue(sec);
   }
 
   mark();

>From 6c0232b5ab1bd82c219fed2d1f9b5763efa0b79d Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 19 Dec 2024 14:25:53 -0800
Subject: [PATCH 27/50] Simplify and correct canonical symbol detection

---
 lld/ELF/MarkLive.cpp | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 1b05b5845a29e6b..7c98ea00acd64f2 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -132,12 +132,13 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
     // discarded, marking the LSDA will unnecessarily retain the text section.
     if (!(fromFDE && ((relSec->flags & (SHF_EXECINSTR | SHF_LINK_ORDER)) ||
                       relSec->nextInSectionGroup))) {
-      Symbol *canonicalSym = d;
-      if (offset >= d->value + d->size)
-        if (Symbol *s = relSec->getEnclosingSymbol(offset))
+      Symbol *canonicalSym = nullptr;
+      if (!d->isSection()) {
+        if (offset < d->value + d->size)
+          canonicalSym = d;
+        else if (Symbol *s = relSec->getEnclosingSymbol(offset))
           canonicalSym = s;
-      if (canonicalSym->isSection())
-        canonicalSym = nullptr;
+      }
       enqueue(relSec, offset, canonicalSym, parent);
     }
     return;

>From bf7cb603e3687946ffe58db23a39643735722b36 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 19 Dec 2024 14:46:18 -0800
Subject: [PATCH 28/50] Trust the symbol reference; it's all we've got

---
 lld/ELF/MarkLive.cpp | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 7c98ea00acd64f2..ac1359a8f861d97 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -132,13 +132,10 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
     // discarded, marking the LSDA will unnecessarily retain the text section.
     if (!(fromFDE && ((relSec->flags & (SHF_EXECINSTR | SHF_LINK_ORDER)) ||
                       relSec->nextInSectionGroup))) {
-      Symbol *canonicalSym = nullptr;
-      if (!d->isSection()) {
-        if (offset < d->value + d->size)
-          canonicalSym = d;
-        else if (Symbol *s = relSec->getEnclosingSymbol(offset))
+      Symbol *canonicalSym = d;
+      if (d->isSection())
+        if (Symbol *s = relSec->getEnclosingSymbol(offset))
           canonicalSym = s;
-      }
       enqueue(relSec, offset, canonicalSym, parent);
     }
     return;

>From 224769456be2d7e01ef45d122e58762a1d5e59b2 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 29 Jan 2025 15:46:25 -0800
Subject: [PATCH 29/50] Initial why-live test

---
 lld/test/ELF/why-live.s | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 lld/test/ELF/why-live.s

diff --git a/lld/test/ELF/why-live.s b/lld/test/ELF/why-live.s
new file mode 100644
index 000000000000000..34a6c38ec88e499
--- /dev/null
+++ b/lld/test/ELF/why-live.s
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -n -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=a | FileCheck %s
+
+# CHECK: blah
+
+.globl _start
+.section ._start,"ax", at progbits
+_start:
+jmp a
+
+.globl a
+.section .a,"ax", at progbits
+a:
+jmp a
+

>From 2d50c974b6939847bddcdffb6c0917e7d4887e55 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 29 Jan 2025 16:16:35 -0800
Subject: [PATCH 30/50] Improve syntax for printing

---
 lld/ELF/MarkLive.cpp | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index ac1359a8f861d97..214281c99e04b45 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -238,20 +238,23 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 }
 
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
-  std::string out;
-  int indent = 0;
   if (!whyLive.contains(s))
     return;
-  for (std::optional<LiveObject> cur = s; cur; indent += 2) {
-    if (indent)
-      out += "\n" + std::string(indent, ' ');
+  auto diag = Msg(ctx);
+  bool first = true;
+  for (std::optional<LiveObject> cur = s; cur;) {
     if (std::holds_alternative<Symbol *>(*cur)) {
       auto *s = std::get<Symbol *>(*cur);
-      out += toStr(ctx, *s) + " from " + toStr(ctx, s->file);
+      // Match the syntax for sections below.
+      diag << toStr(ctx, s->file) << ":(" << toStr(ctx, *s) << ')';
     } else {
       auto *s = std::get<InputSectionBase *>(*cur);
-      // TODO: Fancy formatting
-      out += toStr(ctx, s);
+      diag << toStr(ctx, s);
+    }
+
+    if (first) {
+      diag << " live because:";
+      first = false;
     }
 
     auto it = whyLive.find(*cur);
@@ -270,8 +273,11 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
              "all live objects should have a tracked reason for being live");
       cur = LiveObject{parent};
     }
+
+    if (cur)
+      diag << "\n>>> referenced by "
+           << (std::holds_alternative<Symbol *>(*cur) ? "symbol " : "section ");
   }
-  message(out);
 }
 
 template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *sym) {

>From a107cc0affaa64aef26b650dfb05a828ef2d1b7e Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 30 Jan 2025 14:37:45 -0800
Subject: [PATCH 31/50] Diag->msg

---
 lld/ELF/MarkLive.cpp | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 214281c99e04b45..63acb8efc07569a 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -240,20 +240,21 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
   if (!whyLive.contains(s))
     return;
-  auto diag = Msg(ctx);
+
+  auto msg = Msg(ctx);
   bool first = true;
   for (std::optional<LiveObject> cur = s; cur;) {
     if (std::holds_alternative<Symbol *>(*cur)) {
       auto *s = std::get<Symbol *>(*cur);
       // Match the syntax for sections below.
-      diag << toStr(ctx, s->file) << ":(" << toStr(ctx, *s) << ')';
+      msg << toStr(ctx, s->file) << ":(" << toStr(ctx, *s) << ')';
     } else {
       auto *s = std::get<InputSectionBase *>(*cur);
-      diag << toStr(ctx, s);
+      msg << toStr(ctx, s);
     }
 
     if (first) {
-      diag << " live because:";
+      msg << " live because:";
       first = false;
     }
 
@@ -275,8 +276,8 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
     }
 
     if (cur)
-      diag << "\n>>> referenced by "
-           << (std::holds_alternative<Symbol *>(*cur) ? "symbol " : "section ");
+      msg << "\n>>> referenced by "
+          << (std::holds_alternative<Symbol *>(*cur) ? "symbol " : "section ");
   }
 }
 

>From 7bdca78f0fb2b1a055aaa603969fb9336b45952c Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 30 Jan 2025 14:41:32 -0800
Subject: [PATCH 32/50] Disambiguate between inclusion and reference

---
 lld/ELF/MarkLive.cpp | 40 +++++++++++++++++++++-------------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 63acb8efc07569a..3798c453489f6ac 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -242,28 +242,19 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
     return;
 
   auto msg = Msg(ctx);
-  bool first = true;
-  for (std::optional<LiveObject> cur = s; cur;) {
-    if (std::holds_alternative<Symbol *>(*cur)) {
-      auto *s = std::get<Symbol *>(*cur);
-      // Match the syntax for sections below.
-      msg << toStr(ctx, s->file) << ":(" << toStr(ctx, *s) << ')';
-    } else {
-      auto *s = std::get<InputSectionBase *>(*cur);
-      msg << toStr(ctx, s);
-    }
 
-    if (first) {
-      msg << " live because:";
-      first = false;
-    }
+  msg << "live symbol: " << toStr(ctx, *s);
 
+  std::optional<LiveObject> cur = s;
+  while (cur) {
+    std::optional<LiveObject> next;
     auto it = whyLive.find(*cur);
     if (it != whyLive.end()) {
       // If there is a specific reason this object is live, report it.
       if (!it->second)
         break;
-      cur = *it->second;
+      next = *it->second;
+      msg << "\n>>> referenced by ";
     } else {
       // This object is live merely by being a member of its parent section, so
       // report the parent.
@@ -272,12 +263,23 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
         parent = dyn_cast<InputSectionBase>(d->section);
       assert(parent &&
              "all live objects should have a tracked reason for being live");
-      cur = LiveObject{parent};
+      next = LiveObject{parent};
+      msg << "\n>>> included in ";
+    }
+
+    if (next)
+      msg << (std::holds_alternative<Symbol *>(*cur) ? "symbol " : "section ");
+
+    if (std::holds_alternative<Symbol *>(*next)) {
+      auto *s = std::get<Symbol *>(*next);
+      // Match the syntax for sections below.
+      msg << toStr(ctx, s->file) << ":(" << toStr(ctx, *s) << ')';
+    } else {
+      auto *s = std::get<InputSectionBase *>(*next);
+      msg << toStr(ctx, s);
     }
 
-    if (cur)
-      msg << "\n>>> referenced by "
-          << (std::holds_alternative<Symbol *>(*cur) ? "symbol " : "section ");
+    cur = next;
   }
 }
 

>From 5c5c128c87d29426d48b70a4cddb4c839a102b15 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 30 Jan 2025 14:47:22 -0800
Subject: [PATCH 33/50] Cleanup

---
 lld/ELF/MarkLive.cpp | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 3798c453489f6ac..ccf8484a5cfa32d 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -246,14 +246,13 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
   msg << "live symbol: " << toStr(ctx, *s);
 
   std::optional<LiveObject> cur = s;
-  while (cur) {
-    std::optional<LiveObject> next;
+  while (true) {
     auto it = whyLive.find(*cur);
     if (it != whyLive.end()) {
       // If there is a specific reason this object is live, report it.
       if (!it->second)
         break;
-      next = *it->second;
+      cur = *it->second;
       msg << "\n>>> referenced by ";
     } else {
       // This object is live merely by being a member of its parent section, so
@@ -263,23 +262,20 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
         parent = dyn_cast<InputSectionBase>(d->section);
       assert(parent &&
              "all live objects should have a tracked reason for being live");
-      next = LiveObject{parent};
+      cur = LiveObject{parent};
       msg << "\n>>> included in ";
     }
 
-    if (next)
-      msg << (std::holds_alternative<Symbol *>(*cur) ? "symbol " : "section ");
+    msg << (std::holds_alternative<Symbol *>(*cur) ? "symbol " : "section ");
 
-    if (std::holds_alternative<Symbol *>(*next)) {
-      auto *s = std::get<Symbol *>(*next);
+    if (std::holds_alternative<Symbol *>(*cur)) {
+      auto *s = std::get<Symbol *>(*cur);
       // Match the syntax for sections below.
       msg << toStr(ctx, s->file) << ":(" << toStr(ctx, *s) << ')';
     } else {
-      auto *s = std::get<InputSectionBase *>(*next);
+      auto *s = std::get<InputSectionBase *>(*cur);
       msg << toStr(ctx, s);
     }
-
-    cur = next;
   }
 }
 

>From 5cdf8a93825b0cc59297bd64499d29d28266713d Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 30 Jan 2025 14:47:56 -0800
Subject: [PATCH 34/50] More cleanup

---
 lld/ELF/MarkLive.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index ccf8484a5cfa32d..408e6bbfd55f2ae 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -266,15 +266,13 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
       msg << "\n>>> included in ";
     }
 
-    msg << (std::holds_alternative<Symbol *>(*cur) ? "symbol " : "section ");
-
     if (std::holds_alternative<Symbol *>(*cur)) {
       auto *s = std::get<Symbol *>(*cur);
       // Match the syntax for sections below.
-      msg << toStr(ctx, s->file) << ":(" << toStr(ctx, *s) << ')';
+      msg << "symbol " << toStr(ctx, s->file) << ":(" << toStr(ctx, *s) << ')';
     } else {
       auto *s = std::get<InputSectionBase *>(*cur);
-      msg << toStr(ctx, s);
+      msg << "section " << toStr(ctx, s);
     }
   }
 }

>From dbaa4043b1a7e7f21e0986f5cfd53a05963907d5 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 3 Feb 2025 15:50:24 -0800
Subject: [PATCH 35/50] Simplify aliveness reason

---
 lld/ELF/MarkLive.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 408e6bbfd55f2ae..e2cb7bf14e410f3 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -253,7 +253,6 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
       if (!it->second)
         break;
       cur = *it->second;
-      msg << "\n>>> referenced by ";
     } else {
       // This object is live merely by being a member of its parent section, so
       // report the parent.
@@ -263,16 +262,15 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
       assert(parent &&
              "all live objects should have a tracked reason for being live");
       cur = LiveObject{parent};
-      msg << "\n>>> included in ";
     }
 
+    msg << "\n>>> alive because of ";
     if (std::holds_alternative<Symbol *>(*cur)) {
       auto *s = std::get<Symbol *>(*cur);
-      // Match the syntax for sections below.
-      msg << "symbol " << toStr(ctx, s->file) << ":(" << toStr(ctx, *s) << ')';
+      msg << toStr(ctx, *s);
     } else {
       auto *s = std::get<InputSectionBase *>(*cur);
-      msg << "section " << toStr(ctx, s);
+      msg << toStr(ctx, s);
     }
   }
 }

>From 8cca3799bb204acf774397127ba87df70fbc77d8 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 3 Feb 2025 16:15:32 -0800
Subject: [PATCH 36/50] Complete the first test

---
 lld/test/ELF/why-live.s | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lld/test/ELF/why-live.s b/lld/test/ELF/why-live.s
index 34a6c38ec88e499..fe88db45264d838 100644
--- a/lld/test/ELF/why-live.s
+++ b/lld/test/ELF/why-live.s
@@ -3,7 +3,9 @@
 # RUN: llvm-mc -n -filetype=obj -triple=x86_64 %s -o %t.o
 # RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=a | FileCheck %s
 
-# CHECK: blah
+# CHECK: live symbol: a
+# CHECK: >>> alive because of {{.*}}.o:(._start)
+# CHECK: >>> alive because of _start
 
 .globl _start
 .section ._start,"ax", at progbits

>From b42f411b58965cd44a3ea69b1f2c958b63a3d8c8 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 3 Feb 2025 16:25:59 -0800
Subject: [PATCH 37/50] Comment out things not yet tested

---
 lld/ELF/MarkLive.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index e2cb7bf14e410f3..08ae8c5715d69da 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -141,12 +141,14 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
     return;
   }
 
+#if 0
   if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
     if (!ss->isWeak()) {
       cast<SharedFile>(ss->file)->isNeeded = true;
       whyLive.try_emplace(&sym, parent);
     }
   }
+#endif
 
   for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
     enqueue(sec, 0, nullptr, parent);
@@ -229,7 +231,7 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
     whyLive.try_emplace(sec, sym);
   } else {
     // Otherwise, the parent generically makes the section itself live.
-    whyLive.try_emplace(sec, parent);
+    //whyLive.try_emplace(sec, parent);
   }
 
   // Add input section to the queue.
@@ -256,12 +258,14 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
     } else {
       // This object is live merely by being a member of its parent section, so
       // report the parent.
+#if 0
       InputSectionBase *parent = nullptr;
       if (auto *d = dyn_cast<Defined>(s))
         parent = dyn_cast<InputSectionBase>(d->section);
       assert(parent &&
              "all live objects should have a tracked reason for being live");
       cur = LiveObject{parent};
+#endif
     }
 
     msg << "\n>>> alive because of ";

>From e3b779b1d13bc2f0e66326fa03e18eb9da126165 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 3 Feb 2025 16:26:53 -0800
Subject: [PATCH 38/50] Bit more

---
 lld/ELF/MarkLive.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 08ae8c5715d69da..1f31c33c31023d3 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -240,8 +240,8 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 }
 
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
-  if (!whyLive.contains(s))
-    return;
+  //if (!whyLive.contains(s))
+    //return;
 
   auto msg = Msg(ctx);
 

>From 7d7b4567cfcdd05fa1fd844485e15716110d8c74 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 3 Feb 2025 16:33:51 -0800
Subject: [PATCH 39/50] Test incidental section liveness for symbols

---
 lld/ELF/MarkLive.cpp    |  2 --
 lld/test/ELF/why-live.s | 30 +++++++++++++++++++++---------
 2 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 1f31c33c31023d3..98a8fc8dba5aebf 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -258,14 +258,12 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
     } else {
       // This object is live merely by being a member of its parent section, so
       // report the parent.
-#if 0
       InputSectionBase *parent = nullptr;
       if (auto *d = dyn_cast<Defined>(s))
         parent = dyn_cast<InputSectionBase>(d->section);
       assert(parent &&
              "all live objects should have a tracked reason for being live");
       cur = LiveObject{parent};
-#endif
     }
 
     msg << "\n>>> alive because of ";
diff --git a/lld/test/ELF/why-live.s b/lld/test/ELF/why-live.s
index fe88db45264d838..dac35d50736f097 100644
--- a/lld/test/ELF/why-live.s
+++ b/lld/test/ELF/why-live.s
@@ -1,19 +1,31 @@
 # REQUIRES: x86
 
 # RUN: llvm-mc -n -filetype=obj -triple=x86_64 %s -o %t.o
-# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=a | FileCheck %s
+# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_* | FileCheck %s
 
-# CHECK: live symbol: a
-# CHECK: >>> alive because of {{.*}}.o:(._start)
-# CHECK: >>> alive because of _start
+# CHECK:      live symbol: test_a
+# CHECK-NEXT: >>> alive because of {{.*}}.o:(._start)
+# CHECK-NEXT: >>> alive because of _start
+
+# CHECK:      live symbol: test_b
+# CHECK-NEXT: >>> alive because of {{.*}}.o:(.test_a)
+# CHECK-NEXT: >>> alive because of test_a
+# CHECK-NEXT: >>> alive because of {{.*}}.o:(._start)
+# CHECK-NEXT: >>> alive because of _start
 
 .globl _start
 .section ._start,"ax", at progbits
 _start:
-jmp a
+# DO NOT SUBMIT: If this reads, "jmp a", then LLD hangs.
+jmp test_a
+
+.globl test_a
+.section .test_a,"ax", at progbits
+test_a:
+jmp test_a
 
-.globl a
-.section .a,"ax", at progbits
-a:
-jmp a
+# This is alive merely by virtue of being a member of test_a.
+.globl test_b
+test_b:
+jmp test_b
 

>From c8769afc9ee70a755e3cd73644b8f3e307cceab0 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 3 Feb 2025 16:35:59 -0800
Subject: [PATCH 40/50] Call out one more thing to test

---
 lld/ELF/MarkLive.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 98a8fc8dba5aebf..f0fda9f582f5430 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -132,10 +132,13 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
     // discarded, marking the LSDA will unnecessarily retain the text section.
     if (!(fromFDE && ((relSec->flags & (SHF_EXECINSTR | SHF_LINK_ORDER)) ||
                       relSec->nextInSectionGroup))) {
+// TODO: Test
       Symbol *canonicalSym = d;
+#if 0
       if (d->isSection())
         if (Symbol *s = relSec->getEnclosingSymbol(offset))
           canonicalSym = s;
+#endif
       enqueue(relSec, offset, canonicalSym, parent);
     }
     return;

>From cb26765467fc7e4bf304ba7f6f7023b5737a5736 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 4 Feb 2025 10:35:59 -0800
Subject: [PATCH 41/50] Add TODO messages for places to test; improve message

---
 lld/ELF/MarkLive.cpp    | 12 +++++++-----
 lld/ELF/Options.td      |  8 +++++---
 lld/test/ELF/why-live.s | 12 ++++++------
 3 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index f0fda9f582f5430..45889529b94cacc 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -132,8 +132,8 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
     // discarded, marking the LSDA will unnecessarily retain the text section.
     if (!(fromFDE && ((relSec->flags & (SHF_EXECINSTR | SHF_LINK_ORDER)) ||
                       relSec->nextInSectionGroup))) {
-// TODO: Test
       Symbol *canonicalSym = d;
+// TODO: Test
 #if 0
       if (d->isSection())
         if (Symbol *s = relSec->getEnclosingSymbol(offset))
@@ -144,6 +144,7 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
     return;
   }
 
+// TODO: Test
 #if 0
   if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
     if (!ss->isWeak()) {
@@ -234,7 +235,7 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
     whyLive.try_emplace(sec, sym);
   } else {
     // Otherwise, the parent generically makes the section itself live.
-    //whyLive.try_emplace(sec, parent);
+    // whyLive.try_emplace(sec, parent);
   }
 
   // Add input section to the queue.
@@ -243,8 +244,9 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 }
 
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
-  //if (!whyLive.contains(s))
-    //return;
+  // TODO: Test
+  // if (!whyLive.contains(s))
+  // return;
 
   auto msg = Msg(ctx);
 
@@ -269,7 +271,7 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
       cur = LiveObject{parent};
     }
 
-    msg << "\n>>> alive because of ";
+    msg << "\n>>> kept alive by ";
     if (std::holds_alternative<Symbol *>(*cur)) {
       auto *s = std::get<Symbol *>(*cur);
       msg << toStr(ctx, *s);
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 2e7ee35e762f2d7..babc84f345b955d 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -559,9 +559,11 @@ defm wrap : Eq<"wrap", "Redirect symbol references to __wrap_symbol and "
                        "__real_symbol references to symbol">,
             MetaVarName<"<symbol>">;
 
-defm why_live : EEq<"why-live", "Report a chain of references preventing garbage collection for "
-                                "each symbol matching <glob>">,
-                MetaVarName<"<glob>">;
+defm why_live
+    : EEq<"why-live",
+          "Report a chain of references preventing garbage collection for "
+          "each symbol matching <glob>">,
+      MetaVarName<"<glob>">;
 
 def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
   HelpText<"Linker option extensions">;
diff --git a/lld/test/ELF/why-live.s b/lld/test/ELF/why-live.s
index dac35d50736f097..5fde07b081b958f 100644
--- a/lld/test/ELF/why-live.s
+++ b/lld/test/ELF/why-live.s
@@ -4,14 +4,14 @@
 # RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_* | FileCheck %s
 
 # CHECK:      live symbol: test_a
-# CHECK-NEXT: >>> alive because of {{.*}}.o:(._start)
-# CHECK-NEXT: >>> alive because of _start
+# CHECK-NEXT: >>> kept alive by {{.*}}.o:(._start)
+# CHECK-NEXT: >>> kept alive by _start
 
 # CHECK:      live symbol: test_b
-# CHECK-NEXT: >>> alive because of {{.*}}.o:(.test_a)
-# CHECK-NEXT: >>> alive because of test_a
-# CHECK-NEXT: >>> alive because of {{.*}}.o:(._start)
-# CHECK-NEXT: >>> alive because of _start
+# CHECK-NEXT: >>> kept alive by {{.*}}.o:(.test_a)
+# CHECK-NEXT: >>> kept alive by test_a
+# CHECK-NEXT: >>> kept alive by {{.*}}.o:(._start)
+# CHECK-NEXT: >>> kept alive by _start
 
 .globl _start
 .section ._start,"ax", at progbits

>From 947a02766ad81e92968a64a926ac3d6828176900 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 4 Feb 2025 11:01:16 -0800
Subject: [PATCH 42/50] Let's size start

---
 lld/test/ELF/why-live.s | 25 +++++++++++--------------
 1 file changed, 11 insertions(+), 14 deletions(-)

diff --git a/lld/test/ELF/why-live.s b/lld/test/ELF/why-live.s
index 5fde07b081b958f..f25e0fc679c2dc8 100644
--- a/lld/test/ELF/why-live.s
+++ b/lld/test/ELF/why-live.s
@@ -3,29 +3,26 @@
 # RUN: llvm-mc -n -filetype=obj -triple=x86_64 %s -o %t.o
 # RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_* | FileCheck %s
 
-# CHECK:      live symbol: test_a
-# CHECK-NEXT: >>> kept alive by {{.*}}.o:(._start)
-# CHECK-NEXT: >>> kept alive by _start
-
-# CHECK:      live symbol: test_b
-# CHECK-NEXT: >>> kept alive by {{.*}}.o:(.test_a)
-# CHECK-NEXT: >>> kept alive by test_a
-# CHECK-NEXT: >>> kept alive by {{.*}}.o:(._start)
-# CHECK-NEXT: >>> kept alive by _start
-
 .globl _start
 .section ._start,"ax", at progbits
 _start:
 # DO NOT SUBMIT: If this reads, "jmp a", then LLD hangs.
 jmp test_a
+.size _start, .-_start
 
+# CHECK:      live symbol: test_a
+# CHECK-NEXT: >>> kept alive by _start
 .globl test_a
 .section .test_a,"ax", at progbits
 test_a:
 jmp test_a
 
-# This is alive merely by virtue of being a member of test_a.
-.globl test_b
-test_b:
-jmp test_b
+## This is alive merely by virtue of being a member of test_a.
+# CHECK:      live symbol: test_incidental
+# CHECK-NEXT: >>> kept alive by {{.*}}.o:(.test_a)
+# CHECK-NEXT: >>> kept alive by test_a
+# CHECK-NEXT: >>> kept alive by _start
+.globl test_incidental
+test_incidental:
+jmp test_incidental
 

>From f1a33792b364ff4d850ca74105ad9701237bc6e4 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 4 Feb 2025 11:11:40 -0800
Subject: [PATCH 43/50] Separate out test cases so they work in any order

---
 lld/test/ELF/why-live.s | 39 ++++++++++++++++++++++++++-------------
 1 file changed, 26 insertions(+), 13 deletions(-)

diff --git a/lld/test/ELF/why-live.s b/lld/test/ELF/why-live.s
index f25e0fc679c2dc8..3abfc5fc29ded9d 100644
--- a/lld/test/ELF/why-live.s
+++ b/lld/test/ELF/why-live.s
@@ -1,28 +1,41 @@
 # REQUIRES: x86
 
 # RUN: llvm-mc -n -filetype=obj -triple=x86_64 %s -o %t.o
-# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_* | FileCheck %s
 
 .globl _start
 .section ._start,"ax", at progbits
 _start:
 # DO NOT SUBMIT: If this reads, "jmp a", then LLD hangs.
-jmp test_a
+jmp test_simple
 .size _start, .-_start
 
-# CHECK:      live symbol: test_a
-# CHECK-NEXT: >>> kept alive by _start
-.globl test_a
-.section .test_a,"ax", at progbits
-test_a:
-jmp test_a
+# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_simple | FileCheck %s --check-prefix=SIMPLE
+# SIMPLE:      live symbol: test_simple
+# SIMPLE-NEXT: >>> kept alive by _start
+.globl test_simple
+.section .test_simple,"ax", at progbits
+test_simple:
+jmp test_simple
+jmp test_from_unsized
 
-## This is alive merely by virtue of being a member of test_a.
-# CHECK:      live symbol: test_incidental
-# CHECK-NEXT: >>> kept alive by {{.*}}.o:(.test_a)
-# CHECK-NEXT: >>> kept alive by test_a
-# CHECK-NEXT: >>> kept alive by _start
+## This is alive merely by virtue of being a member of test_simple.
+# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_incidental | FileCheck %s --check-prefix=INCIDENTAL
+# INCIDENTAL:          live symbol: test_incidental
+# INCIDENTAL-NEXT: >>> kept alive by {{.*}}.o:(.test_simple)
+# INCIDENTAL-NEXT: >>> kept alive by test_simple
+# INCIDENTAL-NEXT: >>> kept alive by _start
 .globl test_incidental
 test_incidental:
 jmp test_incidental
 
+## Since test_simple is unsized, the reference to test_from_unsized is accounted to its parent section.
+# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_from_unsized | FileCheck %s --check-prefix=FROM_UNSIZED
+# FROM_UNSIZED:          live symbol: test_from_unsized
+# FROM_UNSIZED-NEXT: >>> kept alive by /usr/local/google/home/dthorn/llvm-project/build/tools/lld/test/ELF/Output/why-live.s.tmp.o:(.test_simple)
+# FROM_UNSIZED-NEXT: >>> kept alive by test_simple
+# FROM_UNSIZED-NEXT: >>> kept alive by _start
+.globl test_from_unsized
+.section .test_from_unsized,"ax", at progbits
+test_from_unsized:
+jmp test_from_unsized
+

>From be2417c73b56220f838ad16914cf4c631923a064 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 4 Feb 2025 13:50:23 -0800
Subject: [PATCH 44/50] Test dead sections; test break due to bug w incidental
 symbol handling

---
 lld/ELF/MarkLive.cpp    |  5 ++---
 lld/test/ELF/why-live.s | 17 ++++++++++-------
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 45889529b94cacc..e854053c352909b 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -244,9 +244,8 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 }
 
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
-  // TODO: Test
-  // if (!whyLive.contains(s))
-  // return;
+  if (!whyLive.contains(s))
+   return;
 
   auto msg = Msg(ctx);
 
diff --git a/lld/test/ELF/why-live.s b/lld/test/ELF/why-live.s
index 3abfc5fc29ded9d..86b60e226c1f8bb 100644
--- a/lld/test/ELF/why-live.s
+++ b/lld/test/ELF/why-live.s
@@ -5,7 +5,6 @@
 .globl _start
 .section ._start,"ax", at progbits
 _start:
-# DO NOT SUBMIT: If this reads, "jmp a", then LLD hangs.
 jmp test_simple
 .size _start, .-_start
 
@@ -28,14 +27,18 @@ jmp test_from_unsized
 test_incidental:
 jmp test_incidental
 
-## Since test_simple is unsized, the reference to test_from_unsized is accounted to its parent section.
-# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_from_unsized | FileCheck %s --check-prefix=FROM_UNSIZED
-# FROM_UNSIZED:          live symbol: test_from_unsized
-# FROM_UNSIZED-NEXT: >>> kept alive by /usr/local/google/home/dthorn/llvm-project/build/tools/lld/test/ELF/Output/why-live.s.tmp.o:(.test_simple)
-# FROM_UNSIZED-NEXT: >>> kept alive by test_simple
-# FROM_UNSIZED-NEXT: >>> kept alive by _start
+# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_from_unsized | FileCheck %s --check-prefix=FROM-UNSIZED
+# FROM-UNSIZED:          live symbol: test_from_unsized
+# FROM-UNSIZED-NEXT: >>> kept alive by /usr/local/google/home/dthorn/llvm-project/build/tools/lld/test/ELF/Output/why-live.s.tmp.o:(.test_simple)
+# FROM-UNSIZED-NEXT: >>> kept alive by test_simple
+# FROM-UNSIZED-NEXT: >>> kept alive by _start
 .globl test_from_unsized
 .section .test_from_unsized,"ax", at progbits
 test_from_unsized:
 jmp test_from_unsized
 
+# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_dead | count 0
+.globl test_dead
+.section .test_dead,"ax", at progbits
+test_dead:
+jmp test_dead

>From 5055993f396bad89d50c88ef2d9797262e3ce2ee Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 4 Feb 2025 14:32:26 -0800
Subject: [PATCH 45/50] Correct reasoning about incidental symbols; still a bit
 broken

---
 lld/ELF/MarkLive.cpp | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index e854053c352909b..c4609a56a408fdc 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -245,37 +245,35 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
   if (!whyLive.contains(s))
-   return;
+    return;
 
   auto msg = Msg(ctx);
 
   msg << "live symbol: " << toStr(ctx, *s);
 
-  std::optional<LiveObject> cur = s;
+  LiveObject cur = s;
   while (true) {
-    auto it = whyLive.find(*cur);
+    auto it = whyLive.find(cur);
     if (it != whyLive.end()) {
       // If there is a specific reason this object is live, report it.
       if (!it->second)
         break;
       cur = *it->second;
     } else {
-      // This object is live merely by being a member of its parent section, so
-      // report the parent.
-      InputSectionBase *parent = nullptr;
-      if (auto *d = dyn_cast<Defined>(s))
-        parent = dyn_cast<InputSectionBase>(d->section);
-      assert(parent &&
-             "all live objects should have a tracked reason for being live");
+      // This object made something else live, but it has no direct reason to be
+      // live. That means it a symbol made alive merely by being a member of its
+      // parent section, so report that section.
+      auto *d = cast<Defined>(std::get<Symbol *>(cur));
+      auto *parent = cast<InputSectionBase>(d->section);
       cur = LiveObject{parent};
     }
 
     msg << "\n>>> kept alive by ";
-    if (std::holds_alternative<Symbol *>(*cur)) {
-      auto *s = std::get<Symbol *>(*cur);
+    if (std::holds_alternative<Symbol *>(cur)) {
+      auto *s = std::get<Symbol *>(cur);
       msg << toStr(ctx, *s);
     } else {
-      auto *s = std::get<InputSectionBase *>(*cur);
+      auto *s = std::get<InputSectionBase *>(cur);
       msg << toStr(ctx, s);
     }
   }

>From 4e617779ea38dcd7d5b4e67cf15b4003401e596e Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 4 Feb 2025 16:38:30 -0800
Subject: [PATCH 46/50] Add an assert and fix whyLive handling for incidentals

---
 lld/ELF/MarkLive.cpp | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index c4609a56a408fdc..3a0f0aa3de4424b 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -231,6 +231,7 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
   if (sym) {
     // If a specific symbol is referenced, the parent makes it alive, and it
     // (may) makes its section alive.
+    assert(parent || isa<Defined>(sym) && "only Defined symbols can be alive");
     whyLive.try_emplace(sym, parent);
     whyLive.try_emplace(sec, sym);
   } else {
@@ -244,11 +245,16 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 }
 
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
-  if (!whyLive.contains(s))
-    return;
+  if (!whyLive.contains(s)) {
+    auto *d = dyn_cast<Defined>(s);
+    if (!d)
+      return;
+    auto *parent = dyn_cast<InputSectionBase>(d->section);
+    if (!parent || !parent->isLive())
+      return;
+  }
 
   auto msg = Msg(ctx);
-
   msg << "live symbol: " << toStr(ctx, *s);
 
   LiveObject cur = s;

>From fba75e6cf69a77d20c6d1fd638c16afe4821402b Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 4 Feb 2025 16:53:30 -0800
Subject: [PATCH 47/50] Add comments

---
 lld/ELF/MarkLive.cpp | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 3a0f0aa3de4424b..460e139489a7363 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -245,10 +245,13 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 }
 
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
+  // If the symbol isn't live, return.
   if (!whyLive.contains(s)) {
     auto *d = dyn_cast<Defined>(s);
+    // TODO: Test
     if (!d)
       return;
+    // TODO: Test both cases
     auto *parent = dyn_cast<InputSectionBase>(d->section);
     if (!parent || !parent->isLive())
       return;
@@ -260,15 +263,16 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
   LiveObject cur = s;
   while (true) {
     auto it = whyLive.find(cur);
+    // If there is a specific reason this object is live...
     if (it != whyLive.end()) {
-      // If there is a specific reason this object is live, report it.
+      // The object may be a liveness root.
       if (!it->second)
         break;
       cur = *it->second;
     } else {
       // This object made something else live, but it has no direct reason to be
-      // live. That means it a symbol made alive merely by being a member of its
-      // parent section, so report that section.
+      // live. That means it a symbol kept live only by being a member of its
+      // parent section. Report that section as the symbol's parent.
       auto *d = cast<Defined>(std::get<Symbol *>(cur));
       auto *parent = cast<InputSectionBase>(d->section);
       cur = LiveObject{parent};

>From fc0007d76c47360807c225150d2f48c13b8c06a7 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 5 Feb 2025 10:56:27 -0800
Subject: [PATCH 48/50] Add undefined symbol test

---
 lld/ELF/MarkLive.cpp    | 1 -
 lld/test/ELF/why-live.s | 4 ++++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 460e139489a7363..67d2970d08ffba6 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -248,7 +248,6 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
   // If the symbol isn't live, return.
   if (!whyLive.contains(s)) {
     auto *d = dyn_cast<Defined>(s);
-    // TODO: Test
     if (!d)
       return;
     // TODO: Test both cases
diff --git a/lld/test/ELF/why-live.s b/lld/test/ELF/why-live.s
index 86b60e226c1f8bb..64a3d4810a0a247 100644
--- a/lld/test/ELF/why-live.s
+++ b/lld/test/ELF/why-live.s
@@ -42,3 +42,7 @@ jmp test_from_unsized
 .section .test_dead,"ax", at progbits
 test_dead:
 jmp test_dead
+
+## Undefined symbols are not considered live.
+# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_undef -u test_undef | count 0
+

>From e47b5dea76850d9dfecbdd854388de07c0c55d31 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 5 Feb 2025 11:05:15 -0800
Subject: [PATCH 49/50] Test live symbol without parent

---
 lld/ELF/MarkLive.cpp    | 15 +++++++++------
 lld/test/ELF/why-live.s |  6 ++++++
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 67d2970d08ffba6..cd504bf2ce287e7 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -250,9 +250,9 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
     auto *d = dyn_cast<Defined>(s);
     if (!d)
       return;
-    // TODO: Test both cases
-    auto *parent = dyn_cast<InputSectionBase>(d->section);
-    if (!parent || !parent->isLive())
+    // A defined symbol is only dead if it has a dead input section parent.
+    auto *parent = dyn_cast_or_null<InputSectionBase>(d->section);
+    if (parent && !parent->isLive())
       return;
   }
 
@@ -266,14 +266,17 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
     if (it != whyLive.end()) {
       // The object may be a liveness root.
       if (!it->second)
-        break;
+        return;
       cur = *it->second;
     } else {
+      auto *d = cast<Defined>(std::get<Symbol *>(cur));
+      auto *parent = dyn_cast_or_null<InputSectionBase>(d->section);
+      if (!parent)
+        return;
+
       // This object made something else live, but it has no direct reason to be
       // live. That means it a symbol kept live only by being a member of its
       // parent section. Report that section as the symbol's parent.
-      auto *d = cast<Defined>(std::get<Symbol *>(cur));
-      auto *parent = cast<InputSectionBase>(d->section);
       cur = LiveObject{parent};
     }
 
diff --git a/lld/test/ELF/why-live.s b/lld/test/ELF/why-live.s
index 64a3d4810a0a247..924231df645339f 100644
--- a/lld/test/ELF/why-live.s
+++ b/lld/test/ELF/why-live.s
@@ -46,3 +46,9 @@ jmp test_dead
 ## Undefined symbols are not considered live.
 # RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_undef -u test_undef | count 0
 
+## Defined symbols without input section parents are considered directly live.
+# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_absolute | FileCheck %s --check-prefix=ABSOLUTE
+# ABSOLUTE: live symbol: test_absolute
+# ABSOLUTE-NOT: >>>
+.globl test_absolute
+test_absolute = 1234

>From 59f826ba63825d4ecd6e6e9bd77bac4ab49120ee Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 5 Feb 2025 12:03:00 -0800
Subject: [PATCH 50/50] Consider all symbols that are not parts of dead
 sections alive

---
 lld/ELF/MarkLive.cpp    | 16 +++++++++-------
 lld/test/ELF/why-live.s |  6 ++++--
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index cd504bf2ce287e7..679817fb38eddc9 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -245,12 +245,8 @@ void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset,
 }
 
 template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
-  // If the symbol isn't live, return.
-  if (!whyLive.contains(s)) {
-    auto *d = dyn_cast<Defined>(s);
-    if (!d)
-      return;
-    // A defined symbol is only dead if it has a dead input section parent.
+  // Skip dead symbols. A symbol is dead if it belongs to a dead section.
+  if (auto *d = dyn_cast<Defined>(s)) {
     auto *parent = dyn_cast_or_null<InputSectionBase>(d->section);
     if (parent && !parent->isLive())
       return;
@@ -269,11 +265,17 @@ template <class ELFT> void MarkLive<ELFT>::printWhyLive(Symbol *s) const {
         return;
       cur = *it->second;
     } else {
-      auto *d = cast<Defined>(std::get<Symbol *>(cur));
+      // TODO: Some sections are enqueued without symbols, so maybe cur could be a section?
+      auto *d = dyn_cast<Defined>(std::get<Symbol *>(cur));
+      if (!d)
+        return;
       auto *parent = dyn_cast_or_null<InputSectionBase>(d->section);
       if (!parent)
         return;
 
+      // TODO: This comment below seems vaguely wrong given the changes to the
+      // logic above.
+
       // This object made something else live, but it has no direct reason to be
       // live. That means it a symbol kept live only by being a member of its
       // parent section. Report that section as the symbol's parent.
diff --git a/lld/test/ELF/why-live.s b/lld/test/ELF/why-live.s
index 924231df645339f..ec437ae1bdca573 100644
--- a/lld/test/ELF/why-live.s
+++ b/lld/test/ELF/why-live.s
@@ -43,8 +43,10 @@ jmp test_from_unsized
 test_dead:
 jmp test_dead
 
-## Undefined symbols are not considered live.
-# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_undef -u test_undef | count 0
+## Undefined symbols are considered live, since they are not attached to dead sections.
+# RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_undef -u test_undef | FileCheck %s --check-prefix=UNDEFINED
+# UNDEFINED: live symbol: test_undef
+# UNDEFINED-NOT: >>>
 
 ## Defined symbols without input section parents are considered directly live.
 # RUN: ld.lld %t.o -o /dev/null --gc-sections --why-live=test_absolute | FileCheck %s --check-prefix=ABSOLUTE



More information about the llvm-commits mailing list