* cranelift: Add FlushInstructionCache for AArch64 on Windows This was previously done on #3426 for linux. * wasmtime: Add FlushInstructionCache for AArch64 on Windows This was previously done on #3426 for linux. * cranelift: Add MemoryUse flag to JIT Memory Manager This allows us to keep the icache flushing code self-contained and not leak implementation details. This also changes the windows icache flushing code to only flush pages that were previously unflushed. * Add jit-icache-coherence crate * cranelift: Use `jit-icache-coherence` * wasmtime: Use `jit-icache-coherence` * jit-icache-coherence: Make rustix feature additive Mutually exclusive features cause issues. * wasmtime: Remove rustix from wasmtime-jit We now use it via jit-icache-coherence * Rename wasmtime-jit-icache-coherency crate * Use cfg-if in wasmtime-jit-icache-coherency crate * Use inline instead of inline(always) * Add unsafe marker to clear_cache * Conditionally compile all rustix operations membarrier does not exist on MacOS * Publish `wasmtime-jit-icache-coherence` * Remove explicit windows check This is implied by the target_os = "windows" above * cranelift: Remove len != 0 check This is redundant as it is done in non_protected_allocations_iter * Comment cleanups Thanks @akirilov-arm! * Make clear_cache safe * Rename pipeline_flush to pipeline_flush_mt * Revert "Make clear_cache safe" This reverts commit 21165d81c9030ed9b291a1021a367214d2942c90. * More docs! * Fix pipeline_flush reference on clear_cache * Update more docs! * Move pipeline flush after `mprotect` calls Technically the `clear_cache` operation is a lie in AArch64, so move the pipeline flush after the `mprotect` calls so that it benefits from the implicit cache cleaning done by it. * wasmtime: Remove rustix backend from icache crate * wasmtime: Use libc for macos * wasmtime: Flush icache on all arch's for windows * wasmtime: Add flags to membarrier call
106 lines
3.9 KiB
Rust
106 lines
3.9 KiB
Rust
//! This crate provides utilities for instruction cache maintenance for JIT authors.
|
|
//!
|
|
//! In self modifying codes such as when writing a JIT, special care must be taken when marking the
|
|
//! code as ready for execution. On fully coherent architectures (X86, S390X) the data cache (D-Cache)
|
|
//! and the instruction cache (I-Cache) are always in sync. However this is not guaranteed for all
|
|
//! architectures such as AArch64 where these caches are not coherent with each other.
|
|
//!
|
|
//! When writing new code there may be a I-cache entry for that same address which causes the
|
|
//! processor to execute whatever was in the cache instead of the new code.
|
|
//!
|
|
//! See the [ARM Community - Caches and Self-Modifying Code] blog post that contains a great
|
|
//! explanation of the above. (It references AArch32 but it has a high level overview of this problem).
|
|
//!
|
|
//! ## Usage
|
|
//!
|
|
//! You should call [clear_cache] on any pages that you write with the new code that you're intending
|
|
//! to execute. You can do this at any point in the code from the moment that you write the page up to
|
|
//! the moment where the code is executed.
|
|
//!
|
|
//! You also need to call [pipeline_flush_mt] to ensure that there isn't any invalid instruction currently
|
|
//! in the pipeline if you are running in a multi threaded environment.
|
|
//!
|
|
//! For single threaded programs you are free to omit [pipeline_flush_mt], otherwise you need to
|
|
//! call both [clear_cache] and [pipeline_flush_mt] in that order.
|
|
//!
|
|
//! ### Example:
|
|
//! ```
|
|
//! # use std::ffi::c_void;
|
|
//! # use std::io;
|
|
//! # use wasmtime_jit_icache_coherence::*;
|
|
//! #
|
|
//! # struct Page {
|
|
//! # addr: *const c_void,
|
|
//! # len: usize,
|
|
//! # }
|
|
//! #
|
|
//! # fn main() -> io::Result<()> {
|
|
//! #
|
|
//! # let run_code = || {};
|
|
//! # let code = vec![0u8; 64];
|
|
//! # let newly_written_pages = vec![Page {
|
|
//! # addr: &code[0] as *const u8 as *const c_void,
|
|
//! # len: code.len(),
|
|
//! # }];
|
|
//! # unsafe {
|
|
//! // Invalidate the cache for all the newly written pages where we wrote our new code.
|
|
//! for page in newly_written_pages {
|
|
//! clear_cache(page.addr, page.len)?;
|
|
//! }
|
|
//!
|
|
//! // Once those are invalidated we also need to flush the pipeline
|
|
//! pipeline_flush_mt()?;
|
|
//!
|
|
//! // We can now safely execute our new code.
|
|
//! run_code();
|
|
//! # }
|
|
//! # Ok(())
|
|
//! # }
|
|
//! ```
|
|
//!
|
|
//! <div class="example-wrap" style="display:inline-block"><pre class="compile_fail" style="white-space:normal;font:inherit;">
|
|
//!
|
|
//! **Warning**: In order to correctly use this interface you should always call [clear_cache].
|
|
//! A followup call to [pipeline_flush_mt] is required if you are running in a multi-threaded environment.
|
|
//!
|
|
//! </pre></div>
|
|
//!
|
|
//! [ARM Community - Caches and Self-Modifying Code]: https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/caches-and-self-modifying-code
|
|
|
|
use std::ffi::c_void;
|
|
use std::io::Result;
|
|
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(target_os = "windows")] {
|
|
mod win;
|
|
use win as imp;
|
|
} else {
|
|
mod libc;
|
|
use crate::libc as imp;
|
|
}
|
|
}
|
|
|
|
/// Flushes instructions in the processor pipeline
|
|
///
|
|
/// This pipeline flush is broadcast to all processors that are executing threads in the current process.
|
|
///
|
|
/// Calling [pipeline_flush_mt] is only required for multi-threaded programs and it *must* be called
|
|
/// after all calls to [clear_cache].
|
|
///
|
|
/// If the architecture does not require a pipeline flush, this function does nothing.
|
|
pub fn pipeline_flush_mt() -> Result<()> {
|
|
imp::pipeline_flush_mt()
|
|
}
|
|
|
|
/// Flushes the instruction cache for a region of memory.
|
|
///
|
|
/// If the architecture does not require an instruction cache flush, this function does nothing.
|
|
///
|
|
/// # Unsafe
|
|
///
|
|
/// It is necessary to call [pipeline_flush_mt] after this function if you are running in a multi-threaded
|
|
/// environment.
|
|
pub unsafe fn clear_cache(ptr: *const c_void, len: usize) -> Result<()> {
|
|
imp::clear_cache(ptr, len)
|
|
}
|