wasmer_types/compilation/
relocation.rs

1//! Relocation is the process of assigning load addresses for position-dependent
2//! code and data of a program and adjusting the code and data to reflect the
3//! assigned addresses.
4//!
5//! [Learn more](https://en.wikipedia.org/wiki/Relocation_(computing)).
6//!
7//! Each time a `Compiler` compiles a WebAssembly function (into machine code),
8//! it also attaches if there are any relocations that need to be patched into
9//! the generated machine code, so a given frontend (JIT or native) can
10//! do the corresponding work to run it.
11
12use super::section::SectionIndex;
13use crate::entity::PrimaryMap;
14use crate::lib::std::fmt;
15use crate::lib::std::vec::Vec;
16use crate::{Addend, CodeOffset};
17use crate::{LibCall, LocalFunctionIndex};
18use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
19#[cfg(feature = "enable-serde")]
20use serde::{Deserialize, Serialize};
21
22/// Relocation kinds for every ISA.
23#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
24#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
25#[derive(
26    RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Copy, Clone, Debug, PartialEq, Eq,
27)]
28#[archive(as = "Self")]
29#[repr(u8)]
30pub enum RelocationKind {
31    /// absolute 4-byte
32    Abs4,
33    /// absolute 8-byte
34    Abs8,
35    /// x86 PC-relative 4-byte
36    X86PCRel4,
37    /// x86 PC-relative 8-byte
38    X86PCRel8,
39    /// x86 call to PC-relative 4-byte
40    X86CallPCRel4,
41    /// x86 call to PLT-relative 4-byte
42    X86CallPLTRel4,
43    /// x86 GOT PC-relative 4-byte
44    X86GOTPCRel4,
45    /// Arm32 call target
46    Arm32Call,
47    /// Arm64 call target
48    Arm64Call,
49    /// Arm64 movk/z part 0
50    Arm64Movw0,
51    /// Arm64 movk/z part 1
52    Arm64Movw1,
53    /// Arm64 movk/z part 2
54    Arm64Movw2,
55    /// Arm64 movk/z part 3
56    Arm64Movw3,
57    /// RISC-V PC-relative high 20bit
58    RiscvPCRelHi20,
59    /// RISC-V PC-relative low 12bit, I-type
60    RiscvPCRelLo12I,
61    /// RISC-V call target
62    RiscvCall,
63    /// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol.
64    ElfX86_64TlsGd,
65    // /// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry.
66    // MachOX86_64Tlv,
67}
68
69impl fmt::Display for RelocationKind {
70    /// Display trait implementation drops the arch, since its used in contexts where the arch is
71    /// already unambiguous, e.g. clif syntax with isa specified. In other contexts, use Debug.
72    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73        match *self {
74            Self::Abs4 => write!(f, "Abs4"),
75            Self::Abs8 => write!(f, "Abs8"),
76            Self::X86PCRel4 => write!(f, "PCRel4"),
77            Self::X86PCRel8 => write!(f, "PCRel8"),
78            Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
79            Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
80            Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
81            Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"),
82            Self::Arm64Movw0 => write!(f, "Arm64MovwG0"),
83            Self::Arm64Movw1 => write!(f, "Arm64MovwG1"),
84            Self::Arm64Movw2 => write!(f, "Arm64MovwG2"),
85            Self::Arm64Movw3 => write!(f, "Arm64MovwG3"),
86            Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
87            Self::RiscvPCRelHi20 => write!(f, "RiscvPCRelHi20"),
88            Self::RiscvPCRelLo12I => write!(f, "RiscvPCRelLo12I"),
89            // Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
90        }
91    }
92}
93
94/// A record of a relocation to perform.
95#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
96#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
97#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
98#[archive_attr(derive(rkyv::CheckBytes, Debug))]
99pub struct Relocation {
100    /// The relocation kind.
101    pub kind: RelocationKind,
102    /// Relocation target.
103    pub reloc_target: RelocationTarget,
104    /// The offset where to apply the relocation.
105    pub offset: CodeOffset,
106    /// The addend to add to the relocation value.
107    pub addend: Addend,
108}
109
110/// Any struct that acts like a `Relocation`.
111#[allow(missing_docs)]
112pub trait RelocationLike {
113    fn kind(&self) -> RelocationKind;
114    fn reloc_target(&self) -> RelocationTarget;
115    fn offset(&self) -> CodeOffset;
116    fn addend(&self) -> Addend;
117
118    /// Given a function start address, provide the relocation relative
119    /// to that address.
120    ///
121    /// The function returns the relocation address and the delta.
122    fn for_address(&self, start: usize, target_func_address: u64) -> (usize, u64) {
123        match self.kind() {
124            RelocationKind::Abs8
125            | RelocationKind::Arm64Movw0
126            | RelocationKind::Arm64Movw1
127            | RelocationKind::Arm64Movw2
128            | RelocationKind::Arm64Movw3
129            | RelocationKind::RiscvPCRelLo12I => {
130                let reloc_address = start + self.offset() as usize;
131                let reloc_addend = self.addend() as isize;
132                let reloc_abs = target_func_address
133                    .checked_add(reloc_addend as u64)
134                    .unwrap();
135                (reloc_address, reloc_abs)
136            }
137            RelocationKind::X86PCRel4 => {
138                let reloc_address = start + self.offset() as usize;
139                let reloc_addend = self.addend() as isize;
140                let reloc_delta_u32 = (target_func_address as u32)
141                    .wrapping_sub(reloc_address as u32)
142                    .checked_add(reloc_addend as u32)
143                    .unwrap();
144                (reloc_address, reloc_delta_u32 as u64)
145            }
146            RelocationKind::X86PCRel8 => {
147                let reloc_address = start + self.offset() as usize;
148                let reloc_addend = self.addend() as isize;
149                let reloc_delta = target_func_address
150                    .wrapping_sub(reloc_address as u64)
151                    .checked_add(reloc_addend as u64)
152                    .unwrap();
153                (reloc_address, reloc_delta)
154            }
155            RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
156                let reloc_address = start + self.offset() as usize;
157                let reloc_addend = self.addend() as isize;
158                let reloc_delta_u32 = (target_func_address as u32)
159                    .wrapping_sub(reloc_address as u32)
160                    .wrapping_add(reloc_addend as u32);
161                (reloc_address, reloc_delta_u32 as u64)
162            }
163            RelocationKind::Arm64Call
164            | RelocationKind::RiscvCall
165            | RelocationKind::RiscvPCRelHi20 => {
166                let reloc_address = start + self.offset() as usize;
167                let reloc_addend = self.addend() as isize;
168                let reloc_delta_u32 = target_func_address
169                    .wrapping_sub(reloc_address as u64)
170                    .wrapping_add(reloc_addend as u64);
171                (reloc_address, reloc_delta_u32)
172            }
173            _ => panic!("Relocation kind unsupported"),
174        }
175    }
176}
177
178impl RelocationLike for Relocation {
179    fn kind(&self) -> RelocationKind {
180        self.kind
181    }
182
183    fn reloc_target(&self) -> RelocationTarget {
184        self.reloc_target
185    }
186
187    fn offset(&self) -> CodeOffset {
188        self.offset
189    }
190
191    fn addend(&self) -> Addend {
192        self.addend
193    }
194}
195
196impl RelocationLike for ArchivedRelocation {
197    fn kind(&self) -> RelocationKind {
198        self.kind
199    }
200
201    fn reloc_target(&self) -> RelocationTarget {
202        self.reloc_target
203    }
204
205    fn offset(&self) -> CodeOffset {
206        self.offset
207    }
208
209    fn addend(&self) -> Addend {
210        self.addend
211    }
212}
213
214/// Destination function. Can be either user function or some special one, like `memory.grow`.
215#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
216#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
217#[derive(
218    RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Debug, Copy, Clone, PartialEq, Eq,
219)]
220#[archive(as = "Self")]
221#[repr(u8)]
222pub enum RelocationTarget {
223    /// A relocation to a function defined locally in the wasm (not an imported one).
224    LocalFunc(LocalFunctionIndex),
225    /// A compiler-generated libcall.
226    LibCall(LibCall),
227    /// Custom sections generated by the compiler
228    CustomSection(SectionIndex),
229}
230
231/// Relocations to apply to function bodies.
232pub type Relocations = PrimaryMap<LocalFunctionIndex, Vec<Relocation>>;