wasm-micro-runtime icon indicating copy to clipboard operation
wasm-micro-runtime copied to clipboard

Runtime Dynamic Linking

Open qinkunbao opened this issue 3 years ago • 5 comments

Hi,

I was wondering if you have any plan for adding support for runtime linking like dlopen and dlsym for webassembly in the future? If not, may I ask what is the main difficulty in your mind?

void* handle = dlopen("library.wasm", 0);
int (*fun_ptr)()= dlsym(handle, "foo");
(*fun_ptr)();

The corresponding web assembly code.

i32.const 1024
i32.const 0
call 1
i32.const 1037
call 2
call_indirect (type 0)

qinkunbao avatar Nov 20 '20 20:11 qinkunbao

Hi,

The biggest problem of dlsym is: you need to call a function in another module, the function index does not exists in current module, so the call_indirect instruction can't find the valid function instance to call.

As a workaround, we can use some runtime wrappers to achieve this. When we get the function pointer from dlsym, we need to use a runtime wrapper to call this function rather than invoke the function pointer directly:

typedef int (*TestFunc)(int);

int main()
{
    int ret;
    void *handle;
    TestFunc test_func;

    handle = dlopen("app.wasm", 0);
    test_func = (TestFunc)dlsym(handle, "test_func");
    // don't use ret = (*test_func)(1);
    ret = invoke_test_func(test_func, 1);
}

The invoke_test_func is a runtime wrapper, it will call the test_func in the other module instance and return the value. We have a patch to test this functionality, you can have a try. test-dlsym.tar.gz

You can use the build.sh to compile app.wasm and test.wasm, and then apply the patch to WAMR, rebuild iwasm, and run

iwasm test.wasm

However, this is just a simple test, there are some other problems. For example, you can't pass a buffer to the function from library.wasm as they are in different module instance, you will need to malloc a space in library.wasm by using wasm_runtime_module_malloc and copy the content of the buffer.

xujuntwt95329 avatar Nov 24 '20 07:11 xujuntwt95329

Hi,

The biggest problem of dlsym is: you need to call a function in another module, the function index does not exists in current module, so the call_indirect instruction can't find the valid function instance to call.

As a workaround, we can use some runtime wrappers to achieve this. When we get the function pointer from dlsym, we need to use a runtime wrapper to call this function rather than invoke the function pointer directly:

typedef int (*TestFunc)(int);

int main()
{
    int ret;
    void *handle;
    TestFunc test_func;

    handle = dlopen("app.wasm", 0);
    test_func = (TestFunc)dlsym(handle, "test_func");
    // don't use ret = (*test_func)(1);
    ret = invoke_test_func(test_func, 1);
}

The invoke_test_func is a runtime wrapper, it will call the test_func in the other module instance and return the value. We have a patch to test this functionality, you can have a try. test-dlsym.tar.gz

You can use the build.sh to compile app.wasm and test.wasm, and then apply the patch to WAMR, rebuild iwasm, and run

iwasm test.wasm

However, this is just a simple test, there are some other problems. For example, you can't pass a buffer to the function from library.wasm as they are in different module instance, you will need to malloc a space in library.wasm by using wasm_runtime_module_malloc and copy the content of the buffer.

Hi @xujuntwt95329 ,

I use your patch and dlsym works. nice work.

Besides, I got a question about build.sh, for the option -nostdlib. When I build wasm file use default build.sh in your patch and run it, an error occured:

Exception: fail to call unlinked import function (env, iprintf)

Then I remove -nostdlib and build again, it works perfectly.

I just wonder how these happened, and the reason for using options -nostdlib.

Thanks!

JeremyJiangH avatar Jan 14 '21 07:01 JeremyJiangH

Exception: fail to call unlinked import function (env, iprintf)

iprintf is not a libc API, I guess you mean printf ? If you pass -nostdlib option when building wasm module, then you need to build runtime with WAMR_BUILD_LIBC_BUILTIN=1, in this case, the printf will be treated as an import function, WAMR's libc-builtin library has provided wrapper for printf

If you remove -nostdlib option, then the wasm module is compiled as WASI mode, the implementation of printf is compiled to wasm and statically linked into your wasm module.

Please refer to build wasm app for more details.

Hope this can help you. Thanks!

xujuntwt95329 avatar Jan 14 '21 07:01 xujuntwt95329

Exception: fail to call unlinked import function (env, iprintf)

iprintf is not a libc API, I guess you mean printf ? If you pass -nostdlib option when building wasm module, then you need to build runtime with WAMR_BUILD_LIBC_BUILTIN=1, in this case, the printf will be treated as an import function, WAMR's libc-builtin library has provided wrapper for printf

If you remove -nostdlib option, then the wasm module is compiled as WASI mode, the implementation of printf is compiled to wasm and statically linked into your wasm module.

Please refer to build wasm app for more details.

Hope this can help you. Thanks!

Sorry for not read the docs carefully, this helps a lot, thanks!

JeremyJiangH avatar Jan 14 '21 07:01 JeremyJiangH

Hi,

The biggest problem of dlsym is: you need to call a function in another module, the function index does not exists in current module, so the call_indirect instruction can't find the valid function instance to call.

As a workaround, we can use some runtime wrappers to achieve this. When we get the function pointer from dlsym, we need to use a runtime wrapper to call this function rather than invoke the function pointer directly:

typedef int (*TestFunc)(int);

int main()
{
    int ret;
    void *handle;
    TestFunc test_func;

    handle = dlopen("app.wasm", 0);
    test_func = (TestFunc)dlsym(handle, "test_func");
    // don't use ret = (*test_func)(1);
    ret = invoke_test_func(test_func, 1);
}

The invoke_test_func is a runtime wrapper, it will call the test_func in the other module instance and return the value. We have a patch to test this functionality, you can have a try. test-dlsym.tar.gz

You can use the build.sh to compile app.wasm and test.wasm, and then apply the patch to WAMR, rebuild iwasm, and run

iwasm test.wasm

However, this is just a simple test, there are some other problems. For example, you can't pass a buffer to the function from library.wasm as they are in different module instance, you will need to malloc a space in library.wasm by using wasm_runtime_module_malloc and copy the content of the buffer.

Hi,

I said your patch works great on my environment, but I find I was wrong. -_-

Now 2 problems encountered:

  1. in dlsym_wrapper, I find wasm_runtime_lookup_function's return value func is smaller than ((AOTModuleInstance*)app_module_inst)->func_ptrs.ptr, leads to func_index a negative value (overflow to a very large integer), then things start to go wrong.
  2. sometimes (about 1/7) problem 1 not show up, then, when carry out wasm_runtime_call_wasm in invoke_test_func_wrapper, I got this error:
func name: unhandled SIGSEGV, si_addr: 0xffffffffffffffe0
Floating point exception (core dumped)

Sorry for cannot upload screenshots about these problems. Hope for some helps.

Thanks!

JeremyJiangH avatar Jan 15 '21 07:01 JeremyJiangH