Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/examples/min-platform/src/main.rs
2450 views
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::{Context, anyhow, bail};
12
use libloading::os::unix::{Library, RTLD_GLOBAL, RTLD_NOW, Symbol};
13
use object::{Object, ObjectSymbol};
14
use wasmtime::{Config, Engine};
15
16
let mut args = std::env::args();
17
let _current_exe = args.next();
18
let triple = args
19
.next()
20
.ok_or_else(|| anyhow!("missing argument 1: triple"))?;
21
let embedding_so_path = args
22
.next()
23
.ok_or_else(|| anyhow!("missing argument 2: path to libembedding.so"))?;
24
let platform_so_path = args
25
.next()
26
.ok_or_else(|| anyhow!("missing argument 3: path to libwasmtime-platform.so"))?;
27
28
// Path to the artifact which is the build of the embedding.
29
//
30
// In this example this is a dynamic library intended to be run on Linux.
31
// Note that this is just an example of an artifact and custom build
32
// processes can produce different kinds of artifacts.
33
let binary = std::fs::read(&embedding_so_path)?;
34
let object = object::File::parse(&binary[..])?;
35
36
// Showcase verification that the dynamic library in question doesn't depend
37
// on much. Wasmtime build in a "minimal platform" mode is allowed to
38
// depend on some standard C symbols such as `memcpy` but any OS-related
39
// symbol must be prefixed by `wasmtime_*` and be documented in
40
// `crates/wasmtime/src/runtime/vm/sys/custom/capi.rs`.
41
//
42
// This is effectively a double-check of the above assertion and showing how
43
// running `libembedding.so` in this case requires only minimal
44
// dependencies.
45
for sym in object.symbols() {
46
if !sym.is_undefined() || sym.is_weak() {
47
continue;
48
}
49
50
match sym.name()? {
51
"memmove" | "memset" | "memcmp" | "memcpy" | "bcmp" | "__tls_get_addr" => {}
52
s if s.starts_with("wasmtime_") => {}
53
other => {
54
panic!("unexpected dependency on symbol `{other}`")
55
}
56
}
57
}
58
59
// Precompile modules for the embedding. Right now Wasmtime in no_std mode
60
// does not have support for Cranelift meaning that AOT mode must be used.
61
// Modules are compiled here and then given to the embedding via the `run`
62
// function below.
63
//
64
// Note that `Config::target` is used here to enable cross-compilation.
65
let mut config = Config::new();
66
config.target(&triple)?;
67
68
// If signals-based-traps are disabled then that additionally means that
69
// some configuration knobs need to be turned to match the expectations of
70
// the guest program being loaded.
71
if !cfg!(feature = "custom") {
72
config.memory_init_cow(false);
73
config.memory_reservation(0);
74
config.memory_guard_size(0);
75
config.memory_reservation_for_growth(0);
76
config.signals_based_traps(false);
77
}
78
79
// For x86_64 targets be sure to enable relevant CPU features to avoid
80
// float-related libcalls which is required for the `x86_64-unknown-none`
81
// target.
82
//
83
// Note that the embedding will need to check that these features are
84
// actually available at runtime. CPU support for these features has
85
// existed since 2013 (Haswell) on Intel chips and 2012 (Piledriver) on
86
// AMD chips.
87
if cfg!(target_arch = "x86_64") {
88
unsafe {
89
config.cranelift_flag_enable("has_sse3");
90
config.cranelift_flag_enable("has_ssse3");
91
config.cranelift_flag_enable("has_sse41");
92
config.cranelift_flag_enable("has_sse42");
93
config.cranelift_flag_enable("has_fma");
94
}
95
}
96
97
let engine = Engine::new(&config)?;
98
let smoke = engine.precompile_module(b"(module)")?;
99
let simple_add = engine.precompile_module(
100
br#"
101
(module
102
(func (export "add") (param i32 i32) (result i32)
103
(i32.add (local.get 0) (local.get 1)))
104
)
105
"#,
106
)?;
107
let simple_host_fn = engine.precompile_module(
108
br#"
109
(module
110
(import "host" "multiply" (func $multiply (param i32 i32) (result i32)))
111
(func (export "add_and_mul") (param i32 i32 i32) (result i32)
112
(i32.add (call $multiply (local.get 0) (local.get 1)) (local.get 2)))
113
)
114
"#,
115
)?;
116
let simple_floats = engine.precompile_module(
117
br#"
118
(module
119
(func (export "frob") (param f32 f32) (result f32)
120
(f32.ceil (local.get 0))
121
(f32.floor (local.get 1))
122
f32.add)
123
)
124
"#,
125
)?;
126
127
// Next is an example of running this embedding, which also serves as test
128
// that basic functionality actually works.
129
//
130
// Here the `wasmtime_*` symbols are implemented by
131
// `./embedding/wasmtime-platform.c` which is an example implementation
132
// against glibc on Linux. This library is compiled into
133
// `libwasmtime-platform.so` and is dynamically opened here to make it
134
// available for later symbol resolution. This is just an implementation
135
// detail of this exable to enably dynamically loading `libembedding.so`
136
// next.
137
//
138
// Next the `libembedding.so` library is opened and the `run` symbol is
139
// run. The dependencies of `libembedding.so` are either satisfied by our
140
// ambient libc (e.g. `memcpy` and friends) or `libwasmtime-platform.so`
141
// (e.g. `wasmtime_*` symbols).
142
//
143
// The embedding is then run to showcase an example and then an error, if
144
// any, is written to stderr.
145
unsafe {
146
let _platform_symbols = Library::open(Some(&platform_so_path), RTLD_NOW | RTLD_GLOBAL)
147
.with_context(|| {
148
format!(
149
"failed to open {platform_so_path:?}; cwd = {:?}",
150
std::env::current_dir()
151
)
152
})?;
153
154
let lib = Library::new(&embedding_so_path).context("failed to create new library")?;
155
let run: Symbol<
156
extern "C" fn(
157
*mut u8,
158
usize,
159
*const u8,
160
usize,
161
*const u8,
162
usize,
163
*const u8,
164
usize,
165
*const u8,
166
usize,
167
) -> usize,
168
> = lib
169
.get(b"run")
170
.context("failed to find the `run` symbol in the library")?;
171
172
let mut error_buf = Vec::with_capacity(1024);
173
let len = run(
174
error_buf.as_mut_ptr(),
175
error_buf.capacity(),
176
smoke.as_ptr(),
177
smoke.len(),
178
simple_add.as_ptr(),
179
simple_add.len(),
180
simple_host_fn.as_ptr(),
181
simple_host_fn.len(),
182
simple_floats.as_ptr(),
183
simple_floats.len(),
184
);
185
error_buf.set_len(len);
186
187
if len > 0 {
188
bail!("{}", String::from_utf8_lossy(&error_buf));
189
}
190
191
#[cfg(feature = "wasi")]
192
{
193
let wasi_component_path = args
194
.next()
195
.ok_or_else(|| anyhow!("missing argument 4: path to wasi component"))?;
196
let wasi_component = std::fs::read(&wasi_component_path)?;
197
let wasi_component = engine.precompile_component(&wasi_component)?;
198
199
let run_wasi: Symbol<extern "C" fn(*mut u8, *mut usize, *const u8, usize) -> usize> =
200
lib.get(b"run_wasi")
201
.context("failed to find the `run_wasi` symbol in the library")?;
202
203
const PRINT_CAPACITY: usize = 1024 * 1024;
204
let mut print_buf = Vec::with_capacity(PRINT_CAPACITY);
205
let mut print_len = PRINT_CAPACITY;
206
let status = run_wasi(
207
print_buf.as_mut_ptr(),
208
std::ptr::from_mut(&mut print_len),
209
wasi_component.as_ptr(),
210
wasi_component.len(),
211
);
212
print_buf.set_len(print_len);
213
let print_buf = String::from_utf8_lossy(&print_buf);
214
215
if status > 0 {
216
bail!("{print_buf}");
217
} else {
218
println!("{print_buf}");
219
}
220
}
221
}
222
Ok(())
223
}
224
225