Path: blob/main/examples/min-platform/src/main.rs
2450 views
use anyhow::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 anyhow::{Context, anyhow, bail};11use libloading::os::unix::{Library, RTLD_GLOBAL, RTLD_NOW, Symbol};12use object::{Object, ObjectSymbol};13use wasmtime::{Config, Engine};1415let mut args = std::env::args();16let _current_exe = args.next();17let triple = args18.next()19.ok_or_else(|| anyhow!("missing argument 1: triple"))?;20let embedding_so_path = args21.next()22.ok_or_else(|| anyhow!("missing argument 2: path to libembedding.so"))?;23let platform_so_path = args24.next()25.ok_or_else(|| anyhow!("missing argument 3: path to libwasmtime-platform.so"))?;2627// Path to the artifact which is the build of the embedding.28//29// In this example this is a dynamic library intended to be run on Linux.30// Note that this is just an example of an artifact and custom build31// processes can produce different kinds of artifacts.32let binary = std::fs::read(&embedding_so_path)?;33let object = object::File::parse(&binary[..])?;3435// Showcase verification that the dynamic library in question doesn't depend36// on much. Wasmtime build in a "minimal platform" mode is allowed to37// depend on some standard C symbols such as `memcpy` but any OS-related38// symbol must be prefixed by `wasmtime_*` and be documented in39// `crates/wasmtime/src/runtime/vm/sys/custom/capi.rs`.40//41// This is effectively a double-check of the above assertion and showing how42// running `libembedding.so` in this case requires only minimal43// dependencies.44for sym in object.symbols() {45if !sym.is_undefined() || sym.is_weak() {46continue;47}4849match sym.name()? {50"memmove" | "memset" | "memcmp" | "memcpy" | "bcmp" | "__tls_get_addr" => {}51s if s.starts_with("wasmtime_") => {}52other => {53panic!("unexpected dependency on symbol `{other}`")54}55}56}5758// Precompile modules for the embedding. Right now Wasmtime in no_std mode59// does not have support for Cranelift meaning that AOT mode must be used.60// Modules are compiled here and then given to the embedding via the `run`61// function below.62//63// Note that `Config::target` is used here to enable cross-compilation.64let mut config = Config::new();65config.target(&triple)?;6667// If signals-based-traps are disabled then that additionally means that68// some configuration knobs need to be turned to match the expectations of69// the guest program being loaded.70if !cfg!(feature = "custom") {71config.memory_init_cow(false);72config.memory_reservation(0);73config.memory_guard_size(0);74config.memory_reservation_for_growth(0);75config.signals_based_traps(false);76}7778// For x86_64 targets be sure to enable relevant CPU features to avoid79// float-related libcalls which is required for the `x86_64-unknown-none`80// target.81//82// Note that the embedding will need to check that these features are83// actually available at runtime. CPU support for these features has84// existed since 2013 (Haswell) on Intel chips and 2012 (Piledriver) on85// AMD chips.86if cfg!(target_arch = "x86_64") {87unsafe {88config.cranelift_flag_enable("has_sse3");89config.cranelift_flag_enable("has_ssse3");90config.cranelift_flag_enable("has_sse41");91config.cranelift_flag_enable("has_sse42");92config.cranelift_flag_enable("has_fma");93}94}9596let engine = Engine::new(&config)?;97let smoke = engine.precompile_module(b"(module)")?;98let simple_add = engine.precompile_module(99br#"100(module101(func (export "add") (param i32 i32) (result i32)102(i32.add (local.get 0) (local.get 1)))103)104"#,105)?;106let simple_host_fn = engine.precompile_module(107br#"108(module109(import "host" "multiply" (func $multiply (param i32 i32) (result i32)))110(func (export "add_and_mul") (param i32 i32 i32) (result i32)111(i32.add (call $multiply (local.get 0) (local.get 1)) (local.get 2)))112)113"#,114)?;115let simple_floats = engine.precompile_module(116br#"117(module118(func (export "frob") (param f32 f32) (result f32)119(f32.ceil (local.get 0))120(f32.floor (local.get 1))121f32.add)122)123"#,124)?;125126// Next is an example of running this embedding, which also serves as test127// that basic functionality actually works.128//129// Here the `wasmtime_*` symbols are implemented by130// `./embedding/wasmtime-platform.c` which is an example implementation131// against glibc on Linux. This library is compiled into132// `libwasmtime-platform.so` and is dynamically opened here to make it133// available for later symbol resolution. This is just an implementation134// detail of this exable to enably dynamically loading `libembedding.so`135// next.136//137// Next the `libembedding.so` library is opened and the `run` symbol is138// run. The dependencies of `libembedding.so` are either satisfied by our139// ambient libc (e.g. `memcpy` and friends) or `libwasmtime-platform.so`140// (e.g. `wasmtime_*` symbols).141//142// The embedding is then run to showcase an example and then an error, if143// any, is written to stderr.144unsafe {145let _platform_symbols = Library::open(Some(&platform_so_path), RTLD_NOW | RTLD_GLOBAL)146.with_context(|| {147format!(148"failed to open {platform_so_path:?}; cwd = {:?}",149std::env::current_dir()150)151})?;152153let lib = Library::new(&embedding_so_path).context("failed to create new library")?;154let run: Symbol<155extern "C" fn(156*mut u8,157usize,158*const u8,159usize,160*const u8,161usize,162*const u8,163usize,164*const u8,165usize,166) -> usize,167> = lib168.get(b"run")169.context("failed to find the `run` symbol in the library")?;170171let mut error_buf = Vec::with_capacity(1024);172let len = run(173error_buf.as_mut_ptr(),174error_buf.capacity(),175smoke.as_ptr(),176smoke.len(),177simple_add.as_ptr(),178simple_add.len(),179simple_host_fn.as_ptr(),180simple_host_fn.len(),181simple_floats.as_ptr(),182simple_floats.len(),183);184error_buf.set_len(len);185186if len > 0 {187bail!("{}", String::from_utf8_lossy(&error_buf));188}189190#[cfg(feature = "wasi")]191{192let wasi_component_path = args193.next()194.ok_or_else(|| anyhow!("missing argument 4: path to wasi component"))?;195let wasi_component = std::fs::read(&wasi_component_path)?;196let wasi_component = engine.precompile_component(&wasi_component)?;197198let run_wasi: Symbol<extern "C" fn(*mut u8, *mut usize, *const u8, usize) -> usize> =199lib.get(b"run_wasi")200.context("failed to find the `run_wasi` symbol in the library")?;201202const PRINT_CAPACITY: usize = 1024 * 1024;203let mut print_buf = Vec::with_capacity(PRINT_CAPACITY);204let mut print_len = PRINT_CAPACITY;205let status = run_wasi(206print_buf.as_mut_ptr(),207std::ptr::from_mut(&mut print_len),208wasi_component.as_ptr(),209wasi_component.len(),210);211print_buf.set_len(print_len);212let print_buf = String::from_utf8_lossy(&print_buf);213214if status > 0 {215bail!("{print_buf}");216} else {217println!("{print_buf}");218}219}220}221Ok(())222}223224225