[PATCH v3 2/2] [ld] Allow relocation finalization for ld -r

Eyal Itkin eyal.itkin@gmail.com
Fri Oct 17 17:50:11 GMT 2025


Introduce the "--finalize-locals" CLI flag, which will
be supported only for relocatables (ld -r).

When provided, relocations that are associated with
local symbols, and that support finalization, will
undergo a finalization step. This change aligns the
ET_REL creation to support the same finalization
semantics as that of the shared library (ET_DYN). In
essence, if the symbols are local, and support finalize,
there is no need to keep these relocations in the binary.

This added support will enable users to properly strip
the output of "ld -r --localize-hidden --finalize-locals",
as the symbols will no longer be tied to unneeded relocations.

The full discussion about the format is available in
the generic-abi group that is responsible for the ELF
standard. Specifically, the summarized architecture can
be found here:
https://groups.google.com/g/generic-abi/c/sT25-xfX9yc/m/J9SIthF4BgAJ

Signed-off-by: Eyal Itkin <eyal.itkin@gmail.com>
---
 bfd/elf64-x86-64.c                     | 54 +++++++++++++++++++++++++-
 include/bfdlink.h                      |  3 ++
 ld/ld.h                                |  3 ++
 ld/ldlex.h                             |  1 +
 ld/lexsup.c                            |  9 +++++
 ld/testsuite/ld-elf/finalize_locals.s  | 23 +++++++++++
 ld/testsuite/ld-elf/finalize_locals1.d | 12 ++++++
 ld/testsuite/ld-elf/finalize_locals2.d | 13 +++++++
 ld/testsuite/ld-elf/finalize_locals3.d |  3 ++
 9 files changed, 120 insertions(+), 1 deletion(-)
 create mode 100644 ld/testsuite/ld-elf/finalize_locals.s
 create mode 100644 ld/testsuite/ld-elf/finalize_locals1.d
 create mode 100644 ld/testsuite/ld-elf/finalize_locals2.d
 create mode 100644 ld/testsuite/ld-elf/finalize_locals3.d

diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 2c7e7c63357..6a6da4843af 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -3158,6 +3158,47 @@ elf_x86_64_tpoff (struct bfd_link_info *info, bfd_vma address)
   return address - static_tls_size - htab->tls_sec->vma;
 }
 
+/* Check if a given symbol-related relocation can be finalized.  */
+
+static int
+elf_x86_64_can_finalize_reloc (Elf_Internal_Rela *rel,
+			       asection *input_section,
+			       asection *output_section)
+{
+  struct bfd_section *input_sec_out;
+  struct bfd_section *sym_sec_out;
+  unsigned int r_type;
+
+  if (output_section == NULL)
+    return 0;
+
+  input_sec_out = input_section->output_section;
+  sym_sec_out = output_section->output_section;
+
+  /* Relocation must not cross section boundaries.  */
+  if (sym_sec_out->index != input_sec_out->index)
+    return 0;
+
+  r_type = ELF32_R_TYPE (rel->r_info);
+
+  /* Some relocation types can not be finalized.  */
+  switch (r_type)
+    {
+    default:
+      break;
+    case R_X86_64_GOTPCREL:
+    case R_X86_64_GOTPCRELX:
+    case R_X86_64_REX_GOTPCRELX:
+    case R_X86_64_CODE_4_GOTPCRELX:
+    case R_X86_64_CODE_5_GOTPCRELX:
+    case R_X86_64_CODE_6_GOTPCRELX:
+    case R_X86_64_GOTPCREL64:
+      return 0;
+    }
+
+  return 1;
+}
+
 /* Relocate an x86_64 ELF section.  */
 
 static int
@@ -3227,7 +3268,9 @@ elf_x86_64_relocate_section (bfd *output_bfd,
       bool converted_reloc;
       bool need_copy_reloc_in_pie;
       bool no_copyreloc_p;
+      bool reloc_skip_symbol;
 
+      reloc_skip_symbol = 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 +3329,9 @@ elf_x86_64_relocate_section (bfd *output_bfd,
 				   h, sec, relocation,
 				   unresolved_reloc, warned, ignored);
 	  st_size = h->size;
+	  if (info->finalize_locals && h->forced_local
+	      && elf_x86_64_can_finalize_reloc (rel, input_section, sec))
+	    reloc_skip_symbol = false;
 	}
 
       if (sec != NULL && discarded_section (sec))
@@ -3310,7 +3356,7 @@ elf_x86_64_relocate_section (bfd *output_bfd,
 	  continue;
 	}
 
-      if (bfd_link_relocatable (info))
+      if (bfd_link_relocatable (info) && reloc_skip_symbol)
 	{
 	  if (wrel != rel)
 	    *wrel = *rel;
@@ -5117,6 +5163,12 @@ elf_x86_64_relocate_section (bfd *output_bfd,
 	    }
 	}
 
+      if (bfd_link_relocatable (info))
+	{
+	  wrel--;
+	  continue;
+	}
+
       if (wrel != rel)
 	*wrel = *rel;
     }
diff --git a/include/bfdlink.h b/include/bfdlink.h
index dda01662a97..f3681a02002 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -489,6 +489,9 @@ struct bfd_link_info
   /* TRUE if supports localizing hidden symbols.  */
   unsigned int localize_hidden: 1;
 
+  /* TRUE if supports finalizing local symbols.  */
+  unsigned int finalize_locals: 1;
+
   /* TRUE if ok to have version with no definition.  */
   unsigned int allow_undefined_version: 1;
 
diff --git a/ld/ld.h b/ld/ld.h
index 83a2353cc4a..2009633c473 100644
--- a/ld/ld.h
+++ b/ld/ld.h
@@ -335,6 +335,9 @@ typedef struct
 
   /* If set, localize hidden symbols.  */
   bool localize_hidden;
+
+  /* If set, finalize locale symbols.  */
+  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 cceb455e68c..01942f5baeb 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -140,6 +140,7 @@ enum option_values
   OPTION_PIE,
   OPTION_NO_PIE,
   OPTION_LOCALIZE_HIDDEN,
+  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 f4023fe4176..5936f3c4f23 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -210,6 +210,8 @@ static const struct ld_option ld_options[] =
     'r', NULL, N_("Generate relocatable output"), TWO_DASHES },
   { {"localize-hidden", no_argument, NULL, OPTION_LOCALIZE_HIDDEN},
     '\0', NULL, N_("Localize hidden relocatable symbols"), TWO_DASHES },
+  { {"finalize-locals", no_argument, NULL, OPTION_FINALIZE_LOCALS},
+    '\0', NULL, N_("Attempt to finalize local relocatable symbols"), TWO_DASHES },
   { {NULL, no_argument, NULL, '\0'},
     'i', NULL, NULL, ONE_DASH },
   { {"just-symbols", required_argument, NULL, 'R'},
@@ -1240,6 +1242,7 @@ parse_args (unsigned argc, char **argv)
 
 	  link_info.type = type_relocatable;
 	  link_info.localize_hidden = false;
+	  link_info.finalize_locals = false;
 	  config.build_constructors = false;
 	  config.magic_demand_paged = false;
 	  config.text_read_only = false;
@@ -1367,6 +1370,12 @@ parse_args (unsigned argc, char **argv)
 		   "--localize-hidden");
 	  link_info.localize_hidden = true;
 	  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.finalize_locals = true;
+	  break;
 	case OPTION_NO_PIE:
 	  link_info.type = type_pde;
 	  break;
diff --git a/ld/testsuite/ld-elf/finalize_locals.s b/ld/testsuite/ld-elf/finalize_locals.s
new file mode 100644
index 00000000000..8df5a43992b
--- /dev/null
+++ b/ld/testsuite/ld-elf/finalize_locals.s
@@ -0,0 +1,23 @@
+	.section .text.2,"ax",%progbits
+	.global foo
+	.type	foo, %function
+foo:
+	.space	4
+	.dc.a hidfn1
+	.dc.a hidfn2
+	.size	foo, 4
+
+	.global hidfn1
+	.type	hidfn1, %function
+	.hidden	hidfn1
+hidfn1:
+	.space	4
+	.size	hidfn1, 4
+
+	.section .text.3,"ax",%progbits
+	.global hidfn2
+	.type	hidfn2, %function
+	.hidden	hidfn2
+hidfn2:
+	.space	4
+	.size	hidfn2, 4
diff --git a/ld/testsuite/ld-elf/finalize_locals1.d b/ld/testsuite/ld-elf/finalize_locals1.d
new file mode 100644
index 00000000000..692f9c15668
--- /dev/null
+++ b/ld/testsuite/ld-elf/finalize_locals1.d
@@ -0,0 +1,12 @@
+#source: finalize_locals.s
+#ld: -r --localize-hidden --finalize-locals
+#readelf: -r
+#target: x86_64-*-*
+
+#...
+.* hidfn2 .*
+#pass
+
+#...
+.* hidfn1 .*
+#fail
diff --git a/ld/testsuite/ld-elf/finalize_locals2.d b/ld/testsuite/ld-elf/finalize_locals2.d
new file mode 100644
index 00000000000..181144277e1
--- /dev/null
+++ b/ld/testsuite/ld-elf/finalize_locals2.d
@@ -0,0 +1,13 @@
+#source: finalize_locals.s
+#ld: -r --localize-hidden --finalize-locals
+#strip: --strip-unneeded
+#readelf: -s
+#target: x86_64-*-*
+
+#...
+.*: [0-9a-f]+ +4 +FUNC +LOCAL +DEFAULT +[0-9]+ hidfn2
+#pass
+
+#...
+.*: [0-9a-f]+ +4 +FUNC +LOCAL +DEFAULT +[0-9]+ hidfn1
+#fail
diff --git a/ld/testsuite/ld-elf/finalize_locals3.d b/ld/testsuite/ld-elf/finalize_locals3.d
new file mode 100644
index 00000000000..b38a0dee73a
--- /dev/null
+++ b/ld/testsuite/ld-elf/finalize_locals3.d
@@ -0,0 +1,3 @@
+#source: finalize_locals.s
+#ld: -shared --finalize-locals
+#error: --finalize-locals may only be used together with -r
-- 
2.31.1.windows.1



More information about the Binutils mailing list