/src/wasmtime/cranelift/jit/src/compiled_blob.rs
Line | Count | Source |
1 | | use std::ptr; |
2 | | |
3 | | use cranelift_codegen::binemit::{Addend, Reloc}; |
4 | | use cranelift_module::{ModuleError, ModuleReloc, ModuleRelocTarget, ModuleResult}; |
5 | | |
6 | | use crate::JITMemoryProvider; |
7 | | use crate::memory::JITMemoryKind; |
8 | | |
9 | | const VENEER_SIZE: usize = 24; // ldr + br + pointer |
10 | | |
11 | | /// Reads a 32bit instruction at `iptr`, and writes it again after |
12 | | /// being altered by `modifier` |
13 | 0 | unsafe fn modify_inst32(iptr: *mut u32, modifier: impl FnOnce(u32) -> u32) { |
14 | 0 | let inst = iptr.read_unaligned(); |
15 | 0 | let new_inst = modifier(inst); |
16 | 0 | iptr.write_unaligned(new_inst); |
17 | 0 | } Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#2}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#4}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#5}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#6}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#7}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#8}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#9}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#1}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#2}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#4}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#5}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#6}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#7}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#8}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#9}>Unexecuted instantiation: cranelift_jit::compiled_blob::modify_inst32::<<cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#1}> |
18 | | |
19 | | #[derive(Clone)] |
20 | | pub(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 | | |
29 | | unsafe impl Send for CompiledBlob {} |
30 | | |
31 | | impl CompiledBlob { |
32 | 23.8k | pub(crate) fn new( |
33 | 23.8k | memory: &mut dyn JITMemoryProvider, |
34 | 23.8k | data: &[u8], |
35 | 23.8k | align: u64, |
36 | 23.8k | relocs: Vec<ModuleReloc>, |
37 | 23.8k | #[cfg(feature = "wasmtime-unwinder")] wasmtime_exception_data: Option<Vec<u8>>, |
38 | 23.8k | kind: JITMemoryKind, |
39 | 23.8k | ) -> ModuleResult<Self> { |
40 | | // Reserve veneers for all function calls just in case |
41 | 23.8k | let mut veneer_count = 0; |
42 | 83.8k | for reloc in &relocs { |
43 | 83.8k | match reloc.kind { |
44 | 0 | Reloc::Arm64Call => veneer_count += 1, |
45 | 83.8k | _ => {} |
46 | | } |
47 | | } |
48 | | |
49 | 23.8k | let ptr = memory |
50 | 23.8k | .allocate(data.len() + veneer_count * VENEER_SIZE, align, kind) |
51 | 23.8k | .map_err(|e| ModuleError::Allocation { err: e })?; |
52 | | |
53 | 23.8k | unsafe { |
54 | 23.8k | ptr::copy_nonoverlapping(data.as_ptr(), ptr, data.len()); |
55 | 23.8k | } |
56 | | |
57 | 23.8k | Ok(CompiledBlob { |
58 | 23.8k | ptr, |
59 | 23.8k | size: data.len(), |
60 | 23.8k | relocs, |
61 | 23.8k | veneer_count, |
62 | 23.8k | #[cfg(feature = "wasmtime-unwinder")] |
63 | 23.8k | wasmtime_exception_data, |
64 | 23.8k | }) |
65 | 23.8k | } |
66 | | |
67 | 0 | pub(crate) fn new_zeroed( |
68 | 0 | memory: &mut dyn JITMemoryProvider, |
69 | 0 | size: usize, |
70 | 0 | align: u64, |
71 | 0 | relocs: Vec<ModuleReloc>, |
72 | 0 | #[cfg(feature = "wasmtime-unwinder")] wasmtime_exception_data: Option<Vec<u8>>, |
73 | 0 | kind: JITMemoryKind, |
74 | 0 | ) -> ModuleResult<Self> { |
75 | 0 | let ptr = memory |
76 | 0 | .allocate(size, align, kind) |
77 | 0 | .map_err(|e| ModuleError::Allocation { err: e })?; |
78 | | |
79 | 0 | unsafe { ptr::write_bytes(ptr, 0, size) }; |
80 | | |
81 | 0 | Ok(CompiledBlob { |
82 | 0 | ptr, |
83 | 0 | size, |
84 | 0 | relocs, |
85 | 0 | veneer_count: 0, |
86 | 0 | #[cfg(feature = "wasmtime-unwinder")] |
87 | 0 | wasmtime_exception_data, |
88 | 0 | }) |
89 | 0 | } |
90 | | |
91 | 49.1k | pub(crate) fn ptr(&self) -> *const u8 { |
92 | 49.1k | self.ptr |
93 | 49.1k | } |
94 | | |
95 | 23.8k | pub(crate) fn size(&self) -> usize { |
96 | 23.8k | self.size |
97 | 23.8k | } |
98 | | |
99 | | #[cfg(feature = "wasmtime-unwinder")] |
100 | 0 | pub(crate) fn wasmtime_exception_data(&self) -> Option<&[u8]> { |
101 | 0 | self.wasmtime_exception_data.as_deref() |
102 | 0 | } |
103 | | |
104 | 23.8k | pub(crate) fn perform_relocations( |
105 | 23.8k | &self, |
106 | 23.8k | get_address: impl Fn(&ModuleRelocTarget) -> *const u8, |
107 | 23.8k | ) { |
108 | | use std::ptr::write_unaligned; |
109 | | |
110 | 23.8k | let mut next_veneer_idx = 0; |
111 | 83.8k | let relocation_target_addr = |name: &ModuleRelocTarget, addend: Addend| { |
112 | 83.8k | let addend = isize::try_from(addend).unwrap(); |
113 | 83.8k | get_address(name) |
114 | 83.8k | .expose_provenance() |
115 | 83.8k | .checked_add_signed(addend) |
116 | 83.8k | .unwrap() |
117 | 83.8k | }; <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#0}Line | Count | Source | 111 | 83.8k | let relocation_target_addr = |name: &ModuleRelocTarget, addend: Addend| { | 112 | 83.8k | let addend = isize::try_from(addend).unwrap(); | 113 | 83.8k | get_address(name) | 114 | 83.8k | .expose_provenance() | 115 | 83.8k | .checked_add_signed(addend) | 116 | 83.8k | .unwrap() | 117 | 83.8k | }; |
Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#0} |
118 | | |
119 | | for ( |
120 | 83.8k | i, |
121 | | &ModuleReloc { |
122 | 83.8k | kind, |
123 | 83.8k | offset, |
124 | 83.8k | ref name, |
125 | 83.8k | addend, |
126 | | }, |
127 | 23.8k | ) in self.relocs.iter().enumerate() |
128 | | { |
129 | 83.8k | debug_assert!((offset as usize) < self.size); |
130 | 83.8k | let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) }; |
131 | 83.8k | match kind { |
132 | 0 | Reloc::Abs4 => { |
133 | 0 | let what = relocation_target_addr(name, addend); |
134 | 0 | unsafe { write_unaligned(at as *mut u32, u32::try_from(what).unwrap()) }; |
135 | 0 | } |
136 | 83.8k | Reloc::Abs8 => { |
137 | 83.8k | let what = relocation_target_addr(name, addend); |
138 | 83.8k | unsafe { write_unaligned(at as *mut u64, u64::try_from(what).unwrap()) }; |
139 | 83.8k | } |
140 | 0 | Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => { |
141 | 0 | let what = relocation_target_addr(name, addend); |
142 | 0 | let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap(); |
143 | 0 | unsafe { write_unaligned(at as *mut i32, pcrel) }; |
144 | 0 | } |
145 | | Reloc::X86GOTPCRel4 => { |
146 | 0 | panic!("GOT relocation shouldn't be generated when !is_pic"); |
147 | | } |
148 | | Reloc::X86CallPLTRel4 => { |
149 | 0 | panic!("PLT relocation shouldn't be generated when !is_pic"); |
150 | | } |
151 | 0 | Reloc::S390xPCRel32Dbl | Reloc::S390xPLTRel32Dbl => { |
152 | 0 | let what = relocation_target_addr(name, addend); |
153 | 0 | let pcrel = i32::try_from(((what as isize) - (at as isize)) >> 1).unwrap(); |
154 | 0 | unsafe { write_unaligned(at as *mut i32, pcrel) }; |
155 | 0 | } |
156 | | Reloc::Arm64Call => { |
157 | 0 | let what = relocation_target_addr(name, addend); |
158 | | // The instruction is 32 bits long. |
159 | 0 | let iptr = at as *mut u32; |
160 | | |
161 | | // The offset encoded in the `bl` instruction is the |
162 | | // number of bytes divided by 4. |
163 | 0 | let diff = ((what as isize) - (at as isize)) >> 2; |
164 | | // Sign propagating right shift disposes of the |
165 | | // included bits, so the result is expected to be |
166 | | // either all sign bits or 0 when in-range, depending |
167 | | // on if the original value was negative or positive. |
168 | 0 | if (diff >> 25 == -1) || (diff >> 25 == 0) { |
169 | | // The lower 26 bits of the `bl` instruction form the |
170 | | // immediate offset argument. |
171 | 0 | let chop = 32 - 26; |
172 | 0 | let imm26 = (diff as u32) << chop >> chop; |
173 | 0 | unsafe { modify_inst32(iptr, |inst| inst | imm26) };Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#1}Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#1} |
174 | | } else { |
175 | | // If the target is out of range for a direct call, insert a veneer at the |
176 | | // end of the function. |
177 | 0 | let veneer_idx = next_veneer_idx; |
178 | 0 | next_veneer_idx += 1; |
179 | 0 | assert!(veneer_idx <= self.veneer_count); |
180 | 0 | let veneer = |
181 | 0 | unsafe { self.ptr.byte_add(self.size + veneer_idx * VENEER_SIZE) }; |
182 | | |
183 | | // Write the veneer |
184 | | // x16 is reserved as scratch register to be used by veneers and PLT entries |
185 | 0 | unsafe { |
186 | 0 | write_unaligned( |
187 | 0 | veneer.cast::<u32>(), |
188 | 0 | 0x58000050, // ldr x16, 0x8 |
189 | 0 | ); |
190 | 0 | write_unaligned( |
191 | 0 | veneer.byte_add(4).cast::<u32>(), |
192 | 0 | 0xd61f0200, // br x16 |
193 | 0 | ); |
194 | 0 | write_unaligned(veneer.byte_add(8).cast::<u64>(), what as u64); |
195 | 0 | }; |
196 | | |
197 | | // Set the veneer as target of the call |
198 | 0 | let diff = ((veneer as isize) - (at as isize)) >> 2; |
199 | 0 | assert!((diff >> 25 == -1) || (diff >> 25 == 0)); |
200 | 0 | let chop = 32 - 26; |
201 | 0 | let imm26 = (diff as u32) << chop >> chop; |
202 | 0 | unsafe { modify_inst32(iptr, |inst| inst | imm26) };Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#2}Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#2} |
203 | | } |
204 | | } |
205 | | Reloc::Aarch64AdrGotPage21 => { |
206 | 0 | panic!("GOT relocation shouldn't be generated when !is_pic"); |
207 | | } |
208 | | Reloc::Aarch64Ld64GotLo12Nc => { |
209 | 0 | panic!("GOT relocation shouldn't be generated when !is_pic"); |
210 | | } |
211 | | Reloc::Aarch64AdrPrelPgHi21 => { |
212 | 0 | let what = relocation_target_addr(name, addend); |
213 | 0 | let get_page = |x| x & (!0xfff); Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#3}Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#3} |
214 | | // NOTE: This should technically be i33 given that this relocation type allows |
215 | | // a range from -4GB to +4GB, not -2GB to +2GB. But this doesn't really matter |
216 | | // as the target is unlikely to be more than 2GB from the adrp instruction. We |
217 | | // need to be careful to not cast to an unsigned int until after doing >> 12 to |
218 | | // compute the upper 21bits of the pcrel address however as otherwise the top |
219 | | // bit of the 33bit pcrel address would be forced 0 through zero extension |
220 | | // instead of being sign extended as it should be. |
221 | 0 | let pcrel = |
222 | 0 | i32::try_from(get_page(what as isize) - get_page(at as isize)).unwrap(); |
223 | 0 | let iptr = at as *mut u32; |
224 | 0 | let hi21 = (pcrel >> 12).cast_unsigned(); |
225 | 0 | let lo = (hi21 & 0x3) << 29; |
226 | 0 | let hi = (hi21 & 0x1ffffc) << 3; |
227 | 0 | unsafe { modify_inst32(iptr, |inst| inst | lo | hi) };Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#4}Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#4} |
228 | | } |
229 | | Reloc::Aarch64AddAbsLo12Nc => { |
230 | 0 | let what = relocation_target_addr(name, addend); |
231 | 0 | let iptr = at as *mut u32; |
232 | 0 | let imm12 = (what as u32 & 0xfff) << 10; |
233 | 0 | unsafe { modify_inst32(iptr, |inst| inst | imm12) };Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#5}Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#5} |
234 | | } |
235 | | Reloc::RiscvCallPlt => { |
236 | | // A R_RISCV_CALL_PLT relocation expects auipc+jalr instruction pair. |
237 | | // It is the equivalent of two relocations: |
238 | | // 1. R_RISCV_PCREL_HI20 on the `auipc` |
239 | | // 2. R_RISCV_PCREL_LO12_I on the `jalr` |
240 | | |
241 | 0 | let what = relocation_target_addr(name, addend); |
242 | 0 | let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap() as u32; |
243 | | |
244 | | // See https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses |
245 | | // for a better explanation of the following code. |
246 | | // |
247 | | // Unlike the regular symbol relocations, here both "sub-relocations" point to the same address. |
248 | | // |
249 | | // `pcrel` is a signed value (+/- 2GiB range), when splitting it into two parts, we need to |
250 | | // ensure that `hi20` is close enough to `pcrel` to be able to add `lo12` to it and still |
251 | | // get a valid address. |
252 | | // |
253 | | // `lo12` is also a signed offset (+/- 2KiB range) relative to the `hi20` value. |
254 | | // |
255 | | // `hi20` should also be shifted right to be the "true" value. But we also need it |
256 | | // left shifted for the `lo12` calculation and it also matches the instruction encoding. |
257 | 0 | let hi20 = pcrel.wrapping_add(0x800) & 0xFFFFF000; |
258 | 0 | let lo12 = pcrel.wrapping_sub(hi20) & 0xFFF; |
259 | | |
260 | | unsafe { |
261 | | // Do a R_RISCV_PCREL_HI20 on the `auipc` |
262 | 0 | let auipc_addr = at as *mut u32; |
263 | 0 | modify_inst32(auipc_addr, |auipc| (auipc & 0xFFF) | hi20); Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#6}Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#6} |
264 | | |
265 | | // Do a R_RISCV_PCREL_LO12_I on the `jalr` |
266 | 0 | let jalr_addr = at.offset(4) as *mut u32; |
267 | 0 | modify_inst32(jalr_addr, |jalr| (jalr & 0xFFFFF) | (lo12 << 20)); Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#7}Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#7} |
268 | | } |
269 | | } |
270 | | Reloc::PulleyPcRel => { |
271 | 0 | let what = relocation_target_addr(name, addend); |
272 | 0 | let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap(); |
273 | 0 | let at = at as *mut i32; |
274 | 0 | unsafe { |
275 | 0 | at.write_unaligned(at.read_unaligned().wrapping_add(pcrel)); |
276 | 0 | } |
277 | | } |
278 | | |
279 | | // See <https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses> |
280 | | // for why `0x800` is added here. |
281 | | Reloc::RiscvPCRelHi20 => { |
282 | 0 | let what = relocation_target_addr(name, addend); |
283 | 0 | let pcrel = i32::try_from((what as isize) - (at as isize) + 0x800) |
284 | 0 | .unwrap() |
285 | 0 | .cast_unsigned(); |
286 | 0 | let at = at as *mut u32; |
287 | | unsafe { |
288 | 0 | modify_inst32(at, |i| i | (pcrel & 0xfffff000)); Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#8}Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#8} |
289 | | } |
290 | | } |
291 | | |
292 | | // The target of this relocation is the `auipc` preceding this |
293 | | // instruction which should be `RiscvPCRelHi20`, and the actual |
294 | | // target that we're relocating against is the target of that |
295 | | // relocation. Assume for now that the previous relocation is |
296 | | // the target of this relocation, and then use that. |
297 | | Reloc::RiscvPCRelLo12I => { |
298 | 0 | let prev_reloc = &self.relocs[i - 1]; |
299 | 0 | assert_eq!(prev_reloc.kind, Reloc::RiscvPCRelHi20); |
300 | 0 | let lo_target = get_address(name); |
301 | 0 | let hi_address = |
302 | 0 | unsafe { self.ptr.offset(isize::try_from(prev_reloc.offset).unwrap()) }; |
303 | 0 | assert_eq!(lo_target, hi_address); |
304 | 0 | let hi_target = get_address(&prev_reloc.name); |
305 | 0 | let pcrel = i32::try_from((hi_target as isize) - (hi_address as isize)) |
306 | 0 | .unwrap() |
307 | 0 | .cast_unsigned(); |
308 | 0 | let at = at as *mut u32; |
309 | | unsafe { |
310 | 0 | modify_inst32(at, |i| i | ((pcrel & 0xfff) << 20)); Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>::{closure#9}Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}>::{closure#9} |
311 | | } |
312 | | } |
313 | | |
314 | 0 | other => unimplemented!("unimplemented reloc {other:?}"), |
315 | | } |
316 | | } |
317 | 23.8k | } <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#0}>Line | Count | Source | 104 | 23.8k | pub(crate) fn perform_relocations( | 105 | 23.8k | &self, | 106 | 23.8k | get_address: impl Fn(&ModuleRelocTarget) -> *const u8, | 107 | 23.8k | ) { | 108 | | use std::ptr::write_unaligned; | 109 | | | 110 | 23.8k | let mut next_veneer_idx = 0; | 111 | 23.8k | 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 | 83.8k | i, | 121 | | &ModuleReloc { | 122 | 83.8k | kind, | 123 | 83.8k | offset, | 124 | 83.8k | ref name, | 125 | 83.8k | addend, | 126 | | }, | 127 | 23.8k | ) in self.relocs.iter().enumerate() | 128 | | { | 129 | 83.8k | debug_assert!((offset as usize) < self.size); | 130 | 83.8k | let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) }; | 131 | 83.8k | match kind { | 132 | 0 | Reloc::Abs4 => { | 133 | 0 | let what = relocation_target_addr(name, addend); | 134 | 0 | unsafe { write_unaligned(at as *mut u32, u32::try_from(what).unwrap()) }; | 135 | 0 | } | 136 | 83.8k | Reloc::Abs8 => { | 137 | 83.8k | let what = relocation_target_addr(name, addend); | 138 | 83.8k | unsafe { write_unaligned(at as *mut u64, u64::try_from(what).unwrap()) }; | 139 | 83.8k | } | 140 | 0 | Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => { | 141 | 0 | let what = relocation_target_addr(name, addend); | 142 | 0 | let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap(); | 143 | 0 | unsafe { write_unaligned(at as *mut i32, pcrel) }; | 144 | 0 | } | 145 | | Reloc::X86GOTPCRel4 => { | 146 | 0 | panic!("GOT relocation shouldn't be generated when !is_pic"); | 147 | | } | 148 | | Reloc::X86CallPLTRel4 => { | 149 | 0 | panic!("PLT relocation shouldn't be generated when !is_pic"); | 150 | | } | 151 | 0 | Reloc::S390xPCRel32Dbl | Reloc::S390xPLTRel32Dbl => { | 152 | 0 | let what = relocation_target_addr(name, addend); | 153 | 0 | let pcrel = i32::try_from(((what as isize) - (at as isize)) >> 1).unwrap(); | 154 | 0 | unsafe { write_unaligned(at as *mut i32, pcrel) }; | 155 | 0 | } | 156 | | Reloc::Arm64Call => { | 157 | 0 | let what = relocation_target_addr(name, addend); | 158 | | // The instruction is 32 bits long. | 159 | 0 | let iptr = at as *mut u32; | 160 | | | 161 | | // The offset encoded in the `bl` instruction is the | 162 | | // number of bytes divided by 4. | 163 | 0 | let diff = ((what as isize) - (at as isize)) >> 2; | 164 | | // Sign propagating right shift disposes of the | 165 | | // included bits, so the result is expected to be | 166 | | // either all sign bits or 0 when in-range, depending | 167 | | // on if the original value was negative or positive. | 168 | 0 | if (diff >> 25 == -1) || (diff >> 25 == 0) { | 169 | | // The lower 26 bits of the `bl` instruction form the | 170 | | // immediate offset argument. | 171 | 0 | let chop = 32 - 26; | 172 | 0 | let imm26 = (diff as u32) << chop >> chop; | 173 | 0 | unsafe { modify_inst32(iptr, |inst| inst | imm26) }; | 174 | | } else { | 175 | | // If the target is out of range for a direct call, insert a veneer at the | 176 | | // end of the function. | 177 | 0 | let veneer_idx = next_veneer_idx; | 178 | 0 | next_veneer_idx += 1; | 179 | 0 | assert!(veneer_idx <= self.veneer_count); | 180 | 0 | let veneer = | 181 | 0 | unsafe { self.ptr.byte_add(self.size + veneer_idx * VENEER_SIZE) }; | 182 | | | 183 | | // Write the veneer | 184 | | // x16 is reserved as scratch register to be used by veneers and PLT entries | 185 | 0 | unsafe { | 186 | 0 | write_unaligned( | 187 | 0 | veneer.cast::<u32>(), | 188 | 0 | 0x58000050, // ldr x16, 0x8 | 189 | 0 | ); | 190 | 0 | write_unaligned( | 191 | 0 | veneer.byte_add(4).cast::<u32>(), | 192 | 0 | 0xd61f0200, // br x16 | 193 | 0 | ); | 194 | 0 | write_unaligned(veneer.byte_add(8).cast::<u64>(), what as u64); | 195 | 0 | }; | 196 | | | 197 | | // Set the veneer as target of the call | 198 | 0 | let diff = ((veneer as isize) - (at as isize)) >> 2; | 199 | 0 | assert!((diff >> 25 == -1) || (diff >> 25 == 0)); | 200 | 0 | let chop = 32 - 26; | 201 | 0 | let imm26 = (diff as u32) << chop >> chop; | 202 | 0 | unsafe { modify_inst32(iptr, |inst| inst | imm26) }; | 203 | | } | 204 | | } | 205 | | Reloc::Aarch64AdrGotPage21 => { | 206 | 0 | panic!("GOT relocation shouldn't be generated when !is_pic"); | 207 | | } | 208 | | Reloc::Aarch64Ld64GotLo12Nc => { | 209 | 0 | panic!("GOT relocation shouldn't be generated when !is_pic"); | 210 | | } | 211 | | Reloc::Aarch64AdrPrelPgHi21 => { | 212 | 0 | let what = relocation_target_addr(name, addend); | 213 | 0 | let get_page = |x| x & (!0xfff); | 214 | | // NOTE: This should technically be i33 given that this relocation type allows | 215 | | // a range from -4GB to +4GB, not -2GB to +2GB. But this doesn't really matter | 216 | | // as the target is unlikely to be more than 2GB from the adrp instruction. We | 217 | | // need to be careful to not cast to an unsigned int until after doing >> 12 to | 218 | | // compute the upper 21bits of the pcrel address however as otherwise the top | 219 | | // bit of the 33bit pcrel address would be forced 0 through zero extension | 220 | | // instead of being sign extended as it should be. | 221 | 0 | let pcrel = | 222 | 0 | i32::try_from(get_page(what as isize) - get_page(at as isize)).unwrap(); | 223 | 0 | let iptr = at as *mut u32; | 224 | 0 | let hi21 = (pcrel >> 12).cast_unsigned(); | 225 | 0 | let lo = (hi21 & 0x3) << 29; | 226 | 0 | let hi = (hi21 & 0x1ffffc) << 3; | 227 | 0 | unsafe { modify_inst32(iptr, |inst| inst | lo | hi) }; | 228 | | } | 229 | | Reloc::Aarch64AddAbsLo12Nc => { | 230 | 0 | let what = relocation_target_addr(name, addend); | 231 | 0 | let iptr = at as *mut u32; | 232 | 0 | let imm12 = (what as u32 & 0xfff) << 10; | 233 | 0 | unsafe { modify_inst32(iptr, |inst| inst | imm12) }; | 234 | | } | 235 | | Reloc::RiscvCallPlt => { | 236 | | // A R_RISCV_CALL_PLT relocation expects auipc+jalr instruction pair. | 237 | | // It is the equivalent of two relocations: | 238 | | // 1. R_RISCV_PCREL_HI20 on the `auipc` | 239 | | // 2. R_RISCV_PCREL_LO12_I on the `jalr` | 240 | | | 241 | 0 | let what = relocation_target_addr(name, addend); | 242 | 0 | let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap() as u32; | 243 | | | 244 | | // See https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses | 245 | | // for a better explanation of the following code. | 246 | | // | 247 | | // Unlike the regular symbol relocations, here both "sub-relocations" point to the same address. | 248 | | // | 249 | | // `pcrel` is a signed value (+/- 2GiB range), when splitting it into two parts, we need to | 250 | | // ensure that `hi20` is close enough to `pcrel` to be able to add `lo12` to it and still | 251 | | // get a valid address. | 252 | | // | 253 | | // `lo12` is also a signed offset (+/- 2KiB range) relative to the `hi20` value. | 254 | | // | 255 | | // `hi20` should also be shifted right to be the "true" value. But we also need it | 256 | | // left shifted for the `lo12` calculation and it also matches the instruction encoding. | 257 | 0 | let hi20 = pcrel.wrapping_add(0x800) & 0xFFFFF000; | 258 | 0 | let lo12 = pcrel.wrapping_sub(hi20) & 0xFFF; | 259 | | | 260 | | unsafe { | 261 | | // Do a R_RISCV_PCREL_HI20 on the `auipc` | 262 | 0 | let auipc_addr = at as *mut u32; | 263 | 0 | modify_inst32(auipc_addr, |auipc| (auipc & 0xFFF) | hi20); | 264 | | | 265 | | // Do a R_RISCV_PCREL_LO12_I on the `jalr` | 266 | 0 | let jalr_addr = at.offset(4) as *mut u32; | 267 | 0 | modify_inst32(jalr_addr, |jalr| (jalr & 0xFFFFF) | (lo12 << 20)); | 268 | | } | 269 | | } | 270 | | Reloc::PulleyPcRel => { | 271 | 0 | let what = relocation_target_addr(name, addend); | 272 | 0 | let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap(); | 273 | 0 | let at = at as *mut i32; | 274 | 0 | unsafe { | 275 | 0 | at.write_unaligned(at.read_unaligned().wrapping_add(pcrel)); | 276 | 0 | } | 277 | | } | 278 | | | 279 | | // See <https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses> | 280 | | // for why `0x800` is added here. | 281 | | Reloc::RiscvPCRelHi20 => { | 282 | 0 | let what = relocation_target_addr(name, addend); | 283 | 0 | let pcrel = i32::try_from((what as isize) - (at as isize) + 0x800) | 284 | 0 | .unwrap() | 285 | 0 | .cast_unsigned(); | 286 | 0 | let at = at as *mut u32; | 287 | | unsafe { | 288 | 0 | modify_inst32(at, |i| i | (pcrel & 0xfffff000)); | 289 | | } | 290 | | } | 291 | | | 292 | | // The target of this relocation is the `auipc` preceding this | 293 | | // instruction which should be `RiscvPCRelHi20`, and the actual | 294 | | // target that we're relocating against is the target of that | 295 | | // relocation. Assume for now that the previous relocation is | 296 | | // the target of this relocation, and then use that. | 297 | | Reloc::RiscvPCRelLo12I => { | 298 | 0 | let prev_reloc = &self.relocs[i - 1]; | 299 | 0 | assert_eq!(prev_reloc.kind, Reloc::RiscvPCRelHi20); | 300 | 0 | let lo_target = get_address(name); | 301 | 0 | let hi_address = | 302 | 0 | unsafe { self.ptr.offset(isize::try_from(prev_reloc.offset).unwrap()) }; | 303 | 0 | assert_eq!(lo_target, hi_address); | 304 | 0 | let hi_target = get_address(&prev_reloc.name); | 305 | 0 | let pcrel = i32::try_from((hi_target as isize) - (hi_address as isize)) | 306 | 0 | .unwrap() | 307 | 0 | .cast_unsigned(); | 308 | 0 | let at = at as *mut u32; | 309 | | unsafe { | 310 | 0 | modify_inst32(at, |i| i | ((pcrel & 0xfff) << 20)); | 311 | | } | 312 | | } | 313 | | | 314 | 0 | other => unimplemented!("unimplemented reloc {other:?}"), | 315 | | } | 316 | | } | 317 | 23.8k | } |
Unexecuted instantiation: <cranelift_jit::compiled_blob::CompiledBlob>::perform_relocations::<<cranelift_jit::backend::JITModule>::finalize_definitions::{closure#1}> |
318 | | } |