使用Rust可以在WebAssembly中进行动态链接吗?

时间:2019-01-24 13:45:26

标签: rust webassembly wasm-bindgen

我正在使用wasm-bindgen在Rust中为网络制作图灵完备的DSL。我希望能够从Web下载任意WASM代码,然后在DSL中使用该文件中的功能。我想到的是一种等效于dlopen的动态链接。

我不知道如何真正实现这一目标。

通过阅读WebAssembly docs,我的印象是确实应该这样做,但是我不了解该文档中的处理细节。

wasm-bindgen参考文献中有一个chapter,其中详细说明了如何从WebAssembly模块内部实例化WebAssembly模块!,但这似乎是通过JavaScript做到的,这似乎不是最优的,而不是什么。 WebAssembly文档描述。

在js-sys中,可以从任意字符串创建JavaScript函数,但这实际上是从JavaScript方面调用Function(/* some arbitrary string */),这似乎又不是最优的,而不是WebAssembly文档所描述的。

是否可以实现我的目标?

2 个答案:

答案 0 :(得分:6)

llvm / lld中对WebAssembly的动态链接支持为still a work in progress。我想,Rust中的动态链接目前在llvm / lld中的动态链接支持上更普遍了。

答案 1 :(得分:1)

不要在生产代码中使用它(它是一副纸牌屋),我只是将我的研究分享给其他也正在研究此主题的人。这使您可以在运行时任意更改绑定。对于每个优化级别来说,今天似乎都能正常工作,但是谁知道明天是否可以工作。要获得真正的支持,请参见sbc100的答案。

/// If at any point you call this function directly, it will probably do exactly what its
/// implementation here is, as it should compile to a "call" instruction when you directly call.
/// That instruction does not appear to be impacted by changes in the function table.
pub fn replace_me() {
    // The function body must be unique, otherwise the optimizer will deduplicate
    // it and you create unintended impacts. This is never called, it's just unique.
    force_call_indirect_for_function_index(replace_me as u32);
}

/// We'll replace the above function with this function for all "call indirect" instructions.
pub fn return_50() -> u64 {
    50
}

/// This allows us to force "call indirect". Both no_mangle and inline(never) seem to be required.
/// You could simply strip every invocation of this function from your final wasm binary, since
/// it takes one value and returns one value. It's here to stop optimizations around the function
/// invocation by index.
#[inline(never)]
#[no_mangle]
fn force_call_indirect_for_function_index(function_index: u32) -> u32 {
    function_index
}

/// Inline this or make it generic or whatever you want for ease of use, this is your calling code.
/// Note that the function index you use does not need to have the same signature as the function it
/// is replaced with.
///
/// This seems to compile to:
/// i32.const, call force_call_indirect_for_function_index, call indirect.
///
/// So stripping force_call_indirect_for_function_index invocations would make this as efficient
/// as possible for a dynamically linked wasm call I think.
fn call_replace_me_indirectly() -> u64 {
    unsafe {
        std::mem::transmute::<u32, fn() -> u64>(force_call_indirect_for_function_index(
            replace_me as u32,
        ))()
    }
}

/// Replaces replace_me with return_50 in the wasm function table. I've tested that this works with
/// Functions exported from other wasm modules. For this example, I'll use a function defined in
/// this module (return_50).
fn replace_replace_me() {
    let function_table: js_sys::WebAssembly::Table = wasm_bindgen::function_table()
        .dyn_into::<js_sys::WebAssembly::Table>()
        .expect("I'm going to find you...");
    let function = function_table
        .get(return_50 as u32)
        .expect("I know you're in there...");
    function_table
        .set(replace_me as u32, &function)
        .expect("It's not unsafe, but is it undefined behavior?");
}

/// Mangles "replace_me" call indirection invocations, and returns 50.
pub fn watch_me() -> u64 {
    replace_replace_me();
    call_replace_me_indirectly()
}