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 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 void elf::markLive(Ctx &ctx) { MarkLive(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(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 ">, + MetaVarName<"">; 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=`` prints garbage collection section listing to a file. + (`#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: