Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic callbacks from guest module. #1704

Closed
make-things-better opened this issue Oct 11, 2020 · 1 comment
Closed

Dynamic callbacks from guest module. #1704

make-things-better opened this issue Oct 11, 2020 · 1 comment
Labels
❓ question I've a question!

Comments

@make-things-better
Copy link

make-things-better commented Oct 11, 2020

Summary

I want to call "host" function from "guest" using function pointer without having to include any host function in "import" object, which means that host function targets are chosen at runtime.
Question: Are there any ways to do it using Wasmer? If not, do you think it is a legitimate use case and have plans for supporting it later on?

(I have talked to @MarkMcCaskey in the Wasmer slack channel about this issue and am just making it more public here for easier tracking later on.)

Additional details

Note: The Wasmer version used in the examples below is "1.0.0-alpha3".

Considering different machine model between host and guest: LP64 vs. LP32 and type check before indirect call in guest module, the only way I could come up with to dynamically call host functions is through manipulating the function pointer table of the guest module. Here is the code I have made for it.

  • Host source code
use wasmer::{Instance, Module, Store, Value, Type, FunctionType, Function};
use wasmer_wasi::WasiState;

fn host_callback(arg1: i32, arg2: i32) -> i32 {
    arg1 + arg2
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let wasm_bytes = std::fs::read("guest/guest.wasm")?;
    let store = Store::default();
    let module = Module::new(&store, wasm_bytes)?;
    let mut wasi_env = WasiState::new("hello").finalize()?;
    let import_object = wasi_env.import_object(&module)?;
    let instance = Instance::new(&module, &import_object)?;
    wasi_env.set_memory(instance.exports.get_memory("memory")?.clone());
    let mut guest_table = instance.exports.get_table("__indirect_function_table")?;

    let f = Function::new_native(&store, host_callback);

    // Replace 1st element of the table with host_callback().
    guest_table.set(1, f.into());

    let guest_func = instance.exports.get_function("call_callback").unwrap();

    // Pass the table index 1 to the guest module.
    let res = guest_func.call(&[Value::I32(1)])?;
    println!("result: {:?}", res);

    Ok(())
}
  • Guest source code (==guest/guest.wasm)
int call_callback(int (*cb)(int, int)) {
    return cb(123, 456);
}

One problem I encountered is that there was nothing I could do to insert a new element in the table other than replacing an existing element in the table because I couldn't resize the table. I couldn't modify maximum size of the guest table at runtime and thus failed to call Table::grow(). To work around this, I made a new Table with bigger size and overwrite the table into the Wasm instance, but it didn't work as expected emitting Trap(TableAccessOutOfBounds). Here is the code.

  • Host source code (Guest source code is the same as before.)
use wasmer::{Instance, Module, Store, Function, Value, TableType, Type, Table, Extern};
use wasmer_wasi::WasiState;

fn host_callback(arg1: i32, arg2: i32) -> i32 {
    arg1 + arg2
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let wasm_bytes =
        std::fs::read("guest/guest.wasm").unwrap();
    let store = Store::default();
    let module = Module::new(&store, wasm_bytes)?;
    let mut wasi_env = WasiState::new("hello").finalize()?;
    let import_object = wasi_env.import_object(&module)?;
    let mut instance = Instance::new(&module, &import_object)?;
    wasi_env.set_memory(instance.exports.get_memory("memory")?.clone());

    let old_table = instance.exports.get_table("__indirect_function_table")?;

    // Make a new table with bigger size.
    let new_table_ty = TableType::new(Type::FuncRef, old_table.ty().minimum, Some(old_table.ty().minimum + 10));
    let new_table_def_val = Function::new_native(&store, ||{});
    let new_table = Table::new(&store, new_table_ty, new_table_def_val.into())?;
    Table::copy(&new_table, 0, old_table, 0, old_table.ty().minimum);

    // Insert the new table into the guest wasm instance.
    instance.exports.insert("__indirect_function_table", Extern::Table(new_table));

    let guest_table = instance.exports.get_table("__indirect_function_table")?;
    let func = Function::new_native(&store, host_callback);

    // Now we can grow the table with a new element.
    let cur_idx = guest_table.grow(1, func.into())?;
    let guest_func = instance.exports.get_function("call_callback")?;

    // Pass the new element index to the guest module.
    guest_func.call(&[Value::I32(cur_idx as i32)])?;

    Ok(())
}

Since the above code also didn't work, this time I wanted the guest wasm module to have bigger table size from the start rather than resizing it. For this, I searched for some clang compile options like --table-size-maximum=?? but there weren't such options. Besides, I searched for some APIs in Wasmer to make the table size bigger before or after the guest module is instantiated, and failed.

Similar issue

#1302

@make-things-better make-things-better added the ❓ question I've a question! label Oct 11, 2020
@stale
Copy link

stale bot commented Oct 11, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the 🏚 stale Inactive issues or PR label Oct 11, 2021
@wasmerio wasmerio locked and limited conversation to collaborators Oct 20, 2021
@stale stale bot removed the 🏚 stale Inactive issues or PR label Oct 20, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
❓ question I've a question!
Projects
None yet
Development

No branches or pull requests

2 participants