wasmer_compiler/types/
relocation.rs

1/*
2 * ! Remove me once rkyv generates doc-comments for fields or generates an #[allow(missing_docs)]
3 * on their own.
4 */
5#![allow(missing_docs)]
6
7//! Relocation is the process of assigning load addresses for position-dependent
8//! code and data of a program and adjusting the code and data to reflect the
9//! assigned addresses.
10//!
11//! [Learn more](https://en.wikipedia.org/wiki/Relocation_(computing)).
12//!
13//! Each time a `Compiler` compiles a WebAssembly function (into machine code),
14//! it also attaches if there are any relocations that need to be patched into
15//! the generated machine code, so a given frontend (JIT or native) can
16//! do the corresponding work to run it.
17
18use super::section::SectionIndex;
19use crate::{Addend, CodeOffset};
20use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
21#[cfg(feature = "enable-serde")]
22use serde::{Deserialize, Serialize};
23use wasmer_types::{FunctionIndex, LibCall, LocalFunctionIndex, entity::PrimaryMap};
24
25/// Relocation kinds for every ISA.
26#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
27#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
28#[derive(RkyvSerialize, RkyvDeserialize, Archive, Copy, Clone, Debug, PartialEq, Eq)]
29#[rkyv(derive(Debug), compare(PartialEq))]
30#[repr(u8)]
31pub enum RelocationKind {
32    /// absolute 4-byte
33    Abs4,
34    /// absolute 8-byte
35    Abs8,
36
37    /// PC-relative 4-byte
38    PCRel4,
39    /// PC-relative 8-byte
40    PCRel8,
41
42    /// x86 call to PC-relative 4-byte
43    X86CallPCRel4,
44    /// x86 call to PLT-relative 4-byte
45    X86CallPLTRel4,
46    /// x86 GOT PC-relative 4-byte
47    X86GOTPCRel4,
48
49    /// R_AARCH64_ADR_PREL_LO21
50    Aarch64AdrPrelLo21,
51
52    /// R_AARCH64_ADR_PREL_PG_HI21
53    Aarch64AdrPrelPgHi21,
54
55    /// R_AARCH64_ADD_ABS_LO12_NC
56    Aarch64AddAbsLo12Nc,
57
58    /// R_AARCH64_LDST128_ABS_LO12_NC
59    Aarch64Ldst128AbsLo12Nc,
60
61    /// R_AARCH64_LDST64_ABS_LO12_NC
62    Aarch64Ldst64AbsLo12Nc,
63
64    /// Arm32 call target
65    Arm32Call,
66    /// Arm64 call target
67    Arm64Call,
68    /// Arm64 movk/z part 0
69    Arm64Movw0,
70    /// Arm64 movk/z part 1
71    Arm64Movw1,
72    /// Arm64 movk/z part 2
73    Arm64Movw2,
74    /// Arm64 movk/z part 3
75    Arm64Movw3,
76    /// RISC-V PC-relative high 20bit
77    RiscvPCRelHi20,
78    /// RISC-V PC-relative low 12bit, I-type
79    RiscvPCRelLo12I,
80    /// RISC-V call target
81    RiscvCall,
82    /// LoongArch absolute high 20bit
83    LArchAbsHi20,
84    /// LoongArch absolute low 12bit
85    LArchAbsLo12,
86    /// LoongArch absolute high 12bit
87    LArchAbs64Hi12,
88    /// LoongArch absolute low 20bit
89    LArchAbs64Lo20,
90    /// LoongArch PC-relative call 38bit
91    LArchCall36,
92    /// LoongArch PC-relative high 20bit
93    LArchPCAlaHi20,
94    /// LoongArch PC-relative low 12bit
95    LArchPCAlaLo12,
96    /// LoongArch PC64-relative high 12bit
97    LArchPCAla64Hi12,
98    /// LoongArch PC64-relative low 20bit
99    LArchPCAla64Lo20,
100    /// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol.
101    ElfX86_64TlsGd,
102    // /// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry.
103    // MachOX86_64Tlv,
104
105    // -- Mach-O-specific relocations
106    //
107    // --- Arm64
108    // (MACHO_ARM64_RELOC_UNSIGNED) for pointers
109    MachoArm64RelocUnsigned,
110    // (MACHO_ARM64_RELOC_SUBTRACTOR) must be followed by a ARM64_RELOC_UNSIGNED
111    MachoArm64RelocSubtractor,
112    // (MACHO_ARM64_RELOC_BRANCH26) a B/BL instruction with 26-bit displacement
113    MachoArm64RelocBranch26,
114    // (MACHO_ARM64_RELOC_PAGE21) pc-rel distance to page of target
115    MachoArm64RelocPage21,
116    // (MACHO_ARM64_RELOC_PAGEOFF12) offset within page, scaled by r_length
117    MachoArm64RelocPageoff12,
118    // (MACHO_ARM64_RELOC_GOT_LOAD_PAGE21) pc-rel distance to page of GOT slot
119    MachoArm64RelocGotLoadPage21,
120    // (MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12) offset within page of GOT slot, scaled by r_length
121    MachoArm64RelocGotLoadPageoff12,
122    // (MACHO_ARM64_RELOC_POINTER_TO_GOT) for pointers to GOT slots
123    MachoArm64RelocPointerToGot,
124    // (MACHO_ARM64_RELOC_TLVP_LOAD_PAGE21) pc-rel distance to page of TLVP slot
125    MachoArm64RelocTlvpLoadPage21,
126    // (MACHO_ARM64_RELOC_TLVP_LOAD_PAGEOFF12) offset within page of TLVP slot, scaled by r_length
127    MachoArm64RelocTlvpLoadPageoff12,
128    // (MACHO_ARM64_RELOC_ADDEND) must be followed by PAGE21 or PAGEOFF12
129    MachoArm64RelocAddend,
130
131    // --- X86_64
132    // (MACHO_X86_64_RELOC_UNSIGNED) for absolute addresses
133    MachoX86_64RelocUnsigned,
134    // (MACHO_X86_64_RELOC_SIGNED) for signed 32-bit displacement
135    MachoX86_64RelocSigned,
136    // (MACHO_X86_64_RELOC_BRANCH) a CALL/JMP instruction with 32-bit displacement
137    MachoX86_64RelocBranch,
138    // (MACHO_X86_64_RELOC_GOT_LOAD) a MOVQ load of a GOT entry
139    MachoX86_64RelocGotLoad,
140    // (MACHO_X86_64_RELOC_GOT) other GOT references
141    MachoX86_64RelocGot,
142    // (MACHO_X86_64_RELOC_SUBTRACTOR) must be followed by a X86_64_RELOC_UNSIGNED
143    MachoX86_64RelocSubtractor,
144    // (MACHO_X86_64_RELOC_SIGNED_1) for signed 32-bit displacement with a -1 addend
145    MachoX86_64RelocSigned1,
146    // (MACHO_X86_64_RELOC_SIGNED_2) for signed 32-bit displacement with a -2 addend
147    MachoX86_64RelocSigned2,
148    // (MACHO_X86_64_RELOC_SIGNED_4) for signed 32-bit displacement with a -4 addend
149    MachoX86_64RelocSigned4,
150    // (MACHO_X86_64_RELOC_TLV) for thread local variables
151    MachoX86_64RelocTlv,
152
153    // TODO: sort the items when we bump the rkyv version
154    /// absolute 6 bits
155    Abs6Bits,
156    /// absolute 1-byte
157    Abs,
158    /// absolute 2-byte
159    Abs2,
160
161    /// addition at the place of the relocation (1-byte)
162    Add,
163    /// addition at the place of the relocation (2-bytes)
164    Add2,
165    /// addition at the place of the relocation (4-bytes)
166    Add4,
167    /// addition at the place of the relocation (8-bytes)
168    Add8,
169
170    /// subtraction at the place of the relocation (6 bits)
171    Sub6Bits,
172    /// subtraction at the place of the relocation (1-byte)
173    Sub,
174    /// subtraction at the place of the relocation (2-bytes)
175    Sub2,
176    /// subtraction at the place of the relocation (4-bytes)
177    Sub4,
178    /// subtraction at the place of the relocation (8-bytes)
179    Sub8,
180}
181
182impl RelocationKind {
183    pub fn needs_got(&self) -> bool {
184        matches!(
185            self,
186            Self::MachoArm64RelocGotLoadPage21
187                | Self::MachoArm64RelocGotLoadPageoff12
188                | Self::MachoArm64RelocPointerToGot
189                | Self::MachoX86_64RelocGotLoad
190                | Self::MachoX86_64RelocGot
191        )
192    }
193}
194
195/// A record of a relocation to perform.
196#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
197#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
198#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
199#[rkyv(derive(Debug), compare(PartialEq))]
200pub struct Relocation {
201    /// The relocation kind.
202    pub kind: RelocationKind,
203    /// Relocation target.
204    pub reloc_target: RelocationTarget,
205    /// The offset where to apply the relocation.
206    pub offset: CodeOffset,
207    /// The addend to add to the relocation value.
208    pub addend: Addend,
209}
210
211/// Any struct that acts like a `Relocation`.
212#[allow(missing_docs)]
213pub trait RelocationLike {
214    fn kind(&self) -> RelocationKind;
215    fn reloc_target(&self) -> RelocationTarget;
216    fn offset(&self) -> CodeOffset;
217    fn addend(&self) -> Addend;
218
219    /// Given a function start address, provide the relocation relative
220    /// to that address.
221    ///
222    /// The function returns the relocation address and the delta.
223    ///
224    // # Nomenclature (from [1]@5.7.3.3)
225    //
226    // * S (when used on its own) is the address of the symbol.
227    // * A is the addend for the relocation.
228    // * P is the address of the place being relocated (derived from r_offset).
229    // * X is the result of a relocation operation, before any masking or bit-selection operation is applied
230    // * Page(expr) is the page address of the expression expr, defined as (expr & ~0xFFF). (This applies even if the machine page size supported by the platform has a different value.)
231    //
232    // [1]: https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
233    fn for_address(&self, start: usize, target_func_address: u64) -> (usize, u64) {
234        match self.kind() {
235            RelocationKind::Abs6Bits
236            | RelocationKind::Abs
237            | RelocationKind::Abs2
238            | RelocationKind::Abs4
239            | RelocationKind::Abs8
240            | RelocationKind::Arm64Movw0
241            | RelocationKind::Arm64Movw1
242            | RelocationKind::Arm64Movw2
243            | RelocationKind::Arm64Movw3
244            | RelocationKind::RiscvPCRelLo12I
245            | RelocationKind::Aarch64Ldst128AbsLo12Nc
246            | RelocationKind::Aarch64Ldst64AbsLo12Nc
247            | RelocationKind::MachoArm64RelocUnsigned
248            | RelocationKind::MachoX86_64RelocUnsigned
249            | RelocationKind::MachoArm64RelocSubtractor
250            | RelocationKind::MachoX86_64RelocSubtractor
251            | RelocationKind::LArchAbsHi20
252            | RelocationKind::LArchAbsLo12
253            | RelocationKind::LArchAbs64Lo20
254            | RelocationKind::LArchAbs64Hi12
255            | RelocationKind::LArchPCAlaLo12
256            | RelocationKind::Add
257            | RelocationKind::Add2
258            | RelocationKind::Add4
259            | RelocationKind::Add8
260            | RelocationKind::Sub6Bits
261            | RelocationKind::Sub
262            | RelocationKind::Sub2
263            | RelocationKind::Sub4
264            | RelocationKind::Sub8 => {
265                let reloc_address = start + self.offset() as usize;
266                let reloc_addend = self.addend() as isize;
267                let reloc_abs = target_func_address
268                    .checked_add(reloc_addend as u64)
269                    .unwrap();
270                (reloc_address, reloc_abs)
271            }
272            RelocationKind::PCRel4 => {
273                let reloc_address = start + self.offset() as usize;
274                let reloc_addend = self.addend() as isize;
275                let reloc_delta_u32 = (target_func_address as u32)
276                    .wrapping_sub(reloc_address as u32)
277                    .checked_add(reloc_addend as u32)
278                    .unwrap();
279                (reloc_address, reloc_delta_u32 as u64)
280            }
281            RelocationKind::PCRel8 => {
282                let reloc_address = start + self.offset() as usize;
283                let reloc_addend = self.addend() as isize;
284                let reloc_delta = target_func_address
285                    .wrapping_sub(reloc_address as u64)
286                    .checked_add(reloc_addend as u64)
287                    .unwrap();
288                (reloc_address, reloc_delta)
289            }
290            RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
291                let reloc_address = start + self.offset() as usize;
292                let reloc_addend = self.addend() as isize;
293                let reloc_delta_u32 = (target_func_address as u32)
294                    .wrapping_sub(reloc_address as u32)
295                    .wrapping_add(reloc_addend as u32);
296                (reloc_address, reloc_delta_u32 as u64)
297            }
298            RelocationKind::Aarch64AdrPrelLo21 => {
299                let s = target_func_address;
300                let p = start + self.offset() as usize;
301                let a = self.addend() as u64;
302
303                (p, s.wrapping_add(a).wrapping_sub(p as u64))
304            }
305
306            RelocationKind::Aarch64AddAbsLo12Nc => {
307                let s = target_func_address;
308                let p = start + self.offset() as usize;
309                let a = self.addend() as u64;
310
311                (p, s.wrapping_add(a))
312            }
313            RelocationKind::Arm64Call
314            | RelocationKind::RiscvCall
315            | RelocationKind::RiscvPCRelHi20 => {
316                let reloc_address = start + self.offset() as usize;
317                let reloc_addend = self.addend() as isize;
318                let reloc_delta_u32 = target_func_address
319                    .wrapping_sub(reloc_address as u64)
320                    .wrapping_add(reloc_addend as u64);
321                (reloc_address, reloc_delta_u32)
322            }
323            RelocationKind::Aarch64AdrPrelPgHi21
324            | RelocationKind::MachoArm64RelocGotLoadPage21
325            | RelocationKind::MachoArm64RelocPage21 => {
326                let reloc_address = start + self.offset() as usize;
327                let reloc_addend = self.addend() as isize;
328                let target_page =
329                    (target_func_address.wrapping_add(reloc_addend as u64) & !(0xFFF)) as usize;
330                let pc_page = reloc_address & !(0xFFF);
331                (reloc_address, target_page.wrapping_sub(pc_page) as u64)
332            }
333            RelocationKind::MachoArm64RelocGotLoadPageoff12
334            | RelocationKind::MachoArm64RelocPageoff12 => {
335                let reloc_address = start + self.offset() as usize;
336                let reloc_addend = self.addend() as isize;
337                let target_offset =
338                    (target_func_address.wrapping_add(reloc_addend as u64) & (0xFFF)) as usize;
339                (reloc_address, target_offset as u64)
340            }
341            RelocationKind::LArchCall36 => {
342                let reloc_address = start + self.offset() as usize;
343                let reloc_addend = self.addend() as isize;
344                let reloc_delta = target_func_address
345                    .wrapping_sub(reloc_address as u64)
346                    .wrapping_add(reloc_addend as u64);
347                (
348                    reloc_address,
349                    reloc_delta.wrapping_add((reloc_delta & 0x20000) << 1),
350                )
351            }
352            RelocationKind::LArchPCAlaHi20 => {
353                let reloc_address = start + self.offset() as usize;
354                let reloc_addend = self.addend() as isize;
355                let target_page = (target_func_address
356                    .wrapping_add(reloc_addend as u64)
357                    .wrapping_add(0x800)
358                    & !(0xFFF)) as usize;
359                let pc_page = reloc_address & !(0xFFF);
360                (reloc_address, target_page.wrapping_sub(pc_page) as u64)
361            }
362            RelocationKind::LArchPCAla64Hi12 | RelocationKind::LArchPCAla64Lo20 => {
363                let reloc_address = start + self.offset() as usize;
364                let reloc_addend = self.addend() as isize;
365                let reloc_offset = match self.kind() {
366                    RelocationKind::LArchPCAla64Lo20 => 8,
367                    RelocationKind::LArchPCAla64Hi12 => 12,
368                    _ => 0,
369                };
370                let target_func_address = target_func_address.wrapping_add(reloc_addend as u64);
371                let target_page = (target_func_address & !(0xFFF)) as usize;
372                let pc_page = (reloc_address - reloc_offset) & !(0xFFF);
373                let mut reloc_delta = target_page.wrapping_sub(pc_page) as u64;
374                reloc_delta = reloc_delta
375                    .wrapping_add((target_func_address & 0x800) << 1)
376                    .wrapping_sub((target_func_address & 0x800) << 21);
377                reloc_delta = reloc_delta.wrapping_add((reloc_delta & 0x80000000) << 1);
378                (reloc_address, reloc_delta)
379            }
380            RelocationKind::MachoArm64RelocPointerToGot => {
381                let reloc_address = start + self.offset() as usize;
382                let reloc_delta =
383                    (target_func_address as isize).wrapping_sub(reloc_address as isize);
384                (reloc_address, reloc_delta as u64)
385            }
386            _ => panic!("Relocation kind unsupported"),
387        }
388    }
389}
390
391impl RelocationLike for Relocation {
392    fn kind(&self) -> RelocationKind {
393        self.kind
394    }
395
396    fn reloc_target(&self) -> RelocationTarget {
397        self.reloc_target
398    }
399
400    fn offset(&self) -> CodeOffset {
401        self.offset
402    }
403
404    fn addend(&self) -> Addend {
405        self.addend
406    }
407}
408
409impl RelocationLike for ArchivedRelocation {
410    fn kind(&self) -> RelocationKind {
411        rkyv::deserialize::<_, String>(&self.kind).unwrap()
412    }
413
414    fn reloc_target(&self) -> RelocationTarget {
415        rkyv::deserialize::<_, String>(&self.reloc_target).unwrap()
416    }
417
418    fn offset(&self) -> CodeOffset {
419        self.offset.into()
420    }
421
422    fn addend(&self) -> Addend {
423        self.addend.into()
424    }
425}
426
427/// Destination function. Can be either user function or some special one, like `memory.grow`.
428#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
429#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
430#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Copy, Clone, PartialEq, Eq, Hash)]
431#[rkyv(derive(Debug, Hash, PartialEq, Eq), compare(PartialEq))]
432#[repr(u8)]
433pub enum RelocationTarget {
434    /// A relocation to a function defined locally in the wasm (not an imported one).
435    LocalFunc(LocalFunctionIndex),
436    /// A relocation to a dynamic trampoline.
437    DynamicTrampoline(FunctionIndex),
438    /// A compiler-generated libcall.
439    LibCall(LibCall),
440    /// Custom sections generated by the compiler
441    CustomSection(SectionIndex),
442}
443
444/// Relocations to apply to function bodies.
445pub type Relocations = PrimaryMap<LocalFunctionIndex, Vec<Relocation>>;