Skip to content

Commit d3ab84d

Browse files
committed
Insert veneers on arm64 in cranelift-jit where necessary
1 parent 1b6f147 commit d3ab84d

File tree

1 file changed

+56
-9
lines changed

1 file changed

+56
-9
lines changed

cranelift/jit/src/compiled_blob.rs

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use cranelift_module::{ModuleError, ModuleReloc, ModuleRelocTarget, ModuleResult
66
use crate::JITMemoryProvider;
77
use crate::memory::JITMemoryKind;
88

9+
const VENEER_SIZE: usize = 24; // ldr + br + pointer
10+
911
/// Reads a 32bit instruction at `iptr`, and writes it again after
1012
/// being altered by `modifier`
1113
unsafe fn modify_inst32(iptr: *mut u32, modifier: impl FnOnce(u32) -> u32) {
@@ -19,6 +21,7 @@ pub(crate) struct CompiledBlob {
1921
ptr: *mut u8,
2022
size: usize,
2123
relocs: Vec<ModuleReloc>,
24+
veneer_count: usize,
2225
#[cfg(feature = "wasmtime-unwinder")]
2326
wasmtime_exception_data: Option<Vec<u8>>,
2427
}
@@ -34,8 +37,17 @@ impl CompiledBlob {
3437
#[cfg(feature = "wasmtime-unwinder")] wasmtime_exception_data: Option<Vec<u8>>,
3538
kind: JITMemoryKind,
3639
) -> ModuleResult<Self> {
40+
// Reserve veneers for all function calls just in case
41+
let mut veneer_count = 0;
42+
for reloc in &relocs {
43+
match reloc.kind {
44+
Reloc::Arm64Call => veneer_count += 1,
45+
_ => {}
46+
}
47+
}
48+
3749
let ptr = memory
38-
.allocate(data.len(), align, kind)
50+
.allocate(data.len() + veneer_count * VENEER_SIZE, align, kind)
3951
.map_err(|e| ModuleError::Allocation { err: e })?;
4052

4153
unsafe {
@@ -46,6 +58,7 @@ impl CompiledBlob {
4658
ptr,
4759
size: data.len(),
4860
relocs,
61+
veneer_count,
4962
#[cfg(feature = "wasmtime-unwinder")]
5063
wasmtime_exception_data,
5164
})
@@ -69,6 +82,7 @@ impl CompiledBlob {
6982
ptr,
7083
size,
7184
relocs,
85+
veneer_count: 0,
7286
#[cfg(feature = "wasmtime-unwinder")]
7387
wasmtime_exception_data,
7488
})
@@ -93,6 +107,8 @@ impl CompiledBlob {
93107
) {
94108
use std::ptr::write_unaligned;
95109

110+
let mut next_veneer_idx = 0;
111+
96112
for (
97113
i,
98114
&ModuleReloc {
@@ -143,19 +159,50 @@ impl CompiledBlob {
143159
let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
144160
// The instruction is 32 bits long.
145161
let iptr = at as *mut u32;
162+
146163
// The offset encoded in the `bl` instruction is the
147164
// number of bytes divided by 4.
148165
let diff = ((what as isize) - (at as isize)) >> 2;
149166
// Sign propagating right shift disposes of the
150167
// included bits, so the result is expected to be
151-
// either all sign bits or 0, depending on if the original
152-
// value was negative or positive.
153-
assert!((diff >> 25 == -1) || (diff >> 25 == 0));
154-
// The lower 26 bits of the `bl` instruction form the
155-
// immediate offset argument.
156-
let chop = 32 - 26;
157-
let imm26 = (diff as u32) << chop >> chop;
158-
unsafe { modify_inst32(iptr, |inst| inst | imm26) };
168+
// either all sign bits or 0 when in-range, depending
169+
// on if the original value was negative or positive.
170+
if (diff >> 25 == -1) || (diff >> 25 == 0) {
171+
// The lower 26 bits of the `bl` instruction form the
172+
// immediate offset argument.
173+
let chop = 32 - 26;
174+
let imm26 = (diff as u32) << chop >> chop;
175+
unsafe { modify_inst32(iptr, |inst| inst | imm26) };
176+
} else {
177+
// If the target is out of range for a direct call, insert a veneer at the
178+
// end of the function.
179+
let veneer_idx = next_veneer_idx;
180+
next_veneer_idx += 1;
181+
assert!(veneer_idx <= self.veneer_count);
182+
let veneer =
183+
unsafe { self.ptr.byte_add(self.size + veneer_idx * VENEER_SIZE) };
184+
185+
// Write the veneer
186+
// x16 is reserved as scratch register to be used by veneers and PLT entries
187+
unsafe {
188+
write_unaligned(
189+
veneer.cast::<u32>(),
190+
0x58000050, // ldr x16, 0x8
191+
);
192+
write_unaligned(
193+
veneer.byte_add(4).cast::<u32>(),
194+
0xd61f0200, // br x16
195+
);
196+
write_unaligned(veneer.byte_add(8).cast::<u64>(), what.addr() as u64);
197+
};
198+
199+
// Set the veneer as target of the call
200+
let diff = ((veneer as isize) - (at as isize)) >> 2;
201+
assert!((diff >> 25 == -1) || (diff >> 25 == 0));
202+
let chop = 32 - 26;
203+
let imm26 = (diff as u32) << chop >> chop;
204+
unsafe { modify_inst32(iptr, |inst| inst | imm26) };
205+
}
159206
}
160207
Reloc::Aarch64AdrGotPage21 => {
161208
panic!("GOT relocation shouldn't be generated when !is_pic");

0 commit comments

Comments
 (0)