Path: blob/main/examples/min-platform/src/main.rs
3054 views
use wasmtime::Result;12#[cfg(not(target_os = "linux"))]3fn main() -> Result<()> {4eprintln!("This example only runs on Linux right now");5Ok(())6}78#[cfg(target_os = "linux")]9fn main() -> Result<()> {10use libloading::os::unix::{Library, RTLD_GLOBAL, RTLD_NOW, Symbol};11use object::{Object, ObjectSymbol};12use wasmtime::{Config, Engine, bail, error::Context as _, format_err};1314let mut args = std::env::args();15let _current_exe = args.next();16let triple = args17.next()18.ok_or_else(|| format_err!("missing argument 1: triple"))?;19let embedding_so_path = args20.next()21.ok_or_else(|| format_err!("missing argument 2: path to libembedding.so"))?;22let platform_so_path = args23.next()24.ok_or_else(|| format_err!("missing argument 3: path to libwasmtime-platform.so"))?;2526// Path to the artifact which is the build of the embedding.27//28// In this example this is a dynamic library intended to be run on Linux.29// Note that this is just an example of an artifact and custom build30// processes can produce different kinds of artifacts.31let binary = std::fs::read(&embedding_so_path)?;32let object = object::File::parse(&binary[..])?;3334// Showcase verification that the dynamic library in question doesn't depend35// on much. Wasmtime build in a "minimal platform" mode is allowed to36// depend on some standard C symbols such as `memcpy` but any OS-related37// symbol must be prefixed by `wasmtime_*` and be documented in38// `crates/wasmtime/src/runtime/vm/sys/custom/capi.rs`.39//40// This is effectively a double-check of the above assertion and showing how41// running `libembedding.so` in this case requires only minimal42// dependencies.43for sym in object.symbols() {44if !sym.is_undefined() || sym.is_weak() {45continue;46}4748match sym.name()? {49"memmove" | "memset" | "memcmp" | "memcpy" | "bcmp" | "__tls_get_addr" => {}50s if s.starts_with("wasmtime_") => {}51other => {52panic!("unexpected dependency on symbol `{other}`")53}54}55}5657// Precompile modules for the embedding. Right now Wasmtime in no_std mode58// does not have support for Cranelift meaning that AOT mode must be used.59// Modules are compiled here and then given to the embedding via the `run`60// function below.61//62// Note that `Config::target` is used here to enable cross-compilation.63let mut config = Config::new();64config.target(&triple)?;6566// If signals-based-traps are disabled then that additionally means that67// some configuration knobs need to be turned to match the expectations of68// the guest program being loaded.69if !cfg!(feature = "custom") {70config.memory_init_cow(false);71config.memory_reservation(0);72config.memory_guard_size(0);73config.memory_reservation_for_growth(0);74config.signals_based_traps(false);75}7677// For x86_64 targets be sure to enable relevant CPU features to avoid78// float-related libcalls which is required for the `x86_64-unknown-none`79// target.80//81// Note that the embedding will need to check that these features are82// actually available at runtime. CPU support for these features has83// existed since 2013 (Haswell) on Intel chips and 2012 (Piledriver) on84// AMD chips.85if cfg!(target_arch = "x86_64") {86unsafe {87config.cranelift_flag_enable("has_sse3");88config.cranelift_flag_enable("has_ssse3");89config.cranelift_flag_enable("has_sse41");90config.cranelift_flag_enable("has_sse42");91config.cranelift_flag_enable("has_fma");92}93}9495let engine = Engine::new(&config)?;96let smoke = engine.precompile_module(b"(module)")?;97let simple_add = engine.precompile_module(98br#"99(module100(func (export "add") (param i32 i32) (result i32)101(i32.add (local.get 0) (local.get 1)))102)103"#,104)?;105let simple_host_fn = engine.precompile_module(106br#"107(module108(import "host" "multiply" (func $multiply (param i32 i32) (result i32)))109(func (export "add_and_mul") (param i32 i32 i32) (result i32)110(i32.add (call $multiply (local.get 0) (local.get 1)) (local.get 2)))111)112"#,113)?;114let simple_floats = engine.precompile_module(115br#"116(module117(func (export "frob") (param f32 f32) (result f32)118(f32.ceil (local.get 0))119(f32.floor (local.get 1))120f32.add)121)122"#,123)?;124125// Next is an example of running this embedding, which also serves as test126// that basic functionality actually works.127//128// Here the `wasmtime_*` symbols are implemented by129// `./embedding/wasmtime-platform.c` which is an example implementation130// against glibc on Linux. This library is compiled into131// `libwasmtime-platform.so` and is dynamically opened here to make it132// available for later symbol resolution. This is just an implementation133// detail of this exable to enably dynamically loading `libembedding.so`134// next.135//136// Next the `libembedding.so` library is opened and the `run` symbol is137// run. The dependencies of `libembedding.so` are either satisfied by our138// ambient libc (e.g. `memcpy` and friends) or `libwasmtime-platform.so`139// (e.g. `wasmtime_*` symbols).140//141// The embedding is then run to showcase an example and then an error, if142// any, is written to stderr.143unsafe {144let _platform_symbols = Library::open(Some(&platform_so_path), RTLD_NOW | RTLD_GLOBAL)145.with_context(|| {146format!(147"failed to open {platform_so_path:?}; cwd = {:?}",148std::env::current_dir()149)150})?;151152let lib = Library::new(&embedding_so_path).context("failed to create new library")?;153let run: Symbol<154extern "C" fn(155*mut u8,156usize,157*const u8,158usize,159*const u8,160usize,161*const u8,162usize,163*const u8,164usize,165) -> usize,166> = lib167.get(b"run")168.context("failed to find the `run` symbol in the library")?;169170let mut error_buf = Vec::with_capacity(1024);171let len = run(172error_buf.as_mut_ptr(),173error_buf.capacity(),174smoke.as_ptr(),175smoke.len(),176simple_add.as_ptr(),177simple_add.len(),178simple_host_fn.as_ptr(),179simple_host_fn.len(),180simple_floats.as_ptr(),181simple_floats.len(),182);183error_buf.set_len(len);184185if len > 0 {186bail!("{}", String::from_utf8_lossy(&error_buf));187}188189#[cfg(feature = "wasi")]190{191let wasi_component_path = args192.next()193.ok_or_else(|| format_err!("missing argument 4: path to wasi component"))?;194let wasi_component = std::fs::read(&wasi_component_path)?;195let wasi_component = engine.precompile_component(&wasi_component)?;196197let run_wasi: Symbol<extern "C" fn(*mut u8, *mut usize, *const u8, usize) -> usize> =198lib.get(b"run_wasi")199.context("failed to find the `run_wasi` symbol in the library")?;200201const PRINT_CAPACITY: usize = 1024 * 1024;202let mut print_buf = Vec::with_capacity(PRINT_CAPACITY);203let mut print_len = PRINT_CAPACITY;204let status = run_wasi(205print_buf.as_mut_ptr(),206std::ptr::from_mut(&mut print_len),207wasi_component.as_ptr(),208wasi_component.len(),209);210print_buf.set_len(print_len);211let print_buf = String::from_utf8_lossy(&print_buf);212213if status > 0 {214bail!("{print_buf}");215} else {216println!("{print_buf}");217}218}219}220Ok(())221}222223224