Is it possible to do dynamic linking in WebAssembly with Rust?

Dynamic linking support in llvm/lld for WebAssembly is still a work in progress. I imagine that dynamic linking in Rust is currently blocked on dynamic linking support in in llvm/lld more generally.


Don't use this in production code (it's a house of cards), I am just sharing my research for others who are also tinkering around this topic. This will let you change the bindings arbitrarily at runtime. Appears to work correctly today for every optimization level, but who can know if it will work tomorrow. For real support, see sbc100's answer.

/// 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()
}