Coverage Report

Created: 2026-07-05 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}