On this page
Linker Scripts and Relocations
Coverage: ld script syntax → relocation types → GOT/PLT relocation → linker relaxation → LLD vs GNU ld vs gold → R_AARCH64_ADR_GOT_PAGE Applicable to: binutils ld, LLD, static/dynamic linking
Relocations: The Linker's Job
Compile time (gcc -c):
.o files contain RELA entries → recording "this address needs filling, target symbol is this"
Example: R_X86_64_PLT32: This 4-byte offset needs to be filled with the symbol's PLT address
Link time (ld):
Iterate through all .o RELA entries → resolve symbols → fill in addresses
If dynamic symbol → generate .rela.plt (resolved at runtime by ld.so)
Common Relocation Types
// x86-64 (AMD64):
R_X86_64_64: Direct 64-bit absolute address
R_X86_64_PC32: 32-bit PC-relative
R_X86_64_PLT32: Similar to PC32 but target is in the PLT
R_X86_64_GOTPCREL: PC-relative offset to GOT entry
R_X86_64_COPY: Copy variable from shared library to main program .bss
// ARM64 (AArch64):
R_AARCH64_ABS64: Absolute address
R_AARCH64_ADR_PREL_PG_HI21: PC-relative, page
R_AARCH64_ADD_ABS_LO12_NC: In-page
R_AARCH64_LDST64_ABS_LO12_NC: Load/store, in-page offset
R_AARCH64_ADR_GOT_PAGE: PC-relative to GOT entry, page granularity + load
ld Script: Linker Script
/* Default ld script: /usr/lib/ldscripts/elf_x86_64.x */
SECTIONS
{
/* Start: Code segment (Read + Execute) */
. = 0x400000 + SIZEOF_HEADERS; /* Non-PIE */
.text : { *(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(.text .stub .text.* .gnu.linkonce.t.*) }
/* Read-only data */
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
. = ALIGN(0x1000);
__init_array_start = .;
.init_array : { *(.init_array) }
__init_array_end = .;
/* Read-write segment (starts on next page) */
. = ALIGN(0x1000);
.data : { *(.data .data.* .gnu.linkonce.d.*) }
.bss : { *(.bss .bss.* .gnu.linkonce.b.*) }
}
LLD vs GNU ld vs gold
| GNU ld (BFD) | gold | LLD (LLVM) | |
|---|---|---|---|
| Speed | Baseline | 2-3x faster | 3-5x faster |
| Memory | High | Lower | Low |
| LTO | Supported (GCC LTO) | Supported (GCC LTO) | Supported (ThinLTO) |
| Linker Scripts | Full support | Partial | Most |
| Default | Traditional | Unmaintained | Future |
Linker Relaxation
Certain relocations can be optimized via "relaxation":
Supported by RISC-V / ARM64:
Example: R_RISCV_CALL → At link time, the target distance is visible → Use shorter instruction sequences
→ call (32-bit offset) → jal (20-bit offset) if distance is sufficient
→ Shorter + faster code
x86 does not support relaxation (fixed instruction sizes, nothing to optimize)
Debugging
# View relocation table
# View default ld script
|
# Trace link process
# Undefined symbols
|
References
- Documentation:
info ld, ELF spec (System V ABI, Chapter 4) - Source Code:
binutils/ld/,lld/ELF/ - LWN: "Linker relaxation"
Keywords: relocation, R_X86_64_PLT32, R_AARCH64_ADR_GOT_PAGE, ld script, LLD, gold, linker relaxation