[PATCH v2] gas: add --reloc-section-sym={all, internal, none} option for ELF
Fangrui Song
i@maskray.me
Sun Feb 22 02:03:38 GMT 2026
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
- temporary: 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 | 34 ++++++++++---------
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 | 10 +++++-
15 files changed, 203 insertions(+), 18 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
diff --git a/gas/NEWS b/gas/NEWS
index e384d1135c0..40120e4c777 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.
+
Changes in 2.46:
* Add support for AMD Zen6 processor.
diff --git a/gas/as.c b/gas/as.c
index f08c7c71d73..328f9bb9e2e 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 1c96a69ea09..065b8db630f 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -413,6 +413,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 6114a178a0d..520af22707c 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 07acb4ad514..757d179008b 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -4,15 +4,15 @@
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
#
# elf tests
@@ -170,30 +170,32 @@ if { [is_elf_format] } then {
switch -glob $target_triplet {
alpha*-*-* { }
am3*-*-* { }
+ avr-*-* { }
*c54x*-*-* { }
cr16*-*-* { }
crx*-*-* { }
h8300-*-* { }
- hppa*-*-* { }
- iq2000*-*-* { }
- mips*-*-* { }
+ loongarch*-*-* { }
+ m681*-*-* { }
+ m68hc*-*-* { }
mn10200-*-* { }
mn10300-*-* { }
msp43*-*-* { }
- rl78-*-* { }
riscv*-*-* { }
+ rl78-*-* { }
rx-*-* { }
- loongarch*-*-* { }
+ xgate-*-* { }
+ vax-*-* { }
default {
- # The next test 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
+ # The following tests require that the target adjusts relocations
+ # referencing local binding symbols to be against section symbols.
run_dump_test equ-reloc
+ run_dump_test redef
+ run_dump_test "reloc-section-sym-all"
+ 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
@@ -245,7 +247,7 @@ if { [is_elf_format] } then {
# in the symbol type test.
# We also need to exclude targets that do not support unique objects.
if { [istarget "*-*-hpux*"]
- || [istarget "arm*-*-*"]
+ || [istarget "arm*-*-*"]
|| [istarget "mips*-*-*"]
|| [istarget "msp*-*-*"]
|| [istarget "visium-*-*"]
@@ -380,7 +382,7 @@ if { [is_elf_format] } then {
run_dump_test "startof"
run_dump_test "missing-build-notes"
-
+
run_dump_test "common1"
run_dump_test "common2"
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..02ff015ab37
--- /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 [^ ]+ +\.text.*
+0*4 [^ ]+ +\.text.*
+#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..33c03716dcb
--- /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 [^ ]+ +\.text.*
+#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..b95bced6465
--- /dev/null
+++ b/gas/testsuite/gas/elf/reloc-section-sym.s
@@ -0,0 +1,9 @@
+ .text
+local:
+ .byte 0
+.Ltemp:
+ .byte 0
+
+ .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..2124759aa79
--- /dev/null
+++ b/gas/testsuite/gas/i386/reloc-section-sym.s
@@ -0,0 +1,14 @@
+ .text
+ nop
+local:
+ .byte 0
+.Ltemp:
+ nop
+
+.section .text1,"ax"
+ call local
+ call .Ltemp
+
+.data
+ .quad 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 3494871270a..3c258069508 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)
@@ -2043,7 +2051,7 @@ maybe_generate_build_notes (void)
else
desc_reloc = BFD_RELOC_64;
}
-
+
/* We have to create a note for *each* code section.
Linker garbage collection might discard some. */
total_size = 0;
--
2.43.0
More information about the Binutils
mailing list