older ld converts push with GOTPCRELX into call - is a local guard ok?
Dora, Sunil Kumar
SunilKumar.Dora@windriver.com
Thu Jul 9 19:17:24 GMT 2026
Hi,
I was debugging rustc segfaults and traced them to GOTPCRELX handling
in older ld.
Since commit 11c2852449825 (binutils 2.45), gas emits
R_X86_64_GOTPCRELX for "push foo@GOTPCREL(%rip)". Older ld's
elf_x86_64_convert_load_reloc() only checks modrm 0x25 (jmp) in the
0xff case and treats everything else as call, so a push (modrm 0x35)
gets silently rewritten to "addr32 call foo". In our case foo is a
read-only LLVM table statically linked into librustc_driver.so, so
rustc calls into .rodata and crashes.
Reproducer (two installs on purpose - that mismatch is the bug):
$ cat repro.s
.text
.globl func
func:
push tbl@GOTPCREL(%rip)
add $8, %rsp
ret
.section .rodata
.globl tbl
.hidden tbl
tbl:
.zero 576
$ as --version | head -1
GNU assembler (GNU Binutils) 2.46.1
$ as -o repro.o repro.s
$ readelf -rW repro.o | grep tbl
0000000000000002 0000000300000029 R_X86_64_GOTPCRELX 0000000000000000 tbl - 4
$ ld --version | head -1
GNU ld (GNU Binutils for Ubuntu) 2.38
$ ld -shared -o bad.so repro.o
$ objdump -d bad.so | grep -A1 '<func>:'
0000000000001000 <func>:
1000: 67 e8 fa 0f 00 00 addr32 call 2000 <tbl>
ld 2.46.1 on the same repro.o keeps "ff 35" (push through GOT).
gas 2.38 emits plain GOTPCREL for this source, so only new-gas
objects with old ld are affected.
Old branches are closed, I know - but some of our build hosts must
stay on distro binutils for a while, so we plan to carry this locally
(skip conversion for anything that isn't call/jmp, matching what newer
ld does for push in a shared library):
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1659,6 +1659,8 @@ elf_x86_64_convert_load_reloc (bfd *abfd,
modrm = bfd_get_8 (abfd, contents + roff - 1);
+ if (modrm != 0x15 && modrm != 0x25)
+ return true;
if (modrm == 0x25)
Tested: repro links correctly with the patched ld, and the rust build
that segfaulted before now passes.
Two things I'd like to confirm:
1. Is this guard safe, or can modrm be something other than
0x15/0x25/0x35 here that needs different handling?
2. Worth documenting this incompatibility (PR or release notes)?
Every ld before 2.45 silently produces bad code from such objects,
and tracing the crash back to the linker was not easy.
Thanks,
Sunil Dora
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://sourceware.org/pipermail/binutils/attachments/20260709/85d39da2/attachment.htm>
More information about the Binutils
mailing list