[binutils-gdb] PR ld/24600: LD: Add options to control mapless archive acceptance

Maciej W. Rozycki macro@sourceware.org
Fri Feb 20 17:42:53 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=6cbaeac8401cdab4308bafb14a8b3498bd6427bc

commit 6cbaeac8401cdab4308bafb14a8b3498bd6427bc
Author: Maciej W. Rozycki <macro@redhat.com>
Date:   Fri Feb 20 17:29:50 2026 +0000

    PR ld/24600: LD: Add options to control mapless archive acceptance
    
    Add `--link-mapless' and `--no-link-mapless' LD command-line options, to
    respectively enable and disable the acceptance of mapless archives on a
    per-file basis, also with XCOFF targets, enabled by default.  Update
    documentation and add test cases accordingly.

Diff:
---
 bfd/archive.c                       |  6 ++++
 bfd/bfd-in2.h                       | 15 +++++++++
 bfd/bfd.c                           | 17 +++++++++-
 bfd/xcofflink.c                     |  5 +++
 ld/NEWS                             |  6 ++++
 ld/ld.texi                          | 15 ++++++++-
 ld/ldlang.c                         |  2 ++
 ld/ldlang.h                         |  3 ++
 ld/ldlex.h                          |  2 ++
 ld/ldmain.c                         |  2 ++
 ld/lexsup.c                         | 11 +++++++
 ld/testsuite/ld-archive/abc.ed      |  2 +-
 ld/testsuite/ld-archive/archive.exp | 65 +++++++++++++++++++++++++++++++++++--
 13 files changed, 145 insertions(+), 6 deletions(-)

diff --git a/bfd/archive.c b/bfd/archive.c
index f26d70fcd42..85b7107938a 100644
--- a/bfd/archive.c
+++ b/bfd/archive.c
@@ -1046,6 +1046,12 @@ _bfd_make_armap (bfd *arch, bfd *first_one)
   bfd **last_one;
   bfd *next_one;
 
+  if (!bfd_link_mapless (arch))
+    {
+      bfd_set_error (bfd_error_no_armap);
+      return false;
+    }
+
   last_one = &(arch->archive_next);
   for (next_one = first_one;
        next_one;
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 8f55c1eb077..0aad6a903e4 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -2130,6 +2130,9 @@ struct bfd
   /* Have archive map.  */
   unsigned int has_armap : 1;
 
+  /* Accept a mapless archive for link.  */
+  unsigned int link_mapless : 1;
+
   /* Set if this is a thin archive.  */
   unsigned int is_thin_archive : 1;
 
@@ -2370,6 +2373,12 @@ bfd_has_map (const bfd *abfd)
   return abfd->has_armap;
 }
 
+static inline bool
+bfd_link_mapless (const bfd *abfd)
+{
+  return abfd->link_mapless;
+}
+
 static inline bool
 bfd_is_thin_archive (const bfd *abfd)
 {
@@ -2390,6 +2399,12 @@ bfd_set_cacheable (bfd * abfd, bool val)
   return true;
 }
 
+static inline void
+bfd_set_link_mapless (bfd *abfd, bool val)
+{
+  abfd->link_mapless = val;
+}
+
 static inline void
 bfd_set_thin_archive (bfd *abfd, bool val)
 {
diff --git a/bfd/bfd.c b/bfd/bfd.c
index 35669075da2..27c43ad3d85 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -280,6 +280,9 @@ CODE_FRAGMENT
 .  {* Have archive map.  *}
 .  unsigned int has_armap : 1;
 .
+.  {* Accept a mapless archive for link.  *}
+.  unsigned int link_mapless : 1;
+.
 .  {* Set if this is a thin archive.  *}
 .  unsigned int is_thin_archive : 1;
 .
@@ -523,6 +526,12 @@ EXTERNAL
 .}
 .
 .static inline bool
+.bfd_link_mapless (const bfd *abfd)
+.{
+.  return abfd->link_mapless;
+.}
+.
+.static inline bool
 .bfd_is_thin_archive (const bfd *abfd)
 .{
 .  return abfd->is_thin_archive;
@@ -543,6 +552,12 @@ EXTERNAL
 .}
 .
 .static inline void
+.bfd_set_link_mapless (bfd *abfd, bool val)
+.{
+.  abfd->link_mapless = val;
+.}
+.
+.static inline void
 .bfd_set_thin_archive (bfd *abfd, bool val)
 .{
 .  abfd->is_thin_archive = val;
@@ -798,7 +813,7 @@ const char *const bfd_errmsgs[] =
   N_("invalid operation"),
   N_("memory exhausted"),
   N_("no symbols"),
-  N_("archive has no index; run ranlib to add one"),
+  N_("archive has no index; run ranlib to add one or use --link-mapless"),
   N_("no more archived files"),
   N_("malformed archive"),
   N_("DSO missing from command line"),
diff --git a/bfd/xcofflink.c b/bfd/xcofflink.c
index 691acc854ae..fde21c9f958 100644
--- a/bfd/xcofflink.c
+++ b/bfd/xcofflink.c
@@ -2612,6 +2612,11 @@ _bfd_xcoff_bfd_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
 		 (abfd, info, xcoff_link_check_archive_element)))
 	    return false;
 	}
+      else if (!bfd_link_mapless (abfd))
+	{
+	  bfd_set_error (bfd_error_no_armap);
+	  return false;
+	}
 
       {
 	bfd *member;
diff --git a/ld/NEWS b/ld/NEWS
index 3be78507245..9c9896d950a 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -1,5 +1,11 @@
 -*- text -*-
 
+Changes in 2.47:
+
+* Add linker support for archives with no symbol index in their all formats,
+  not only XCOFF.  On by default and controlled with --no-link-mapless and
+  --link-mapless options, also for XCOFF.
+
 Changes in 2.46:
 
 * Add --gnu-tls-tag/--no-gnu-tls-tag options to i386 ELF linker to add
diff --git a/ld/ld.texi b/ld/ld.texi
index 3dc76a1f8c6..13aef27612f 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -1069,7 +1069,8 @@ restored with one corresponding @option{--pop-state} option.
 The option which are covered are: @option{-Bdynamic}, @option{-Bstatic},
 @option{-dn}, @option{-dy}, @option{-call_shared}, @option{-non_shared},
 @option{-static}, @option{-N}, @option{-n}, @option{--whole-archive},
-@option{--no-whole-archive}, @option{-r}, @option{-Ur},
+@option{--no-whole-archive}, @option{--link-mapless},
+@option{--no-link-mapless}, @option{-r}, @option{-Ur},
 @option{--copy-dt-needed-entries}, @option{--no-copy-dt-needed-entries},
 @option{--as-needed}, @option{--no-as-needed}, and @option{-a}.
 
@@ -2373,6 +2374,12 @@ library during a library search.  This option silences the warning.
 Turn off the effect of the @option{--whole-archive} option for subsequent
 archive files.
 
+@kindex --no-link-mapless
+@item --no-link-mapless
+Reject archives that lack an index aka symbol map for all the subsequent
+archives referred on the command line.  If such a file is encountered,
+a link error occurs.  This is the traditional behavior.
+
 @cindex output file after errors
 @kindex --noinhibit-exec
 @item --noinhibit-exec
@@ -3186,6 +3193,12 @@ Second, don't forget to use @option{-Wl,-no-whole-archive} after your
 list of archives, because gcc will add its own list of archives to
 your link and you may not want this flag to affect those as well.
 
+@kindex --link-mapless
+@item --link-mapless
+Accept archives that lack an index aka symbol map for all the subsequent
+archives referred on the command line.  If missing, such an index is made
+internally on the fly at performance cost.  This is the default.
+
 @kindex --wrap=@var{symbol}
 @item --wrap=@var{symbol}
 Use a wrapper function for @var{symbol}.  Any undefined reference to
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 5ce8c754eac..9f748f38ed6 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -1209,6 +1209,7 @@ new_afile (const char *name,
   p->flags.add_DT_NEEDED_for_dynamic = input_flags.add_DT_NEEDED_for_dynamic;
   p->flags.add_DT_NEEDED_for_regular = input_flags.add_DT_NEEDED_for_regular;
   p->flags.whole_archive = input_flags.whole_archive;
+  p->flags.link_mapless = input_flags.link_mapless;
   p->flags.sysrooted = input_flags.sysrooted;
   p->sort_key = NULL;
 
@@ -3203,6 +3204,7 @@ load_symbols (lang_input_statement_type *entry,
     case bfd_archive:
       check_excluded_libs (entry->the_bfd);
 
+      bfd_set_link_mapless (entry->the_bfd, entry->flags.link_mapless);
       bfd_set_usrdata (entry->the_bfd, entry);
       if (entry->flags.whole_archive)
 	{
diff --git a/ld/ldlang.h b/ld/ldlang.h
index c3ee40a980c..70b91941817 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -276,6 +276,9 @@ struct lang_input_statement_flags
   /* Whether to include the entire contents of an archive.  */
   unsigned int whole_archive : 1;
 
+  /* Whether to accept archives without a symbol map.  */
+  unsigned int link_mapless : 1;
+
   /* Set when bfd opening is successful.  */
   unsigned int loaded : 1;
 
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 85e06b6fcc4..86718087ed5 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -52,6 +52,7 @@ enum option_values
   OPTION_NOINHIBIT_EXEC,
   OPTION_NON_SHARED,
   OPTION_NO_WHOLE_ARCHIVE,
+  OPTION_NO_LINK_MAPLESS,
   OPTION_OFORMAT,
   OPTION_RELAX,
   OPTION_NO_RELAX,
@@ -98,6 +99,7 @@ enum option_values
   OPTION_SPLIT_BY_RELOC,
   OPTION_SPLIT_BY_FILE ,
   OPTION_WHOLE_ARCHIVE,
+  OPTION_LINK_MAPLESS,
   OPTION_ADD_DT_NEEDED_FOR_DYNAMIC,
   OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC,
   OPTION_ADD_DT_NEEDED_FOR_REGULAR,
diff --git a/ld/ldmain.c b/ld/ldmain.c
index afbf039b1e2..77b167fd642 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -799,6 +799,8 @@ main (int argc, char **argv)
   link_info.start_stop_gc = false;
   link_info.start_stop_visibility = STV_PROTECTED;
 
+  input_flags.link_mapless = true;
+
   ldfile_add_arch ("");
   emulation = get_emulation (argc, argv);
   ldemul_choose_mode (emulation);
diff --git a/ld/lexsup.c b/ld/lexsup.c
index 158abb6dd8f..d93e8b4ca77 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -421,6 +421,8 @@ static const struct ld_option ld_options[] =
     TWO_DASHES},
   { {"no-whole-archive", no_argument, NULL, OPTION_NO_WHOLE_ARCHIVE},
     '\0', NULL, N_("Turn off --whole-archive"), TWO_DASHES },
+  { {"no-link-mapless", no_argument, NULL, OPTION_NO_LINK_MAPLESS},
+    '\0', NULL, N_("Reject archives without a symbol map"), TWO_DASHES },
   { {"noinhibit-exec", no_argument, NULL, OPTION_NOINHIBIT_EXEC},
     '\0', NULL, N_("Create an output file even if errors occur"),
     TWO_DASHES },
@@ -602,6 +604,9 @@ static const struct ld_option ld_options[] =
   { {"whole-archive", no_argument, NULL, OPTION_WHOLE_ARCHIVE},
     '\0', NULL, N_("Include all objects from following archives"),
     TWO_DASHES },
+  { {"link-mapless", no_argument, NULL, OPTION_LINK_MAPLESS},
+    '\0', NULL, N_("Accept archives without a symbol map"),
+    TWO_DASHES },
   { {"wrap", required_argument, NULL, OPTION_WRAP},
     '\0', N_("SYMBOL"), N_("Use wrapper functions for SYMBOL"), TWO_DASHES },
   { {"ignore-unresolved-symbol", required_argument, NULL,
@@ -1177,6 +1182,9 @@ parse_args (unsigned argc, char **argv)
 	case OPTION_NO_WHOLE_ARCHIVE:
 	  input_flags.whole_archive = false;
 	  break;
+	case OPTION_NO_LINK_MAPLESS:
+	  input_flags.link_mapless = false;
+	  break;
 	case 'O':
 	  /* FIXME "-O<non-digits> <value>" used to set the address of
 	     section <non-digits>.  Was this for compatibility with
@@ -1657,6 +1665,9 @@ parse_args (unsigned argc, char **argv)
 	case OPTION_WHOLE_ARCHIVE:
 	  input_flags.whole_archive = true;
 	  break;
+	case OPTION_LINK_MAPLESS:
+	  input_flags.link_mapless = true;
+	  break;
 	case OPTION_ADD_DT_NEEDED_FOR_DYNAMIC:
 	  input_flags.add_DT_NEEDED_for_dynamic = true;
 	  break;
diff --git a/ld/testsuite/ld-archive/abc.ed b/ld/testsuite/ld-archive/abc.ed
index 10a696e755e..fecdb767445 100644
--- a/ld/testsuite/ld-archive/abc.ed
+++ b/ld/testsuite/ld-archive/abc.ed
@@ -1 +1 @@
-[^\n]*: tmpdir/abn.a: error adding symbols: archive has no index; run ranlib to add one
+.*: error adding symbols: archive has no index; run ranlib to add one or use --link-mapless
diff --git a/ld/testsuite/ld-archive/archive.exp b/ld/testsuite/ld-archive/archive.exp
index 73742cfb560..f77c449a4f1 100644
--- a/ld/testsuite/ld-archive/archive.exp
+++ b/ld/testsuite/ld-archive/archive.exp
@@ -230,9 +230,9 @@ run_ld_link_tests [list \
     ] \
 ]
 
-# Single non-index archive tests.
+# Single non-index archive accept tests.
 run_ld_link_tests [list \
-    [list "Regular archive w/o index link" \
+    [list "Regular archive w/o index link accept" \
 	"-e ff" "tmpdir/abn.a" \
 	"" {abc.s} \
 	[ld_archive_filter_tests {{nm "" abc.nd} \
@@ -242,7 +242,7 @@ run_ld_link_tests [list \
 ]
 setup_xfail "binutils/33484" "alpha*-*-linux*ecoff*" "alpha*-*-osf*"
 run_ld_link_tests [list \
-    [list "Thin archive w/o index link" \
+    [list "Thin archive w/o index link accept" \
 	"-e ff" "tmpdir/abnt.a" \
 	"" {abc.s} \
 	[ld_archive_filter_tests {{nm "" abc.nd} \
@@ -251,6 +251,32 @@ run_ld_link_tests [list \
     ] \
 ]
 
+# Dual non-index archive accept tests.
+setup_xfail "binutils/33484" "alpha*-*-linux*ecoff*" "alpha*-*-osf*"
+setup_xfail "binutils/33485" "cris-*-*aout*" "i\[3-7\]86-*-bsd*" \
+    "i\[3-7\]86-*-msdos*" "ns32k-*-*" "pdp11-*-*"
+run_ld_link_tests [list \
+    [list "Regular archive w/o index plus thin link accept" \
+	"-e ff" "--no-link-mapless tmpdir/cdt.a --link-mapless tmpdir/abn.a" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abncdte" \
+    ] \
+]
+setup_xfail "binutils/33484" "alpha*-*-linux*ecoff*" "alpha*-*-osf*"
+run_ld_link_tests [list \
+    [list "Thin archive w/o index plus regular link accept" \
+	"-e ff" "--no-link-mapless tmpdir/cd.a --link-mapless tmpdir/abnt.a" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abntcde" \
+    ] \
+]
+
 # Group non-index archive tests.
 run_ld_link_tests [list \
     [list "Regular non-group archive w/o index link reject" \
@@ -292,3 +318,36 @@ run_ld_link_tests [list \
 ]
 
 set LDFLAGS $old_ldflags
+
+# Alpha/VMS archives always have an index; we're done with the tests.
+if { [istarget "alpha*-*-*vms*"] } {
+    return
+}
+
+# Non-index archive reject tests.
+run_ld_link_tests [list \
+    [list "Regular archive w/o index link reject" \
+	"-e ff" "--no-link-mapless tmpdir/abn.a" \
+	"" {abc.s} \
+	{{ld abc.ed}} \
+	"abnc" \
+    ] \
+    [list "Thin archive w/o index link reject" \
+	"-e ff" "--no-link-mapless tmpdir/abnt.a" \
+	"" {abc.s} \
+	{{ld abc.ed}} \
+	"abntc" \
+    ] \
+    [list "Regular archive w/o index plus thin link reject" \
+	"-e ff" "--no-link-mapless tmpdir/abn.a --link-mapless tmpdir/cdt.a" \
+	"" {abcde.s} \
+	{{ld abc.ed}} \
+	"abncdte" \
+    ] \
+    [list "Thin archive w/o index plus regular link reject" \
+	"-e ff" "--no-link-mapless tmpdir/abnt.a --link-mapless tmpdir/cd.a" \
+	"" {abcde.s} \
+	{{ld abc.ed}} \
+	"abntcde" \
+    ] \
+]


More information about the Binutils-cvs mailing list