[PATCH v4 13/14] PR ld/24600: BFD: Add general linker support for fake archives
Maciej W. Rozycki
macro@orcam.me.uk
Tue Jan 13 01:45:26 GMT 2026
From: Maciej W. Rozycki <macro@redhat.com>
Add support to BFD for file collections pretending to be archives, with
the usual linker symbol resolution semantics as with actual archives.
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.
---
Changes from v3 (17/18),
<https://inbox.sourceware.org/binutils/alpine.DEB.2.21.2511200009060.57987@angie.orcam.me.uk/>:
- Correct `fbfd' parameter reference in `bfd_openr_fake_archive' function
documentation.
- Mention "linker" in the paragraph as well.
No change from v2 (15/16),
<https://inbox.sourceware.org/binutils/alpine.DEB.2.21.2511061829480.25436@angie.orcam.me.uk/>.
New change in v2.
---
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(-)
binutils-bfd-fake-archive.diff
Index: binutils-gdb/bfd/archive.c
===================================================================
--- binutils-gdb.orig/bfd/archive.c
+++ binutils-gdb/bfd/archive.c
@@ -702,6 +702,8 @@ _bfd_get_elt_at_filepos (bfd *archive, f
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 *archi
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 (bf
{
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 *abf
{
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,
Index: binutils-gdb/bfd/archive64.c
===================================================================
--- binutils-gdb.orig/bfd/archive64.c
+++ binutils-gdb/bfd/archive64.c
@@ -49,6 +49,8 @@ _bfd_archive_64_bit_slurp_armap (bfd *ab
bfd_size_type amt;
ufile_ptr filesize;
+ BFD_ASSERT (!bfd_is_fake_archive (abfd));
+
ardata->symdefs = NULL;
/* Get the name of the first element. */
Index: binutils-gdb/bfd/bfd-in2.h
===================================================================
--- binutils-gdb.orig/bfd/bfd-in2.h
+++ binutils-gdb/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)
{
@@ -2413,6 +2426,12 @@ bfd_set_thin_archive (bfd *abfd, bool va
}
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;
@@ -3029,6 +3048,8 @@ bfd *bfd_fdopenw (const char *filename,
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),
Index: binutils-gdb/bfd/bfd.c
===================================================================
--- binutils-gdb.orig/bfd/bfd.c
+++ binutils-gdb/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;
Index: binutils-gdb/bfd/coff-alpha.c
===================================================================
--- binutils-gdb.orig/bfd/coff-alpha.c
+++ binutils-gdb/bfd/coff-alpha.c
@@ -2202,6 +2202,8 @@ alpha_ecoff_openr_next_archived_file (bf
{
ufile_ptr filestart;
+ BFD_ASSERT (!bfd_is_fake_archive (archive));
+
if (last_file == NULL)
filestart = bfd_ardata (archive)->first_file.file_offset;
else
Index: binutils-gdb/bfd/coff-rs6000.c
===================================================================
--- binutils-gdb.orig/bfd/coff-rs6000.c
+++ binutils-gdb/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
{
ufile_ptr filestart;
+ BFD_ASSERT (!bfd_is_fake_archive (archive));
+
if (x_artdata (archive) == NULL)
{
bfd_set_error (bfd_error_invalid_operation);
Index: binutils-gdb/bfd/coff64-rs6000.c
===================================================================
--- binutils-gdb.orig/bfd/coff64-rs6000.c
+++ binutils-gdb/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)
Index: binutils-gdb/bfd/ecoff.c
===================================================================
--- binutils-gdb.orig/bfd/ecoff.c
+++ binutils-gdb/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)
Index: binutils-gdb/bfd/opncls.c
===================================================================
--- binutils-gdb.orig/bfd/opncls.c
+++ binutils-gdb/bfd/opncls.c
@@ -494,6 +494,47 @@ bfd_openstreamr (const char *filename, c
/*
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
SYNOPSIS
Index: binutils-gdb/bfd/som.c
===================================================================
--- binutils-gdb.orig/bfd/som.c
+++ binutils-gdb/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. */
Index: binutils-gdb/bfd/vms-lib.c
===================================================================
--- binutils-gdb.orig/bfd/vms-lib.c
+++ binutils-gdb/bfd/vms-lib.c
@@ -1550,6 +1550,8 @@ _bfd_vms_lib_openr_next_archived_file (b
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
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;
Index: binutils-gdb/binutils/bucomm.c
===================================================================
--- binutils-gdb.orig/binutils/bucomm.c
+++ binutils-gdb/binutils/bucomm.c
@@ -477,6 +477,7 @@ print_arelt_descr (FILE *file, bfd *abfd
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
mailing list