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