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/// A provider of memory for the JIT.
20pub trait JITMemoryProvider {
21    /// Allocate memory that will be executable once finalized.
22    fn allocate_readexec(&mut self, size: usize, align: u64) -> io::Result<*mut u8>;
23    /// Allocate writable memory.
24    fn allocate_readwrite(&mut self, size: usize, align: u64) -> io::Result<*mut u8>;
25    /// Allocate memory that will be read-only once finalized.
26    fn allocate_readonly(&mut self, size: usize, align: u64) -> io::Result<*mut u8>;
27
28    /// Free the memory region.
29    unsafe fn free_memory(&mut self);
30    /// Finalize the memory region and apply memory protections.
31    fn finalize(&mut self, branch_protection: BranchProtection) -> ModuleResult<()>;
32}
33
34/// Marks the memory region as readable and executable.
35///
36/// This function deals with applies branch protection and clears the icache,
37/// but *doesn't* flush the pipeline. Callers have to ensure that
38/// [`wasmtime_jit_icache_coherence::pipeline_flush_mt`] is called before the
39/// mappings are used.
40pub(crate) fn set_readable_and_executable(
41    ptr: *mut u8,
42    len: usize,
43    branch_protection: BranchProtection,
44) -> ModuleResult<()> {
45    // Clear all the newly allocated code from cache if the processor requires it
46    //
47    // Do this before marking the memory as R+X, technically we should be able to do it after
48    // but there are some CPU's that have had errata about doing this with read only memory.
49    unsafe {
50        wasmtime_jit_icache_coherence::clear_cache(ptr as *const libc::c_void, len)
51            .expect("Failed cache clear")
52    };
53
54    unsafe {
55        region::protect(ptr, len, region::Protection::READ_EXECUTE).map_err(|e| {
56            ModuleError::Backend(
57                anyhow::Error::new(e).context("unable to make memory readable+executable"),
58            )
59        })?;
60    }
61
62    // If BTI is requested, and the architecture supports it, use mprotect to set the PROT_BTI flag.
63    if branch_protection == BranchProtection::BTI {
64        #[cfg(all(target_arch = "aarch64", target_os = "linux"))]
65        if std::arch::is_aarch64_feature_detected!("bti") {
66            let prot = libc::PROT_EXEC | libc::PROT_READ | /* PROT_BTI */ 0x10;
67
68            unsafe {
69                if libc::mprotect(ptr as *mut libc::c_void, len, prot) < 0 {
70                    return Err(ModuleError::Backend(
71                        anyhow::Error::new(io::Error::last_os_error())
72                            .context("unable to make memory readable+executable"),
73                    ));
74                }
75            }
76        }
77    }
78
79    Ok(())
80}