bytecodealliance / wasm-micro-runtime

WebAssembly Micro Runtime (WAMR)
Apache License 2.0
4.95k stars 624 forks source link

Running my host in GDB gives "uvwasi init failed" #2090

Open Photosounder opened 1 year ago

Photosounder commented 1 year ago

Edit: It also happens when I launch the host by double-clicking the .exe file, so the only time the problem doesn't occur is when I launch the program from the MSYS2 command line.

I made a host in C that loads and executes a function from a WASM file, and everything works smoothly when I run the host outside of a debugger in the normal way, but not when I run the host in GDB in which case wasm_runtime_instantiate() fails with the error message uvwasi init failed. That's all it says, everything up to that point works correctly. So somehow running my host program in GDB makes "uvwasi init" fail.

To compile the host I use clang version 15.0.7 Target: x86_64-w64-windows-gnu (that's the MinGW64 version of clang) and GDB is GNU gdb (GDB) 13.1 (also MinGW64's version of GDB).

Here's the relevant code up to the failure:

typedef struct
{
    wasm_module_t module;
    wasm_module_inst_t module_inst;
    wasm_exec_env_t exec_env;
    uint32_t stack_size, heap_size;
    char error_buf[128];
} wasm_ctx_t;

void native_print(wasm_exec_env_t exec_env, char *string)
{
    fprintf(stdout, "native_print(\"%s\") (pointer = 0x%08X)\n", string, string);
}

static NativeSymbol native_symbols[] = { { "native_print", native_print, "($)" } };

void wasm_host()
{
    static int init = 1;
    static wasm_ctx_t ctx = {0};

    if (init)
    {
        init = 0;

        ctx.stack_size = 256+40 + (1<<20);
        ctx.heap_size = 1024*1024+9 + (1<<20);

        // Init runtime
        if (!wasm_runtime_init())
        {
            fprintf(stderr, "Init runtime environment wasm_runtime_init() failed.\n");
            return;
        }

        // Register native functions
        if (!wasm_runtime_register_natives("env", native_symbols, sizeof(native_symbols) / sizeof(NativeSymbol)))
        {
            fprintf(stderr, "Register native functions wasm_runtime_register_natives() failed.\n");
            return;
        }

        // Load WASM file
        buffer_t wasm_buf = buf_load_raw_file("test.wasm");

        ctx.module = wasm_runtime_load(wasm_buf.buf, wasm_buf.len, ctx.error_buf, sizeof(ctx.error_buf));
        if (!ctx.module)
        {
            fprintf(stderr, "Load wasm module wasm_runtime_load() failed. error: %s\n", ctx.error_buf);
            return;
        }

        free_buf(&wasm_buf);

        // Create instance of WASM module
        ctx.module_inst = wasm_runtime_instantiate(ctx.module, ctx.stack_size, ctx.heap_size, ctx.error_buf, sizeof(ctx.error_buf));
        if (!ctx.module_inst)
        {
            fprintf(stderr, "Create instance of WASM module wasm_runtime_instantiate() failed. error: %s\n", ctx.error_buf);
            return;
        }

Host compilation command:

clang wasm_host.c rl.c -o wasm_host.exe -lwinmm -lcomdlg32 -lole32 -lOpengl32 -lShcore -lDbgHelp \
    -Wno-incompatible-pointer-types -Wno-dangling-else -Wno-parentheses -Wno-pointer-sign -Wno-shift-op-parentheses -Wno-tautological-pointer-compare \
    `sdl2-config --cflags --libs` \
    -I/d/VC_libs/include/ -I/home/lib/wasm-micro-runtime-main/core/iwasm/include \
    -L/d/VC_libs/lib/ -L/home/lib/wasm-micro-runtime-main/product-mini/platforms/windows/build \
    -O1 -g -llibiwasm
wenyongh commented 1 year ago

Hi, could you try setting wasi arguments after loading wasm module? Refer to: https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/product-mini/platforms/posix/main.c#L695-L697

Photosounder commented 1 year ago

I added this but it didn't change anything:

        // Set WASI args
#if WASM_ENABLE_LIBC_WASI != 0
        const char *dir_list[8] = { NULL };
        uint32 dir_list_size = 0;
        const char *env_list[8] = { NULL };
        uint32 env_list_size = 0;
        const char *argv[8] = { NULL };
        int argc = 0;
        wasm_runtime_set_wasi_args(ctx.module, dir_list, dir_list_size, NULL, 0,
                               env_list, env_list_size, argv, argc);
#endif

Maybe some of these values have to be set to something? My WASM file doesn't have an entry point or main() in case that matters.

wenyongh commented 1 year ago

No good idea yet, could you try tracing it here: https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/core/iwasm/common/wasm_runtime_common.c#L3053-L3056

Or simply disable uvwasi by cmake -DWAMR_BUILD_LIBC_UVWASI=0?

Photosounder commented 1 year ago

I'd love to but GDB doesn't go into API functions, I'm not sure what I'm missing for it to happen. This is how I compiled (with MinGW64):

cd product-mini/platforms/windows/
rm -rf build 2>/dev/null; mkdir build && cd build
cmake .. -G"Unix Makefiles" -DWAMR_DISABLE_HW_BOUND_CHECK=1
cmake --build . --config Debug
wenyongh commented 1 year ago

I am not sure also, maybe you can add some printf in the uvwasi source code? Normally it is cloned under: product-mini\platforms\windows\build\_deps\uvwasi-src, the unwasi_init function is implemented in source file src\uvwasi.c

Refer to: https://github.com/nodejs/uvwasi/blob/main/src/uvwasi.c#L235

Photosounder commented 1 year ago

In the meantime I tried with -DWAMR_BUILD_LIBC_UVWASI=0, and as before it works fine when launching the host from the command line, but when double-clicking the host or running it with GDB (same circumstances that caused the earlier failure) now I get Lookup WASM function plugin_draw() with wasm_runtime_lookup_function() failed., which happens in my code right after the instantiation that previously failed:

        // Create instance of WASM module
        ctx.module_inst = wasm_runtime_instantiate(ctx.module, ctx.stack_size, ctx.heap_size, ctx.error_buf, sizeof(ctx.error_buf));
        if (!ctx.module_inst)
        {
            fprintf(stderr, "Create instance of WASM module wasm_runtime_instantiate() failed. error: %s\n", ctx.error_buf);
            return;
        }

        // Lookup WASM function plugin_draw()
        func_plugin_draw = wasm_runtime_lookup_function(ctx.module_inst, "plugin_draw", NULL);
        if (!func_plugin_draw)
        {
            fprintf(stderr, "Lookup WASM function plugin_draw() with wasm_runtime_lookup_function() failed.\n");
            return;
        }

There must be something different, maybe related to environment variables, that changes between launching from the MinGW command line and launching from GDB or Windows Explorer.

Photosounder commented 1 year ago

Alright I was able to debug the library, turns out I needed to specify -DCMAKE_BUILD_TYPE=Debug. Seems like the problem has to do with detecting stdin as an unknown file type. Btw whoever wrote all this code needs to learn to use fprintf(stderr, to report errors so we'd know what's going wrong more easily. 2023-04-01 06 24 25

Photosounder commented 1 year ago

I tried compiling the libraries and my host on MSVC and I get the exact same problem.

There's definitely something weird in uvwasi__get_filetype_by_fd(). Here's the call stack: 2023-04-05 22 20 42

So it's simple, we essentially call uv__get_osfhandle(int fd = 0), _get_osfhandle(fd); returns handle = 0xfffffffffffffffe, then back in the caller fs__lstat() we then call fs__stat_handle(handle, &req->statbuf, 0). In there we call pNtQueryInformationFile() on our weird handle to get all the information we can about this "file" (we're talking about stdin here, why is this being done?) and pNtQueryInformationFile() doesn't seem to like it because it returns 0xc0000024 which means STATUS_OBJECT_TYPE_MISMATCH and means "{Wrong Type} There is a mismatch between the type of object required by the requested operation and the type of object that is specified in the request." because we're trying to treat stdin like a normal Windows file for some reason. GetLastError() returns ERROR_INVALID_HANDLE and that's the end of the story.

So clearly I'm looking at things being done that shouldn't be done at all. Note that it comes from a pretty normal call to wasm_runtime_instantiate(). I should point out that I compiled everything in Debug mode, maybe that matters.