2. ARM Relocation Reference
2.1. Introduction
ARM (32-bit) supports two instruction set architectures: the 32-bit ARM ISA and the 16/32-bit Thumb/Thumb-2 ISA. Relocations encode symbol references into instruction immediate fields and, for branch relocations, into the encoded offset. The linker selects ARM or Thumb encoding based on whether the target symbol is a Thumb function (the T bit).
2.2. Relocation Conventions
Symbol |
Meaning |
|---|---|
|
Runtime address of the referenced symbol |
|
Relocation addend |
|
Address of the relocation site (place) |
|
1 if the target symbol is a Thumb function, 0 otherwise |
|
Base address of the segment containing symbol |
|
Address of the Global Offset Table |
|
Address of the GOT entry for symbol |
|
TLS segment base address |
|
Thread pointer |
2.2.1. The T Bit
ARM and Thumb functions are distinguished by the least-significant bit of a branch target. When T = 1, the expression (S + A) \| T sets bit 0, signalling an interworking branch to a Thumb function. Plain data relocations (R_ARM_ABS16, R_ARM_ABS8) do not OR in T.
2.3. Absolute Relocations
Relocation |
Expression |
Bits |
Range Check |
|---|---|---|---|
|
|
32 |
none |
|
|
16 |
none |
|
|
8 |
none |
|
|
|
none |
|
|
|
none |
|
|
|
none |
|
|
|
none |
R_ARM_MOVW_ABS_NC and R_ARM_MOVT_ABS are used in pairs: MOVW loads bits [15:0] and MOVT loads bits [31:16] of a 32-bit address.
2.3.1. Example: Loading a 32-bit Address (ARM)
movw r0, #:lower16:symbol @ R_ARM_MOVW_ABS_NC
movt r0, #:upper16:symbol @ R_ARM_MOVT_ABS
2.3.2. Example: Loading a 32-bit Address (Thumb)
movw r0, #:lower16:symbol @ R_ARM_THM_MOVW_ABS_NC
movt r0, #:upper16:symbol @ R_ARM_THM_MOVT_ABS
2.4. PC-Relative Relocations
Relocation |
Expression |
Bits |
Range Check |
|---|---|---|---|
|
|
32 |
none |
|
|
32 |
none |
|
|
31 |
none |
|
|
32 |
none |
|
|
|
none |
|
|
|
none |
|
|
|
none |
|
|
|
none |
|
|
|
none |
|
|
|
none |
|
|
|
none |
|
|
top 8 bits, 4-bit rotation |
none |
R_ARM_SBREL32 uses the same handler as R_ARM_REL32 but produces a segment-base-relative offset. R_ARM_PREL31 is used in ARM exception table entries.
2.5. Branch Relocations
ARM branch instructions encode a signed byte offset divided by 4 into a 24-bit field, giving a range of ±32 MB. Thumb branch instructions encode the offset divided by 2.
2.5.1. ARM Branch
Relocation |
Expression |
Instruction |
Encoded bits |
Range |
|---|---|---|---|---|
|
|
|
24-bit signed ( |
±32 MB |
|
|
|
24-bit signed ( |
±32 MB |
|
|
|
24-bit signed ( |
±32 MB |
|
|
|
24-bit signed ( |
±32 MB |
2.5.2. Thumb Branch
Relocation |
Expression |
Instruction |
Encoded bits |
Range |
|---|---|---|---|---|
|
|
|
8-bit signed ( |
±256 B |
|
|
|
11-bit signed ( |
±2 KB |
|
|
|
21-bit signed ( |
±1 MB |
|
|
|
25-bit signed ( |
±16 MB |
|
|
|
25-bit signed ( |
±16 MB |
2.6. GOT-Relative Relocations
Relocation |
Expression |
Bits |
Range Check |
|---|---|---|---|
|
|
32 |
none |
|
|
12 |
none |
|
|
32 |
none |
2.7. TLS Relocations
Relocation |
Expression |
Notes |
|---|---|---|
|
|
General Dynamic — two-GOT-entry sequence |
|
|
Local Dynamic — module GOT entry |
|
|
Initial Exec — GOT-indirect TP offset |
|
|
Local Dynamic offset within TLS block |
|
|
Local Exec — TP-relative offset, static only |
2.8. Unsupported Relocations
The table below lists every relocation that ELD’s ARM backend maps to the unsupport handler. Applying any of these to a non-dynamic object causes a link error. Relocations not listed here are handled. The ABI category column records how the ARM ELF ABI classifies the entry, and the TODO column records cases where the linker could reasonably add support.
Type |
Relocation |
ABI category |
TODO |
|---|---|---|---|
4 |
|
Group reloc — literal-pool PC-relative LDR (G0) |
Implement LDR PC-group G0 |
5 |
|
16-bit absolute |
Implement 16-bit absolute |
6 |
|
12-bit absolute (LDR/STR immediate) |
Implement ABS12 |
7 |
|
Thumb 5-bit absolute (LDR/STR) |
|
8 |
|
8-bit absolute |
|
11 |
|
Thumb 8-bit PC-relative (LDR literal) |
|
12 |
|
Dynamic — adjustment for R_ARM_TLS_DESC |
Dynamic only |
13 |
|
Dynamic TLS descriptor |
Dynamic only |
14 |
|
Obsolete |
|
15 |
|
Obsolete |
|
16 |
|
Obsolete |
|
17 |
|
Dynamic TLS module index |
Dynamic only |
18 |
|
Dynamic TLS symbol offset |
Dynamic only |
19 |
|
Dynamic TLS thread-pointer offset |
Dynamic only |
20 |
|
Dynamic copy reloc |
Dynamic only |
21 |
|
Dynamic GOT fill |
Dynamic only |
22 |
|
Dynamic PLT fill |
Dynamic only |
23 |
|
Dynamic base-relative |
Dynamic only |
31 |
|
Segment-base absolute |
|
32 |
|
Deprecated (ELF B02 name for ALU_PC_G0_NC) |
|
33 |
|
Deprecated (ELF B02 name for ALU_PC_G1_NC) |
|
34 |
|
Deprecated (ELF B02 name for ALU_PC_G2) |
|
35 |
|
Deprecated section-base relative LDR |
|
36 |
|
Deprecated section-base relative ALU |
|
37 |
|
Deprecated section-base relative ALU |
|
39 |
|
Deprecated 31-bit section-relative |
|
52 |
|
Thumb 6-bit branch (CBZ/CBNZ) |
|
53 |
|
Thumb-2 ALU PC-relative 11:0 |
|
54 |
|
Thumb-2 LDR/STR 12-bit PC-relative |
|
55 |
|
32-bit absolute, no interworking bit |
|
56 |
|
32-bit PC-relative, no interworking bit |
|
57 |
|
Group reloc — ALU PC-relative G0, no overflow |
Implement ALU_PC_G group |
59 |
|
Group reloc — ALU PC-relative G1, no overflow |
Implement ALU_PC_G group |
60 |
|
Group reloc — ALU PC-relative G1 |
Implement ALU_PC_G group |
61 |
|
Group reloc — ALU PC-relative G2 |
Implement ALU_PC_G group |
62 |
|
Group reloc — LDR PC-relative G1 |
Implement LDR PC-group |
63 |
|
Group reloc — LDR PC-relative G2 |
Implement LDR PC-group |
64 |
|
Group reloc — LDRD/STRD PC-relative G0 |
Implement LDRS PC-group |
65 |
|
Group reloc — LDRD/STRD PC-relative G1 |
Implement LDRS PC-group |
66 |
|
Group reloc — LDRD/STRD PC-relative G2 |
Implement LDRS PC-group |
67 |
|
Group reloc — LDC/STC PC-relative G0 |
|
68 |
|
Group reloc — LDC/STC PC-relative G1 |
|
69 |
|
Group reloc — LDC/STC PC-relative G2 |
|
70 |
|
Group reloc — ALU section-base G0, no overflow |
Implement ALU_SB_G group |
71 |
|
Group reloc — ALU section-base G0 |
Implement ALU_SB_G group |
72 |
|
Group reloc — ALU section-base G1, no overflow |
Implement ALU_SB_G group |
73 |
|
Group reloc — ALU section-base G1 |
Implement ALU_SB_G group |
74 |
|
Group reloc — ALU section-base G2 |
Implement ALU_SB_G group |
75 |
|
Group reloc — LDR section-base G0 |
Implement LDR_SB_G group |
76 |
|
Group reloc — LDR section-base G1 |
Implement LDR_SB_G group |
77 |
|
Group reloc — LDR section-base G2 |
Implement LDR_SB_G group |
78 |
|
Group reloc — LDRD/STRD section-base G0 |
|
79 |
|
Group reloc — LDRD/STRD section-base G1 |
|
80 |
|
Group reloc — LDRD/STRD section-base G2 |
|
81 |
|
Group reloc — LDC/STC section-base G0 |
|
82 |
|
Group reloc — LDC/STC section-base G1 |
|
83 |
|
Group reloc — LDC/STC section-base G2 |
|
84 |
|
MOVW section-base relative, no overflow |
Implement MOVW/T_BREL (ARM) |
85 |
|
MOVT section-base relative |
Implement MOVW/T_BREL (ARM) |
86 |
|
MOVW section-base relative |
Implement MOVW/T_BREL (ARM) |
90 |
|
TLS descriptor GOT entry |
TLS-desc relaxation not implemented |
91 |
|
TLS descriptor call (ARM) |
TLS-desc relaxation not implemented |
92 |
|
TLS descriptor sequence (ARM) |
TLS-desc relaxation not implemented |
93 |
|
TLS descriptor call (Thumb) |
TLS-desc relaxation not implemented |
94 |
|
PLT entry absolute address |
|
95 |
|
GOT entry absolute address |
|
97 |
|
12-bit GOT-relative (LDR/STR) |
|
98 |
|
12-bit GOT-offset |
|
99 |
|
Linker relaxation hint |
|
100 |
|
Deprecated GNU C++ vtable entry |
|
101 |
|
Deprecated GNU C++ vtable inheritance |
|
109 |
|
TLS LDO 12-bit offset (LDR/STR) |
|
110 |
|
TLS LE 12-bit TP offset |
|
111 |
|
TLS IE 12-bit GOT-relative |
|
112–127 |
|
Reserved for private use — not allocatable in portable objects |
|
128 |
|
Obsolete |
|
129 |
|
Thumb-1 TLS descriptor sequence |
TLS-desc relaxation not implemented |
130 |
|
Thumb-2 TLS descriptor sequence |
TLS-desc relaxation not implemented |
2.9. ABI Comparison Notes
The following discrepancies or gaps were found when comparing this document against the AAELF32 ABI specification:
Area |
Observed difference |
TODO |
|---|---|---|
|
Document shows |
Fix expression in TLS table to |
|
Document shows |
Clarify as |
|
Documented as identical to |
Fix expression to |
|
Documented in this file and handled by ELD via the |
Add a note that only G0 is supported |
Thumb branch ranges |
|
Add conditional range note matching the Thumb Branch table in the Veneers section |
|
Handled by ELD (as |
Add |
Deprecated relocations |
|
Add deprecation note |
2.10. Veneers (Branch Island Stubs)
When a branch target is out of range or requires an ISA mode switch (ARM ↔ Thumb), the linker inserts a small stub function called a veneer (also called a trampoline or branch island). The veneer is placed near the caller and jumps to the real target using an absolute or PC-relative address.
2.10.1. When the Linker Creates Veneers
Caller ISA |
Target ISA |
Relocation |
Condition |
Notes |
|---|---|---|---|---|
ARM |
ARM |
|
Target out of ±32 MB range |
No stub if target is Thumb (T bit = 1) |
ARM |
Thumb |
|
Always — instruction cannot be rewritten as |
|
ARM |
Thumb |
|
Target out of ±32 MB range |
|
Thumb |
ARM |
|
Always — Thumb |
Disabled on microcontroller profiles (Cortex-M0) |
Thumb |
ARM |
|
Target out of ±4 MB range (23-bit) or ±16 MB range (25-bit J1J2) |
Disabled on microcontroller profiles |
Thumb |
Thumb |
|
Target out of ±4 MB range (23-bit) or ±16 MB range (25-bit J1J2) |
No stub if target is ARM (T bit = 0) |
Microcontroller profile: For CPUs such as Cortex-M0 (and those with the M-profile CPU attribute), Thumb→ARM veneers are never created — the ARM ISA is not available. Thumb→Thumb veneers still use Thumb-1-safe templates (see below).
J1J2 encoding: Thumb-2 processors with J1J2 branch encoding support a 25-bit offset (±16 MB); older Thumb-2 without J1J2 use a 23-bit offset (±4 MB). The linker detects this from the object attribute section.
2.10.2. Branch Range Reference
Caller ISA |
Branch encoding |
Range |
|---|---|---|
ARM |
24-bit signed |
±32 MB |
Thumb |
23-bit signed |
±4 MB |
Thumb (J1J2) |
25-bit signed |
±16 MB |
2.10.3. Veneer Templates
The linker selects a veneer template based on link-time options and the target CPU profile:
Template |
Selected when |
Instruction sequence |
|---|---|---|
ABS (default) |
Static link, no |
|
PIC |
Position-independent code ( |
|
MOV |
|
|
THUMB1 |
Microcontroller without MOVT/MOVW |
|
For Thumb→Thumb veneers the stub body first switches to ARM mode (bx pc; nop) before executing the ABS or PIC template, because the ARM load/branch sequence is simpler. The MOV and THUMB1 templates operate entirely in Thumb-2 or Thumb-1 respectively and therefore do not need that prefix.
2.11. References
ELF for the Arm Architecture (AAELF32)
https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst