[PATCH] strip: Don't treat fat IR objects as plugin object
H.J. Lu
hjl.tools@gmail.com
Tue Aug 5 02:59:55 GMT 2025
Fat IR objects contains both regular sections and IR sections. After
commit 717a38e9a02109fcbcb18bb2ec3aa251e2ad0a0d
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Sun May 4 05:12:46 2025 +0800
strip: Add GCC LTO IR support
"strip --strip-debug" no longer strips debug sections in fat IR objects
since fat IR objects are recognized as plugin object and copied as unknown
objects. Update strip not to treat archive member nor standalone fat IR
object as IR object so that strip can remove debug and IR sections in
fat IR object. For archive member, it is copied as an unknown object if
it isn't a fat IR object nor a slim IR object.
bfd/
PR binutils/33246
* archive.c: Include "plugin-api.h" and "plugin.h" if plugin is
enabled.
(_bfd_compute_and_write_armap): Don't complain plugin is needed
when the plugin target is in use.
* bfd-in2.h: Regenerated.
* format.c (bfd_check_format_lto): Add a bool argument to
indicate called from strip.
(bfd_check_format): Updated.
(bfd_set_lto_type): If there is .llvm.lto section, set LTO type
to lto_fat_ir_object.
(bfd_check_format_matches_lto): Add a bool argument to indicate
called from strip. When called from strip, don't treat archive
member nor standalone fat IR object as an IR object. Don't set
LTO type when setting format. Set LTO type if IR sections won't
be removed.
(bfd_check_format_matches): Updated.
* plugin.c (bfd_plugin_get_symbols_in_object_only): Copy LTO
type derived from input sections.
nm/
PR binutils/33246
* nm.c (filter_symbols): Don't complain plugin is needed when
the plugin target is in use.
(display_rel_file): Likewise.
* objcopy.c (copy_archive): Pass true to bfd_check_format_lto
to indicat called from strip. Also copy slim IR archive member
as unknown object.
(copy_file): Call bfd_check_format_lto, instead of
bfd_check_format. Pass true to bfd_check_format_lto and
bfd_check_format_matches_lto.
(strip_main): Keep .gnu.debuglto_* sections unless all GCC LTO
sections will be removed.
ld/
PR binutils/33246
* testsuite/ld-plugin/lto-binutils.exp (run_pr33246_test): New.
Run binutils/33246 tests with GCC and Clang.
* testsuite/ld-plugin/pr33246.c: New file.
Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
---
bfd/archive.c | 10 +-
bfd/bfd-in2.h | 5 +-
bfd/format.c | 48 +++++--
bfd/plugin.c | 3 +
binutils/nm.c | 6 +-
binutils/objcopy.c | 21 ++-
ld/testsuite/ld-plugin/lto-binutils.exp | 175 ++++++++++++++++++++++++
ld/testsuite/ld-plugin/pr33246.c | 4 +
8 files changed, 248 insertions(+), 24 deletions(-)
create mode 100644 ld/testsuite/ld-plugin/pr33246.c
diff --git a/bfd/archive.c b/bfd/archive.c
index c61d4b12658..c323fb4fbd3 100644
--- a/bfd/archive.c
+++ b/bfd/archive.c
@@ -141,6 +141,10 @@ SUBSECTION
#include "hashtab.h"
#include "filenames.h"
#include "bfdlink.h"
+#if BFD_SUPPORTS_PLUGINS
+#include "plugin-api.h"
+#include "plugin.h"
+#endif
#ifndef errno
extern int errno;
@@ -2342,7 +2346,8 @@ _bfd_compute_and_write_armap (bfd *arch, unsigned int elength)
long symcount;
long src_count;
- if (bfd_get_lto_type (current) == lto_slim_ir_object
+ if (!bfd_plugin_target_p (current->xvec)
+ && bfd_get_lto_type (current) == lto_slim_ir_object
&& report_plugin_err)
{
report_plugin_err = false;
@@ -2398,7 +2403,8 @@ _bfd_compute_and_write_armap (bfd *arch, unsigned int elength)
map = new_map;
}
- if (bfd_lto_slim_symbol_p (current,
+ if (!bfd_plugin_target_p (current->xvec)
+ && bfd_lto_slim_symbol_p (current,
syms[src_count]->name)
&& report_plugin_err)
{
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 2ff3e930bfa..351c9a97c52 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -2904,13 +2904,14 @@ bool generic_core_file_matches_executable_p
/* Extracted from format.c. */
bool bfd_check_format_lto (bfd *abfd, bfd_format format,
- bool lto_sections_removed);
+ bool lto_sections_removed,
+ bool from_strip);
bool bfd_check_format (bfd *abfd, bfd_format format);
bool bfd_check_format_matches_lto
(bfd *abfd, bfd_format format, char ***matching,
- bool lto_sections_removed);
+ bool lto_sections_removed, bool from_strip);
bool bfd_check_format_matches
(bfd *abfd, bfd_format format, char ***matching);
diff --git a/bfd/format.c b/bfd/format.c
index f3a0774af08..bc7a5bc2cac 100644
--- a/bfd/format.c
+++ b/bfd/format.c
@@ -60,7 +60,8 @@ FUNCTION
SYNOPSIS
bool bfd_check_format_lto (bfd *abfd, bfd_format format,
- bool lto_sections_removed);
+ bool lto_sections_removed,
+ bool from_strip);
DESCRIPTION
Verify if the file attached to the BFD @var{abfd} is compatible
@@ -69,6 +70,8 @@ DESCRIPTION
If LTO_SECTION_REMOVED is true, ignore plugin target.
+ If FROM_STRIP is true, this is called from strip.
+
If the BFD has been set to a specific target before the
call, only the named target and format combination is
checked. If the target has not been set, or has been set to
@@ -104,10 +107,11 @@ DESCRIPTION
bool
bfd_check_format_lto (bfd *abfd, bfd_format format,
- bool lto_sections_removed)
+ bool lto_sections_removed, bool from_strip)
{
return bfd_check_format_matches_lto (abfd, format, NULL,
- lto_sections_removed);
+ lto_sections_removed,
+ from_strip);
}
@@ -126,7 +130,7 @@ DESCRIPTION
bool
bfd_check_format (bfd *abfd, bfd_format format)
{
- return bfd_check_format_matches_lto (abfd, format, NULL, false);
+ return bfd_check_format_matches_lto (abfd, format, NULL, false, false);
}
struct bfd_preserve
@@ -413,6 +417,11 @@ bfd_set_lto_type (bfd *abfd ATTRIBUTE_UNUSED)
abfd->object_only_section = sec;
break;
}
+ else if (strcmp (sec->name, ".llvm.lto") == 0)
+ {
+ type = lto_fat_ir_object;
+ break;
+ }
else if (lsection.major_version == 0
&& startswith (sec->name, ".gnu.lto_.lto.")
&& bfd_get_section_contents (abfd, sec, &lsection, 0,
@@ -436,7 +445,7 @@ FUNCTION
SYNOPSIS
bool bfd_check_format_matches_lto
(bfd *abfd, bfd_format format, char ***matching,
- bool lto_sections_removed);
+ bool lto_sections_removed, bool from_strip);
DESCRIPTION
Like <<bfd_check_format>>, except when it returns FALSE with
@@ -450,12 +459,15 @@ DESCRIPTION
should free it.
If LTO_SECTION_REMOVED is true, ignore plugin target.
+
+ If FROM_STRIP is true, this is called from strip.
*/
bool
bfd_check_format_matches_lto (bfd *abfd, bfd_format format,
char ***matching,
- bool lto_sections_removed ATTRIBUTE_UNUSED)
+ bool lto_sections_removed ATTRIBUTE_UNUSED,
+ bool from_strip ATTRIBUTE_UNUSED)
{
extern const bfd_target binary_vec;
const bfd_target * const *target;
@@ -481,10 +493,7 @@ bfd_check_format_matches_lto (bfd *abfd, bfd_format format,
}
if (abfd->format != bfd_unknown)
- {
- bfd_set_lto_type (abfd);
- return abfd->format == format;
- }
+ return abfd->format == format;
if (matching != NULL || *bfd_associated_vector != NULL)
{
@@ -537,7 +546,16 @@ bfd_check_format_matches_lto (bfd *abfd, bfd_format format,
cleanup = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
- if (cleanup)
+ /* When called from strip, don't treat archive member nor
+ standalone fat IR object as an IR object. For archive
+ member, it will be copied as an unknown object if it
+ isn't a fat IR object. For standalone fat IR object,
+ it will be copied as non-IR object. */
+ if (cleanup
+ && (!from_strip
+ || !bfd_plugin_target_p (abfd->xvec)
+ || (abfd->lto_type != lto_fat_ir_object
+ && abfd->my_archive == NULL)))
goto ok_ret;
/* For a long time the code has dropped through to check all
@@ -621,6 +639,11 @@ bfd_check_format_matches_lto (bfd *abfd, bfd_format format,
{
int match_priority = abfd->xvec->match_priority;
+ /* Set LTO type if IR sections won't be removed so that it
+ can be checked for fat and slim IR objects. */
+ if (!lto_sections_removed)
+ bfd_set_lto_type (abfd);
+
if (abfd->format != bfd_archive
|| (bfd_has_map (abfd)
&& bfd_get_error () != bfd_error_wrong_object_format))
@@ -854,7 +877,8 @@ DESCRIPTION
bool
bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
{
- return bfd_check_format_matches_lto (abfd, format, matching, false);
+ return bfd_check_format_matches_lto (abfd, format, matching, false,
+ false);
}
/*
diff --git a/bfd/plugin.c b/bfd/plugin.c
index 1c72b748a8f..6dd22d75137 100644
--- a/bfd/plugin.c
+++ b/bfd/plugin.c
@@ -203,6 +203,9 @@ bfd_plugin_get_symbols_in_object_only (bfd *abfd)
bfd_close (nbfd);
return;
}
+
+ /* Copy LTO type derived from input sections. */
+ abfd->lto_type = nbfd->lto_type;
}
else
{
diff --git a/binutils/nm.c b/binutils/nm.c
index a5d56311dde..94333042ee2 100644
--- a/binutils/nm.c
+++ b/binutils/nm.c
@@ -801,7 +801,8 @@ filter_symbols (bfd *abfd, bool is_dynamic, void *minisyms,
if (sym == NULL)
continue;
- if (bfd_lto_slim_symbol_p (abfd, sym->name)
+ if (!bfd_plugin_target_p (abfd->xvec)
+ && bfd_lto_slim_symbol_p (abfd, sym->name)
&& report_plugin_err)
{
report_plugin_err = false;
@@ -1484,7 +1485,8 @@ display_rel_file (bfd *abfd, bfd *archive_bfd)
/* lto_type is set to lto_non_ir_object when a bfd is loaded with a
compiler LTO plugin. */
- if (bfd_get_lto_type (abfd) == lto_slim_ir_object)
+ if (!bfd_plugin_target_p (abfd->xvec)
+ && bfd_get_lto_type (abfd) == lto_slim_ir_object)
{
report_plugin_err = false;
non_fatal (_("%s: plugin needed to handle lto object"),
diff --git a/binutils/objcopy.c b/binutils/objcopy.c
index 5774711abe6..496f0a13daa 100644
--- a/binutils/objcopy.c
+++ b/binutils/objcopy.c
@@ -3746,7 +3746,7 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target,
#if BFD_SUPPORTS_PLUGINS
/* Ignore plugin target if all LTO sections should be removed. */
ok_object = bfd_check_format_lto (this_element, bfd_object,
- lto_sections_removed);
+ lto_sections_removed, true);
#else
ok_object = bfd_check_format (this_element, bfd_object);
#endif
@@ -3768,7 +3768,9 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target,
#if BFD_SUPPORTS_PLUGINS
/* Copy LTO IR file as unknown object. */
- if (bfd_plugin_target_p (this_element->xvec))
+ if ((!lto_sections_removed
+ && this_element->lto_type == lto_slim_ir_object)
+ || bfd_plugin_target_p (this_element->xvec))
ok_object = false;
else
#endif
@@ -3946,7 +3948,8 @@ copy_file (const char *input_filename, const char *output_filename, int ofd,
break;
}
- if (bfd_check_format (ibfd, bfd_archive))
+ if (bfd_check_format_lto (ibfd, bfd_archive, lto_sections_removed,
+ true))
{
bool force_output_target;
bfd *obfd;
@@ -3994,13 +3997,14 @@ copy_file (const char *input_filename, const char *output_filename, int ofd,
removed. Try with plugin target next if ignoring plugin
target fails to match the format. */
bfd_check_format_matches_lto (ibfd, bfd_object, &obj_matching,
- lto_sections_removed)
+ lto_sections_removed, true)
|| (lto_sections_removed
&& bfd_check_format_matches_lto (ibfd, bfd_object,
- &obj_matching, false))
+ &obj_matching, false,
+ true))
#else
bfd_check_format_matches_lto (ibfd, bfd_object, &obj_matching,
- false)
+ false, true)
#endif
)
{
@@ -5066,6 +5070,11 @@ strip_main (int argc, char *argv[])
SECTION_CONTEXT_REMOVE)
|| !!find_section_list (".llvm.lto", false,
SECTION_CONTEXT_REMOVE));
+ /* NB: Must keep .gnu.debuglto_* sections unless all GCC LTO sections
+ will be removed to avoid undefined references to symbols in GCC LTO
+ debug sections. */
+ if (!lto_sections_removed)
+ find_section_list (".gnu.debuglto_*", true, SECTION_CONTEXT_KEEP);
#endif
i = optind;
diff --git a/ld/testsuite/ld-plugin/lto-binutils.exp b/ld/testsuite/ld-plugin/lto-binutils.exp
index 88d35171045..de017f0b946 100644
--- a/ld/testsuite/ld-plugin/lto-binutils.exp
+++ b/ld/testsuite/ld-plugin/lto-binutils.exp
@@ -355,3 +355,178 @@ run_cc_link_tests [list \
"tmpdir/libstrip-1b-fat-s.a" \
] \
]
+
+proc run_pr33246_test { llvm fat } {
+ global srcdir
+ global subdir
+ global plug_opt
+ global llvm_plug_opt
+ global ar
+ global CLANG_FOR_TARGET
+ global CC_FOR_TARGET
+ global NM
+ global READELF
+ global strip
+
+ set strip_flags "--strip-debug --enable-deterministic-archives"
+
+ set test pr33246
+ set testname "${test}${llvm}${fat} with $strip_flags"
+
+ if { "$llvm" == "-llvm" } {
+ # Skip native x32 and i?86 targets since system LLVMgold.so may
+ # not be compatible with native x32 and i?86 targets binutils.
+ if { [istarget "x86_64-*-linux*-gnux32"]
+ || [istarget "i?86-*-*"]
+ || ![info exists CLANG_FOR_TARGET]
+ || [string match "" $llvm_plug_opt] } then {
+ untested $testname
+ return
+ }
+ set CC $CLANG_FOR_TARGET
+ set binutils_plug_opt "$llvm_plug_opt"
+ } else {
+ if { ![info exists CC_FOR_TARGET]
+ || [string match "" $plug_opt] } then {
+ untested $testname
+ return
+ }
+ set CC $CC_FOR_TARGET
+ set binutils_plug_opt "$plug_opt"
+ }
+
+ append strip_flags " $binutils_plug_opt"
+
+ set src $srcdir/$subdir/${test}.c
+ set obj tmpdir/${test}${llvm}${fat}.o
+ set archive tmpdir/${test}${llvm}${fat}.a
+ set CFLAGS "-c -g -O2 -flto"
+ if { "$fat" == "-fat" } {
+ append CFLAGS " -ffat-lto-objects"
+ } else {
+ append CFLAGS " -fno-fat-lto-objects"
+ }
+
+ set cmd "$CC $CFLAGS -o $obj $src"
+ send_log "$cmd\n"
+ verbose "$cmd" 1
+ catch "exec $cmd" got
+ if ![string match "" $got] then {
+ send_log "$got\n"
+ verbose "$got" 1
+ fail "$testname ($obj)"
+ return
+ }
+
+ set cmd "$strip $strip_flags $obj -o ${obj}.strip"
+ send_log "$cmd\n"
+ verbose "$cmd" 1
+ catch "exec $cmd" got
+ if ![string match "" $got] then {
+ send_log "$got\n"
+ verbose "$got" 1
+ fail "$testname (strip $obj)"
+ return
+ }
+
+ set cmd "$NM $binutils_plug_opt ${obj}.strip"
+ send_log "$cmd\n"
+ verbose "$cmd" 1
+ catch "exec $cmd" got
+ if ![regexp "0+ T foo" $got] then {
+ send_log "$got\n"
+ verbose "$got" 1
+ fail "$testname (strip $obj)"
+ return
+ }
+
+ if { "$fat" == "-fat" } {
+ set cmd "$READELF -SW ${obj}.strip"
+ send_log "$cmd\n"
+ verbose "$cmd" 1
+ catch "exec $cmd" got
+ if [regexp " \.debug_" $got] then {
+ send_log "$got\n"
+ verbose "$got" 1
+ fail "$testname (strip $obj)"
+ return
+ }
+ } else {
+ set cmd "cmp $obj ${obj}.strip"
+ send_log "$cmd\n"
+ verbose "$cmd" 1
+ catch "exec $cmd" got
+ if ![string match "" $got] then {
+ send_log "$got\n"
+ verbose "$got" 1
+ fail "$testname (strip $obj)"
+ return
+ }
+ }
+
+ pass "$testname (strip $obj)"
+
+ set cmd "$ar $binutils_plug_opt -D -s -r -c $archive $obj"
+ send_log "$cmd\n"
+ verbose "$cmd" 1
+ catch "exec $cmd" got
+ if ![string match "" $got] then {
+ send_log "$got\n"
+ verbose "$got" 1
+ fail "$testname ($archive)"
+ return
+ }
+
+ set cmd "$strip $strip_flags $archive -o ${archive}.strip"
+ send_log "$cmd\n"
+ verbose "$cmd" 1
+ catch "exec $cmd" got
+ if ![string match "" $got] then {
+ send_log "$got\n"
+ verbose "$got" 1
+ fail "$testname (strip $archive)"
+ return
+ }
+
+ set cmd "$NM $binutils_plug_opt ${archive}.strip"
+ send_log "$cmd\n"
+ verbose "$cmd" 1
+ catch "exec $cmd" got
+ if ![regexp "0+ T foo" $got] then {
+ send_log "$got\n"
+ verbose "$got" 1
+ fail "$testname (strip $archive)"
+ return
+ }
+
+ if { "$fat" == "-fat" } {
+ set cmd "$READELF -SW ${archive}.strip"
+ send_log "$cmd\n"
+ verbose "$cmd" 1
+ catch "exec $cmd" got
+ if [regexp " \.debug_" $got] then {
+ send_log "$got\n"
+ verbose "$got" 1
+ fail "$testname (strip $archive)"
+ return
+ }
+ } else {
+ set cmd "cmp $archive ${archive}.strip"
+ send_log "$cmd\n"
+ verbose "$cmd" 1
+ catch "exec $cmd" got
+ if ![string match "" $got] then {
+ send_log "$got\n"
+ verbose "$got" 1
+ fail "$testname (strip $archive)"
+ return
+ }
+ }
+
+ pass "$testname (strip $archive)"
+}
+
+run_pr33246_test "" ""
+run_pr33246_test "" "-fat"
+run_pr33246_test "-llvm" ""
+run_pr33246_test "-llvm" "-fat"
diff --git a/ld/testsuite/ld-plugin/pr33246.c b/ld/testsuite/ld-plugin/pr33246.c
new file mode 100644
index 00000000000..cd0130cacdf
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pr33246.c
@@ -0,0 +1,4 @@
+void
+foo (void)
+{
+}
--
2.50.1
More information about the Binutils
mailing list