如何通过rust编译wasm来访问全局Val?

问题描述 投票:0回答:1

目前,我正在开发一个项目,该项目使用

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 wasm-pack
1个回答
0
投票

Rust 不支持 WebAssembly 导入全局变量。这是因为他们的地址不能被拿走,但是每个 Rust

static
的地址必须能被拿走。 Rust 和/或 WebAssembly 需要更新才能正常工作,如herehere所述。可能可以通过一些 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(())
}
© www.soinside.com 2019 - 2024. All rights reserved.