wasmer_compiler/
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 crate::lib::std::fmt;
13use crate::lib::std::vec::Vec;
14use crate::section::SectionIndex;
15use crate::{Addend, CodeOffset, JumpTable};
16use wasmer_types::entity::PrimaryMap;
17use wasmer_types::LocalFunctionIndex;
18use wasmer_vm::libcalls::LibCall;
19
20/// Relocation kinds for every ISA.
21#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Copy, Clone, Debug, PartialEq, Eq)]
22pub enum RelocationKind {
23    /// absolute 4-byte
24    Abs4,
25    /// absolute 8-byte
26    Abs8,
27    /// x86 PC-relative 4-byte
28    X86PCRel4,
29    /// x86 PC-relative 8-byte
30    X86PCRel8,
31    /// x86 PC-relative 4-byte offset to trailing rodata
32    X86PCRelRodata4,
33    /// x86 call to PC-relative 4-byte
34    X86CallPCRel4,
35    /// x86 call to PLT-relative 4-byte
36    X86CallPLTRel4,
37    /// x86 GOT PC-relative 4-byte
38    X86GOTPCRel4,
39    /// Arm32 call target
40    Arm32Call,
41    /// Arm64 call target
42    Arm64Call,
43    /// Arm64 movk/z part 0
44    Arm64Movw0,
45    /// Arm64 movk/z part 1
46    Arm64Movw1,
47    /// Arm64 movk/z part 2
48    Arm64Movw2,
49    /// Arm64 movk/z part 3
50    Arm64Movw3,
51    // /// RISC-V call target
52    // RiscvCall,
53    /// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol.
54    ElfX86_64TlsGd,
55    // /// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry.
56    // MachOX86_64Tlv,
57}
58
59impl fmt::Display for RelocationKind {
60    /// Display trait implementation drops the arch, since its used in contexts where the arch is
61    /// already unambiguous, e.g. clif syntax with isa specified. In other contexts, use Debug.
62    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63        match *self {
64            Self::Abs4 => write!(f, "Abs4"),
65            Self::Abs8 => write!(f, "Abs8"),
66            Self::X86PCRel4 => write!(f, "PCRel4"),
67            Self::X86PCRel8 => write!(f, "PCRel8"),
68            Self::X86PCRelRodata4 => write!(f, "PCRelRodata4"),
69            Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
70            Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
71            Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
72            Self::Arm32Call | Self::Arm64Call => write!(f, "Call"),
73            Self::Arm64Movw0 => write!(f, "Arm64MovwG0"),
74            Self::Arm64Movw1 => write!(f, "Arm64MovwG1"),
75            Self::Arm64Movw2 => write!(f, "Arm64MovwG2"),
76            Self::Arm64Movw3 => write!(f, "Arm64MovwG3"),
77            Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
78            // Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
79        }
80    }
81}
82
83/// A record of a relocation to perform.
84#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)]
85pub struct Relocation {
86    /// The relocation kind.
87    pub kind: RelocationKind,
88    /// Relocation target.
89    pub reloc_target: RelocationTarget,
90    /// The offset where to apply the relocation.
91    pub offset: CodeOffset,
92    /// The addend to add to the relocation value.
93    pub addend: Addend,
94}
95
96/// Destination function. Can be either user function or some special one, like `memory.grow`.
97#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Copy, Clone, PartialEq, Eq)]
98pub enum RelocationTarget {
99    /// A relocation to a function defined locally in the wasm (not an imported one).
100    LocalFunc(LocalFunctionIndex),
101    /// A compiler-generated libcall.
102    LibCall(LibCall),
103    /// Jump table index.
104    JumpTable(LocalFunctionIndex, JumpTable),
105    /// Custom sections generated by the compiler
106    CustomSection(SectionIndex),
107}
108
109impl Relocation {
110    /// Given a function start address, provide the relocation relative
111    /// to that address.
112    ///
113    /// The function returns the relocation address and the delta.
114    pub fn for_address(&self, start: usize, target_func_address: u64) -> (usize, u64) {
115        match self.kind {
116            RelocationKind::Abs8
117            | RelocationKind::Arm64Movw0
118            | RelocationKind::Arm64Movw1
119            | RelocationKind::Arm64Movw2
120            | RelocationKind::Arm64Movw3 => {
121                let reloc_address = start + self.offset as usize;
122                let reloc_addend = self.addend as isize;
123                let reloc_abs = target_func_address
124                    .checked_add(reloc_addend as u64)
125                    .unwrap();
126                (reloc_address, reloc_abs)
127            }
128            RelocationKind::X86PCRel4 => {
129                let reloc_address = start + self.offset as usize;
130                let reloc_addend = self.addend as isize;
131                let reloc_delta_u32 = (target_func_address as u32)
132                    .wrapping_sub(reloc_address as u32)
133                    .checked_add(reloc_addend as u32)
134                    .unwrap();
135                (reloc_address, reloc_delta_u32 as u64)
136            }
137            RelocationKind::X86PCRel8 => {
138                let reloc_address = start + self.offset as usize;
139                let reloc_addend = self.addend as isize;
140                let reloc_delta = target_func_address
141                    .wrapping_sub(reloc_address as u64)
142                    .checked_add(reloc_addend as u64)
143                    .unwrap();
144                (reloc_address, reloc_delta)
145            }
146            RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
147                let reloc_address = start + self.offset as usize;
148                let reloc_addend = self.addend as isize;
149                let reloc_delta_u32 = (target_func_address as u32)
150                    .wrapping_sub(reloc_address as u32)
151                    .wrapping_add(reloc_addend as u32);
152                (reloc_address, reloc_delta_u32 as u64)
153            }
154            RelocationKind::Arm64Call => {
155                let reloc_address = start + self.offset as usize;
156                let reloc_addend = self.addend as isize;
157                let reloc_delta_u32 = target_func_address
158                    .wrapping_sub(reloc_address as u64)
159                    .wrapping_add(reloc_addend as u64);
160                (reloc_address, reloc_delta_u32)
161            }
162            // RelocationKind::X86PCRelRodata4 => {
163            //     (start, target_func_address)
164            // }
165            _ => panic!("Relocation kind unsupported"),
166        }
167    }
168}
169
170/// Relocations to apply to function bodies.
171pub type Relocations = PrimaryMap<LocalFunctionIndex, Vec<Relocation>>;