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.
- 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".”
- other
Acknowledges the issue and the proposer's explanation.
“I was debugging rustc segfaults and traced them to GOTPCRELX handling in older ld.”
- 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…
- 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.