You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust编译WAT函数签名不匹配问题求助

问题原因分析

WASM的C ABI(对应Rust中的extern "C")原生不支持多值返回。当你在Rust中定义返回(u32, u32)或#[repr(C)]结构体的extern "C"函数时,Rust编译器会自动将其转换为调用者提供输出缓冲区指针的调用模式:

  • 原本的返回值会被拆分为写入内存的操作
  • 函数签名会新增一个i32类型的参数,用于传递输出缓冲区的指针
  • 函数本身不再有返回值,这就是生成的WAT签名为(func (param i32 i32 i32))而非预期带返回值签名的核心原因

你尝试的返回元组指针、改用#[repr(C)]结构体都没解决问题,本质是这些写法依然触发了Rust的C ABI返回值转换逻辑。

解决方法

方法一:适配Rust的C ABI内存写入模式

该方法无需启用不稳定特性,兼容所有WASM运行时:

WASM侧Rust代码

#![no_std]
#![no_main]

use core::ptr;

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    loop {}
}

#[no_mangle]
pub extern "C" fn syscall(a: u32, b: u32, output_ptr: *mut (u32, u32)) {
    // 将结果写入调用者提供的输出缓冲区
    unsafe {
        ptr::write(output_ptr, (a + 1, b + 1));
    }
}

主机端(Wasmtime)代码

use wasmtime::{Engine, Linker, Module, Store, Val};

fn main() -> anyhow::Result<()> {
    let engine = Engine::default();
    let module = Module::from_file(&engine, "target/wasm32-unknown-unknown/release/wasm_syscall.wasm")?;

    let mut linker = Linker::new(&engine);
    linker.func_wrap("env", "syscall", |mut caller: wasmtime::Caller<'_, ()>, a: u32, b: u32, output_ptr: u32| {
        // 计算结果
        let res = (a + 100, b + 200);
        // 将结果写入WASM内存
        let mem = caller.get_export("memory").unwrap().into_memory().unwrap();
        unsafe {
            let ptr = mem.data_ptr(caller.as_context_mut()).add(output_ptr as usize) as *mut (u32, u32);
            core::ptr::write(ptr, res);
        }
        Ok(())
    })?;

    let mut store = Store::new(&engine, ());
    let instance = linker.instantiate(&mut store, &module)?;

    // 测试调用:先分配输出缓冲区(WASM内存中)
    let mem = instance.get_memory(&mut store, "memory").unwrap();
    // 实际使用时建议引入内存分配器(如dlmalloc),此处用地址0仅作示例
    let output_ptr = 0u32;
    let syscall = instance.get_typed_func::<(u32, u32, u32), ()>(&mut store, "syscall")?;
    syscall.call(&mut store, (10, 20, output_ptr))?;

    // 读取结果
    let res = unsafe {
        let ptr = mem.data_ptr(&mut store).add(output_ptr as usize) as *const (u32, u32);
        core::ptr::read(ptr)
    };
    println!("Result: {:?}", res); // 预期输出 (110, 220)

    Ok(())
}

方法二:启用WASM多值返回特性

该方法需使用Rust不稳定特性,但可直接生成带多返回值的函数签名,更符合预期:

WASM侧Rust代码

#![no_std]
#![no_main]
#![feature(wasm_multi_value)]

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    loop {}
}

#[no_mangle]
pub extern "C" fn syscall(a: u32, b: u32) -> (u32, u32) {
    (a + 1, b + 1)
}

编译命令

需指定启用多值的目标特性:

rustc --target wasm32-unknown-unknown -C target-feature=+multivalue --release src/lib.rs

主机端(Wasmtime)代码

use wasmtime::{Engine, Linker, Module, Store};

fn main() -> anyhow::Result<()> {
    let engine = Engine::default();
    let module = Module::from_file(&engine, "target/wasm32-unknown-unknown/release/wasm_syscall.wasm")?;

    let mut linker = Linker::new(&engine);
    // 直接绑定返回(u32, u32)的函数,Wasmtime支持多值返回的Func::wrap
    linker.func_wrap("env", "syscall", |_caller: wasmtime::Caller<'_, ()>, a: u32, b: u32| {
        Ok((a + 100, b + 200))
    })?;

    let mut store = Store::new(&engine, ());
    let instance = linker.instantiate(&mut store, &module)?;

    // 调用函数并直接获取多返回值
    let syscall = instance.get_typed_func::<(u32, u32), (u32, u32)>(&mut store, "syscall")?;
    let res = syscall.call(&mut store, (10, 20))?;
    println!("Result: {:?}", res); // 预期输出 (110, 220)

    Ok(())
}
注意事项
  • 方法一中,实际使用时应在WASM侧引入内存分配器(如dlmalloc),避免直接使用固定内存地址导致冲突
  • 方法二中,wasm_multi_value是Rust不稳定特性,需使用nightly版本编译器

内容的提问来源于stack exchange,提问作者J-Cake

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 05:22:42