#aot #cranelift #wasmtime #jit #wasi #sandbox #runtime #webassembly #standalone

wasmtime-jit-icache-coherence

Utilities for JIT icache maintenance

4 stable releases

5.0.0 Jan 20, 2023
3.0.0 Dec 21, 2022
2.0.1 Dec 1, 2022
2.0.0 Nov 21, 2022

#330 in WebAssembly

Download history 1694/week @ 2022-11-20 4336/week @ 2022-11-27 4340/week @ 2022-12-04 7482/week @ 2022-12-11 9495/week @ 2022-12-18 5213/week @ 2022-12-25 6859/week @ 2023-01-01 9659/week @ 2023-01-08 11619/week @ 2023-01-15 12383/week @ 2023-01-22

40,960 downloads per month
Used in 38 crates (2 directly)

Apache-2.0 WITH LLVM-exception

12KB
93 lines

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(())
# }

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.

Dependencies

~0–6MB
~88K SLoC