wasmer icon indicating copy to clipboard operation
wasmer copied to clipboard

Emscripten runner erroneously cannot be used

Open jnickg opened this issue 2 years ago • 3 comments

Describe the bug

(Follow-up from a discussion in the #newbie channel on Discord)

When compiling toy programs to be run with wasmer via Emscripten, I am able to accomplish it using the below Emscripten build/link flags:

            -sWASM=1
            -sSTANDALONE_WASM=1
            -sPURE_WASI=1

However, when building googletest from source and linking against that (implementing my own main for UTs, if that matters), I get the following error when trying to run a hello-world style test program:

error: Instantiation failed
╰─▶ 1: Error while importing "env"."invoke_iii": unknown import. Expected Function(FunctionType { params: [I32, I32, I32], results: [I32] })

Following the advice of @theduke, when using a .toml file to try and coerce wasmer to detecting an Emscripten build, I instead get the following error:

jgiampietro@Jamess-MacBook-Pro-2 bin % wasmer run .
error: Atom "test_wpe" is not an emscripten module

This seems to suggest that some combination of the following is occurring:

  • The Emscripten toolchain isn't properly honoring the WASI flags mentioned above, and is adding dependencies on its generated JS code when it ought not to
  • wasmer, which experimentally supports an Emscripten runtime and attempts to detect the need for it, is failing to do so

...So I'm filing this bug to suss out whether there's anything that should be done on the wasmer side to support this. We use Emscripten because we support large C++ code bases, and have numerous frontend applications. I'd like to use wasmer to simplify CI use cases (like running wasm-compiled unit tests) and to pave the way for revamping backend infrastructure to use Webassembly/wasmer/etc.

Wasmer version (installed via homebrew):

wasmer 4.2.2 (??????? 2023-10-05)
binary: wasmer-cli
commit-hash: 
commit-date: 2023-10-05
host: aarch64-apple-darwin
compiler: cranelift

Steps to reproduce

say.hpp

#pragma once
#include <string>
namespace wpe {
size_t say(const std::string& message);
} // namespace wpe

say.cpp

#include <wpe/say.hpp>
#include <iostream>
namespace wpe {
size_t say(const std::string& message) {
    std::cout << message << std::endl;
    return message.length();
}
} // namespace wpe

test_say.cpp

#include <gtest/gtest.h>
#include <wpe/say.hpp>
TEST(test_wpe, say) {
    std::string what = "Hello, world!";
    auto len = wpe::say(what);
    TEST_EQ(what.length(), len);
}

If more code is needed let me know, and I can clean up the working repo and zip it.

Attached are the compiled output binaries from Emscripten, given the above source code and earlier-mentioned flags: test_wpe.zip

When using a toml file to invoke, here is what I used (which I manually added to the bin directory of my build output)

[package]
name = "wpe/wpe_wasm"
version = "0.0.1"
description = "TBD"
entrypoint = "main"

[[module]]
name = "test_wpe"
source = "./test_wpe.wasm"

[[command]]
name = "main"
module = "test_wpe"
runner = "https://webc.org/runner/emscripten"

Expected behavior

Output should be analogous to the natively-compiled version:

jgiampietro@Jamess-MacBook-Pro-2 wasm_package_example % ./build/Debug/native/bin/test_wpe 
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from test_wpe
[ RUN      ] test_wpe.say
Hello, world!
[       OK ] test_wpe.say (0 ms)
[----------] 1 test from test_wpe (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

Actual behavior

The below error is emitted, which seems to be an Emscripten builtin:

error: Instantiation failed
╰─▶ 1: Error while importing "env"."invoke_iii": unknown import. Expected Function(FunctionType { params: [I32, I32, I32], results: [I32] })

jnickg avatar Oct 18 '23 15:10 jnickg

Of significant note is that this is JS glue code Emscripten uses to support exceptions. If I compile with the -fwasm-exceptions flag this problem goes away, but then I am left with the following error:

error: Unable to compile "/Users/jgiampietro/code/git.corp.adobe.com/jgiampietro/wasm_package_example/build/Debug/web/bin/test_wpe.wasm"
╰─▶ 1: Validation error: exceptions proposal not enabled (at offset 0x3a40)

Cross-linking with #3307 because of that. Is there any way to prioritize exception support and/or get visibility into the timeline for its support? I've worked in embedded systems where exceptions aren't supported, even that is rare nowadays, as exceptions are so pervasive, and required for so many third-party libraries.

jnickg avatar Oct 20 '23 16:10 jnickg

If you are running into issues when doing wasmer run some_emscripten_module.wasm, it's probably a bug in the wasmer_emscripten::is_emscripten_module() check used by the wasmer run command:

https://github.com/wasmerio/wasmer/blob/9127dde836d3fd12dedfa74054508bb66ddd49df/lib/cli/src/commands/run.rs#L146-L152

Michael-F-Bryan avatar Oct 24 '23 08:10 Michael-F-Bryan

It looks like is_emscripten_module is looking for imports that emscripten (no longer?) provides:

https://github.com/wasmerio/wasmer/blob/94edff8e2bb4a1e7cad5f2979e04d3634a3e867f/lib/emscripten/src/utils.rs#L14-L27

$ wasmer inspect llvm-box.wasm 
Type: wasm
Size: 59.4 MB
Imports:
  Functions:
    "wasi_snapshot_preview1"."fd_close": [I32] -> [I32]
    "env"."invoke_ii": [I32, I32] -> [I32]
    "wasi_snapshot_preview1"."proc_exit": [I32] -> []
    "wasi_snapshot_preview1"."fd_write": [I32, I32, I32, I32] -> [I32]
    "env"."__syscall_unlinkat": [I32, I32, I32] -> [I32]
    "wasi_snapshot_preview1"."fd_read": [I32, I32, I32, I32] -> [I32]
    "wasi_snapshot_preview1"."fd_fdstat_get": [I32, I32] -> [I32]
    "env"."invoke_iiii": [I32, I32, I32, I32] -> [I32]
    "wasi_snapshot_preview1"."environ_sizes_get": [I32, I32] -> [I32]
    "wasi_snapshot_preview1"."clock_time_get": [I32, I64, I32] -> [I32]
    "env"."emscripten_notify_memory_growth": [I32] -> []
    "wasi_snapshot_preview1"."args_get": [I32, I32] -> [I32]
    "env"."_emscripten_throw_longjmp": [] -> []
    "env"."__syscall_utimensat": [I32, I32, I32, I32] -> [I32]
    "wasi_snapshot_preview1"."args_sizes_get": [I32, I32] -> [I32]
    "env"."__syscall_symlink": [I32, I32] -> [I32]
    "env"."__syscall_statfs64": [I32, I32, I32] -> [I32]
    "env"."__syscall_renameat": [I32, I32, I32, I32] -> [I32]
    "env"."__syscall_rmdir": [I32] -> [I32]
    "env"."__syscall_readlinkat": [I32, I32, I32, I32] -> [I32]
    "env"."__syscall_getdents64": [I32, I32, I32] -> [I32]
    "env"."__call_sighandler": [I32, I32] -> []
    "env"."invoke_vi": [I32, I32] -> []
    "wasi_snapshot_preview1"."fd_pread": [I32, I32, I32, I64, I32] -> [I32]
    "env"."__syscall_mkdirat": [I32, I32, I32] -> [I32]
    "wasi_snapshot_preview1"."fd_seek": [I32, I64, I32, I32] -> [I32]
    "env"."__syscall_getcwd": [I32, I32] -> [I32]
    "env"."__syscall_ftruncate64": [I32, I64] -> [I32]
    "env"."__syscall_lstat64": [I32, I32] -> [I32]
    "env"."__syscall_newfstatat": [I32, I32, I32, I32] -> [I32]
    "env"."__syscall_stat64": [I32, I32] -> [I32]
    "env"."__syscall_fchown32": [I32, I32, I32] -> [I32]
    "env"."invoke_vii": [I32, I32, I32] -> []
    "env"."__syscall_fchmod": [I32, I32] -> [I32]
    "env"."_dlinit": [I32] -> []
    "env"."_dlsym_js": [I32, I32] -> [I32]
    "env"."_dlopen_js": [I32] -> [I32]
    "env"."__syscall_dup3": [I32, I32, I32] -> [I32]
    "env"."__syscall_chmod": [I32, I32] -> [I32]
    "env"."__syscall_chdir": [I32] -> [I32]
    "env"."__syscall_faccessat": [I32, I32, I32, I32] -> [I32]
    "wasi_snapshot_preview1"."environ_get": [I32, I32] -> [I32]
  Memories:
  Tables:
  Globals:
Exports:
  Functions:
    "__main_argc_argv": [I32, I32] -> [I32]
    "free": [I32] -> []
    "malloc": [I32] -> [I32]
    "_start": [] -> []
    "emscripten_builtin_memalign": [I32, I32] -> [I32]
    "setThrew": [I32, I32] -> []
    "stackSave": [] -> [I32]
    "stackRestore": [I32] -> []
  Memories:
    "memory": not shared (256 pages..32768 pages)
  Tables:
    "__indirect_function_table": FuncRef (34828..34828)
  Globals:

davidar avatar Jul 27 '24 02:07 davidar

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[bot] avatar Sep 13 '25 00:09 stale[bot]

Feel free to reopen the issue if it has been closed by mistake.

stale[bot] avatar Oct 13 '25 09:10 stale[bot]