[lld] cbb60a3 - [ELF] Add --print-gc-sections=<file> (#159706)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 19 09:22:41 PDT 2025
Author: Fangrui Song
Date: 2025-09-19T16:22:37Z
New Revision: cbb60a32d79fb65401719216dda7e85468bad2d9
URL: https://github.com/llvm/llvm-project/commit/cbb60a32d79fb65401719216dda7e85468bad2d9
DIFF: https://github.com/llvm/llvm-project/commit/cbb60a32d79fb65401719216dda7e85468bad2d9.diff
LOG: [ELF] Add --print-gc-sections=<file> (#159706)
Add `--print-gc-sections=<file>` to redirect garbage collection section
listing to a file, avoiding contamination of stdout with other linker
output. mold has recently added the option.
GNU ld feature request:
https://sourceware.org/bugzilla/show_bug.cgi?id=33331
Added:
Modified:
lld/ELF/Config.h
lld/ELF/Driver.cpp
lld/ELF/MarkLive.cpp
lld/ELF/Options.td
lld/docs/ReleaseNotes.rst
lld/docs/ld.lld.1
lld/test/ELF/gc-sections-print.s
Removed:
################################################################################
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index a83a4c1176f6f..fd57967a1d21f 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -358,7 +358,7 @@ struct Config {
bool optRemarksWithHotness;
bool picThunk;
bool pie;
- bool printGcSections;
+ llvm::StringRef printGcSections;
bool printIcfSections;
bool printMemoryUsage;
std::optional<uint64_t> randomizeSectionPadding;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 6c2f318ffe469..1beab8d33f4ba 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1510,8 +1510,14 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
ctx.arg.pie = args.hasFlag(OPT_pie, OPT_no_pie, false);
ctx.arg.printIcfSections =
args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false);
- ctx.arg.printGcSections =
- args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
+ if (auto *arg =
+ args.getLastArg(OPT_print_gc_sections, OPT_no_print_gc_sections,
+ OPT_print_gc_sections_eq)) {
+ if (arg->getOption().matches(OPT_print_gc_sections))
+ ctx.arg.printGcSections = "-";
+ else if (arg->getOption().matches(OPT_print_gc_sections_eq))
+ ctx.arg.printGcSections = arg->getValue();
+ }
ctx.arg.printMemoryUsage = args.hasArg(OPT_print_memory_usage);
ctx.arg.printArchiveStats = args.getLastArgValue(OPT_print_archive_stats);
ctx.arg.printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order);
diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 8dd341b79c8fc..83ae9fb7689e0 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -528,10 +528,18 @@ template <class ELFT> void elf::markLive(Ctx &ctx) {
MarkLive<ELFT, false>(ctx, 1).moveToMain();
// Report garbage-collected sections.
- if (ctx.arg.printGcSections)
- for (InputSectionBase *sec : ctx.inputSections)
- if (!sec->isLive())
- Msg(ctx) << "removing unused section " << sec;
+ if (ctx.arg.printGcSections.empty())
+ return;
+ std::error_code ec;
+ raw_fd_ostream os = ctx.openAuxiliaryFile(ctx.arg.printGcSections, ec);
+ if (ec) {
+ Err(ctx) << "cannot open --print-gc-sections= file "
+ << ctx.arg.printGcSections << ": " << ec.message();
+ return;
+ }
+ for (InputSectionBase *sec : ctx.inputSections)
+ if (!sec->isLive())
+ os << "removing unused section " << toStr(ctx, sec) << '\n';
}
template void elf::markLive<ELF32LE>(Ctx &);
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index cc91680550b4b..f0523185a0a31 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -388,6 +388,9 @@ defm pie: B<"pie",
defm print_gc_sections: B<"print-gc-sections",
"List removed unused sections",
"Do not list removed unused sections (default)">;
+def print_gc_sections_eq: JJ<"print-gc-sections=">,
+ HelpText<"List removed unused sections to <file>">,
+ MetaVarName<"<file>">;
defm print_icf_sections: B<"print-icf-sections",
"List identical folded sections",
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index cc1628f48bb0e..6ea1ea0fd6c2f 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -29,6 +29,9 @@ Non-comprehensive list of changes in this release
ELF Improvements
----------------
+* ``--print-gc-sections=<file>`` prints garbage collection section listing to a file.
+ (`#159706 <https://github.com/llvm/llvm-project/pull/159706>`_)
+
Breaking changes
----------------
diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index 1835879b671e8..bb1a53ad1112a 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -522,6 +522,8 @@ Don't use.
.It Fl -print-gc-sections
List removed unused sections.
+.It Fl -print-gc-sections Ns = Ns Ar file
+List removed unused sections to the specified file.
.It Fl -print-icf-sections
List identical folded sections.
.It Fl -print-map
diff --git a/lld/test/ELF/gc-sections-print.s b/lld/test/ELF/gc-sections-print.s
index a822e9ef34793..f105dc10c2471 100644
--- a/lld/test/ELF/gc-sections-print.s
+++ b/lld/test/ELF/gc-sections-print.s
@@ -1,16 +1,25 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: ld.lld %t --gc-sections --print-gc-sections -o %t2 2>&1 | FileCheck -check-prefix=PRINT %s
+# RUN: ld.lld %t --gc-sections --print-gc-sections=- -o %t2 2>&1 | FileCheck -check-prefix=PRINT %s
+# RUN: ld.lld %t --gc-sections --print-gc-sections=%t.txt
+# RUN: FileCheck --check-prefix=PRINT %s --input-file=%t.txt
# PRINT: removing unused section {{.*}}:(.text.x)
# PRINT-NEXT: removing unused section {{.*}}:(.text.y)
-# RUN: ld.lld %t --gc-sections --print-gc-sections --no-print-gc-sections -o %t2 >& %t.log
+# RUN: rm %t.txt
+# RUN: ld.lld %t --gc-sections --print-gc-sections --print-gc-sections=%t.txt --no-print-gc-sections -o %t2 >& %t.log
+# RUN: not ls %t.txt
# RUN: echo >> %t.log
# RUN: FileCheck -check-prefix=NOPRINT %s < %t.log
# NOPRINT-NOT: removing
+# RUN: not ld.lld %t --gc-sections --print-gc-sections=/ -o %t2 2>&1 | FileCheck --check-prefix=ERR %s
+
+# ERR: error: cannot open --print-gc-sections= file /: {{.*}}
+
.globl _start
.protected a, x, y
_start:
More information about the llvm-commits
mailing list