[PATCH] [RFC][ld] Introduce static bundle object support

Eyal Itkin eyal.itkin@gmail.com
Tue Oct 7 18:44:30 GMT 2025


Note: This patch is a draft for review.

Introduce a new "Static Bundle Object" which is a
relocatable object that supports symbol visibility
and finalization of local relocations. This format
is aimed to be incorporated within static archives
so to replace the current static archives of plain
object code files that are used to form static
libraries.

The full discussion about the format is available in the
generic-abi group that is responsible for the ELF standard:
https://groups.google.com/g/generic-abi/c/sT25-xfX9yc

Some questions I had about the process itself:
1. I have tests of my own for the feature, yet would
like to understand if there are some guidelines I should
follow when adding the tests as part of my patch.

2. The patch is architecture dependent given that
relocations support is found in both bfd/elflink.c AND
in some arch-specific files (such as bfd/elf64-x86-64.c).
Hence, what is the standard way for marking some archs as
not yet supported? And what is the recommended process for
adding said support (especially given need to test it)?

Signed-off-by: Eyal Itkin <eyal.itkin@gmail.com>
---
 bfd/elf64-x86-64.c | 23 +++++++++++++++++++++--
 bfd/elflink.c      | 43 +++++++++++++++++++++++++++++++++----------
 include/bfdlink.h  |  5 +++++
 ld/ld.h            |  3 +++
 ld/ldlex.h         |  1 +
 ld/lexsup.c        | 15 ++++++++++++---
 6 files changed, 75 insertions(+), 15 deletions(-)

diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 59b43149897..7238590e46b 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -3227,7 +3227,9 @@ elf_x86_64_relocate_section (bfd *output_bfd,
       bool converted_reloc;
       bool need_copy_reloc_in_pie;
       bool no_copyreloc_p;
+      bool skip_reloc_registration;
 
+      skip_reloc_registration = true;
       r_type = ELF32_R_TYPE (rel->r_info);
       if (r_type == (int) R_X86_64_GNU_VTINHERIT
 	  || r_type == (int) R_X86_64_GNU_VTENTRY)
@@ -3286,6 +3288,15 @@ elf_x86_64_relocate_section (bfd *output_bfd,
 				   h, sec, relocation,
 				   unresolved_reloc, warned, ignored);
 	  st_size = h->size;
+	  skip_reloc_registration = !info->static_bundle || !h->forced_local;
+
+	  struct bfd_section *input_sec_out = input_section->output_section;
+	  struct bfd_section *sym_sec_out = sec ? sec->output_section : NULL;
+
+	  /* A relocatable can not finalize a cross-section relocation.  */
+	  if (sym_sec_out != NULL
+	      && sym_sec_out->index != input_sec_out->index)
+	    skip_reloc_registration = true;
 	}
 
       if (sec != NULL && discarded_section (sec))
@@ -3310,7 +3321,7 @@ elf_x86_64_relocate_section (bfd *output_bfd,
 	  continue;
 	}
 
-      if (bfd_link_relocatable (info))
+      if (bfd_link_relocatable (info) && skip_reloc_registration)
 	{
 	  if (wrel != rel)
 	    *wrel = *rel;
@@ -5117,6 +5128,13 @@ elf_x86_64_relocate_section (bfd *output_bfd,
 	    }
 	}
 
+      /* Skip the applied relocation if based on local relocation.  */
+      if (bfd_link_relocatable (info) && !skip_reloc_registration)
+	{
+	  wrel--;
+	  continue;
+	}
+
       if (wrel != rel)
 	*wrel = *rel;
     }
@@ -5409,7 +5427,8 @@ elf_x86_64_finish_dynamic_symbol (bfd *output_bfd,
   if (h->got.offset != (bfd_vma) -1
       && ! GOT_TLS_GD_ANY_P (elf_x86_hash_entry (h)->tls_type)
       && elf_x86_hash_entry (h)->tls_type != GOT_TLS_IE
-      && !local_undefweak)
+      && !local_undefweak
+      && !(bfd_link_relocatable (info) && info->static_bundle))
     {
       Elf_Internal_Rela rela;
       asection *relgot = htab->elf.srelgot;
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 3f3ea2cce51..45188272730 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -803,9 +803,9 @@ bfd_elf_record_link_assignment (bfd *output_bfd,
       (*bed->elf_backend_hide_symbol) (info, h, true);
     }
 
-  /* STV_HIDDEN and STV_INTERNAL symbols must be STB_LOCAL in shared objects
-     and executables.  */
-  if (!bfd_link_relocatable (info)
+  /* STV_HIDDEN and STV_INTERNAL symbols must be STB_LOCAL in shared objects,
+     executables and static bundles.  */
+  if (!(bfd_link_relocatable (info) && info->static_bundle)
       && h->dynindx != -1
       && (ELF_ST_VISIBILITY (h->other) == STV_HIDDEN
 	  || ELF_ST_VISIBILITY (h->other) == STV_INTERNAL))
@@ -3151,7 +3151,10 @@ _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
 	h->def_regular = 1;
     }
 
-  /* Backend specific symbol fixup.  */
+  /* Backend specific symbol fixup, only needed for dynamic case.  */
+  if (elf_hash_table (eif->info)->dynobj == NULL)
+    return true;
+
   bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
   if (bed->elf_backend_fixup_symbol
       && !(*bed->elf_backend_fixup_symbol) (eif->info, h))
@@ -3197,7 +3200,9 @@ _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
      visibility.  If the symbol has hidden or internal visibility, we
      will force it local.  */
   else if (h->needs_plt
-	   && bfd_link_pic (eif->info)
+	   && (bfd_link_pic (eif->info)
+	       || (bfd_link_relocatable (eif->info)
+		   && eif->info->static_bundle))
 	   && is_elf_hash_table (eif->info->hash)
 	   && (SYMBOLIC_BIND (eif->info, h)
 	       || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
@@ -5680,7 +5685,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 		    goto error_free_vers;
 		}
 	    }
-	  else if (h->dynindx != -1)
+	  else if (h->dynindx != -1
+		   || (bfd_link_relocatable (info) && info->static_bundle))
 	    /* If the symbol already has a dynamic index, but
 	       visibility says it should not be visible, turn it into
 	       a local symbol.  */
@@ -6925,6 +6931,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
 {
   bfd *dynobj;
   const struct elf_backend_data *bed;
+  struct elf_info_failed asvinfo;
 
   *sinterpptr = NULL;
 
@@ -6948,10 +6955,22 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
 
   dynobj = elf_hash_table (info)->dynobj;
 
-  if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
+  /* Symbol visibility should be applied in some non-dynamic cases.  */
+  if (bfd_link_relocatable (info) && info->static_bundle)
+    {
+      /* Attach all the symbols to their version information.  */
+      asvinfo.info = info;
+      asvinfo.failed = false;
+
+      elf_link_hash_traverse (elf_hash_table (info),
+			      _bfd_elf_link_assign_sym_version,
+			      &asvinfo);
+      if (asvinfo.failed)
+	return false;
+    }
+  else if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
     {
       struct bfd_elf_version_tree *verdefs;
-      struct elf_info_failed asvinfo;
       struct bfd_elf_version_tree *t;
       struct bfd_elf_version_expr *d;
       asection *s;
@@ -11039,11 +11058,15 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data)
        && !bfd_link_relocatable (flinfo->info))
       || ((h->dynindx != -1
 	   || h->forced_local)
-	  && ((bfd_link_pic (flinfo->info)
+	  && (((bfd_link_pic (flinfo->info)
+		|| (bfd_link_relocatable (flinfo->info)
+		    && flinfo->info->static_bundle))
 	       && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
 		   || h->root.type != bfd_link_hash_undefweak))
 	      || !h->forced_local)
-	  && elf_hash_table (flinfo->info)->dynamic_sections_created))
+	  && (elf_hash_table (flinfo->info)->dynamic_sections_created
+	      || (bfd_link_relocatable (flinfo->info)
+		  && flinfo->info->static_bundle))))
     {
       if (! ((*bed->elf_backend_finish_dynamic_symbol)
 	     (flinfo->output_bfd, flinfo->info, h, &sym)))
diff --git a/include/bfdlink.h b/include/bfdlink.h
index 3d4d71b3347..6e652a41073 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -476,6 +476,11 @@ struct bfd_link_info
      statics.  */
   unsigned int task_link: 1;
 
+  /* TRUE if BFD should generate a "static bundle" object file,
+     similar to relocatable but also with local relocations being
+     finalized.  */
+  unsigned int static_bundle: 1;
+
   /* TRUE if ok to have multiple definitions, without warning.  */
   unsigned int allow_multiple_definition: 1;
 
diff --git a/ld/ld.h b/ld/ld.h
index c8688153bd4..cd1046b4a78 100644
--- a/ld/ld.h
+++ b/ld/ld.h
@@ -332,6 +332,9 @@ typedef struct
 
   /* Compress DWARF debug sections.  */
   enum compressed_debug_section_type compress_debug;
+
+  /* If set, finalize local symbols withing the relocatable object.  */
+  bool finalize_locals;
 } ld_config_type;
 
 /* An enumeration of the linker phases for which resource usage information
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 24cac1cdfc0..4add8f53148 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -139,6 +139,7 @@ enum option_values
   OPTION_NO_ACCEPT_UNKNOWN_INPUT_ARCH,
   OPTION_PIE,
   OPTION_NO_PIE,
+  OPTION_FINALIZE_LOCALS,
   OPTION_UNRESOLVED_SYMBOLS,
   OPTION_WARN_UNRESOLVED_SYMBOLS,
   OPTION_ERROR_UNRESOLVED_SYMBOLS,
diff --git a/ld/lexsup.c b/ld/lexsup.c
index 5cb77992733..cadfd883a45 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -208,6 +208,8 @@ static const struct ld_option ld_options[] =
     'q', NULL, "Generate relocations in final output", TWO_DASHES },
   { {"relocatable", no_argument, NULL, 'r'},
     'r', NULL, N_("Generate relocatable output"), TWO_DASHES },
+  { {"finalize-locals", no_argument, NULL, OPTION_FINALIZE_LOCALS},
+    '\0', NULL, N_("Finalize local relocations within the relocatable output"), TWO_DASHES },
   { {NULL, no_argument, NULL, '\0'},
     'i', NULL, NULL, ONE_DASH },
   { {"just-symbols", required_argument, NULL, 'R'},
@@ -1237,6 +1239,7 @@ parse_args (unsigned argc, char **argv)
 		   bfd_link_dll (&link_info) ? "-shared" : "-pie");
 
 	  link_info.type = type_relocatable;
+	  link_info.static_bundle = false;
 	  config.build_constructors = false;
 	  config.magic_demand_paged = false;
 	  config.text_read_only = false;
@@ -1357,6 +1360,12 @@ parse_args (unsigned argc, char **argv)
 	  else
 	    fatal (_("%P: -shared not supported\n"));
 	  break;
+	case OPTION_FINALIZE_LOCALS:
+	  if (!bfd_link_relocatable (&link_info))
+	    fatal (_("%P: %s may only be used together with -r\n"),
+		   "--finalize-locals");
+	  link_info.static_bundle = true;
+	  break;
 	case OPTION_NO_PIE:
 	  link_info.type = type_pde;
 	  break;
@@ -2029,12 +2038,12 @@ parse_args (unsigned argc, char **argv)
       link_info.strip = strip_all;
     }
 
-  if (!bfd_link_dll (&link_info))
+  if (!bfd_link_dll (&link_info) && !link_info.static_bundle)
     {
       if (command_line.filter_shlib)
-	fatal (_("%P: -F may not be used without -shared\n"));
+	fatal (_("%P: -F may not be used without -shared or --finalize-locals\n"));
       if (command_line.auxiliary_filters)
-	fatal (_("%P: -f may not be used without -shared\n"));
+	fatal (_("%P: -f may not be used without -shared or --finalize-locals\n"));
     }
 
   /* Treat ld -r -s as ld -r -S -x (i.e., strip all local symbols).  I
-- 
2.31.1.windows.1



More information about the Binutils mailing list