[v4] gas: add --reloc-section-sym={all, internal, none} option for ELF

Message ID 20260701035918.1453550-1-i@maskray.me
State New
Headers
Series [v4] gas: add --reloc-section-sym={all, internal, none} option for ELF |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 fail Patch failed to apply

Commit Message

Fangrui Song July 1, 2026, 3:59 a.m. UTC
  From: Fangrui Song <maskray@sourceware.org>

When generating relocations for non-ifunc local symbols that satisfies
several conditions, GAS converts them to reference the section symbol
(STT_SECTION) instead, folding the original symbol's offset into the
addend.  This allows the original local symbol to be omitted from
.symtab, but the STT_SECTION symbol itself must be present, so the
conversion saves .symtab entries only when a section has more than one
local symbol referenced by relocations.

Add --reloc-section-sym to control this conversion:

- all (default): convert all eligible local symbols
- internal: only convert compiler-generated locals (.L prefix)
- none: never convert; keep all symbols as-is in relocations

This is useful for debugging and for tools that benefit from preserved
symbol names.

PR gas/33885
---
 gas/NEWS                                      |  4 ++++
 gas/as.c                                      | 20 +++++++++++++++-
 gas/as.h                                      | 10 ++++++++
 gas/doc/as.texi                               | 11 +++++++++
 gas/testsuite/gas/elf/elf.exp                 |  9 +++++---
 gas/testsuite/gas/elf/reloc-section-sym-all.d | 12 ++++++++++
 .../gas/elf/reloc-section-sym-internal.d      | 12 ++++++++++
 .../gas/elf/reloc-section-sym-none.d          | 12 ++++++++++
 gas/testsuite/gas/elf/reloc-section-sym.s     |  9 ++++++++
 .../gas/i386/reloc-section-sym-all.d          | 23 +++++++++++++++++++
 .../gas/i386/reloc-section-sym-internal.d     | 23 +++++++++++++++++++
 .../gas/i386/reloc-section-sym-none.d         | 23 +++++++++++++++++++
 gas/testsuite/gas/i386/reloc-section-sym.s    | 14 +++++++++++
 gas/testsuite/gas/i386/x86-64.exp             |  4 ++++
 gas/write.c                                   |  8 +++++++
 15 files changed, 190 insertions(+), 4 deletions(-)
 create mode 100644 gas/testsuite/gas/elf/reloc-section-sym-all.d
 create mode 100644 gas/testsuite/gas/elf/reloc-section-sym-internal.d
 create mode 100644 gas/testsuite/gas/elf/reloc-section-sym-none.d
 create mode 100644 gas/testsuite/gas/elf/reloc-section-sym.s
 create mode 100644 gas/testsuite/gas/i386/reloc-section-sym-all.d
 create mode 100644 gas/testsuite/gas/i386/reloc-section-sym-internal.d
 create mode 100644 gas/testsuite/gas/i386/reloc-section-sym-none.d
 create mode 100644 gas/testsuite/gas/i386/reloc-section-sym.s
  

Comments

Alan Modra July 2, 2026, 2:58 a.m. UTC | #1
On Tue, Jun 30, 2026 at 08:59:18PM -0700, Fangrui Song wrote:
> From: Fangrui Song <maskray@sourceware.org>
> 
> When generating relocations for non-ifunc local symbols that satisfies
> several conditions, GAS converts them to reference the section symbol
> (STT_SECTION) instead, folding the original symbol's offset into the
> addend.  This allows the original local symbol to be omitted from
> .symtab, but the STT_SECTION symbol itself must be present, so the
> conversion saves .symtab entries only when a section has more than one
> local symbol referenced by relocations.
> 
> Add --reloc-section-sym to control this conversion:
> 
> - all (default): convert all eligible local symbols
> - internal: only convert compiler-generated locals (.L prefix)
> - none: never convert; keep all symbols as-is in relocations
> 
> This is useful for debugging and for tools that benefit from preserved
> symbol names.
> 
> PR gas/33885

OK, thanks.
  

Patch

diff --git a/gas/NEWS b/gas/NEWS
index b36bcb5cf3d..ffc9a0f06be 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,5 +1,9 @@ 
 -*- text -*-
 
+* New command line option --reloc-section-sym=[all|internal|none]
+  controls whether relocations referencing local binding symbols are adjusted
+  to use section symbols.
+
 * The legacy .vtable_entry and .vtable_inherit directives that were
   needed for versions of GCC prior to gcc-3.0 (when the generic C++
   ABI was adopted) are now considered obsolete.  They are now deprecated
diff --git a/gas/as.c b/gas/as.c
index f33f6ec85cd..b652e81286c 100644
--- a/gas/as.c
+++ b/gas/as.c
@@ -326,6 +326,10 @@  Options:\n\
 	   DEFAULT_SFRAME ? "yes" : "no");
   fprintf (stream, _("\
   --gsframe-<N>           generate SFrame version <N> information. 3 == <N>\n"));
+  fprintf (stream, _("\
+  --reloc-section-sym=[all|internal|none]\n\
+                          adjust eligible relocations to use section symbols\n\
+                          (default: all)\n"));
 # if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
   fprintf (stream, _("\
   --scfi=experimental     Synthesize DWARF CFI for hand-written asm\n\
@@ -523,7 +527,8 @@  parse_args (int * pargc, char *** pargv)
       OPTION_SFRAME_3,
       OPTION_SCFI,
       OPTION_INFO,
-      OPTION_NOINFO
+      OPTION_NOINFO,
+      OPTION_RELOC_SECTION_SYM
     /* When you add options here, check that they do
        not collide with OPTION_MD_BASE.  See as.h.  */
     };
@@ -556,6 +561,7 @@  parse_args (int * pargc, char *** pargv)
     ,{"generate-missing-build-notes", required_argument, NULL, OPTION_ELF_BUILD_NOTES}
     ,{"gsframe", optional_argument, NULL, OPTION_SFRAME}
     ,{"gsframe-3", no_argument, NULL, OPTION_SFRAME_3}
+    ,{"reloc-section-sym", required_argument, NULL, OPTION_RELOC_SECTION_SYM}
 # if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
     ,{"scfi", required_argument, NULL, OPTION_SCFI}
 # endif
@@ -1043,6 +1049,18 @@  This program has absolutely no warranty.\n"));
 	  flag_sectname_subst = 1;
 	  break;
 
+	case OPTION_RELOC_SECTION_SYM:
+	  if (strcasecmp (optarg, "all") == 0)
+	    flag_reloc_section_sym = reloc_section_sym_all;
+	  else if (strcasecmp (optarg, "internal") == 0)
+	    flag_reloc_section_sym = reloc_section_sym_internal;
+	  else if (strcasecmp (optarg, "none") == 0)
+	    flag_reloc_section_sym = reloc_section_sym_none;
+	  else
+	    as_fatal (_("Invalid --reloc-section-sym= option: `%s'"),
+		      optarg);
+	  break;
+
 	case OPTION_ELF_BUILD_NOTES:
 	  if (strcasecmp (optarg, "no") == 0)
 	    flag_generate_build_notes = false;
diff --git a/gas/as.h b/gas/as.h
index 94bc816be1e..2fe0a48a225 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -405,6 +405,16 @@  enum multibyte_input_handling
 };
 COMMON enum multibyte_input_handling multibyte_handling;
 
+/* Controls whether relocations referencing local symbols are converted
+   to use section symbols.  */
+enum reloc_section_sym_type
+{
+  reloc_section_sym_all = 0,
+  reloc_section_sym_internal,
+  reloc_section_sym_none
+};
+COMMON enum reloc_section_sym_type flag_reloc_section_sym;
+
 /* TRUE if we should produce a listing.  */
 extern int listing;
 
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 3c4d20bd7d4..5d7772d62d3 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -257,6 +257,7 @@  gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
  [@b{--multibyte-handling=[allow|warn|warn-sym-only]}]
  [@b{--no-pad-sections}]
  [@b{-o} @var{objfile}] [@b{-R}]
+ [@b{--reloc-section-sym=[all|internal|none]}]
  [@b{--scfi=experimental}]
  [@b{--sectname-subst}]
  [@b{--size-check=[error|warning]}]
@@ -956,6 +957,16 @@  Ignored.  Supported for compatibility with tools that pass the same option to
 both the assembler and the linker.
 
 @ifset ELF
+@item --reloc-section-sym=all
+@itemx --reloc-section-sym=internal
+@itemx --reloc-section-sym=none
+Control whether relocations referencing local binding symbols are adjusted to
+use section symbols instead.  With @code{all} (the default), relocations
+against all eligible local binding symbols are converted.  With
+@code{internal}, only relocations against internal labels (symbols matching the
+@code{.L} prefix) are converted.  With @code{none}, no such conversions are
+performed.
+
 @item --scfi=experimental
 This option controls whether the assembler should synthesize CFI for
 hand-written input.  If the input already contains some synthesizable CFI
diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp
index 2232e2d58d9..5580c494c54 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -188,15 +188,18 @@  if { [is_elf_format] } then {
 	rx-*-* { }
 	loongarch*-*-* { }
 	default {
-	    # The next test can fail if the target does not convert fixups
+	    # The following tests can fail if the target does not convert fixups
 	    # against ordinary symbols into relocations against section symbols.
-	    # This is usually revealed by the error message:
-	    #  symbol `sym' required but not present
 	    setup_xfail "m681*-*-*" "m68hc*-*-*" "xgate-*-*" "vax-*-*" "avr-*-*"
 	    run_dump_test redef
 	    run_dump_test equ-reloc
+	    setup_xfail "m681*-*-*" "m68hc*-*-*" "xgate-*-*" "vax-*-*" "avr-*-*"
+	    run_dump_test "reloc-section-sym-all"
+	    setup_xfail "m681*-*-*" "m68hc*-*-*" "xgate-*-*" "avr-*-*"
+	    run_dump_test "reloc-section-sym-internal"
 	}
     }
+    run_dump_test "reloc-section-sym-none"
     run_dump_test "pseudo"
     run_dump_test "text-prev" $dump_opts
     run_dump_test "text-subsect" $dump_opts
diff --git a/gas/testsuite/gas/elf/reloc-section-sym-all.d b/gas/testsuite/gas/elf/reloc-section-sym-all.d
new file mode 100644
index 00000000000..b4da309c062
--- /dev/null
+++ b/gas/testsuite/gas/elf/reloc-section-sym-all.d
@@ -0,0 +1,12 @@ 
+#source: reloc-section-sym.s
+#as: --reloc-section-sym=all
+#objdump: -rsj .data
+#name: reloc-section-sym=all
+
+.*: +file format .*
+
+RELOCATION RECORDS FOR \[\.data\]:
+OFFSET +TYPE +VALUE
+0*0 [^ ]+ +\.bss.*
+0*4 [^ ]+ +\.bss.*
+#pass
diff --git a/gas/testsuite/gas/elf/reloc-section-sym-internal.d b/gas/testsuite/gas/elf/reloc-section-sym-internal.d
new file mode 100644
index 00000000000..0c0f464472a
--- /dev/null
+++ b/gas/testsuite/gas/elf/reloc-section-sym-internal.d
@@ -0,0 +1,12 @@ 
+#source: reloc-section-sym.s
+#as: --reloc-section-sym=internal
+#objdump: -rsj .data
+#name: reloc-section-sym=internal
+
+.*: +file format .*
+
+RELOCATION RECORDS FOR \[\.data\]:
+OFFSET +TYPE +VALUE
+0*0 [^ ]+ +local.*
+0*4 [^ ]+ +\.bss.*
+#pass
diff --git a/gas/testsuite/gas/elf/reloc-section-sym-none.d b/gas/testsuite/gas/elf/reloc-section-sym-none.d
new file mode 100644
index 00000000000..557728b1ee3
--- /dev/null
+++ b/gas/testsuite/gas/elf/reloc-section-sym-none.d
@@ -0,0 +1,12 @@ 
+#source: reloc-section-sym.s
+#as: --reloc-section-sym=none
+#objdump: -rsj .data
+#name: reloc-section-sym=none
+
+.*: +file format .*
+
+RELOCATION RECORDS FOR \[\.data\]:
+OFFSET +TYPE +VALUE
+0*0 [^ ]+ +local.*
+0*4 [^ ]+ +\.Ltemp.*
+#pass
diff --git a/gas/testsuite/gas/elf/reloc-section-sym.s b/gas/testsuite/gas/elf/reloc-section-sym.s
new file mode 100644
index 00000000000..37f1258f102
--- /dev/null
+++ b/gas/testsuite/gas/elf/reloc-section-sym.s
@@ -0,0 +1,9 @@ 
+	.section .bss
+local:
+	.zero 1
+.Ltemp:
+	.zero 1
+
+	.data
+	.long local + 16
+	.long .Ltemp + 16
diff --git a/gas/testsuite/gas/i386/reloc-section-sym-all.d b/gas/testsuite/gas/i386/reloc-section-sym-all.d
new file mode 100644
index 00000000000..2c8fd576ab5
--- /dev/null
+++ b/gas/testsuite/gas/i386/reloc-section-sym-all.d
@@ -0,0 +1,23 @@ 
+#source: reloc-section-sym.s
+#as: --reloc-section-sym=all
+#objdump: -rsj .data -j .text1
+#name: reloc-section-sym=all
+
+.*:     file format .*
+
+RELOCATION RECORDS FOR \[\.data\]:
+OFFSET +TYPE +VALUE
+0+0 R_X86_64_64 +\.text\+0x0+11
+0+8 R_X86_64_64 +\.text\+0x0+12
+
+
+RELOCATION RECORDS FOR \[\.text1\]:
+OFFSET +TYPE +VALUE
+0+1 R_X86_64_PC32 +\.text-0x0+3
+0+6 R_X86_64_PC32 +\.text-0x0+2
+
+
+Contents of section \.data:
+ 0000 00000000 00000000 00000000 00000000  \.+
+Contents of section \.text1:
+ 0000 e8000000 00e80000 0000 +.*
diff --git a/gas/testsuite/gas/i386/reloc-section-sym-internal.d b/gas/testsuite/gas/i386/reloc-section-sym-internal.d
new file mode 100644
index 00000000000..2387fab9e56
--- /dev/null
+++ b/gas/testsuite/gas/i386/reloc-section-sym-internal.d
@@ -0,0 +1,23 @@ 
+#source: reloc-section-sym.s
+#as: --reloc-section-sym=internal
+#objdump: -rsj .data -j .text1
+#name: reloc-section-sym=internal
+
+.*:     file format .*
+
+RELOCATION RECORDS FOR \[\.data\]:
+OFFSET +TYPE +VALUE
+0+0 R_X86_64_64 +named_local\+0x0+10
+0+8 R_X86_64_64 +\.text\+0x0+12
+
+
+RELOCATION RECORDS FOR \[\.text1\]:
+OFFSET +TYPE +VALUE
+0+1 R_X86_64_PC32 +named_local-0x0+4
+0+6 R_X86_64_PC32 +\.text-0x0+2
+
+
+Contents of section \.data:
+ 0000 00000000 00000000 00000000 00000000  \.+
+Contents of section \.text1:
+ 0000 e8000000 00e80000 0000 +.*
diff --git a/gas/testsuite/gas/i386/reloc-section-sym-none.d b/gas/testsuite/gas/i386/reloc-section-sym-none.d
new file mode 100644
index 00000000000..8f7fe3f2a52
--- /dev/null
+++ b/gas/testsuite/gas/i386/reloc-section-sym-none.d
@@ -0,0 +1,23 @@ 
+#source: reloc-section-sym.s
+#as: --reloc-section-sym=none
+#objdump: -rsj .data -j .text1
+#name: reloc-section-sym=none
+
+.*:     file format .*
+
+RELOCATION RECORDS FOR \[\.data\]:
+OFFSET +TYPE +VALUE
+0+0 R_X86_64_64 +named_local\+0x0+10
+0+8 R_X86_64_64 +\.Ltemp\+0x0+10
+
+
+RELOCATION RECORDS FOR \[\.text1\]:
+OFFSET +TYPE +VALUE
+0+1 R_X86_64_PC32 +named_local-0x0+4
+0+6 R_X86_64_PC32 +\.Ltemp-0x0+4
+
+
+Contents of section \.data:
+ 0000 00000000 00000000 00000000 00000000  \.+
+Contents of section \.text1:
+ 0000 e8000000 00e80000 0000 +.*
diff --git a/gas/testsuite/gas/i386/reloc-section-sym.s b/gas/testsuite/gas/i386/reloc-section-sym.s
new file mode 100644
index 00000000000..dcb25d3b6ab
--- /dev/null
+++ b/gas/testsuite/gas/i386/reloc-section-sym.s
@@ -0,0 +1,14 @@ 
+	.text
+	nop
+named_local:
+	.byte 0
+.Ltemp:
+	nop
+
+.section .text1,"ax"
+	call named_local
+	call .Ltemp
+
+.data
+	.quad named_local + 16
+	.quad .Ltemp + 16
diff --git a/gas/testsuite/gas/i386/x86-64.exp b/gas/testsuite/gas/i386/x86-64.exp
index 8ddf05481a5..3e5ffd8d13f 100644
--- a/gas/testsuite/gas/i386/x86-64.exp
+++ b/gas/testsuite/gas/i386/x86-64.exp
@@ -775,6 +775,10 @@  if [is_elf_format] then {
 	run_dump_test "x86-64-align-branch-3"
     }
     run_dump_test ehinterp
+
+    run_dump_test "reloc-section-sym-all"
+    run_dump_test "reloc-section-sym-internal"
+    run_dump_test "reloc-section-sym-none"
 }
 run_dump_test pr27198
 run_dump_test pr29483
diff --git a/gas/write.c b/gas/write.c
index 9514c3df42e..cd02d8c7fb8 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -909,6 +909,14 @@  adjust_reloc_syms (bfd *abfd ATTRIBUTE_UNUSED,
 	if ((symsec->flags & SEC_THREAD_LOCAL) != 0)
 	  continue;
 
+	/* With --reloc-section-sym=none, skip adjustment.
+	   With --reloc-section-sym=internal, only adjust relocs against
+	   internal labels (e.g. .L prefix symbols in ELF).  */
+	if (flag_reloc_section_sym == reloc_section_sym_none
+	    || (flag_reloc_section_sym == reloc_section_sym_internal
+		&& !bfd_is_local_label (stdoutput, symbol_get_bfdsym (sym))))
+	  continue;
+
 	val = S_GET_VALUE (sym);
 
 #if defined(TC_AARCH64) && defined(OBJ_COFF)