---
title: Linker Scripts and Relocations
url: https://doc.liz6.com/en/systems-programming/07-toolchain-and-compilation/02-linker-scripts-and-relocations
locale: en
area: systems-programming
tags:
- systems-programming
- toolchain-and-compilation
date: 2026-06-30
modified: 2026-07-16
description: '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'
---

# 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

```c
// x86-64 (AMD64):
R_X86_64_64:        Direct 64-bit absolute address
R_X86_64_PC32:      32-bit PC-relative offset (call/jmp target)
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 granularity (ADR)
R_AARCH64_ADD_ABS_LO12_NC:  In-page offset (ADD)
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

```ld
/* 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

```bash
# View relocation table
readelf -r file.o

# View default ld script
ld --verbose | less

# Trace link process
ld -Map output.map -o prog *.o  # Write detailed map file
gcc -Wl,--trace -o prog *.o     # Print each linked file

# Undefined symbols
ld -o prog *.o 2>&1 | grep undefined
```

## 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*
