Skip to main content

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}