wasmtime_jit_icache_coherence/
lib.rs

1//! This crate provides utilities for instruction cache maintenance for JIT authors.
2//!
3//! In self modifying codes such as when writing a JIT, special care must be taken when marking the
4//! code as ready for execution. On fully coherent architectures (X86, S390X) the data cache (D-Cache)
5//! and the instruction cache (I-Cache) are always in sync. However this is not guaranteed for all
6//! architectures such as AArch64 where these caches are not coherent with each other.
7//!
8//! When writing new code there may be a I-cache entry for that same address which causes the
9//! processor to execute whatever was in the cache instead of the new code.
10//!
11//! See the [ARM Community - Caches and Self-Modifying Code] blog post that contains a great
12//! explanation of the above. (It references AArch32 but it has a high level overview of this problem).
13//!
14//! ## Usage
15//!
16//! You should call [clear_cache] on any pages that you write with the new code that you're intending
17//! to execute. You can do this at any point in the code from the moment that you write the page up to
18//! the moment where the code is executed.
19//!
20//! You also need to call [pipeline_flush_mt] to ensure that there isn't any invalid instruction currently
21//! in the pipeline if you are running in a multi threaded environment.
22//!
23//! For single threaded programs you are free to omit [pipeline_flush_mt], otherwise you need to
24//! call both [clear_cache] and [pipeline_flush_mt] in that order.
25//!
26//! ### Example:
27//! ```
28//! # use std::ffi::c_void;
29//! # use std::io;
30//! # use wasmtime_jit_icache_coherence::*;
31//! #
32//! # struct Page {
33//! #   addr: *const c_void,
34//! #   len: usize,
35//! # }
36//! #
37//! # fn main() -> anyhow::Result<()> {
38//! #
39//! # let run_code = || {};
40//! # let code = vec![0u8; 64];
41//! # let newly_written_pages = vec![Page {
42//! #    addr: &code[0] as *const u8 as *const c_void,
43//! #    len: code.len(),
44//! # }];
45//! # unsafe {
46//! // Invalidate the cache for all the newly written pages where we wrote our new code.
47//! for page in newly_written_pages {
48//!     clear_cache(page.addr, page.len)?;
49//! }
50//!
51//! // Once those are invalidated we also need to flush the pipeline
52//! pipeline_flush_mt()?;
53//!
54//! // We can now safely execute our new code.
55//! run_code();
56//! # }
57//! # Ok(())
58//! # }
59//! ```
60//!
61//! <div class="example-wrap" style="display:inline-block"><pre class="compile_fail" style="white-space:normal;font:inherit;">
62//!
63//!  **Warning**: In order to correctly use this interface you should always call [clear_cache].
64//!  A followup call to [pipeline_flush_mt] is required if you are running in a multi-threaded environment.
65//!
66//! </pre></div>
67//!
68//! [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
69
70#![no_std]
71
72use core::ffi::c_void;
73
74cfg_if::cfg_if! {
75    if #[cfg(target_os = "windows")] {
76        mod win;
77        use win as imp;
78    } else if #[cfg(miri)] {
79        mod miri;
80        use crate::miri as imp;
81    } else {
82        mod libc;
83        use crate::libc as imp;
84    }
85}
86
87/// Flushes instructions in the processor pipeline
88///
89/// This pipeline flush is broadcast to all processors that are executing threads in the current process.
90///
91/// Calling [pipeline_flush_mt] is only required for multi-threaded programs and it *must* be called
92/// after all calls to [clear_cache].
93///
94/// If the architecture does not require a pipeline flush, this function does nothing.
95pub fn pipeline_flush_mt() -> imp::Result<()> {
96    imp::pipeline_flush_mt()
97}
98
99/// Flushes the instruction cache for a region of memory.
100///
101/// If the architecture does not require an instruction cache flush, this function does nothing.
102///
103/// # Unsafe
104///
105/// It is necessary to call [pipeline_flush_mt] after this function if you are running in a multi-threaded
106/// environment.
107pub unsafe fn clear_cache(ptr: *const c_void, len: usize) -> imp::Result<()> {
108    imp::clear_cache(ptr, len)
109}