[binutils-gdb] PR ld/24600: BFD: Add general linker support for fake archives

Maciej W. Rozycki macro@sourceware.org
Fri Feb 20 17:43:18 GMT 2026


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

commit 8141e0f796be6fdb93d0f04b71a7cb72486d0406
Author: Maciej W. Rozycki <macro@redhat.com>
Date:   Fri Feb 20 17:29:51 2026 +0000

    PR ld/24600: BFD: Add general linker support for fake archives
    
    Add support to BFD for file collections pretending to be archives, with
    the usual linker symbol resolution semantics as with actual archives.
    This is an underlying feature to implement `--start-lib'/`--end-lib'
    group support of the GOLD linker being phased out.
    
    It comes in the form of `bfd_openr_fake_archive' function which arranges
    for a list of input files supplied to be treated as an archive, with
    `bfd_openr_next_archived_file' then referring to successive elements of
    the list previously supplied.
    
    Use the BFD pointer member of the first file and proxy handle members of
    `struct bfd' and `struct artdata' respectively to hold references to the
    members of such an artificial archive once arranged.  Such an archive
    has to be a mapless one necessarily and any use will require a symbol
    index to be produced on the fly.
    
    Add assertions throughout backend code that is supposed not to be ever
    reached in the handling of such archives and which continues to use the
    first file and proxy handle members of `struct bfd' and `struct artdata'
    solely to keep offsets into the owning archive file.

Diff:
---
 bfd/archive.c       | 20 ++++++++++++++++++--
 bfd/archive64.c     |  2 ++
 bfd/bfd-in2.h       | 33 +++++++++++++++++++++++++++------
 bfd/bfd.c           | 31 +++++++++++++++++++++++++------
 bfd/coff-alpha.c    |  2 ++
 bfd/coff-rs6000.c   |  4 ++++
 bfd/coff64-rs6000.c |  2 ++
 bfd/ecoff.c         |  2 ++
 bfd/opncls.c        | 41 +++++++++++++++++++++++++++++++++++++++++
 bfd/som.c           |  3 +++
 bfd/vms-lib.c       |  6 ++++++
 binutils/bucomm.c   |  1 +
 12 files changed, 133 insertions(+), 14 deletions(-)

diff --git a/bfd/archive.c b/bfd/archive.c
index 15adb6ac678..6841e0a5aae 100644
--- a/bfd/archive.c
+++ b/bfd/archive.c
@@ -702,6 +702,8 @@ _bfd_get_elt_at_filepos (bfd *archive, file_ptr filepos,
   bfd *n_bfd;
   char *filename;
 
+  BFD_ASSERT (!bfd_is_fake_archive (archive));
+
   n_bfd = _bfd_look_for_bfd_in_cache (archive, filepos);
   if (n_bfd)
     return n_bfd;
@@ -868,8 +870,12 @@ bfd_openr_next_archived_file (bfd *archive, bfd *last_file)
       return NULL;
     }
 
-  return BFD_SEND (archive,
-		   openr_next_archived_file, (archive, last_file));
+  if (bfd_is_fake_archive (archive))
+    return (last_file ? last_file->proxy_handle.abfd
+	    : bfd_ardata (archive)->first_file.abfd);
+  else
+    return BFD_SEND (archive,
+		     openr_next_archived_file, (archive, last_file));
 }
 
 bfd *
@@ -877,6 +883,8 @@ bfd_generic_openr_next_archived_file (bfd *archive, bfd *last_file)
 {
   ufile_ptr filestart;
 
+  BFD_ASSERT (!bfd_is_fake_archive (archive));
+
   if (!last_file)
     filestart = bfd_ardata (archive)->first_file.file_offset;
   else
@@ -916,6 +924,8 @@ bfd_generic_archive_p (bfd *abfd)
   char armag[SARMAG + 1];
   size_t amt;
 
+  BFD_ASSERT (!bfd_is_fake_archive (abfd));
+
   if (bfd_read (armag, SARMAG, abfd) != SARMAG)
     {
       if (bfd_get_error () != bfd_error_system_call)
@@ -1098,6 +1108,8 @@ do_slurp_bsd_armap (bfd *abfd)
   size_t amt, string_size;
   carsym *set;
 
+  BFD_ASSERT (!bfd_is_fake_archive (abfd));
+
   mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
   if (mapdata == NULL)
     return false;
@@ -1189,6 +1201,8 @@ do_slurp_coff_armap (bfd *abfd)
   char int_buf[4];
   struct areltdata *tmp;
 
+  BFD_ASSERT (!bfd_is_fake_archive (abfd));
+
   mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
   if (mapdata == NULL)
     return false;
@@ -1377,6 +1391,8 @@ _bfd_slurp_extended_name_table (bfd *abfd)
 {
   char nextname[17];
 
+  BFD_ASSERT (!bfd_is_fake_archive (abfd));
+
   /* FIXME:  Formatting sucks here, and in case of failure of BFD_READ,
      we probably don't want to return TRUE.  */
   if (bfd_seek (abfd, bfd_ardata (abfd)->first_file.file_offset,
diff --git a/bfd/archive64.c b/bfd/archive64.c
index 6d4c268b629..1201a2f73a7 100644
--- a/bfd/archive64.c
+++ b/bfd/archive64.c
@@ -49,6 +49,8 @@ _bfd_archive_64_bit_slurp_armap (bfd *abfd)
   bfd_size_type amt;
   ufile_ptr filesize;
 
+  BFD_ASSERT (!bfd_is_fake_archive (abfd));
+
   ardata->symdefs = NULL;
 
   /* Get the name of the first element.  */
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 7186c3c85d8..2b6811c72b2 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -2136,6 +2136,9 @@ struct bfd
   /* Set if this is a thin archive.  */
   unsigned int is_thin_archive : 1;
 
+  /* Set if this is a collection of files pretending to be an archive.  */
+  unsigned int is_fake_archive : 1;
+
   /* Set if this archive should not cache element positions.  */
   unsigned int no_element_cache : 1;
 
@@ -2176,12 +2179,16 @@ struct bfd
      contained in an archive.  */
   ufile_ptr origin;
 
-  /* A reference in the archive for the proxy entry.  This will
-     normally be the same as origin, except for thin archives,
-     when it will contain the current offset of the proxy in the
-     thin archive rather than the offset of the bfd in its actual
-     container.  Room for a BFD pointer is alternatively provided
-     for future use.  */
+  /* A reference in the archive for the proxy entry as follows:
+
+     1. For regular archives this will be the same as origin.
+
+     2. For thin archives it will contain the current offset
+	of the proxy in the thin archive rather than the offset
+	of the bfd in its actual container.
+
+     3. For fake archives it will contain the next archive member's
+	BFD reference or a NULL pointer if this is the last member.  */
   ufile_ptr_or_bfd proxy_handle;
 
   /* A hash table for section names.  */
@@ -2386,6 +2393,12 @@ bfd_is_thin_archive (const bfd *abfd)
   return abfd->is_thin_archive;
 }
 
+static inline bool
+bfd_is_fake_archive (const bfd *abfd)
+{
+  return abfd->is_fake_archive;
+}
+
 static inline void *
 bfd_usrdata (const bfd *abfd)
 {
@@ -2412,6 +2425,12 @@ bfd_set_thin_archive (bfd *abfd, bool val)
   abfd->is_thin_archive = val;
 }
 
+static inline void
+bfd_set_fake_archive (bfd *abfd, bool val)
+{
+  abfd->is_fake_archive = val;
+}
+
 static inline void
 bfd_set_usrdata (bfd *abfd, void *val)
 {
@@ -3030,6 +3049,8 @@ bfd *bfd_fdopenw (const char *filename, const char *target, int fd);
 bfd *bfd_openstreamr (const char * filename, const char * target,
     void * stream);
 
+bfd *bfd_openr_fake_archive (bfd *fbfd);
+
 bfd *bfd_openr_iovec (const char *filename, const char *target,
     void *(*open_func) (struct bfd *nbfd,
 	void *open_closure),
diff --git a/bfd/bfd.c b/bfd/bfd.c
index 5c84dc404fe..e97503c7a81 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -286,6 +286,9 @@ CODE_FRAGMENT
 .  {* Set if this is a thin archive.  *}
 .  unsigned int is_thin_archive : 1;
 .
+.  {* Set if this is a collection of files pretending to be an archive.  *}
+.  unsigned int is_fake_archive : 1;
+.
 .  {* Set if this archive should not cache element positions.  *}
 .  unsigned int no_element_cache : 1;
 .
@@ -326,12 +329,16 @@ CODE_FRAGMENT
 .     contained in an archive.  *}
 .  ufile_ptr origin;
 .
-.  {* A reference in the archive for the proxy entry.  This will
-.     normally be the same as origin, except for thin archives,
-.     when it will contain the current offset of the proxy in the
-.     thin archive rather than the offset of the bfd in its actual
-.     container.  Room for a BFD pointer is alternatively provided
-.     for future use.  *}
+.  {* A reference in the archive for the proxy entry as follows:
+.
+.     1. For regular archives this will be the same as origin.
+.
+.     2. For thin archives it will contain the current offset
+.	 of the proxy in the thin archive rather than the offset
+.	 of the bfd in its actual container.
+.
+.     3. For fake archives it will contain the next archive member's
+.	 BFD reference or a NULL pointer if this is the last member.  *}
 .  ufile_ptr_or_bfd proxy_handle;
 .
 .  {* A hash table for section names.  *}
@@ -538,6 +545,12 @@ EXTERNAL
 .  return abfd->is_thin_archive;
 .}
 .
+.static inline bool
+.bfd_is_fake_archive (const bfd *abfd)
+.{
+.  return abfd->is_fake_archive;
+.}
+.
 .static inline void *
 .bfd_usrdata (const bfd *abfd)
 .{
@@ -565,6 +578,12 @@ EXTERNAL
 .}
 .
 .static inline void
+.bfd_set_fake_archive (bfd *abfd, bool val)
+.{
+.  abfd->is_fake_archive = val;
+.}
+.
+.static inline void
 .bfd_set_usrdata (bfd *abfd, void *val)
 .{
 .  abfd->usrdata = val;
diff --git a/bfd/coff-alpha.c b/bfd/coff-alpha.c
index 622e9e19aeb..0396a1fd9fc 100644
--- a/bfd/coff-alpha.c
+++ b/bfd/coff-alpha.c
@@ -2202,6 +2202,8 @@ alpha_ecoff_openr_next_archived_file (bfd *archive, bfd *last_file)
 {
   ufile_ptr filestart;
 
+  BFD_ASSERT (!bfd_is_fake_archive (archive));
+
   if (last_file == NULL)
     filestart = bfd_ardata (archive)->first_file.file_offset;
   else
diff --git a/bfd/coff-rs6000.c b/bfd/coff-rs6000.c
index 39f13cd7f09..62caae64f4e 100644
--- a/bfd/coff-rs6000.c
+++ b/bfd/coff-rs6000.c
@@ -1503,6 +1503,8 @@ _bfd_xcoff_archive_p (bfd *abfd)
   char magic[SXCOFFARMAG];
   size_t amt = SXCOFFARMAG;
 
+  BFD_ASSERT (!bfd_is_fake_archive (abfd));
+
   if (bfd_read (magic, amt, abfd) != amt)
     {
       if (bfd_get_error () != bfd_error_system_call)
@@ -1762,6 +1764,8 @@ _bfd_xcoff_openr_next_archived_file (bfd *archive, bfd *last_file)
 {
   ufile_ptr filestart;
 
+  BFD_ASSERT (!bfd_is_fake_archive (archive));
+
   if (x_artdata (archive) == NULL)
     {
       bfd_set_error (bfd_error_invalid_operation);
diff --git a/bfd/coff64-rs6000.c b/bfd/coff64-rs6000.c
index e7ba511efa6..fa1759b5925 100644
--- a/bfd/coff64-rs6000.c
+++ b/bfd/coff64-rs6000.c
@@ -1905,6 +1905,8 @@ xcoff64_archive_p (bfd *abfd)
   struct xcoff_ar_file_hdr_big hdr;
   size_t amt = SXCOFFARMAG;
 
+  BFD_ASSERT (!bfd_is_fake_archive (abfd));
+
   if (bfd_read (magic, amt, abfd) != amt)
     {
       if (bfd_get_error () != bfd_error_system_call)
diff --git a/bfd/ecoff.c b/bfd/ecoff.c
index 725433f0f09..7ddce8be6d2 100644
--- a/bfd/ecoff.c
+++ b/bfd/ecoff.c
@@ -2928,6 +2928,8 @@ _bfd_ecoff_slurp_armap (bfd *abfd)
   char *stringbase;
   bfd_size_type amt;
 
+  BFD_ASSERT (!bfd_is_fake_archive (abfd));
+
   /* Get the name of the first element.  */
   i = bfd_read (nextname, 16, abfd);
   if (i == 0)
diff --git a/bfd/opncls.c b/bfd/opncls.c
index 9e90e0e9a24..a8fa6ebbf17 100644
--- a/bfd/opncls.c
+++ b/bfd/opncls.c
@@ -492,6 +492,47 @@ bfd_openstreamr (const char *filename, const char *target, void *streamarg)
   return nbfd;
 }
 
+/*
+FUNCTION
+	bfd_openr_fake_archive
+
+SYNOPSIS
+	bfd *bfd_openr_fake_archive (bfd *fbfd);
+
+DESCRIPTION
+	Open a list of BFDs starting from @var{fbfd} as an artificial
+	archive.  Subsequent members of the archive are indicated by each
+	BFD's proxy_handle.abfd member, with the final one holding a NULL
+	pointer there.  The newly opened archive will necessarily also be
+	a thin archive as there will be member files only to refer to and
+	no containing archive file.
+*/
+
+bfd *
+bfd_openr_fake_archive (bfd *fbfd)
+{
+  bfd *abfd, *nbfd;
+
+  if (fbfd == NULL)
+    return NULL;
+
+  nbfd = _bfd_new_bfd ();
+  if (nbfd == NULL)
+    return NULL;
+
+  nbfd->xvec = fbfd->xvec;
+  bfd_set_format (nbfd, bfd_archive);
+  bfd_set_thin_archive (nbfd, true);
+  bfd_set_fake_archive (nbfd, true);
+  bfd_ardata (nbfd)->first_file.abfd = fbfd;
+  nbfd->direction = read_direction;
+
+  for (abfd = fbfd; abfd != NULL; abfd = abfd->proxy_handle.abfd)
+    abfd->my_archive = nbfd;
+
+  return nbfd;
+}
+
 /*
 FUNCTION
 	bfd_openr_iovec
diff --git a/bfd/som.c b/bfd/som.c
index 34c0a4fd4d0..a39c061762d 100644
--- a/bfd/som.c
+++ b/bfd/som.c
@@ -6124,6 +6124,9 @@ som_slurp_armap (bfd *abfd)
   struct artdata *ardata = bfd_ardata (abfd);
   char nextname[17];
   size_t amt = 16;
+
+  BFD_ASSERT (!bfd_is_fake_archive (abfd));
+
   int i = bfd_read (nextname, amt, abfd);
 
   /* Special cases.  */
diff --git a/bfd/vms-lib.c b/bfd/vms-lib.c
index ff241291fd4..a718705038f 100644
--- a/bfd/vms-lib.c
+++ b/bfd/vms-lib.c
@@ -1550,6 +1550,8 @@ _bfd_vms_lib_openr_next_archived_file (bfd *archive,
   unsigned int idx;
   bfd *res;
 
+  BFD_ASSERT (!bfd_is_fake_archive (archive));
+
   if (!last_file)
     idx = 0;
   else
@@ -1702,6 +1704,8 @@ vms_write_index (bfd *abfd,
   unsigned int kbn_vbn = 0;  /* VBN of the kbn block.  */
   unsigned char *kbn_blk = NULL; /* Contents of the kbn block.  */
 
+  BFD_ASSERT (abfd == NULL || !bfd_is_fake_archive (abfd));
+
   if (nbr == 0)
     {
       /* No entries.  Very easy to handle.  */
@@ -2144,6 +2148,8 @@ _bfd_vms_lib_write_archive_contents (bfd *arch)
   bool is_elfidx = tdata->kind == vms_lib_ia64;
   unsigned int max_keylen = is_elfidx ? MAX_EKEYLEN : MAX_KEYLEN;
 
+  BFD_ASSERT (!bfd_is_fake_archive (arch));
+
   /* Count the number of modules (and do a first sanity check).  */
   nbr_modules = 0;
   for (current = arch->archive_head;
diff --git a/binutils/bucomm.c b/binutils/bucomm.c
index 4d6730ae2ca..14a0db22425 100644
--- a/binutils/bucomm.c
+++ b/binutils/bucomm.c
@@ -477,6 +477,7 @@ print_arelt_descr (FILE *file, bfd *abfd, bool verbose, bool offsets)
 
   if (offsets)
     {
+      assert (!bfd_is_fake_archive (abfd));
       if (bfd_is_thin_archive (abfd) && abfd->proxy_handle.file_offset)
 	fprintf (file, " 0x%lx",
 		 (unsigned long) abfd->proxy_handle.file_offset);


More information about the Binutils-cvs mailing list