[PATCH v4 08/14] PR ld/24600: BFD: Add general linker support for mapless archives

Maciej W. Rozycki macro@orcam.me.uk
Tue Jan 13 01:44:55 GMT 2026


From: Maciej W. Rozycki <macro@redhat.com>

Expand linker mapless archive support from XCOFF targets only across the 
remaining ones, except for VMS targets whose archive format always has a 
symbol map.  For this tranform BFD code used by AR to produce symbol 
maps to archive files such as to have a handler supplied to either write 
a map to a file or convert symbol data to an archive symbol definition 
table attached to an archive BFD as if read from a symbol map, but using 
pointers to member BFDs rather than file offsets.  Retain XCOFF handling 
code as it is.

Where the archive group feature is used a given archive may be opened 
multiple times in a single link.  If this happens then a reference to a 
symbol the definition of which is provided by said archive will change 
from undefined on the first access to the archive to defined on later 
accesses.  The symbol table is pulled from an archive only for undefined 
references, so if a symbol table has been pulled on first access and 
then dropped by on-the-fly symbol map generation on a subsequent access, 
then the symbol table is never re-read.  Later on when the linker wants 
to access it to actually resolve symbol references it won't have been 
reloaded and a crash would happen on a null pointer dereference.

To prevent this from happening add code to `_bfd_compute_and_push_armap' 
to let the caller request the symbol table to be retained and ask for it 
when building a symbol map on the fly in the linker.  The symbol table 
will likely be used further down the link anyway.

Additionally always check the format of the first member of an archive, 
even if no symbol map is present, and respect the format determined, so 
that the correct format is used even if it is not the default one the LD 
has been configured for, preventing segmentation faults from triggering 
in `cris-aout' LD with the default (`crisaout') emulation or with the 
`criself' emulation explicitly requested where archive members are in 
the ELF format and consequntly removing failures with new tests:

cris-aout  -FAIL: CRIS regular archive w/o index link (explicit emulation)
cris-aout  -FAIL: CRIS regular archive w/o index link (implicit emulation)
cris-aout  -FAIL: CRIS thin archive w/o index link (explicit emulation)
cris-aout  -FAIL: CRIS thin archive w/o index link (implicit emulation)

Update documentation and adjust test cases accordingly now that mapless 
archives are accepted for link by all targets except for VMS ones, which 
never produce them in the first place.
---
Changes from v3 (12/18),
<https://inbox.sourceware.org/binutils/alpine.DEB.2.21.2511192324220.57987@angie.orcam.me.uk/>:

- Update the condition for the first archive member check according to 
  plugin handling changes made with 05/14 in `bfd_generic_archive_p' and 
  make a matching amendment to the change description.

- Factor out updates to archive member accesses now possibly made via a 
  BFD rather than file pointer from `elf_link_add_archive_symbols' and 
  `_bfd_generic_link_add_archive_symbols' to new `_bfd_elt_nil' and 
  `_bfd_elt_eq' helpers; revert the initial archive member file pointer 
  setting for an inexistent member back to the pre-v2 value.

- Rename the BFD pointer parameter of newly-added functions consistently 
  from ABFD to ARCH.

- Remove a needless K&R cast from a new call to `bfd_alloc'.

Changes from v2 (10/16), 
<https://inbox.sourceware.org/binutils/alpine.DEB.2.21.2511060351270.25436@angie.orcam.me.uk/>:

- Also check and respect the format of the first archive member if no 
  symbol map is present.

- Add CRIS linker tests for emulation switching with mapless archives.

Changes from v1 (7/8), 
<https://inbox.sourceware.org/binutils/alpine.DEB.2.21.2509262121560.63399@angie.orcam.me.uk/>:

- Update to use `ufile_ptr_or_bfd' rather than `file_ptr_or_bfd'.

- Retain the symbol table loaded with maps created by the linker.

- Set an archive's `has_armap' flag once a map has been built on the fly.

- Also update `alpha_ecoff_get_elt_at_index', although the function does
  not appear to be used.

- Update archive.exp according to changes for Alpha/VMS made with 02/16.

- Add archive group tests.

- Remove ld/testsuite/ld-archive/abct.ed.

- Update the description according to the VMS situation.

- Update the description according to the current CRIS target situation.
---
 bfd/archive.c                       |  153 +++++++++++++++++++++++++++++++-----
 bfd/coff-alpha.c                    |    5 -
 bfd/coff-rs6000.c                   |    6 -
 bfd/ecoff.c                         |    9 +-
 bfd/elflink.c                       |   24 +++--
 bfd/format.c                        |   11 --
 bfd/libbfd-in.h                     |   24 +++++
 bfd/libbfd.h                        |   44 +++++++++-
 bfd/linker.c                        |   22 ++---
 binutils/doc/binutils.texi          |   16 +--
 ld/testsuite/ld-archive/abct.ed     |    1 
 ld/testsuite/ld-archive/abncng.vd   |   28 ++++++
 ld/testsuite/ld-archive/abntcntg.vd |   28 ++++++
 ld/testsuite/ld-archive/archive.exp |   61 +++++++++++---
 ld/testsuite/ld-cris/archive.exp    |   32 +++++++
 15 files changed, 382 insertions(+), 82 deletions(-)

binutils-bfd-ar-noindex.diff
Index: binutils-gdb/bfd/archive.c
===================================================================
--- binutils-gdb.orig/bfd/archive.c
+++ binutils-gdb/bfd/archive.c
@@ -179,6 +179,26 @@ INTERNAL
 .  int namidx;		{* Index into string table.  *}
 .};
 .
+.{* Return an inexistent element reference for archive ARCH.  *}
+.
+.static inline ufile_ptr_or_bfd
+._bfd_elt_nil (bfd *arch)
+.{
+.  return (bfd_ardata (arch)->symdef_use_bfd
+.	   ? (ufile_ptr_or_bfd) { .abfd = NULL }
+.	   : (ufile_ptr_or_bfd) { .file_offset = -1 });
+.}
+.
+.{* Tell if ELTREF1 and ELTREF2 refer the same element of archive ARCH.  *}
+.
+.static inline bool
+._bfd_elt_eq (bfd *arch, ufile_ptr_or_bfd eltref1, ufile_ptr_or_bfd eltref2)
+.{
+.  return (bfd_ardata (arch)->symdef_use_bfd
+.	   ? eltref1.abfd == eltref2.abfd
+.	   : eltref1.file_offset == eltref2.file_offset);
+.}
+.
 */
 
 /* We keep a cache of archive filepointers to archive elements to
@@ -811,7 +831,7 @@ _bfd_generic_get_elt_at_index (bfd *abfd
   carsym *entry;
 
   entry = bfd_ardata (abfd)->symdefs + sym_index;
-  return _bfd_get_elt_at_filepos (abfd, entry->u.file_offset, NULL);
+  return _bfd_get_elt_from_symdef (abfd, entry, NULL);
 }
 
 bfd *
@@ -928,17 +948,15 @@ bfd_generic_archive_p (bfd *abfd)
       return NULL;
     }
 
-  if ((abfd->target_defaulted || abfd->is_linker_input)
-      && bfd_has_map (abfd))
+  if (abfd->target_defaulted || abfd->is_linker_input)
     {
       bfd *first;
       unsigned int save;
 
-      /* This archive has a map, so we may presume that the contents
-	 are object files.  Make sure that if the first file in the
-	 archive can be recognized as an object file, it is for this
-	 target.  If not, assume that this is the wrong format.  If
-	 the first file is not an object file, somebody is doing
+      /* Make sure that if the first file in the archive can be
+	 recognized as an object file, it is for this target.
+	 If not, assume that this is the wrong format.  If the
+	 first file is not an object file, somebody is doing
 	 something weird, and we permit it so that ar -t will work.
 
 	 This is done because any normal format will recognize any
@@ -964,6 +982,84 @@ bfd_generic_archive_p (bfd *abfd)
   return _bfd_no_cleanup;
 }
 
+/* Given archive ARCH and symbol map MAP counting ORL_COUNT entries
+   load the symbols for use by the archive.  */
+
+static bool
+_bfd_load_armap (bfd *arch, unsigned int elength ATTRIBUTE_UNUSED,
+		 struct orl *map, unsigned int orl_count,
+		 int stridx ATTRIBUTE_UNUSED)
+{
+  struct artdata *ardata = bfd_ardata (arch);
+  size_t symdef_size;
+  size_t counter;
+  carsym *set;
+
+  if (_bfd_mul_overflow (orl_count, sizeof (carsym), &symdef_size))
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
+  ardata->symdefs = bfd_alloc (arch, symdef_size);
+  if (!ardata->symdefs)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
+  ardata->symdef_count = orl_count;
+
+  for (counter = 0, set = ardata->symdefs;
+       counter < ardata->symdef_count;
+       counter++, set++)
+    {
+      bfd_size_type namelen = strlen (*map[counter].name) + 1;
+      char *name = bfd_alloc (arch, namelen);
+
+      if (name == NULL)
+	{
+	  bfd_set_error (bfd_error_no_memory);
+	  goto release_symdefs;
+	}
+
+      memcpy (name, *map[counter].name, namelen);
+      set->name = name;
+      set->u.abfd = map[counter].abfd;
+    }
+
+  ardata->symdef_use_bfd = true;
+  arch->has_armap = true;
+  return true;
+
+ release_symdefs:
+  bfd_release (arch, ardata->symdefs);
+  ardata->symdef_count = 0;
+  ardata->symdefs = NULL;
+  return false;
+}
+
+/* Iterate over members of archive ARCH starting from FIRST_ONE and
+   load their symbols for use by the archive.  */
+
+bool
+_bfd_make_armap (bfd *arch, bfd *first_one)
+{
+  bfd **last_one;
+  bfd *next_one;
+
+  last_one = &(arch->archive_next);
+  for (next_one = first_one;
+       next_one;
+       next_one = bfd_openr_next_archived_file (arch, next_one))
+    {
+      *last_one = next_one;
+      last_one = &next_one->archive_next;
+    }
+  *last_one = NULL;
+  bfd_set_archive_head (arch, first_one);
+
+  return _bfd_compute_and_push_armap (arch, 0, true, _bfd_load_armap);
+}
+
 /* Some constants for a 32 bit BSD archive structure.  We do not
    support 64 bit archives presently; so far as I know, none actually
    exist.  Supporting them would require changing these constants, and
@@ -2215,7 +2311,8 @@ _bfd_write_archive_contents (bfd *arch)
 
   if (makemap && hasobjects)
     {
-      if (! _bfd_compute_and_write_armap (arch, (unsigned int) elength))
+      if (!_bfd_compute_and_push_armap (arch, (unsigned int) elength, false,
+					_bfd_write_armap))
 	return false;
     }
 
@@ -2313,10 +2410,32 @@ _bfd_write_archive_contents (bfd *arch)
   return false;
 }
 
-/* Note that the namidx for the first symbol is 0.  */
+/* Given archive ARCH write symbol map MAP counting ORL_COUNT entries
+   and using STRIDX bytes for symbol names to the archive file, with
+   ELENGTH holding the length of any extended name table.  */
 
 bool
-_bfd_compute_and_write_armap (bfd *arch, unsigned int elength)
+_bfd_write_armap (bfd *arch, unsigned int elength,
+		  struct orl *map, unsigned int orl_count, int stridx)
+{
+  /* Dunno if this is the best place for this info...  */
+  if (elength != 0)
+    elength += sizeof (struct ar_hdr);
+  elength += elength % 2;
+
+  return BFD_SEND (arch, write_armap,
+		   (arch, elength, map, orl_count, stridx));
+}
+
+/* Iterate over members of archive ARCH retrieving their symbols and then
+   push the symbols out using PUSH_ARMAP handler, giving it extended name
+   table length ELENGTH.  Retain the information according to KEEP_SYMTAB.
+   Note that the namidx for the first symbol is 0.  */
+
+bool
+_bfd_compute_and_push_armap
+  (bfd *arch, unsigned int elength, bool keep_symtab,
+   bool (*push_armap) (bfd *, unsigned int, struct orl *, unsigned int, int))
 {
   char *first_name = NULL;
   bfd *current;
@@ -2330,11 +2449,6 @@ _bfd_compute_and_write_armap (bfd *arch,
   size_t amt;
   static bool report_plugin_err = true;
 
-  /* Dunno if this is the best place for this info...  */
-  if (elength != 0)
-    elength += sizeof (struct ar_hdr);
-  elength += elength % 2;
-
   amt = orl_max * sizeof (struct orl);
   map = (struct orl *) bfd_malloc (amt);
   if (map == NULL)
@@ -2451,14 +2565,13 @@ _bfd_compute_and_write_armap (bfd *arch,
 
 	  /* Now ask the BFD to free up any cached information, so we
 	     don't fill all of memory with symbol tables.  */
-	  if (! bfd_free_cached_info (current))
+	  if (!keep_symtab && !bfd_free_cached_info (current))
 	    goto error_return;
 	}
     }
 
-  /* OK, now we have collected all the data, let's write them out.  */
-  ret = BFD_SEND (arch, write_armap,
-		  (arch, elength, map, orl_count, stridx));
+  /* OK, now we have collected all the data, let's push them out.  */
+  ret = push_armap (arch, elength, map, orl_count, stridx);
 
   free (syms);
   free (map);
Index: binutils-gdb/bfd/coff-alpha.c
===================================================================
--- binutils-gdb.orig/bfd/coff-alpha.c
+++ binutils-gdb/bfd/coff-alpha.c
@@ -2240,7 +2240,10 @@ alpha_ecoff_get_elt_at_index (bfd *abfd,
   carsym *entry;
 
   entry = bfd_ardata (abfd)->symdefs + sym_index;
-  return alpha_ecoff_get_elt_at_filepos (abfd, entry->u.file_offset, NULL);
+  if (bfd_ardata (abfd)->symdef_use_bfd)
+    return entry->u.abfd;
+  else
+    return alpha_ecoff_get_elt_at_filepos (abfd, entry->u.file_offset, NULL);
 }
 
 static void
Index: binutils-gdb/bfd/coff-rs6000.c
===================================================================
--- binutils-gdb.orig/bfd/coff-rs6000.c
+++ binutils-gdb/bfd/coff-rs6000.c
@@ -2498,7 +2498,8 @@ xcoff_write_archive_contents_old (bfd *a
       BFD_ASSERT (nextoff == bfd_tell (abfd));
       sprintf (fhdr->symoff, "%ld", (long) nextoff);
       bfd_ardata (abfd)->tdata = &xtdata;
-      bool ret = _bfd_compute_and_write_armap (abfd, 0);
+      bool ret = _bfd_compute_and_push_armap (abfd, 0, false,
+					      _bfd_write_armap);
       bfd_ardata (abfd)->tdata = NULL;
       if (!ret)
 	return false;
@@ -2766,7 +2767,8 @@ xcoff_write_archive_contents_big (bfd *a
       PRINT20 (fhdr->symoff, nextoff);
 
       bfd_ardata (abfd)->tdata = &xtdata;
-      bool ret = _bfd_compute_and_write_armap (abfd, 0);
+      bool ret = _bfd_compute_and_push_armap (abfd, 0, false,
+					      _bfd_write_armap);
       bfd_ardata (abfd)->tdata = NULL;
       if (!ret)
 	return false;
Index: binutils-gdb/bfd/ecoff.c
===================================================================
--- binutils-gdb.orig/bfd/ecoff.c
+++ binutils-gdb/bfd/ecoff.c
@@ -3606,11 +3606,14 @@ ecoff_link_add_archive_symbols (bfd *abf
 
   if (! bfd_has_map (abfd))
     {
+      bfd *first_one = bfd_openr_next_archived_file (abfd, NULL);
+
       /* An empty archive is a special case.  */
-      if (bfd_openr_next_archived_file (abfd, NULL) == NULL)
+      if (first_one == NULL)
 	return true;
-      bfd_set_error (bfd_error_no_armap);
-      return false;
+
+      if (!_bfd_make_armap (abfd, first_one))
+	return false;
     }
 
   /* If we don't have any raw data for this archive, as can happen on
Index: binutils-gdb/bfd/elflink.c
===================================================================
--- binutils-gdb.orig/bfd/elflink.c
+++ binutils-gdb/bfd/elflink.c
@@ -3707,7 +3707,7 @@ elf_link_is_defined_archive_symbol (bfd
   Elf_Internal_Sym *isymend;
   bool result;
 
-  abfd = _bfd_get_elt_at_filepos (abfd, symdef->u.file_offset, NULL);
+  abfd = _bfd_get_elt_from_symdef (abfd, symdef, NULL);
   if (abfd == NULL)
     return false;
 
@@ -6261,11 +6261,14 @@ elf_link_add_archive_symbols (bfd *abfd,
 
   if (! bfd_has_map (abfd))
     {
+      bfd *first_one = bfd_openr_next_archived_file (abfd, NULL);
+
       /* An empty archive is a special case.  */
-      if (bfd_openr_next_archived_file (abfd, NULL) == NULL)
+      if (first_one == NULL)
 	return true;
-      bfd_set_error (bfd_error_no_armap);
-      return false;
+
+      if (!_bfd_make_armap (abfd, first_one))
+	return false;
     }
 
   /* Keep track of all symbols we know to be already defined, and all
@@ -6285,13 +6288,13 @@ elf_link_add_archive_symbols (bfd *abfd,
 
   do
     {
-      ufile_ptr last;
+      ufile_ptr_or_bfd last;
       symindex i;
       carsym *symdef;
       carsym *symdefend;
 
       loop = false;
-      last = -1;
+      last = _bfd_elt_nil (abfd);
 
       symdef = symdefs;
       symdefend = symdef + c;
@@ -6304,7 +6307,7 @@ elf_link_add_archive_symbols (bfd *abfd,
 
 	  if (included[i])
 	    continue;
-	  if (symdef->u.file_offset == last)
+	  if (_bfd_elt_eq (abfd, symdef->u, last))
 	    {
 	      included[i] = true;
 	      continue;
@@ -6389,8 +6392,7 @@ elf_link_add_archive_symbols (bfd *abfd,
 	    }
 
 	  /* We need to include this archive member.  */
-	  element = _bfd_get_elt_at_filepos (abfd, symdef->u.file_offset,
-					     info);
+	  element = _bfd_get_elt_from_symdef (abfd, symdef, info);
 	  if (element == NULL)
 	    goto error_return;
 
@@ -6425,11 +6427,11 @@ elf_link_add_archive_symbols (bfd *abfd,
 		break;
 	      --mark;
 	    }
-	  while (symdefs[mark].u.file_offset == symdef->u.file_offset);
+	  while (_bfd_elt_eq (abfd, symdefs[mark].u, symdef->u));
 
 	  /* We mark subsequent symbols from this object file as we go
 	     on through the loop.  */
-	  last = symdef->u.file_offset;
+	  last = symdef->u;
 	}
     }
   while (loop);
Index: binutils-gdb/bfd/format.c
===================================================================
--- binutils-gdb.orig/bfd/format.c
+++ binutils-gdb/bfd/format.c
@@ -549,13 +549,7 @@ bfd_check_format_matches (bfd *abfd, bfd
 	      /* An archive with object files matching the archive
 		 target is OK.  Other archives should be further
 		 tested.  */
-	      || (bfd_has_map (abfd)
-		  && bfd_get_error () != bfd_error_wrong_object_format)
-	      /* Empty archives can match the current target.
-		 Attempting to read the armap will result in a file
-		 truncated error.  */
-	      || (!bfd_has_map (abfd)
-		  && bfd_get_error () == bfd_error_file_truncated))
+	      || bfd_get_error () != bfd_error_wrong_object_format)
 	    goto ok_ret;
 	}
       else
@@ -629,8 +623,7 @@ bfd_check_format_matches (bfd *abfd, bfd
 	  int match_priority = abfd->xvec->match_priority;
 
 	  if (abfd->format != bfd_archive
-	      || (bfd_has_map (abfd)
-		  && bfd_get_error () != bfd_error_wrong_object_format))
+	      || bfd_get_error () != bfd_error_wrong_object_format)
 	    {
 	      /* If this is the default target, accept it, even if
 		 other targets might match.  People who want those
Index: binutils-gdb/bfd/libbfd-in.h
===================================================================
--- binutils-gdb.orig/bfd/libbfd-in.h
+++ binutils-gdb/bfd/libbfd-in.h
@@ -66,6 +66,7 @@ struct artdata
   htab_t cache;
   carsym *symdefs;		/* The symdef entries.  */
   symindex symdef_count;	/* How many there are.  */
+  unsigned int symdef_use_bfd:1; /* Whether entries hold a BFD pointer.  */
   char *extended_names;		/* Clever intel extension.  */
   bfd_size_type extended_names_size; /* Size of extended names.  */
   /* When more compilers are standard C, this can be a time_t.  */
@@ -121,6 +122,8 @@ extern char *_bfd_append_relative_path
   (bfd *, char *) ATTRIBUTE_HIDDEN;
 extern bfd_cleanup bfd_generic_archive_p
   (bfd *) ATTRIBUTE_HIDDEN;
+extern bool _bfd_make_armap
+  (bfd *, bfd *) ATTRIBUTE_HIDDEN;
 extern bool bfd_slurp_armap
   (bfd *) ATTRIBUTE_HIDDEN;
 #define bfd_slurp_bsd_armap bfd_slurp_armap
@@ -153,13 +156,30 @@ extern bool _bfd_construct_extended_name
   (bfd *, bool, char **, bfd_size_type *) ATTRIBUTE_HIDDEN;
 extern bool _bfd_write_archive_contents
   (bfd *) ATTRIBUTE_HIDDEN;
-extern bool _bfd_compute_and_write_armap
-  (bfd *, unsigned int) ATTRIBUTE_HIDDEN;
+extern bool _bfd_write_armap
+  (bfd *, unsigned int, struct orl *, unsigned int, int) ATTRIBUTE_HIDDEN;
+extern bool _bfd_compute_and_push_armap
+  (bfd *, unsigned int, bool,
+   bool (*) (bfd *, unsigned int, struct orl *, unsigned int, int))
+  ATTRIBUTE_HIDDEN;
 extern bfd *_bfd_get_elt_at_filepos
   (bfd *, file_ptr, struct bfd_link_info *) ATTRIBUTE_HIDDEN;
 extern bfd *_bfd_generic_get_elt_at_index
   (bfd *, symindex) ATTRIBUTE_HIDDEN;
 
+/* Get a handle for the element of archive ARCH referred by archive symbol
+   definition SYMDEF and using linker information INFO.  */
+
+static inline bfd *
+_bfd_get_elt_from_symdef (bfd *arch, carsym *symdef,
+			  struct bfd_link_info *info)
+{
+  if (bfd_ardata (arch)->symdef_use_bfd)
+    return symdef->u.abfd;
+  else
+    return _bfd_get_elt_at_filepos (arch, symdef->u.file_offset, info);
+}
+
 extern bool _bfd_bool_bfd_false
   (bfd *) ATTRIBUTE_HIDDEN;
 extern bool _bfd_bool_bfd_asymbol_false
Index: binutils-gdb/bfd/libbfd.h
===================================================================
--- binutils-gdb.orig/bfd/libbfd.h
+++ binutils-gdb/bfd/libbfd.h
@@ -72,6 +72,7 @@ struct artdata
   htab_t cache;
   carsym *symdefs;		/* The symdef entries.  */
   symindex symdef_count;	/* How many there are.  */
+  unsigned int symdef_use_bfd:1; /* Whether entries hold a BFD pointer.  */
   char *extended_names;		/* Clever intel extension.  */
   bfd_size_type extended_names_size; /* Size of extended names.  */
   /* When more compilers are standard C, this can be a time_t.  */
@@ -127,6 +128,8 @@ extern char *_bfd_append_relative_path
   (bfd *, char *) ATTRIBUTE_HIDDEN;
 extern bfd_cleanup bfd_generic_archive_p
   (bfd *) ATTRIBUTE_HIDDEN;
+extern bool _bfd_make_armap
+  (bfd *, bfd *) ATTRIBUTE_HIDDEN;
 extern bool bfd_slurp_armap
   (bfd *) ATTRIBUTE_HIDDEN;
 #define bfd_slurp_bsd_armap bfd_slurp_armap
@@ -159,13 +162,30 @@ extern bool _bfd_construct_extended_name
   (bfd *, bool, char **, bfd_size_type *) ATTRIBUTE_HIDDEN;
 extern bool _bfd_write_archive_contents
   (bfd *) ATTRIBUTE_HIDDEN;
-extern bool _bfd_compute_and_write_armap
-  (bfd *, unsigned int) ATTRIBUTE_HIDDEN;
+extern bool _bfd_write_armap
+  (bfd *, unsigned int, struct orl *, unsigned int, int) ATTRIBUTE_HIDDEN;
+extern bool _bfd_compute_and_push_armap
+  (bfd *, unsigned int, bool,
+   bool (*) (bfd *, unsigned int, struct orl *, unsigned int, int))
+  ATTRIBUTE_HIDDEN;
 extern bfd *_bfd_get_elt_at_filepos
   (bfd *, file_ptr, struct bfd_link_info *) ATTRIBUTE_HIDDEN;
 extern bfd *_bfd_generic_get_elt_at_index
   (bfd *, symindex) ATTRIBUTE_HIDDEN;
 
+/* Get a handle for the element of archive ARCH referred by archive symbol
+   definition SYMDEF and using linker information INFO.  */
+
+static inline bfd *
+_bfd_get_elt_from_symdef (bfd *arch, carsym *symdef,
+			  struct bfd_link_info *info)
+{
+  if (bfd_ardata (arch)->symdef_use_bfd)
+    return symdef->u.abfd;
+  else
+    return _bfd_get_elt_at_filepos (arch, symdef->u.file_offset, info);
+}
+
 extern bool _bfd_bool_bfd_false
   (bfd *) ATTRIBUTE_HIDDEN;
 extern bool _bfd_bool_bfd_asymbol_false
@@ -1011,6 +1031,26 @@ struct orl             /* Output ranlib.
   int namidx;          /* Index into string table.  */
 };
 
+/* Return an inexistent element reference for archive ARCH.  */
+
+static inline ufile_ptr_or_bfd
+_bfd_elt_nil (bfd *arch)
+{
+  return (bfd_ardata (arch)->symdef_use_bfd
+	  ? (ufile_ptr_or_bfd) { .abfd = NULL }
+	  : (ufile_ptr_or_bfd) { .file_offset = -1 });
+}
+
+/* Tell if ELTREF1 and ELTREF2 refer the same element of archive ARCH.  */
+
+static inline bool
+_bfd_elt_eq (bfd *arch, ufile_ptr_or_bfd eltref1, ufile_ptr_or_bfd eltref2)
+{
+  return (bfd_ardata (arch)->symdef_use_bfd
+	  ? eltref1.abfd == eltref2.abfd
+	  : eltref1.file_offset == eltref2.file_offset);
+}
+
 /* Extracted from archures.c.  */
 extern const bfd_arch_info_type bfd_default_arch_struct;
 
Index: binutils-gdb/bfd/linker.c
===================================================================
--- binutils-gdb.orig/bfd/linker.c
+++ binutils-gdb/bfd/linker.c
@@ -939,11 +939,14 @@ _bfd_generic_link_add_archive_symbols
 
   if (! bfd_has_map (abfd))
     {
+      bfd *first_one = bfd_openr_next_archived_file (abfd, NULL);
+
       /* An empty archive is a special case.  */
-      if (bfd_openr_next_archived_file (abfd, NULL) == NULL)
+      if (first_one == NULL)
 	return true;
-      bfd_set_error (bfd_error_no_armap);
-      return false;
+
+      if (!_bfd_make_armap (abfd, first_one))
+	return false;
     }
 
   amt = bfd_ardata (abfd)->symdef_count;
@@ -960,7 +963,7 @@ _bfd_generic_link_add_archive_symbols
       carsym *arsym_end;
       carsym *arsym;
       unsigned int indx;
-      ufile_ptr last_ar_offset = -1;
+      ufile_ptr_or_bfd last = _bfd_elt_nil (abfd);
       bool needed = false;
       bfd *element = NULL;
 
@@ -974,7 +977,7 @@ _bfd_generic_link_add_archive_symbols
 
 	  if (included[indx])
 	    continue;
-	  if (needed && arsym->u.file_offset == last_ar_offset)
+	  if (needed && _bfd_elt_eq (abfd, arsym->u, last))
 	    {
 	      included[indx] = 1;
 	      continue;
@@ -1003,11 +1006,10 @@ _bfd_generic_link_add_archive_symbols
 	      continue;
 	    }
 
-	  if (last_ar_offset != arsym->u.file_offset)
+	  if (!_bfd_elt_eq (abfd, last, arsym->u))
 	    {
-	      last_ar_offset = arsym->u.file_offset;
-	      element = _bfd_get_elt_at_filepos (abfd, last_ar_offset,
-						 info);
+	      last = arsym->u;
+	      element = _bfd_get_elt_from_symdef (abfd, arsym, info);
 	      if (element == NULL
 		  || !bfd_check_format (element, bfd_object))
 		goto error_return;
@@ -1034,7 +1036,7 @@ _bfd_generic_link_add_archive_symbols
 		    break;
 		  --mark;
 		}
-	      while (arsyms[mark].u.file_offset == last_ar_offset);
+	      while (_bfd_elt_eq (abfd, arsyms[mark].u, last));
 
 	      if (undefs_tail != info->hash->undefs_tail)
 		loop = true;
Index: binutils-gdb/binutils/doc/binutils.texi
===================================================================
--- binutils-gdb.orig/binutils/doc/binutils.texi
+++ binutils-gdb/binutils/doc/binutils.texi
@@ -205,9 +205,7 @@ subroutines.  Since libraries often will
 object modules in the archive when you specify the modifier @samp{s}.
 Once created, this index is updated in the archive whenever @command{ar}
 makes a change to its contents (save for the @samp{q} update operation).
-An archive with such an index speeds up linking to the library, and
-allows routines in the library to call each other without regard to
-their placement in the archive.
+An archive with such an index speeds up linking to the library.
 
 You may use @samp{nm -s} or @samp{nm --print-armap} to list this index
 table.  If an archive lacks the table, another form of @command{ar} called
@@ -501,10 +499,10 @@ archive is equivalent to running @samp{r
 @item S
 @cindex not writing archive index
 Do not generate an archive symbol table.  This can speed up building a
-large library in several steps.  The resulting archive can not be used
-with the linker.  In order to build a symbol table, you must omit the
-@samp{S} modifier on the last execution of @samp{ar}, or you must run
-@samp{ranlib} on the archive.
+large library in several steps, but will negatively impact performance
+if the resulting archive is used with the linker.  In order to build a
+symbol table, you must omit the @samp{S} modifier on the last execution
+of @samp{ar}, or you must run @samp{ranlib} on the archive.
 
 @item T
 Deprecated alias for @option{--thin}.  @option{T} is not recommended because in
@@ -3164,9 +3162,7 @@ member of an archive that is a relocatab
 
 You may use @samp{nm -s} or @samp{nm --print-armap} to list this index.
 
-An archive with such an index speeds up linking to the library and
-allows routines in the library to call each other without regard to
-their placement in the archive.
+An archive with such an index speeds up linking to the library.
 
 The @sc{gnu} @command{ranlib} program is another form of @sc{gnu} @command{ar}; running
 @command{ranlib} is completely equivalent to executing @samp{ar -s}.
Index: binutils-gdb/ld/testsuite/ld-archive/abct.ed
===================================================================
--- binutils-gdb.orig/ld/testsuite/ld-archive/abct.ed
+++ /dev/null
@@ -1 +0,0 @@
-[^\n]*: tmpdir/abnt.a: error adding symbols: archive has no index; run ranlib to add one
Index: binutils-gdb/ld/testsuite/ld-archive/abncng.vd
===================================================================
--- /dev/null
+++ binutils-gdb/ld/testsuite/ld-archive/abncng.vd
@@ -0,0 +1,28 @@
+#...
+using ..ternal linker script:.*
+==================================================
+#...
+==================================================
+.*: mode .*
+attempt to open tmpdir/abn\.a succeeded
+tmpdir/abn\.a
+attempt to open tmpdir/abcn\.a succeeded
+tmpdir/abcn\.a
+Archive member included to satisfy reference by file \(symbol\)
+
+tmpdir/abcn\.a\(abc\.o(:?bj)?\) +\(ff\)
+\(tmpdir/abcn\.a\)abc\.o(:?bj)?
+tmpdir/abn\.a
+tmpdir/abn\.a\(a\.o(:?bj)?\) +tmpdir/abcn\.a\(abc\.o(:?bj)?\) \(aa\)
+\(tmpdir/abn\.a\)a\.o(:?bj)?
+tmpdir/abn\.a\(b\.o(:?bj)?\) +tmpdir/abcn\.a\(abc\.o(:?bj)?\) \(bb\)
+\(tmpdir/abn\.a\)b\.o(:?bj)?
+tmpdir/abcn\.a
+!(:?\()?tmpdir/.*
+#...
+START GROUP
+LOAD tmpdir/abn\.a
+LOAD tmpdir/abcn\.a
+END GROUP
+!LOAD .*
+#pass
Index: binutils-gdb/ld/testsuite/ld-archive/abntcntg.vd
===================================================================
--- /dev/null
+++ binutils-gdb/ld/testsuite/ld-archive/abntcntg.vd
@@ -0,0 +1,28 @@
+#...
+using ..ternal linker script:.*
+==================================================
+#...
+==================================================
+.*: mode .*
+attempt to open tmpdir/abnt\.a succeeded
+tmpdir/abnt\.a
+attempt to open tmpdir/abcnt\.a succeeded
+tmpdir/abcnt\.a
+Archive member included to satisfy reference by file \(symbol\)
+
+tmpdir/abc\.o +\(ff\)
+tmpdir/abc\.o
+tmpdir/abnt\.a
+tmpdir/a\.o +tmpdir/abc\.o \(aa\)
+tmpdir/a\.o
+tmpdir/b\.o +tmpdir/abc\.o \(bb\)
+tmpdir/b\.o
+tmpdir/abcnt\.a
+!(:?\()?tmpdir/.*
+#...
+START GROUP
+LOAD tmpdir/abnt\.a
+LOAD tmpdir/abcnt\.a
+END GROUP
+!LOAD .*
+#pass
Index: binutils-gdb/ld/testsuite/ld-archive/archive.exp
===================================================================
--- binutils-gdb.orig/ld/testsuite/ld-archive/archive.exp
+++ binutils-gdb/ld/testsuite/ld-archive/archive.exp
@@ -230,26 +230,65 @@ run_ld_link_tests [list \
     ] \
 ]
 
-set LDFLAGS $old_ldflags
-
-# Alpha/VMS archives always have an index and XCOFF targets currently
-# accept archives without one.
-if { [istarget "alpha*-*-*vms*"] || [is_xcoff_format] } {
-    return
-}
-
 # Single non-index archive tests.
 run_ld_link_tests [list \
     [list "Regular archive w/o index link" \
 	"-e ff" "tmpdir/abn.a" \
 	"" {abc.s} \
-	{{ld abc.ed}} \
+	[ld_archive_filter_tests {{nm "" abc.nd} \
+				  {nm "" nx.nd}}] \
 	"abnc" \
     ] \
+]
+setup_xfail "binutils/33484" "alpha*-*-linux*ecoff*" "alpha*-*-osf*"
+run_ld_link_tests [list \
     [list "Thin archive w/o index link" \
 	"-e ff" "tmpdir/abnt.a" \
 	"" {abc.s} \
-	{{ld abct.ed}} \
-	"abnct" \
+	[ld_archive_filter_tests {{nm "" abc.nd} \
+				  {nm "" nx.nd}}] \
+	"abntc" \
     ] \
 ]
+
+# Group non-index archive tests.
+run_ld_link_tests [list \
+    [list "Regular non-group archive w/o index link reject" \
+	"-e ff tmpdir/abn.a" "tmpdir/abcn.a" \
+	"" {} \
+	{{ld abcn.ed}} \
+	"abncnn" \
+    ] \
+    [list "Thin non-group archive w/o index link reject" \
+	"-e ff tmpdir/abnt.a" "tmpdir/abcnt.a" \
+	"" {} \
+	{{ld abcn.ed}} \
+	"abntcntn" \
+    ] \
+    [list "Regular group archive w/o index link accept" \
+	"-e ff -\\( tmpdir/abn.a" "tmpdir/abcn.a -\\) --verbose --print-map" \
+	"" {} \
+	[ld_archive_filter_tests {{ld abncng.vd} \
+				  {nm "" abc.nd} \
+				  {nm "" nx.nd}}] \
+	"abncng" \
+    ] \
+]
+setup_xfail "binutils/33484" "alpha*-*-linux*ecoff*" "alpha*-*-osf*"
+setup_xfail "binutils/33600" "alpha*-*-*vms*"
+if { [is_xcoff_format] } {
+    setup_xfail "binutils/33600" "*-*-*"
+}
+run_ld_link_tests [list \
+    [list "Thin group archive w/o index link accept" \
+	"-e ff\
+	 -\\( tmpdir/abnt.a" "tmpdir/abcnt.a -\\) --verbose --print-map" \
+	"" {} \
+	[ld_archive_filter_tests {{ld abntcntg.vd} \
+				  {nm "" abc.nd} \
+				  {nm "" nx.nd}}] \
+	"abntcntg" \
+    ] \
+]
+
+set LDFLAGS $old_ldflags
Index: binutils-gdb/ld/testsuite/ld-cris/archive.exp
===================================================================
--- binutils-gdb.orig/ld/testsuite/ld-cris/archive.exp
+++ binutils-gdb/ld/testsuite/ld-cris/archive.exp
@@ -103,3 +103,35 @@ run_ld_link_tests [list \
 	"abtci" \
     ] \
 ]
+
+# Single non-index archive tests.
+run_ld_link_tests [list \
+    [list "CRIS regular archive w/o index link (explicit emulation)" \
+	"$ldemul -e ff" "tmpdir/abn.a" \
+	"$asemul" {../ld-archive/abc.s} \
+	{{nm "" ../ld-archive/abc.nd} \
+	 {nm "" ../ld-archive/nx.nd}} \
+	"abncx" \
+    ] \
+    [list "CRIS regular archive w/o index link (implicit emulation)" \
+	"-e ff" "tmpdir/abn.a" \
+	"$asemul" {../ld-archive/abc.s} \
+	{{nm "" ../ld-archive/abc.nd} \
+	 {nm "" ../ld-archive/nx.nd}} \
+	"abnci" \
+    ] \
+    [list "CRIS thin archive w/o index link (explicit emulation)" \
+	"$ldemul -e ff" "tmpdir/abnt.a" \
+	"$asemul" {../ld-archive/abc.s} \
+	{{nm "" ../ld-archive/abc.nd} \
+	 {nm "" ../ld-archive/nx.nd}} \
+	"abntcx" \
+    ] \
+    [list "CRIS thin archive w/o index link (implicit emulation)" \
+	"-e ff" "tmpdir/abnt.a" \
+	"$asemul" {../ld-archive/abc.s} \
+	{{nm "" ../ld-archive/abc.nd} \
+	 {nm "" ../ld-archive/nx.nd}} \
+	"abntci" \
+    ] \
+]


More information about the Binutils mailing list