目前,我正在开发一个项目,该项目使用
wasmtime
(由 rust 实现的 wasm
运行时)来加载和启动一些 wasm
模块。
我想要一个
wasm
模块来访问全局值。所以我写了一些代码:
extern "C" {
static offset: i32;
}
#[no_mangle]
pub extern "C" fn hello() -> i32 {
unsafe { offset }
}
我使用命令将其编译为wasm:
cargo build --target wasm32-unknown-unknown
接下来,我编写另一个 Rust 程序来加载并启动
wasm
模块:
use wasmtime::*;
struct MyState {
count: i32,
}
fn main() -> Result<()> {
let engine = Engine::default();
let mut store = Store::new(&engine, MyState { count: 0 });
// the path is corrected.
let module = Module::from_file(&engine, "./src/linkerglobal.wasm")?;
let mut linker = Linker::new(&engine);
// create a global value
let ty = GlobalType::new(ValType::I32, Mutability::Var);
let my_global = Global::new(&mut store, ty, Val::I32(0x1234))?;
linker.define(&mut store, "host", "offset", my_global)?;
let instance = linker.instantiate(&mut store, &module)?;
let hello = instance.get_typed_func::<(), i32>(&mut store, "hello")?;
let res = hello.call(&mut store, ())?;
println!("res: {}", res); // it should be 0x1235
Ok(())
}
我认为输出应该是:
res: 4660
,然而,输出是res: 0
。
你能帮我吗?非常感谢
Rust 不支持 WebAssembly 导入全局变量。这是因为他们的地址不能被拿走,但是每个 Rust
static
的地址必须能被拿走。 Rust 和/或 WebAssembly 需要更新才能正常工作,如here和here所述。可能可以通过一些 proc-macro 和生成的 wasm 文件的后处理来完成(就像 wasm-bindgen
所做的那样),但我不知道有什么工具可以做到这一点。
但是,您可以做的是定义并导出
static
by Rust,然后通过引擎在主机中对其进行变异。 static
必须是 static mut
或位于 UnsafeCell
内,这样才不会成为 UB,但它可以很好地封装:
use std::cell::UnsafeCell;
use std::ops::Deref;
#[repr(transparent)]
pub struct ExternalStatic<T> {
value: UnsafeCell<T>,
}
impl<T> ExternalStatic<T> {
pub const fn new(value: T) -> Self {
Self {
value: UnsafeCell::new(value),
}
}
}
// SAFETY: See `Deref`.
unsafe impl<T> Sync for ExternalStatic<T> {}
impl<T> Deref for ExternalStatic<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
// SAFETY: We never provide any mutable reference to this value.
// It may be changed externally by the executor, but the executor
// can cause all sorts of UB inside the Rust code, and this isn't
// really something of our responsibility. The executor should be
// cautious and only change it correctly.
unsafe { &*self.value.get() }
}
}
然后:
#[no_mangle]
pub static offset: ExternalStatic<i32> = ExternalStatic::new(0);
#[no_mangle]
pub extern "C" fn hello() -> i32 {
*offset
}
然后在主机中进行如下设置:
use wasmtime::*;
fn main() -> Result<()> {
let engine = Engine::default();
let mut store = Store::new(&engine, ());
// the path is corrected.
let module = Module::from_file(
&engine,
"./target/wasm32-unknown-unknown/release/my_test4.wasm",
)?;
let linker = Linker::new(&engine);
// Set `offset` to 0x1234.
let instance = linker.instantiate(&mut store, &module)?;
let offset_global = instance
.get_export(&mut store, "offset")
.unwrap()
.into_global()
.unwrap();
let offset_addr = offset_global.get(&mut store).unwrap_i32();
let memory = instance.get_memory(&mut store, "memory").unwrap();
memory
.write(&mut store, offset_addr as usize, &0x1234i32.to_le_bytes())
.unwrap();
let hello = instance.get_typed_func::<(), i32>(&mut store, "hello")?;
let res = hello.call(&mut store, ())?;
println!("res: 0x{:X}", res); // it should be 0x1235
Ok(())
}