object_lang/reloc.rs
1//! Relocations: places in a section that a linker must patch with an address once it
2//! is known.
3
4use crate::model::{Architecture, SectionId, SymbolId};
5
6/// An x86-64 relocation. Each variant names its ELF counterpart; in the formulas, `S` is
7/// the target's address, `A` the addend, `P` the address of the patched bytes, `L` the
8/// address of the target's PLT entry (or the target itself in a static link), and `G`
9/// the address of the target's GOT entry.
10///
11/// # Examples
12///
13/// ```
14/// use object_lang::{RelocationKind, X86_64Reloc};
15///
16/// let kind = RelocationKind::from(X86_64Reloc::Pc32);
17/// assert_eq!(kind.size(), 4);
18/// ```
19#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
20#[non_exhaustive]
21pub enum X86_64Reloc {
22 /// `S + A`, 64 bits (`R_X86_64_64`).
23 Abs64,
24 /// `S + A`, 32 bits, zero-extended: must fit in `u32` (`R_X86_64_32`).
25 Abs32,
26 /// `S + A`, 32 bits, sign-extended: must fit in `i32` (`R_X86_64_32S`).
27 Abs32Signed,
28 /// `S + A - P`, 32 bits (`R_X86_64_PC32`).
29 Pc32,
30 /// `S + A - P`, 64 bits (`R_X86_64_PC64`).
31 Pc64,
32 /// `L + A - P`, 32 bits: a call or jump to a function (`R_X86_64_PLT32`).
33 Plt32,
34 /// `G + A - P`, 32 bits: a load of the target's GOT entry (`R_X86_64_GOTPCREL`).
35 GotPcRel,
36 /// As [`GotPcRel`](X86_64Reloc::GotPcRel), and the linker may relax the instruction
37 /// to avoid the GOT (`R_X86_64_GOTPCRELX`).
38 GotPcRelX,
39 /// As [`GotPcRelX`](X86_64Reloc::GotPcRelX) for an instruction with a REX prefix
40 /// (`R_X86_64_REX_GOTPCRELX`).
41 RexGotPcRelX,
42}
43
44/// An AArch64 relocation. Each variant names its ELF counterpart; in the formulas, `S`
45/// is the target's address, `A` the addend, `P` the address of the patched bytes,
46/// `Page(x)` is `x` with its low 12 bits cleared, and `G` the address of the target's
47/// GOT entry. Instruction relocations patch an immediate field of a 4-byte instruction,
48/// so their offset must be a multiple of 4.
49///
50/// # Examples
51///
52/// ```
53/// use object_lang::{Aarch64Reloc, RelocationKind};
54///
55/// assert!(RelocationKind::from(Aarch64Reloc::Call26).is_instruction());
56/// assert!(!RelocationKind::from(Aarch64Reloc::Abs64).is_instruction());
57/// ```
58#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
59#[non_exhaustive]
60pub enum Aarch64Reloc {
61 /// `S + A`, 64 bits of data (`R_AARCH64_ABS64`).
62 Abs64,
63 /// `S + A`, 32 bits of data (`R_AARCH64_ABS32`).
64 Abs32,
65 /// `S + A - P`, 64 bits of data (`R_AARCH64_PREL64`).
66 Prel64,
67 /// `S + A - P`, 32 bits of data (`R_AARCH64_PREL32`).
68 Prel32,
69 /// `S + A - P` into a `BL` (`R_AARCH64_CALL26`): ±128 MiB.
70 Call26,
71 /// `S + A - P` into a `B` (`R_AARCH64_JUMP26`): ±128 MiB.
72 Jump26,
73 /// `S + A - P` into a `B.cond`, `CBZ`, or `CBNZ` (`R_AARCH64_CONDBR19`): ±1 MiB.
74 CondBr19,
75 /// `S + A - P` into a `TBZ` or `TBNZ` (`R_AARCH64_TSTBR14`): ±32 KiB.
76 TstBr14,
77 /// `S + A - P` into an `ADR` (`R_AARCH64_ADR_PREL_LO21`): ±1 MiB.
78 AdrPrelLo21,
79 /// `Page(S + A) - Page(P)` into an `ADRP` (`R_AARCH64_ADR_PREL_PG_HI21`): ±4 GiB.
80 AdrPrelPgHi21,
81 /// Low 12 bits of `S + A` into an `ADD` immediate (`R_AARCH64_ADD_ABS_LO12_NC`).
82 AddAbsLo12Nc,
83 /// Low 12 bits of `S + A` into an 8-bit `LDR`/`STR` offset
84 /// (`R_AARCH64_LDST8_ABS_LO12_NC`).
85 Ldst8AbsLo12Nc,
86 /// Bits 11:1 of `S + A` into a 16-bit `LDR`/`STR` offset
87 /// (`R_AARCH64_LDST16_ABS_LO12_NC`).
88 Ldst16AbsLo12Nc,
89 /// Bits 11:2 of `S + A` into a 32-bit `LDR`/`STR` offset
90 /// (`R_AARCH64_LDST32_ABS_LO12_NC`).
91 Ldst32AbsLo12Nc,
92 /// Bits 11:3 of `S + A` into a 64-bit `LDR`/`STR` offset
93 /// (`R_AARCH64_LDST64_ABS_LO12_NC`).
94 Ldst64AbsLo12Nc,
95 /// Bits 11:4 of `S + A` into a 128-bit `LDR`/`STR` offset
96 /// (`R_AARCH64_LDST128_ABS_LO12_NC`).
97 Ldst128AbsLo12Nc,
98 /// `Page(G) - Page(P)` into an `ADRP` (`R_AARCH64_ADR_GOT_PAGE`).
99 AdrGotPage,
100 /// Bits 11:3 of `G` into an `LDR` (`R_AARCH64_LD64_GOT_LO12_NC`).
101 Ld64GotLo12Nc,
102}
103
104/// How a relocation patches its bytes: an architecture-specific kind.
105///
106/// Kinds are named for what they compute, not for one format's numbering, so each
107/// writer maps them to its own relocation types. Build one from the architecture enum
108/// with `From`.
109///
110/// # Examples
111///
112/// ```
113/// use object_lang::{Aarch64Reloc, Architecture, RelocationKind, X86_64Reloc};
114///
115/// let x = RelocationKind::from(X86_64Reloc::Abs64);
116/// let a = RelocationKind::from(Aarch64Reloc::AdrPrelPgHi21);
117/// assert_eq!(x.architecture(), Architecture::X86_64);
118/// assert_eq!(a.architecture(), Architecture::Aarch64);
119/// ```
120#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
121#[non_exhaustive]
122pub enum RelocationKind {
123 /// An x86-64 relocation.
124 X86_64(X86_64Reloc),
125 /// An AArch64 relocation.
126 Aarch64(Aarch64Reloc),
127}
128
129impl RelocationKind {
130 /// The architecture this kind belongs to. An object may only carry relocations of
131 /// its own architecture.
132 ///
133 /// # Examples
134 ///
135 /// ```
136 /// use object_lang::{Architecture, RelocationKind, X86_64Reloc};
137 ///
138 /// assert_eq!(RelocationKind::from(X86_64Reloc::Plt32).architecture(), Architecture::X86_64);
139 /// ```
140 #[must_use]
141 pub const fn architecture(self) -> Architecture {
142 match self {
143 RelocationKind::X86_64(_) => Architecture::X86_64,
144 RelocationKind::Aarch64(_) => Architecture::Aarch64,
145 }
146 }
147
148 /// The number of bytes the relocation patches, starting at its offset.
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// use object_lang::{Aarch64Reloc, RelocationKind, X86_64Reloc};
154 ///
155 /// assert_eq!(RelocationKind::from(X86_64Reloc::Abs64).size(), 8);
156 /// assert_eq!(RelocationKind::from(X86_64Reloc::Plt32).size(), 4);
157 /// assert_eq!(RelocationKind::from(Aarch64Reloc::Call26).size(), 4);
158 /// ```
159 #[must_use]
160 pub const fn size(self) -> u64 {
161 match self {
162 RelocationKind::X86_64(X86_64Reloc::Abs64 | X86_64Reloc::Pc64)
163 | RelocationKind::Aarch64(Aarch64Reloc::Abs64 | Aarch64Reloc::Prel64) => 8,
164 _ => 4,
165 }
166 }
167
168 /// Whether the relocation patches an instruction's immediate field rather than a
169 /// plain data word. AArch64 instructions are 4-byte aligned, so such a relocation's
170 /// offset must be a multiple of 4.
171 ///
172 /// # Examples
173 ///
174 /// ```
175 /// use object_lang::{Aarch64Reloc, RelocationKind, X86_64Reloc};
176 ///
177 /// assert!(RelocationKind::from(Aarch64Reloc::AddAbsLo12Nc).is_instruction());
178 /// assert!(!RelocationKind::from(Aarch64Reloc::Prel32).is_instruction());
179 /// assert!(!RelocationKind::from(X86_64Reloc::Pc32).is_instruction());
180 /// ```
181 #[must_use]
182 pub const fn is_instruction(self) -> bool {
183 match self {
184 RelocationKind::X86_64(_) => false,
185 RelocationKind::Aarch64(kind) => !matches!(
186 kind,
187 Aarch64Reloc::Abs64
188 | Aarch64Reloc::Abs32
189 | Aarch64Reloc::Prel64
190 | Aarch64Reloc::Prel32
191 ),
192 }
193 }
194}
195
196impl From<X86_64Reloc> for RelocationKind {
197 fn from(kind: X86_64Reloc) -> Self {
198 RelocationKind::X86_64(kind)
199 }
200}
201
202impl From<Aarch64Reloc> for RelocationKind {
203 fn from(kind: Aarch64Reloc) -> Self {
204 RelocationKind::Aarch64(kind)
205 }
206}
207
208/// What a relocation's address comes from.
209///
210/// # Examples
211///
212/// ```
213/// use object_lang::{Architecture, Object, RelocationTarget, Section, SectionKind};
214///
215/// let mut obj = Object::relocatable(Architecture::X86_64);
216/// let rodata = obj.add_section(Section::new(".rodata", SectionKind::ReadOnlyData));
217/// // An address inside .rodata, without a named symbol for it.
218/// let target = RelocationTarget::Section(rodata);
219/// assert_eq!(target, RelocationTarget::Section(rodata));
220/// ```
221#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
222#[non_exhaustive]
223pub enum RelocationTarget {
224 /// The address of a symbol of the object.
225 Symbol(SymbolId),
226 /// The start address of a section of the object; the addend picks the place
227 /// inside it. This is how compilers refer to unnamed data such as string literals.
228 Section(SectionId),
229}
230
231/// A place in a section to patch: the bytes at `offset` receive the value `kind`
232/// computes from the address of `target` plus `addend`.
233///
234/// The addend is always explicit, whatever format the relocation is written in.
235///
236/// # Examples
237///
238/// ```
239/// use object_lang::{Relocation, RelocationKind, RelocationTarget, SymbolId, X86_64Reloc};
240/// # use object_lang::{Architecture, Object, Symbol};
241/// # let mut obj = Object::relocatable(Architecture::X86_64);
242/// # let target = obj.add_symbol(Symbol::undefined("callee"));
243///
244/// // `call callee` is e8 followed by a 32-bit displacement from the next instruction.
245/// let reloc = Relocation::new(1, X86_64Reloc::Plt32, RelocationTarget::Symbol(target), -4);
246/// assert_eq!(reloc.kind, RelocationKind::X86_64(X86_64Reloc::Plt32));
247/// assert_eq!(reloc.addend, -4);
248/// ```
249#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
250#[non_exhaustive]
251pub struct Relocation {
252 /// Where the patched bytes start, as an offset within the section.
253 pub offset: u64,
254 /// How the bytes are computed and how many are patched.
255 pub kind: RelocationKind,
256 /// The symbol or section whose address is used.
257 pub target: RelocationTarget,
258 /// A constant added to the target's address.
259 pub addend: i64,
260}
261
262impl Relocation {
263 /// A relocation at `offset` of `kind`, against `target`, with `addend`.
264 ///
265 /// # Examples
266 ///
267 /// ```
268 /// use object_lang::{Aarch64Reloc, Relocation, RelocationTarget};
269 /// # use object_lang::{Architecture, Object, Section, SectionKind};
270 /// # let mut obj = Object::relocatable(Architecture::Aarch64);
271 /// # let data = obj.add_section(Section::new(".data", SectionKind::Data));
272 ///
273 /// let r = Relocation::new(8, Aarch64Reloc::Abs64, RelocationTarget::Section(data), 16);
274 /// assert_eq!((r.offset, r.addend), (8, 16));
275 /// ```
276 #[must_use]
277 pub fn new(
278 offset: u64,
279 kind: impl Into<RelocationKind>,
280 target: RelocationTarget,
281 addend: i64,
282 ) -> Self {
283 Relocation {
284 offset,
285 kind: kind.into(),
286 target,
287 addend,
288 }
289 }
290}