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