cranelift_jit/memory/mod.rs
1use cranelift_module::{ModuleError, ModuleResult};
2use std::io;
3
4mod arena;
5mod system;
6
7pub use arena::ArenaMemoryProvider;
8pub use system::SystemMemoryProvider;
9
10/// Type of branch protection to apply to executable memory.
11#[derive(Clone, Copy, Debug, PartialEq)]
12pub enum BranchProtection {
13 /// No protection.
14 None,
15 /// Use the Branch Target Identification extension of the Arm architecture.
16 BTI,
17}
18
19/// The kind of memory allocation requested by a [`JITMemoryProvider`].
20pub enum JITMemoryKind {
21 /// Allocate memory that will be executable once finalized.
22 Executable,
23 /// Allocate writable memory.
24 Writable,
25 /// Allocate memory that will be read-only once finalized.
26 ReadOnly,
27}
28
29/// A provider of memory for the JIT.
30pub trait JITMemoryProvider {
31 /// Allocate memory
32 fn allocate(&mut self, size: usize, align: u64, kind: JITMemoryKind) -> io::Result<*mut u8>;
33
34 /// Free the memory region.
35 unsafe fn free_memory(&mut self);
36 /// Finalize the memory region and apply memory protections.
37 fn finalize(&mut self, branch_protection: BranchProtection) -> ModuleResult<()>;
38}
39
40/// Marks the memory region as readable and executable.
41///
42/// This function deals with applies branch protection and clears the icache,
43/// but *doesn't* flush the pipeline. Callers have to ensure that
44/// [`wasmtime_jit_icache_coherence::pipeline_flush_mt`] is called before the
45/// mappings are used.
46pub(crate) fn set_readable_and_executable(
47 ptr: *mut u8,
48 len: usize,
49 branch_protection: BranchProtection,
50) -> ModuleResult<()> {
51 // Clear all the newly allocated code from cache if the processor requires it
52 //
53 // Do this before marking the memory as R+X, technically we should be able to do it after
54 // but there are some CPU's that have had errata about doing this with read only memory.
55 unsafe {
56 wasmtime_jit_icache_coherence::clear_cache(ptr as *const libc::c_void, len)
57 .expect("Failed cache clear")
58 };
59
60 unsafe {
61 region::protect(ptr, len, region::Protection::READ_EXECUTE).map_err(|e| {
62 ModuleError::Backend(
63 anyhow::Error::new(e).context("unable to make memory readable+executable"),
64 )
65 })?;
66 }
67
68 // If BTI is requested, and the architecture supports it, use mprotect to set the PROT_BTI flag.
69 if branch_protection == BranchProtection::BTI {
70 #[cfg(all(target_arch = "aarch64", target_os = "linux"))]
71 if std::arch::is_aarch64_feature_detected!("bti") {
72 let prot = libc::PROT_EXEC | libc::PROT_READ | /* PROT_BTI */ 0x10;
73
74 unsafe {
75 if libc::mprotect(ptr as *mut libc::c_void, len, prot) < 0 {
76 return Err(ModuleError::Backend(
77 anyhow::Error::new(io::Error::last_os_error())
78 .context("unable to make memory readable+executable"),
79 ));
80 }
81 }
82 }
83 }
84
85 Ok(())
86}