WebAssembly/wasi-sdk

C++ std output results in wasm runtime error: null function or function signature mismatch

Closed this issue · 2 comments

I have a simple example of a wasm module:

#define export __attribute__((visibility("default")))

#include <iostream>

extern "C" export int add(int a, int b) {
    std::cout << "adding " << a << " and " << b << std::endl;
    return a + b;
}

I'm compiling using the following (not using emscripten):

clang++ --target=wasm32-wasi -nostartfiles -ffunction-sections -fdata-sections -fno-exceptions -Wl,--no-entry -Wl,--strip-all -Wl,--export-dynamic -Wl,--import-memory -Wl,--export-memory -fvisibility=hidden -Wl,--gc-sections --sysroot wasi-sdk-22-sysroot-dir/ -o add.wasm add.cpp

This compiles fine.

I'm then incorporating this into a simple webpage using the browser wasi shim: https://github.com/bjorn3/browser_wasi_shim. Here is my index.js for reference:

  let args = [];
  let fds = [
    new OpenFile(new File([])), // stdin
    ConsoleStdout.lineBuffered(msg => console.log(`[WASI stdout] ${msg}`)),
    ConsoleStdout.lineBuffered(msg => console.warn(`[WASI stderr] ${msg}`)),
    new PreopenDirectory(".", []),
  ];
  let wasi = new WASI(args, [], fds);

  const module = await WebAssembly.compileStreaming(fetch('add.wasm'));
  const memory = new WebAssembly.Memory({ initial: 32 });
  const env = { memory };

  const instance = await WebAssembly.instantiate(module, { env, "wasi_snapshot_preview1": wasi.wasiImport });
  wasi.initialize(instance)
  const { add } = instance.exports
  let result = add(1, 2);
  console.log(`js result = ${result}`)
  result = add(2, 3);
  console.log(`js result = ${result}`)

This results in the following console output:

[WASI stdout] adding 1 and 2
js result = 3
[WASI stdout] adding 2 and 3
add.wasm:0x68963 Uncaught (in promise) RuntimeError: null function or function signature mismatch
    at add.wasm:0x68963
    at add.wasm:0x76482
    at add.wasm:0x76499
    at index.js:24:12

Interestingly, if I remove the std::cout statement from my C++ source above, this works just fine; however with the std::cout the second invocation of the add function results in this runtime error. I've also tried different WASI shims, e.g., wasmer but same result.

This almost certain comes down to how/when the static constrcutors and destructors are being run.

Can you try building with -mexec-model=reactor instead of -Wl,--no-entry, and then calling _initialize before calling any other exports?

Thank you @sbc100! I also had to remove the -nostartfiles flag, but that does seem to do the trick. Closing this ticket.

Learned something new today. If you have good references on the command/reactor model for further reading that would be appreciated.