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 /// A provider of memory for the JIT. 20 pub 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. 40 pub(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 } 81