1 use anyhow::Result; 2 3 #[cfg(not(target_os = "linux"))] 4 fn main() -> Result<()> { 5 eprintln!("This example only runs on Linux right now"); 6 Ok(()) 7 } 8 9 #[cfg(target_os = "linux")] 10 fn main() -> Result<()> { 11 use anyhow::{anyhow, Context}; 12 use libloading::os::unix::{Library, Symbol, RTLD_GLOBAL, RTLD_NOW}; 13 use object::{Object, ObjectSymbol}; 14 use std::io::Write; 15 use wasmtime::{Config, Engine}; 16 17 let mut args = std::env::args(); 18 let _current_exe = args.next(); 19 let triple = args 20 .next() 21 .ok_or_else(|| anyhow!("missing argument 1: triple"))?; 22 let embedding_so_path = args 23 .next() 24 .ok_or_else(|| anyhow!("missing argument 2: path to libembedding.so"))?; 25 let platform_so_path = args 26 .next() 27 .ok_or_else(|| anyhow!("missing argument 3: path to libwasmtime-platform.so"))?; 28 29 // Path to the artifact which is the build of the embedding. 30 // 31 // In this example this is a dynamic library intended to be run on Linux. 32 // Note that this is just an example of an artifact and custom build 33 // processes can produce different kinds of artifacts. 34 let binary = std::fs::read(&embedding_so_path)?; 35 let object = object::File::parse(&binary[..])?; 36 37 // Showcase verification that the dynamic library in question doesn't depend 38 // on much. Wasmtime build in a "minimal platform" mode is allowed to 39 // depend on some standard C symbols such as `memcpy` but any OS-related 40 // symbol must be prefixed by `wasmtime_*` and be documented in 41 // `crates/wasmtime/src/runtime/vm/sys/custom/capi.rs`. 42 // 43 // This is effectively a double-check of the above assertion and showing how 44 // running `libembedding.so` in this case requires only minimal 45 // dependencies. 46 for sym in object.symbols() { 47 if !sym.is_undefined() || sym.is_weak() { 48 continue; 49 } 50 51 match sym.name()? { 52 "memmove" | "memset" | "memcmp" | "memcpy" | "bcmp" | "__tls_get_addr" => {} 53 s if s.starts_with("wasmtime_") => {} 54 other => { 55 panic!("unexpected dependency on symbol `{other}`") 56 } 57 } 58 } 59 60 // Precompile modules for the embedding. Right now Wasmtime in no_std mode 61 // does not have support for Cranelift meaning that AOT mode must be used. 62 // Modules are compiled here and then given to the embedding via the `run` 63 // function below. 64 // 65 // Note that `Config::target` is used here to enable cross-compilation. 66 let mut config = Config::new(); 67 config.target(&triple)?; 68 69 // If signals-based-traps are disabled then that additionally means that 70 // some configuration knobs need to be turned to match the expectations of 71 // the guest program being loaded. 72 if !cfg!(feature = "custom") { 73 config.memory_init_cow(false); 74 config.memory_reservation(0); 75 config.memory_guard_size(0); 76 config.memory_reservation_for_growth(0); 77 config.signals_based_traps(false); 78 } 79 80 let engine = Engine::new(&config)?; 81 let smoke = engine.precompile_module(b"(module)")?; 82 let simple_add = engine.precompile_module( 83 br#" 84 (module 85 (func (export "add") (param i32 i32) (result i32) 86 (i32.add (local.get 0) (local.get 1))) 87 ) 88 "#, 89 )?; 90 let simple_host_fn = engine.precompile_module( 91 br#" 92 (module 93 (import "host" "multiply" (func $multiply (param i32 i32) (result i32))) 94 (func (export "add_and_mul") (param i32 i32 i32) (result i32) 95 (i32.add (call $multiply (local.get 0) (local.get 1)) (local.get 2))) 96 ) 97 "#, 98 )?; 99 100 // Next is an example of running this embedding, which also serves as test 101 // that basic functionality actually works. 102 // 103 // Here the `wasmtime_*` symbols are implemented by 104 // `./embedding/wasmtime-platform.c` which is an example implementation 105 // against glibc on Linux. This library is compiled into 106 // `libwasmtime-platform.so` and is dynamically opened here to make it 107 // available for later symbol resolution. This is just an implementation 108 // detail of this exable to enably dynamically loading `libembedding.so` 109 // next. 110 // 111 // Next the `libembedding.so` library is opened and the `run` symbol is 112 // run. The dependencies of `libembedding.so` are either satisfied by our 113 // ambient libc (e.g. `memcpy` and friends) or `libwasmtime-platform.so` 114 // (e.g. `wasmtime_*` symbols). 115 // 116 // The embedding is then run to showcase an example and then an error, if 117 // any, is written to stderr. 118 unsafe { 119 let _platform_symbols = Library::open(Some(&platform_so_path), RTLD_NOW | RTLD_GLOBAL) 120 .with_context(|| { 121 format!( 122 "failed to open {platform_so_path:?}; cwd = {:?}", 123 std::env::current_dir() 124 ) 125 })?; 126 127 let lib = Library::new(&embedding_so_path).context("failed to create new library")?; 128 let run: Symbol< 129 extern "C" fn( 130 *mut u8, 131 usize, 132 *const u8, 133 usize, 134 *const u8, 135 usize, 136 *const u8, 137 usize, 138 ) -> usize, 139 > = lib 140 .get(b"run") 141 .context("failed to find the `run` symbol in the library")?; 142 143 let mut error_buf = Vec::with_capacity(1024); 144 let len = run( 145 error_buf.as_mut_ptr(), 146 error_buf.capacity(), 147 smoke.as_ptr(), 148 smoke.len(), 149 simple_add.as_ptr(), 150 simple_add.len(), 151 simple_host_fn.as_ptr(), 152 simple_host_fn.len(), 153 ); 154 error_buf.set_len(len); 155 156 std::io::stderr().write_all(&error_buf).unwrap(); 157 } 158 Ok(()) 159 } 160