binutils Newspaper
JULY 29, 2026
binutils Proposed Sentiment 8 / 10

ld converts push with GOTPCRELX into call - is a local guard okay?

Identifies a bug in older ld versions where GOTPCRELX on 'push' is incorrectly converted to 'call', causing crashes, and proposes a workaround.

A bug in older versions of the ld linker (prior to binutils 2.45) causes a crash in Rust programs by misinterpreting the R_X86_64_GOTPCRELX relocation for push instructions. Instead of performing a push operation using the Global Offset Table (GOT), ld incorrectly converts it into an addr32 call to a read-only .rodata section. This patch proposes a local workaround to prevent ld from converting non-call/jmp relocations, which mimics the behavior of newer ld versions and resolves the issue for affected build hosts.

In the Thread 3 participants
  1. Sunil Kumar Dora <sunilkumar.dora@windriver.com> proposer

    Identifies a bug in older `ld` versions where `R_X86_64_GOTPCRELX` for `push` instructions is incorrectly converted to `call`, leading to crashes in Rust programs, and proposes a local fix.

    “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".”
  2. Sam James <sam@gentoo.org> other

    Acknowledges the issue and the proposer's explanation.

    “I was debugging rustc segfaults and traced them to GOTPCRELX handling in older ld.”
  3. Sunil Kumar Dora <sunilkumar.dora@windriver.com> proposer

    Provides further context on the specific behavior of older `ld` versions.

    “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):”

Technical Tradeoffs

  • Addresses a critical bug affecting compatibility with newer assembler outputs.
  • Provides a workaround for older `ld` versions, allowing continued use on systems with distro-provided binutils.
  • Introduces a small code change to `bfd/elf64-x86-64.c`.

In Details

This discussion addresses a compatibility issue in older ld versions (pre-2.45) when handling R_X86_64_GOTPCRELX relocations generated by newer gas (2.46.1). The elf_x86_64_convert_load_reloc function incorrectly interprets push@GOTPCREL(%rip) (modrm 0x35) as a call, applying an addr32 call override. This misinterpretation leads to a crash when the target is a read-only data segment, as seen in Rust compiler builds. The proposed solution is a local modification to bfd/elf64-x86-64.c to prevent this conversion for non-jmp (0x25) instructions in the 0xff opcode pattern, effe…

For Context
ld
The GNU linker, which combines object files into an executable or library.
GOTPCRELX
A relocation type used on x86-64 for accessing Global Offset Table (GOT) entries relative to the instruction pointer (RIP).
RIP-relative addressing
A method of addressing memory where the address is calculated relative to the Instruction Pointer (RIP) register.
Global Offset Table (GOT)
A table used in position-independent code (PIC) to store addresses of external functions and global variables, allowing them to be resolved at runtime.
modrm
A ModR/M byte in x86 assembly language that specifies an operand for an instruction.
readelf
A utility that displays information about ELF files, including relocation entries.
objdump
A utility that displays information from object files, including disassembled code.
relocation
An instruction in an object file that tells the linker how to modify code or data addresses when creating the final executable or library.
Filed Under: linkerx86-64relocationrustcbugfix