cranelift_jit/
compiled_blob.rs1use std::ptr;
2
3use cranelift_codegen::binemit::{Addend, Reloc};
4use cranelift_module::{ModuleError, ModuleReloc, ModuleRelocTarget, ModuleResult};
5
6use crate::JITMemoryProvider;
7use crate::memory::JITMemoryKind;
8
9const VENEER_SIZE: usize = 24; unsafe fn modify_inst32(iptr: *mut u32, modifier: impl FnOnce(u32) -> u32) {
14 let inst = iptr.read_unaligned();
15 let new_inst = modifier(inst);
16 iptr.write_unaligned(new_inst);
17}
18
19#[derive(Clone)]
20pub(crate) struct CompiledBlob {
21 ptr: *mut u8,
22 size: usize,
23 relocs: Vec<ModuleReloc>,
24 veneer_count: usize,
25 #[cfg(feature = "wasmtime-unwinder")]
26 wasmtime_exception_data: Option<Vec<u8>>,
27}
28
29unsafe impl Send for CompiledBlob {}
30
31impl CompiledBlob {
32 pub(crate) fn new(
33 memory: &mut dyn JITMemoryProvider,
34 data: &[u8],
35 align: u64,
36 relocs: Vec<ModuleReloc>,
37 #[cfg(feature = "wasmtime-unwinder")] wasmtime_exception_data: Option<Vec<u8>>,
38 kind: JITMemoryKind,
39 ) -> ModuleResult<Self> {
40 let mut veneer_count = 0;
42 for reloc in &relocs {
43 match reloc.kind {
44 Reloc::Arm64Call => veneer_count += 1,
45 _ => {}
46 }
47 }
48
49 let ptr = memory
50 .allocate(data.len() + veneer_count * VENEER_SIZE, align, kind)
51 .map_err(|e| ModuleError::Allocation { err: e })?;
52
53 unsafe {
54 ptr::copy_nonoverlapping(data.as_ptr(), ptr, data.len());
55 }
56
57 Ok(CompiledBlob {
58 ptr,
59 size: data.len(),
60 relocs,
61 veneer_count,
62 #[cfg(feature = "wasmtime-unwinder")]
63 wasmtime_exception_data,
64 })
65 }
66
67 pub(crate) fn new_zeroed(
68 memory: &mut dyn JITMemoryProvider,
69 size: usize,
70 align: u64,
71 relocs: Vec<ModuleReloc>,
72 #[cfg(feature = "wasmtime-unwinder")] wasmtime_exception_data: Option<Vec<u8>>,
73 kind: JITMemoryKind,
74 ) -> ModuleResult<Self> {
75 let ptr = memory
76 .allocate(size, align, kind)
77 .map_err(|e| ModuleError::Allocation { err: e })?;
78
79 unsafe { ptr::write_bytes(ptr, 0, size) };
80
81 Ok(CompiledBlob {
82 ptr,
83 size,
84 relocs,
85 veneer_count: 0,
86 #[cfg(feature = "wasmtime-unwinder")]
87 wasmtime_exception_data,
88 })
89 }
90
91 pub(crate) fn ptr(&self) -> *const u8 {
92 self.ptr
93 }
94
95 pub(crate) fn size(&self) -> usize {
96 self.size
97 }
98
99 #[cfg(feature = "wasmtime-unwinder")]
100 pub(crate) fn wasmtime_exception_data(&self) -> Option<&[u8]> {
101 self.wasmtime_exception_data.as_deref()
102 }
103
104 pub(crate) fn perform_relocations(
105 &self,
106 get_address: impl Fn(&ModuleRelocTarget) -> *const u8,
107 ) {
108 use std::ptr::write_unaligned;
109
110 let mut next_veneer_idx = 0;
111 let relocation_target_addr = |name: &ModuleRelocTarget, addend: Addend| {
112 let addend = isize::try_from(addend).unwrap();
113 get_address(name)
114 .expose_provenance()
115 .checked_add_signed(addend)
116 .unwrap()
117 };
118
119 for (
120 i,
121 &ModuleReloc {
122 kind,
123 offset,
124 ref name,
125 addend,
126 },
127 ) in self.relocs.iter().enumerate()
128 {
129 debug_assert!((offset as usize) < self.size);
130 let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) };
131 match kind {
132 Reloc::Abs4 => {
133 let what = relocation_target_addr(name, addend);
134 unsafe { write_unaligned(at as *mut u32, u32::try_from(what).unwrap()) };
135 }
136 Reloc::Abs8 => {
137 let what = relocation_target_addr(name, addend);
138 unsafe { write_unaligned(at as *mut u64, u64::try_from(what).unwrap()) };
139 }
140 Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
141 let what = relocation_target_addr(name, addend);
142 let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap();
143 unsafe { write_unaligned(at as *mut i32, pcrel) };
144 }
145 Reloc::X86GOTPCRel4 => {
146 panic!("GOT relocation shouldn't be generated when !is_pic");
147 }
148 Reloc::X86CallPLTRel4 => {
149 panic!("PLT relocation shouldn't be generated when !is_pic");
150 }
151 Reloc::S390xPCRel32Dbl | Reloc::S390xPLTRel32Dbl => {
152 let what = relocation_target_addr(name, addend);
153 let pcrel = i32::try_from(((what as isize) - (at as isize)) >> 1).unwrap();
154 unsafe { write_unaligned(at as *mut i32, pcrel) };
155 }
156 Reloc::Arm64Call => {
157 let what = relocation_target_addr(name, addend);
158 let iptr = at as *mut u32;
160
161 let diff = ((what as isize) - (at as isize)) >> 2;
164 if (diff >> 25 == -1) || (diff >> 25 == 0) {
169 let chop = 32 - 26;
172 let imm26 = (diff as u32) << chop >> chop;
173 unsafe { modify_inst32(iptr, |inst| inst | imm26) };
174 } else {
175 let veneer_idx = next_veneer_idx;
178 next_veneer_idx += 1;
179 assert!(veneer_idx <= self.veneer_count);
180 let veneer =
181 unsafe { self.ptr.byte_add(self.size + veneer_idx * VENEER_SIZE) };
182
183 unsafe {
186 write_unaligned(
187 veneer.cast::<u32>(),
188 0x58000050, );
190 write_unaligned(
191 veneer.byte_add(4).cast::<u32>(),
192 0xd61f0200, );
194 write_unaligned(veneer.byte_add(8).cast::<u64>(), what as u64);
195 };
196
197 let diff = ((veneer as isize) - (at as isize)) >> 2;
199 assert!((diff >> 25 == -1) || (diff >> 25 == 0));
200 let chop = 32 - 26;
201 let imm26 = (diff as u32) << chop >> chop;
202 unsafe { modify_inst32(iptr, |inst| inst | imm26) };
203 }
204 }
205 Reloc::Aarch64AdrGotPage21 => {
206 panic!("GOT relocation shouldn't be generated when !is_pic");
207 }
208 Reloc::Aarch64Ld64GotLo12Nc => {
209 panic!("GOT relocation shouldn't be generated when !is_pic");
210 }
211 Reloc::Aarch64AdrPrelPgHi21 => {
212 let what = relocation_target_addr(name, addend);
213 let get_page = |x| x & (!0xfff);
214 let pcrel =
222 i32::try_from(get_page(what as isize) - get_page(at as isize)).unwrap();
223 let iptr = at as *mut u32;
224 let hi21 = (pcrel >> 12).cast_unsigned();
225 let lo = (hi21 & 0x3) << 29;
226 let hi = (hi21 & 0x1ffffc) << 3;
227 unsafe { modify_inst32(iptr, |inst| inst | lo | hi) };
228 }
229 Reloc::Aarch64AddAbsLo12Nc => {
230 let what = relocation_target_addr(name, addend);
231 let iptr = at as *mut u32;
232 let imm12 = (what as u32 & 0xfff) << 10;
233 unsafe { modify_inst32(iptr, |inst| inst | imm12) };
234 }
235 Reloc::RiscvCallPlt => {
236 let what = relocation_target_addr(name, addend);
242 let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap() as u32;
243
244 let hi20 = pcrel.wrapping_add(0x800) & 0xFFFFF000;
258 let lo12 = pcrel.wrapping_sub(hi20) & 0xFFF;
259
260 unsafe {
261 let auipc_addr = at as *mut u32;
263 modify_inst32(auipc_addr, |auipc| (auipc & 0xFFF) | hi20);
264
265 let jalr_addr = at.offset(4) as *mut u32;
267 modify_inst32(jalr_addr, |jalr| (jalr & 0xFFFFF) | (lo12 << 20));
268 }
269 }
270 Reloc::PulleyPcRel => {
271 let what = relocation_target_addr(name, addend);
272 let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap();
273 let at = at as *mut i32;
274 unsafe {
275 at.write_unaligned(at.read_unaligned().wrapping_add(pcrel));
276 }
277 }
278
279 Reloc::RiscvPCRelHi20 => {
282 let what = relocation_target_addr(name, addend);
283 let pcrel = i32::try_from((what as isize) - (at as isize) + 0x800)
284 .unwrap()
285 .cast_unsigned();
286 let at = at as *mut u32;
287 unsafe {
288 modify_inst32(at, |i| i | (pcrel & 0xfffff000));
289 }
290 }
291
292 Reloc::RiscvPCRelLo12I => {
298 let prev_reloc = &self.relocs[i - 1];
299 assert_eq!(prev_reloc.kind, Reloc::RiscvPCRelHi20);
300 let lo_target = get_address(name);
301 let hi_address =
302 unsafe { self.ptr.offset(isize::try_from(prev_reloc.offset).unwrap()) };
303 assert_eq!(lo_target, hi_address);
304 let hi_target = get_address(&prev_reloc.name);
305 let pcrel = i32::try_from((hi_target as isize) - (hi_address as isize))
306 .unwrap()
307 .cast_unsigned();
308 let at = at as *mut u32;
309 unsafe {
310 modify_inst32(at, |i| i | ((pcrel & 0xfff) << 20));
311 }
312 }
313
314 other => unimplemented!("unimplemented reloc {other:?}"),
315 }
316 }
317 }
318}