[lld] [ELF] --retain-symbols-file: keep listed symbols in .symtab, not .dynsym (PR #209063)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 13 23:59:12 PDT 2026


https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/209063

>From cc081f34c9088a0328859cc4a4bf022d6053fc3e Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 12 Jul 2026 15:24:20 -0700
Subject: [PATCH 1/3] [ELF] --retain-symbols-file: keep listed symbols in
 .symtab, not .dynsym

Commit c0fc25307143dd06c12fb2f5ebdb1dade7290ccf (2017) implements
--retain-symbols-file with symbol version machinery
`{local: *; global: listed;}`, which removes unlisted symbols from
.dynsym and leaves .symtab untouched. GNU ld does the opposite: it keeps
only the listed symbols in .symtab and does not touch .dynsym.

Reuse the --discard-{locals,all} mechanism to reimplement the feature,
matching GNU ld.
Use --export-dynamic-symbol or a version script to control .dynsym.

Extend `markUsedSymbols` to mark referenced globals as well, so
--emit-relocs/-r keep a valid symbol index.

Fix #91055
---
 lld/ELF/Config.h                   |  1 +
 lld/ELF/Driver.cpp                 |  9 ++----
 lld/ELF/MarkLive.cpp               | 29 ++++++++++-------
 lld/ELF/SymbolTable.cpp            | 10 +-----
 lld/ELF/Writer.cpp                 | 13 +++++++-
 lld/docs/ReleaseNotes.rst          |  3 ++
 lld/docs/ld.lld.1                  |  5 ++-
 lld/test/ELF/retain-symbols-file.s | 50 ++++++++++++++++++++++--------
 lld/test/ELF/retain-und.s          | 15 ++++-----
 lld/test/ELF/version-script-warn.s |  3 +-
 10 files changed, 87 insertions(+), 51 deletions(-)

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 12b16ded61fca..863f3223094bb 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -299,6 +299,7 @@ struct Config {
   llvm::StringRef thinLTOPrefixReplaceNativeObject;
   std::string rpath;
   llvm::SmallVector<VersionDefinition, 0> versionDefinitions;
+  std::optional<llvm::DenseSet<llvm::StringRef>> retainSymbols;
   llvm::SmallVector<llvm::StringRef, 0> auxiliaryList;
   llvm::SmallVector<llvm::StringRef, 0> filterList;
   llvm::SmallVector<llvm::StringRef, 0> passPlugins;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 7ec7dfcae6bca..77821e8c813ef 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1961,15 +1961,12 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
   ctx.arg.versionDefinitions.push_back(
       {"global", (uint16_t)VER_NDX_GLOBAL, {}, {}});
 
-  // If --retain-symbol-file is used, we'll keep only the symbols listed in
-  // the file and discard all others.
+  // Keep only these symbols in .symtab (not .dynsym), matching GNU ld.
   if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) {
-    ctx.arg.versionDefinitions[VER_NDX_LOCAL].nonLocalPatterns.push_back(
-        {"*", /*isExternCpp=*/false, /*hasWildcard=*/true});
+    ctx.arg.retainSymbols.emplace();
     if (std::optional<MemoryBufferRef> buffer = readFile(ctx, arg->getValue()))
       for (StringRef s : args::getLines(*buffer))
-        ctx.arg.versionDefinitions[VER_NDX_GLOBAL].nonLocalPatterns.push_back(
-            {s, /*isExternCpp=*/false, /*hasWildcard=*/false});
+        ctx.arg.retainSymbols->insert(s);
   }
 
   for (opt::Arg *arg : args.filtered(OPT_warn_backrefs_exclude)) {
diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index d1e05b5eb9296..ef6d20c8e8137 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -339,10 +339,15 @@ void MarkLive<ELFT, TrackWhyLive>::markSymbol(Symbol *sym, StringRef reason) {
       enqueue(isec, d->value, sym, {std::nullopt, reason});
 }
 
-template <class ELFT> static void markUsedLocalSymbols(InputSectionBase &sec) {
+// If -r or --emit-relocs, ensure referenced symbols are preserved in .symtab.
+// --discard-* drops only locals; --retain-symbols-file also drops globals, so
+// mark those too.
+template <class ELFT>
+static void markUsedSymbols(Ctx &ctx, InputSectionBase &sec) {
+  bool retain = ctx.arg.retainSymbols.has_value();
   auto mark = [&](const auto &rel) {
     Symbol &sym = sec.file->getRelocTargetSym(rel);
-    if (sym.isLocal())
+    if (sym.isLocal() || retain)
       sym.setFlags(USED);
   };
   const RelsOrRelas<ELFT> rels = sec.template relsOrRelas<ELFT>();
@@ -385,7 +390,10 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
   // referenced by .eh_frame sections, so we scan them for that here.
   for (EhInputSection *eh : ctx.ehInputSections)
     scanEhFrameSection(*eh);
-  bool markUsed = ctx.arg.copyRelocs && ctx.arg.discard != DiscardPolicy::None;
+  // See markUsedSymbols.
+  bool markUsed =
+      ctx.arg.copyRelocs &&
+      (ctx.arg.discard != DiscardPolicy::None || ctx.arg.retainSymbols);
   for (InputSectionBase *sec : ctx.inputSections) {
     if (sec->flags & SHF_GNU_RETAIN) {
       enqueue(sec, /*offset=*/0, /*sym=*/nullptr, {std::nullopt, "retained"});
@@ -421,10 +429,8 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
         sec->markLive();
         for (InputSection *isec : sec->dependentSections)
           isec->markLive();
-        // If -r or --emit-relocs, ensure referenced local symbols are
-        // preserved by --discard-{locals,all} (see shouldKeepInSymtab).
         if (markUsed)
-          markUsedLocalSymbols<ELFT>(*sec);
+          markUsedSymbols<ELFT>(ctx, *sec);
       }
     }
 
@@ -602,14 +608,13 @@ template <class ELFT> void elf::markLive(Ctx &ctx) {
       if (auto *s = dyn_cast<SharedSymbol>(sym))
         if (s->isUsedInRegularObj && !s->isWeak())
           cast<SharedFile>(s->file)->isNeeded = true;
-    // If -r or --emit-relocs, ensure referenced local symbols are preserved so
-    // that they won't be discarded by --discard-{locals,all} (see
-    // shouldKeepInSymtab).
-    if (ctx.arg.copyRelocs && ctx.arg.discard != DiscardPolicy::None)
-      parallelForEach(ctx.objectFiles, [](ELFFileBase *file) {
+    // See markUsedSymbols.
+    if (ctx.arg.copyRelocs &&
+        (ctx.arg.discard != DiscardPolicy::None || ctx.arg.retainSymbols))
+      parallelForEach(ctx.objectFiles, [&ctx](ELFFileBase *file) {
         for (InputSectionBase *sec : file->getSections())
           if (sec)
-            markUsedLocalSymbols<ELFT>(*sec);
+            markUsedSymbols<ELFT>(ctx, *sec);
       });
     return;
   }
diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp
index a76f5d4434a0d..c5ea3ee04ff4c 100644
--- a/lld/ELF/SymbolTable.cpp
+++ b/lld/ELF/SymbolTable.cpp
@@ -312,15 +312,7 @@ void SymbolTable::scanVersionScript() {
   bool asteriskReported = false;
   auto assignAsterisk = [&](SymbolVersion &pat, VersionDefinition *ver,
                             bool isLocal) {
-    // Avoid issuing a warning if both '--retain-symbol-file' and a version
-    // script with `global: *` are used.
-    //
-    // '--retain-symbol-file' adds a "*" pattern to
-    // 'versionDefinitions[VER_NDX_LOCAL].nonLocalPatterns', see
-    // 'readConfigs()' in 'Driver.cpp'. Note that it is not '.localPatterns',
-    // and may seem counterintuitive, but still works as expected. Here we can
-    // exploit that and skip analyzing the pattern added for this option.
-    if (!asteriskReported && (isLocal || ver->id > VER_NDX_LOCAL)) {
+    if (!asteriskReported) {
       if ((isLocal && globalAsteriskFound) ||
           (!isLocal && localAsteriskFound)) {
         Warn(ctx)
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 7567e1923d944..99e748080d0c7 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -375,6 +375,12 @@ template <class ELFT> void Writer<ELFT>::run() {
   }
 }
 
+static bool retainKeepsInSymtab(Ctx &ctx, const Symbol &sym) {
+  if (sym.hasFlag(USED) && ctx.arg.copyRelocs)
+    return true;
+  return ctx.arg.retainSymbols->contains(sym.getName());
+}
+
 static bool shouldKeepInSymtab(Ctx &ctx, const Defined &sym) {
   if (sym.isSection())
     return false;
@@ -407,6 +413,10 @@ static bool shouldKeepInSymtab(Ctx &ctx, const Defined &sym) {
       (ctx.arg.discard == DiscardPolicy::Locals ||
        (sym.section && (sym.section->flags & SHF_MERGE))))
     return false;
+  // If --retain-symbols-file= is specified, keep in .symtab only listed symbols
+  // plus those referenced by emitted relocations.
+  if (LLVM_UNLIKELY(ctx.arg.retainSymbols))
+    return retainKeepsInSymtab(ctx, sym);
   return true;
 }
 
@@ -1934,7 +1944,8 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
         continue;
       if (!ctx.arg.relocatable)
         sym->binding = sym->computeBinding(ctx);
-      if (ctx.in.symTab)
+      if (ctx.in.symTab &&
+          (!ctx.arg.retainSymbols || retainKeepsInSymtab(ctx, *sym)))
         ctx.in.symTab->addSymbol(sym);
 
       // computeBinding might localize a symbol that was considered exported
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 058173413e41a..f01e5c451f43c 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -40,6 +40,9 @@ ELF Improvements
   beats positional last-match-wins; default: positional). In ELF, the glob
   matches input section names (e.g. ``.text.unlikely.code1``).
 
+* ``--retain-symbols-file`` now filters ``.symtab`` instead of ``.dynsym``,
+  matching GNU ld.
+
 * When a ``SECTIONS`` command interleaves relro and non-relro sections, lld now
   emits one ``PT_GNU_RELRO`` segment per contiguous run of relro sections
   instead of reporting a ``not contiguous with other relro sections`` error.
diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index 70541cf786f64..7dedf69881b3f 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -582,7 +582,10 @@ version.txt containing the output of ld.lld --version.
 The archive when
 unpacked can be used to re-run the linker with the same options and input files.
 .It Fl -retain-symbols-file Ns = Ns Ar file
-Retain only the symbols listed in the file.
+Keep only the symbols listed in
+.Ar file
+in the static symbol table, along with symbols referenced by emitted relocations.
+This does not affect the dynamic symbol table.
 .It Fl -rpath Ns = Ns Ar value , Fl R Ar value
 Add a
 .Dv DT_RUNPATH
diff --git a/lld/test/ELF/retain-symbols-file.s b/lld/test/ELF/retain-symbols-file.s
index 903d5440e973f..ea72cf1962466 100644
--- a/lld/test/ELF/retain-symbols-file.s
+++ b/lld/test/ELF/retain-symbols-file.s
@@ -1,34 +1,58 @@
 # REQUIRES: x86
-## --retain-symbols-file removes unlisted symbols from .dynsym.
+## --retain-symbols-file filters .symtab, not .dynsym, matching GNU ld.
 
 # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
 # RUN: echo foo > %t.retain
 # RUN: echo bar >> %t.retain
 
 # RUN: ld.lld -shared --retain-symbols-file=%t.retain %t.o -o %t.so
-# RUN: llvm-readelf --dyn-syms %t.so | FileCheck %s --check-prefix=DYN
+# RUN: llvm-readelf --dyn-syms -s %t.so | FileCheck %s --check-prefix=SYM
 
 ## Separate-argument form.
 # RUN: ld.lld -shared --retain-symbols-file %t.retain %t.o -o %t2.so
 # RUN: cmp %t.so %t2.so
 
-# DYN:      Symbol table '.dynsym'
-# DYN-DAG:  NOTYPE GLOBAL DEFAULT UND und
-# DYN-DAG:  FUNC GLOBAL DEFAULT {{.*}} foo
-# DYN-DAG:  FUNC GLOBAL DEFAULT {{.*}} bar
-# DYN-NOT:  zed
-# DYN-NOT:  _start
+## .dynsym keeps every exported symbol, including unlisted ones.
+# SYM-DAG: NOTYPE GLOBAL DEFAULT UND und
+# SYM-DAG: NOTYPE GLOBAL DEFAULT {{.*}} _start
+# SYM-DAG: FUNC GLOBAL DEFAULT {{.*}} zed
+# SYM-DAG: FUNC GLOBAL DEFAULT {{.*}} foo
+# SYM-DAG: FUNC GLOBAL DEFAULT {{.*}} bar
 
-## --emit-relocs/-r preserves symbols referenced by emitted relocations.
+## .symtab keeps only the listed foo and bar.
+# SYM:      Symbol table '.symtab'
+# SYM-NOT:  _start
+# SYM-NOT:  zed
+# SYM-NOT:  und
+# SYM-NOT:  loc
+# SYM:      FUNC GLOBAL DEFAULT {{.*}} foo
+# SYM:      FUNC GLOBAL DEFAULT {{.*}} bar
+
+## --emit-relocs keeps unlisted symbols referenced by emitted relocations.
+# RUN: ld.lld -shared --emit-relocs --retain-symbols-file=%t.retain %t.o -o %t.er.so
+# RUN: llvm-readelf --symbols %t.er.so | FileCheck %s --check-prefix=EMIT
+# RUN: llvm-readelf -r %t.er.so | FileCheck %s --check-prefix=REL
+# EMIT:      Symbol table '.symtab'
+# EMIT-DAG:  FUNC GLOBAL DEFAULT {{.*}} zed
+# EMIT-DAG:  NOTYPE GLOBAL DEFAULT UND und
+# EMIT-DAG:  FUNC GLOBAL DEFAULT {{.*}} foo
+# EMIT-DAG:  FUNC GLOBAL DEFAULT {{.*}} bar
+# REL-DAG:   R_X86_64_PLT32 {{.*}} zed - 4
+# REL-DAG:   R_X86_64_PLT32 {{.*}} und - 4
+
+## --discard-none: relocation-referenced symbols are kept by --retain-symbols-file
+## itself, not --discard-*.
 # RUN: ld.lld -shared --emit-relocs --discard-none --retain-symbols-file=%t.retain %t.o -o %t.dn.so
 # RUN: llvm-readelf -r %t.dn.so | FileCheck %s --check-prefix=REL
-# REL-DAG:  R_X86_64_PLT32 {{.*}} zed - 4
-# REL-DAG:  R_X86_64_PLT32 {{.*}} und - 4
 
-## An empty file lists nothing, localizing every defined symbol out of .dynsym.
+## An empty file drops every symbol from .symtab, unlike omitting the option.
 # RUN: ld.lld -shared --retain-symbols-file=/dev/null %t.o -o %t.empty.so
-# RUN: llvm-readelf --dyn-syms %t.empty.so | FileCheck %s --check-prefix=EMPTY
+# RUN: llvm-readelf --dyn-syms --symbols %t.empty.so | FileCheck %s --check-prefix=EMPTY
 # EMPTY:      Symbol table '.dynsym'
+# EMPTY-DAG:  FUNC GLOBAL DEFAULT {{.*}} foo
+# EMPTY-DAG:  FUNC GLOBAL DEFAULT {{.*}} bar
+# EMPTY-DAG:  FUNC GLOBAL DEFAULT {{.*}} zed
+# EMPTY:      Symbol table '.symtab'
 # EMPTY-NOT:  foo
 # EMPTY-NOT:  bar
 # EMPTY-NOT:  zed
diff --git a/lld/test/ELF/retain-und.s b/lld/test/ELF/retain-und.s
index 7a45d92e540ac..b11e5a90fdff2 100644
--- a/lld/test/ELF/retain-und.s
+++ b/lld/test/ELF/retain-und.s
@@ -1,17 +1,18 @@
 # REQUIRES: x86
-## An empty --retain-symbols-file behaves like a `{ local: *; }` version script,
-## keeping the R_X86_64_64 dynamic relocation to the weak undefined foo.
+## An unlisted undefined symbol referenced by a dynamic relocation stays in
+## .dynsym (keeping the relocation valid) but is dropped from .symtab.
 
 # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
 # RUN: echo > %t.retain
-# RUN: echo '{ local: *; };' > %t.script
-# RUN: ld.lld -shared --version-script %t.script %t.o -o %t1.so
-# RUN: ld.lld -shared --retain-symbols-file %t.retain %t.o -o %t2.so
-# RUN: llvm-readelf -r %t1.so | FileCheck %s
-# RUN: llvm-readelf -r %t2.so | FileCheck %s
+# RUN: ld.lld -shared --retain-symbols-file=%t.retain %t.o -o %t.so
+# RUN: llvm-readelf -r --dyn-syms --symbols %t.so | FileCheck %s
 
 # CHECK:      Relocation section '.rela.dyn' {{.+}} contains 1
 # CHECK:      R_X86_64_64 {{.*}} foo + 0
+# CHECK:      Symbol table '.dynsym'
+# CHECK:      NOTYPE WEAK DEFAULT UND foo
+# CHECK:      Symbol table '.symtab'
+# CHECK-NOT:  foo
 
 .data
 .quad foo
diff --git a/lld/test/ELF/version-script-warn.s b/lld/test/ELF/version-script-warn.s
index 9aba596165796..13bb734b2887b 100644
--- a/lld/test/ELF/version-script-warn.s
+++ b/lld/test/ELF/version-script-warn.s
@@ -20,8 +20,7 @@
 # RUN: echo 'V1 { local: *; }; V2 { local: *;};' > %t.ver
 # RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so --fatal-warnings
 
-## --retain-symbols-file uses the same internal infrastructure as the support
-## for version scripts. Do not show the warings if they both are used.
+## --retain-symbols-file with a version-script '*' must not warn.
 # RUN: echo 'foo' > %t_retain.txt
 # RUN: echo '{ local: *; };' > %t_local.ver
 # RUN: echo '{ global: *; };' > %t_global.ver

>From 696df79c506d26a03a8672aa0fd6c6d1853c9898 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 13 Jul 2026 23:54:14 -0700
Subject: [PATCH 2/3] Simplify markUsedSymbols

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

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index ef6d20c8e8137..fb2165a5126ef 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -339,16 +339,14 @@ void MarkLive<ELFT, TrackWhyLive>::markSymbol(Symbol *sym, StringRef reason) {
       enqueue(isec, d->value, sym, {std::nullopt, reason});
 }
 
-// If -r or --emit-relocs, ensure referenced symbols are preserved in .symtab.
-// --discard-* drops only locals; --retain-symbols-file also drops globals, so
-// mark those too.
+// If -r or --emit-relocs, mark symbols referenced by relocations as used so
+// .symtab retains them and the relocations keep valid symbol indices. Callers
+// invoke this only when .symtab filtering is active (--discard-* or
+// --retain-symbols-file); otherwise .symtab keeps every symbol anyway.
 template <class ELFT>
-static void markUsedSymbols(Ctx &ctx, InputSectionBase &sec) {
-  bool retain = ctx.arg.retainSymbols.has_value();
+static void markUsedSymbols(InputSectionBase &sec) {
   auto mark = [&](const auto &rel) {
-    Symbol &sym = sec.file->getRelocTargetSym(rel);
-    if (sym.isLocal() || retain)
-      sym.setFlags(USED);
+    sec.file->getRelocTargetSym(rel).setFlags(USED);
   };
   const RelsOrRelas<ELFT> rels = sec.template relsOrRelas<ELFT>();
   for (const typename ELFT::Rel &rel : rels.rels)
@@ -430,7 +428,7 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
         for (InputSection *isec : sec->dependentSections)
           isec->markLive();
         if (markUsed)
-          markUsedSymbols<ELFT>(ctx, *sec);
+          markUsedSymbols<ELFT>(*sec);
       }
     }
 
@@ -611,10 +609,10 @@ template <class ELFT> void elf::markLive(Ctx &ctx) {
     // See markUsedSymbols.
     if (ctx.arg.copyRelocs &&
         (ctx.arg.discard != DiscardPolicy::None || ctx.arg.retainSymbols))
-      parallelForEach(ctx.objectFiles, [&ctx](ELFFileBase *file) {
+      parallelForEach(ctx.objectFiles, [](ELFFileBase *file) {
         for (InputSectionBase *sec : file->getSections())
           if (sec)
-            markUsedSymbols<ELFT>(ctx, *sec);
+            markUsedSymbols<ELFT>(*sec);
       });
     return;
   }

>From a3f349c4827017641b6e812f3d1864de8843d8e7 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 13 Jul 2026 23:59:00 -0700
Subject: [PATCH 3/3] clang-format; release note link

---
 lld/ELF/MarkLive.cpp      | 3 +--
 lld/docs/ReleaseNotes.rst | 5 +++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index fb2165a5126ef..2820818133d5c 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -343,8 +343,7 @@ void MarkLive<ELFT, TrackWhyLive>::markSymbol(Symbol *sym, StringRef reason) {
 // .symtab retains them and the relocations keep valid symbol indices. Callers
 // invoke this only when .symtab filtering is active (--discard-* or
 // --retain-symbols-file); otherwise .symtab keeps every symbol anyway.
-template <class ELFT>
-static void markUsedSymbols(InputSectionBase &sec) {
+template <class ELFT> static void markUsedSymbols(InputSectionBase &sec) {
   auto mark = [&](const auto &rel) {
     sec.file->getRelocTargetSym(rel).setFlags(USED);
   };
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 6991b0ec86c11..36f50db965f04 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -76,6 +76,9 @@ ELF Improvements
   ``.debug_info``), fixing DWARF corruption in the output.
   (`#209035 <https://github.com/llvm/llvm-project/pull/209035>`_)
   (`#209042 <https://github.com/llvm/llvm-project/pull/209042>`_)
+* ``--retain-symbols-file`` now filters ``.symtab`` instead of ``.dynsym``,
+  matching GNU ld.
+  (`#209063 <https://github.com/llvm/llvm-project/pull/209063>`_)
 * ``INCLUDE`` in linker scripts now fully parses its own content instead of
   sharing a lexer buffer stack with the includer, fixing spurious acceptance
   of malformed scripts.
@@ -87,8 +90,6 @@ ELF Improvements
 * Thunks are no longer reused across an ``OVERLAY`` boundary unless the
   target output section is guaranteed to be resident at the same time.
   (`#200415 <https://github.com/llvm/llvm-project/pull/200415>`_)
-* ``--retain-symbols-file`` now filters ``.symtab`` instead of ``.dynsym``,
-  matching GNU ld.
 * When a ``SECTIONS`` command interleaves relro and non-relro sections, lld now
   emits one ``PT_GNU_RELRO`` segment per contiguous run of relro sections
   instead of reporting a ``not contiguous with other relro sections`` error.



More information about the llvm-commits mailing list