bytecodealliance / wasmtime-dotnet

.NET embedding of Wasmtime https://bytecodealliance.github.io/wasmtime-dotnet/
Apache License 2.0
409 stars 52 forks source link

Calling an exported function causes a panic #265

Closed MindSwipe closed 1 year ago

MindSwipe commented 1 year ago

I'm trying to compile C# to WASM and then execute that WASM using wasmtime-dotnet but for some reason it panics when trying to invoke the method. Here's the C# code that gets compiled to WASM:

public class Program
{
    public static unsafe void SayHello()
    {
        System.Console.WriteLine("Hello World!");
    }
}

Using some native C to actually get the SayHello method to be exported as __say_hello (I got it to work using this issue on the dotnet-wasi-sdk repo), here is that C:

MonoMethod* method_HandleGuestCall;
MonoObject* interop_instance = 0;

__attribute__((export_name("__say_hello")))
void __say_hello() {
    if (!method_HandleGuestCall) {
        method_HandleGuestCall = lookup_dotnet_method("LimeLead.Core.dll", "LimeLead.Core", "Program", "SayHello", -1);
        assert(method_HandleGuestCall);
    }

    MonoObject *exception;
    mono_wasm_invoke_method(method_HandleGuestCall, NULL, NULL, &exception);
    assert(!exception);
}

I'm then using the following code to try and invoke the __say_hello method:

using var engine = new Engine();
using var linker = new Linker(engine);
using var store = new Store(engine);

linker.DefineWasi();

using var csharpModule = Module.FromFile("myWasm.wasm");
var csharpInstance = linker.Instantiate(store, csharpModule);
var sayHello = csharpInstance.GetAction("__say_hello");
sayHello();

This fails with the exception being SEHException (0x80004005): External component has thrown an exception. and the console window shows the following before the stacktrace:

thread '' panicked at 'called Option::unwrap() on a None value', crates\c-api\src\linker.rs:105:80
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace.

Calling the function directly with the wasmtime cli using wasmtime --invoke __say_hello .\myWasm.wasm results in the following error:

* Assertion at /home/runner/work/dotnet-wasi-sdk/dotnet-wasi-sdk/modules/runtime/src/mono/mono/metadata/assembly-load-context.c:81, condition `default_alc' not met

Error: failed to run main module `.\myWasm.wasm`

Caused by:
    0: failed to invoke `__say_hello`
    1: wasm trap: wasm `unreachable` instruction executed
       wasm backtrace:
           0: 0x4041fc - <unknown>!<wasm function 7608>
           1: 0x3f579e - <unknown>!<wasm function 7285>
           2: 0x3f6222 - <unknown>!<wasm function 7300>
           3: 0x3f5ed5 - <unknown>!<wasm function 7295>
           4: 0x3f5d81 - <unknown>!<wasm function 7293>
           5: 0x3f5fc9 - <unknown>!<wasm function 7297>
           6: 0x3f6054 - <unknown>!<wasm function 7298>
           7: 0x29f796 - <unknown>!<wasm function 4242>
           8: 0xf9967 - <unknown>!<wasm function 915>
           9: 0xe1ecb - <unknown>!<wasm function 701>
          10: 0xe28a2 - <unknown>!<wasm function 708>
          11: 0x3381 - <unknown>!<wasm function 36>
          12: 0x41790f - <unknown>!<wasm function 7892>

I now found this issue on the ASP.NET Core repo where another user had the same conditiondefault_alc' not met, their fix is calling_startbefore anything else, this sadly does not fix my problem as calling_startfrom C# fails withSEHException (0x80004005): External component has thrown an exception.and Rustthread '' panicked at 'called Option::unwrap() on a None value', crates\c-api\src\linker.rs:105:80`, however it does work when calling it directly using the wasmtime cli.

MindSwipe commented 1 year ago

Hmm, I got it to work by switching over my C# project which gets compiled to WASM to be an Exe instead of a Library, as well as removing the linker.DefineWasi() line and stubbing out the WASI methods as described here

peterhuene commented 1 year ago

You're missing a call to store.SetWasiConfiguration(new WasiConfiguration()); (if the default configuration is acceptable) to configure WASI on the store.

Unfortunately the Wasmtime internals underlying Linker.DefineWasi will panic if the store has no WASI configuration at instantiation time.

There's no good way for us to improve this experience from the .NET side of things as the callback mechanism by which the linker extracts the WASI configuration from the store is internal to the Wasmtime API and it is expected to panic if the API isn't used properly.

peterhuene commented 1 year ago

At a bare minimum, we could at least change the unwrap call in the Wasmtime API to an expect explaining that a WASI configuration was not set in the store.