8. x86-64 Relocation Reference
8.1. Introduction
This document describes the x86-64 relocations handled by ELD, including their encoding types, operations, range checks, and dynamic relocation behaviour.
The official ABI specification is the System V AMD64 ABI: https://gitlab.com/x86-psABIs/x86-64-ABI
8.2. Relocation Conventions
Symbol |
Meaning |
|---|---|
|
Runtime address of the referenced symbol |
|
Relocation addend |
|
Address of the relocation site |
|
Base address of the shared object (for |
|
Address of the symbol’s entry in the Global Offset Table |
|
Address of the symbol’s entry in the Procedure Linkage Table |
|
Thread pointer (base of the TLS block for the current thread) |
|
Dynamic thread pointer (base of the TLS module block) |
|
Size of the TLS template (used to compute |
8.3. Encoding Types
x86-64 uses only flat-field encodings — no bit-scrambling.
Encoding |
Value bits written |
Field size |
|---|---|---|
|
none |
— |
|
|
8-bit |
|
|
16-bit |
|
|
32-bit |
|
|
64-bit |
8.4. Range Checks
Two distinct checks are applied, depending on the relocation:
VerifyRange (hard error): Enabled only for specific relocations
(see table). Checks whether the computed value X = result >> Shift fits
in the encoding field. Signed relocations use llvm::isInt<N>, unsigned
use llvm::isUInt<N>.
Truncation check (forceVerify, diagnostic only): Enabled when the
user passes --verify-reloc <name>. Checks whether the unsigned result
overflows the field width. No error is raised unless explicitly requested.
The table below lists the exact bounds for each relocation.
Relocation |
Encoding |
Signed |
|
Effective bound |
|---|---|---|---|---|
|
|
— |
no |
— |
|
|
no |
no |
|
|
|
no |
no |
|
|
|
yes |
no |
|
|
|
no |
no |
|
|
|
no |
no |
|
|
|
no |
no |
|
|
|
no |
no |
|
|
|
no |
no |
|
|
|
no |
no |
|
|
|
yes |
no |
|
|
|
no |
no |
|
|
|
yes |
no |
|
|
|
yes |
no |
|
|
|
yes |
no |
|
|
|
yes |
yes |
|
|
|
yes |
yes |
|
|
|
yes |
yes |
|
|
|
yes |
yes |
|
|
|
yes |
yes |
|
|
|
yes |
no |
|
|
|
yes |
no |
|
|
|
no |
no |
filled by dynamic linker |
Why only TLS 32-bit relocations have
VerifyRange: These relocations encode offsets within a TLS block or a PC-relative offset to a GOT entry, both of which must fit in a sign-extended 32-bit immediate field in the instruction encoding. A value outside[−2^31, 2^31 − 1]would silently truncate and produce a wrong address at runtime. For general data and PC-relative relocations the ABI permits truncation; for TLS it is always an error.
Implicit truncation for unsigned relocations:
R_X86_64_32,R_X86_64_16, andR_X86_64_8silently truncate the result to the field width. Use--verify-reloc R_X86_64_32(etc.) to promote truncation to a diagnostic.
8.5. Relocation Table
8.5.1. Absolute Relocations
The computed value S + A is written into the relocation site.
Relocation |
Type |
Encoding |
Operation |
Signed |
Range check |
|---|---|---|---|---|---|
|
0 |
|
— |
— |
— |
|
1 |
|
|
no |
truncated to 64 bits |
|
10 |
|
|
no |
truncated to 32 bits |
|
11 |
|
|
yes |
truncated to 32 bits, sign-extended on load |
|
12 |
|
|
no |
truncated to 16 bits |
|
14 |
|
|
no |
truncated to 8 bits |
R_X86_64_32vsR_X86_64_32S: Both write 32 bits.R_X86_64_32zero-extends (result must fit in[0, 2^32));R_X86_64_32Ssign-extends (result must fit in[-2^31, 2^31)). The compiler emits32Sfor addresses that are sign-extended when loaded into 64-bit registers, such as data or code in the low 2 GiB.
8.5.2. PC-Relative Relocations
The computed value S + A - P is written. P is the address of the
relocation site; the caller’s instruction already adds P back at runtime,
so the net effect is a direct reference to S + A.
Relocation |
Type |
Encoding |
Operation |
Signed |
Range check |
|---|---|---|---|---|---|
|
2 |
|
|
no |
truncated to 32 bits |
|
13 |
|
|
no |
truncated to 16 bits |
|
15 |
|
|
no |
truncated to 8 bits |
|
24 |
|
|
no |
truncated to 64 bits |
|
4 |
|
|
yes |
truncated to 32 bits |
R_X86_64_PLT32: Used for function calls (call foo). Iffoois non-preemptible the linker writesS + A - Pdirectly. Iffoois preemptible a PLT entry is created and the formula becomesPLT(S) + A - P.
8.5.3. GOT-Relative Relocations
These embed the PC-relative offset to the symbol’s GOT entry. At runtime the instruction loads the actual symbol address from the GOT.
Relocation |
Type |
Encoding |
Operation |
Signed |
Range check |
|---|---|---|---|---|---|
|
3 |
|
|
no |
truncated to 32 bits |
|
9 |
|
|
yes |
truncated to 32 bits |
|
41 |
|
|
yes |
truncated to 32 bits |
|
42 |
|
|
yes |
truncated to 32 bits |
GOTPCRELX/REX_GOTPCRELX: Relaxable variants ofGOTPCREL.GOTPCRELXis used without a REX prefix;REX_GOTPCRELXis used with one. When the symbol is non-preemptible the linker may rewrite the instruction sequence to avoid the GOT load entirely (see Relaxations).
8.5.4. Dynamic / Runtime Relocations
Written by the dynamic linker at load time; not applied statically by ELD.
Relocation |
Type |
Encoding |
Operation |
|---|---|---|---|
|
5 |
|
Copy symbol value from DSO into the executable |
|
6 |
|
|
|
7 |
|
|
|
8 |
|
|
8.5.5. TLS Relocations
8.5.6. General Dynamic (GD) — R_X86_64_TLSGD
Calls __tls_get_addr with a two-word GOT descriptor (module ID +
DTP offset). Works in all contexts.
Relocation |
Type |
Encoding |
Operation |
Signed |
Range check |
|---|---|---|---|---|---|
|
19 |
|
|
yes |
|
leaq x@tlsgd(%rip), %rdi # R_X86_64_TLSGD; GOT holds {dtpmod, dtpoff}
call __tls_get_addr@PLT
# %rax now holds &x in the current thread's TLS block
8.5.7. Local Dynamic (LD) — R_X86_64_TLSLD
Like GD but for symbols local to the module; one __tls_get_addr call
covers all LD symbols in the module.
Relocation |
Type |
Encoding |
Operation |
Signed |
Range check |
|---|---|---|---|---|---|
|
20 |
|
|
yes |
|
leaq x@tlsld(%rip), %rdi # R_X86_64_TLSLD; GOT holds {dtpmod, 0}
call __tls_get_addr@PLT
# %rax is the base of this module's TLS block
movl x@dtpoff(%rax), %eax # R_X86_64_DTPOFF32: DTP offset added by linker
8.5.8. Initial Exec (IE) — R_X86_64_GOTTPOFF
Loads the TP-relative offset from the GOT; no runtime call.
Relocation |
Type |
Encoding |
Operation |
Signed |
Range check |
|---|---|---|---|---|---|
|
22 |
|
|
yes |
|
movq x@gottpoff(%rip), %rax # R_X86_64_GOTTPOFF; GOT[x] = tpoff(x)
movl %fs:(%rax), %eax # load x relative to TP
8.5.9. Local Exec (LE) — R_X86_64_TPOFF32 / R_X86_64_TPOFF64
Encodes the TP-relative offset directly; fastest model, only valid in executables.
Relocation |
Type |
Encoding |
Operation |
Signed |
Range check |
|---|---|---|---|---|---|
|
23 |
|
|
yes |
|
|
18 |
|
|
yes |
truncated to 64 bits |
movl %fs:x@tpoff, %eax # R_X86_64_TPOFF32: offset baked in at link time
8.5.10. DTP Offset Relocations
Used inside GOT entries created for GD/LD access.
Relocation |
Type |
Encoding |
Operation |
Signed |
Range check |
|---|---|---|---|---|---|
|
16 |
|
TLS module index (filled by dynamic linker) |
no |
— |
|
17 |
|
|
yes |
truncated to 64 bits |
|
21 |
|
|
yes |
|
8.6. Dynamic Relocation Behaviour
ELD emits dynamic relocations during link when static resolution is not possible. The table below summarises when each dynamic relocation type is created.
Dynamic relocation |
Trigger condition |
|---|---|
|
Non-preemptible symbol referenced by |
|
Preemptible symbol with a GOT entry ( |
|
Preemptible symbol referenced by |
|
Symbol referenced via PLT ( |
|
Global data symbol referenced from a non-PIC executable |
|
TLS IE/LD symbol in a shared object ( |
|
TLS GD/LD first GOT entry (module ID) |
|
TLS GD second GOT entry for preemptible symbol |
8.7. Relaxations
x86-64 supports GOT-load relaxation for GOTPCRELX and REX_GOTPCRELX
when the referenced symbol is non-preemptible. The linker rewrites the
instruction in-place; no separate flag is required.
8.7.1. GOTPCRELX Relaxation (no REX prefix)
Applies to instructions that load or call through the GOT without a REX prefix (typically 32-bit operand-size or short-form instructions).
call *foo@GOTPCREL(%rip) → call foo
# Before (FF /2 — indirect call through GOT, 6 bytes)
call *foo@GOTPCREL(%rip) # R_X86_64_GOTPCRELX
# encodes: FF 15 <pcrel32 to GOT slot>
# After (E8 — direct call, 5 bytes + 1 NOP)
call foo # direct; R_X86_64_PC32 applied to offset
nop # 1-byte filler to preserve instruction length
jmp *foo@GOTPCREL(%rip) → jmp foo
# Before (FF /4 — indirect jump, 6 bytes)
jmp *foo@GOTPCREL(%rip) # R_X86_64_GOTPCRELX
# After (E9 — direct jump, 5 bytes + 1 NOP)
jmp foo
nop
mov foo@GOTPCREL(%rip), %reg → lea foo(%rip), %reg
# Before: load address from GOT
movq foo@GOTPCREL(%rip), %rax # R_X86_64_GOTPCRELX
# After: compute address directly (non-preemptible, no GOT needed)
leaq foo(%rip), %rax # R_X86_64_PC32
8.7.2. REX_GOTPCRELX Relaxation (REX prefix present)
Applies to 64-bit forms of the same instructions (REX.W prefix).
movq foo@GOTPCREL(%rip), %reg → leaq foo(%rip), %reg
# Before (REX.W + 8B /5 — 64-bit GOT load, 7 bytes)
movq foo@GOTPCREL(%rip), %rax # R_X86_64_REX_GOTPCRELX
# After (REX.W + 8D /5 — 64-bit LEA, 7 bytes)
leaq foo(%rip), %rax # R_X86_64_PC32
movq foo@GOTPCREL(%rip), %reg → movq $imm32, %reg (small absolute)
# Before (REX.W + 8B — GOT load, 7 bytes)
movq foo@GOTPCREL(%rip), %rax # R_X86_64_REX_GOTPCRELX
# After (REX.W + C7 — sign-extended 32-bit immediate, 7 bytes)
movq $foo, %rax # R_X86_64_32S; valid when foo fits in 32 bits
8.8. Common Instruction Sequences
8.8.1. Absolute Address (64-bit)
movabsq $symbol, %rax # R_X86_64_64
8.8.2. Absolute Address (32-bit zero-extend)
movl $symbol, %eax # R_X86_64_32; valid when symbol < 2^32
8.8.3. PC-Relative Data Access
leaq symbol(%rip), %rax # R_X86_64_PC32
8.8.4. Function Call (direct or via PLT)
call foo # R_X86_64_PLT32
# linker uses direct offset if non-preemptible,
# PLT stub otherwise
8.8.5. GOT-Indirect Load
movq foo@GOTPCREL(%rip), %rax # R_X86_64_REX_GOTPCRELX (or GOTPCREL)
movq (%rax), %rax # dereference the loaded address
8.8.6. TLS Local-Exec
movl %fs:x@tpoff, %eax # R_X86_64_TPOFF32
8.8.7. TLS Initial-Exec
movq x@gottpoff(%rip), %rax # R_X86_64_GOTTPOFF
movl %fs:(%rax), %eax
8.9. References
System V AMD64 ABI specification: https://gitlab.com/x86-psABIs/x86-64-ABI
Intel 64 and IA-32 Architectures Software Developer Manuals: https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html