Search code examples
c++emscriptenmismatch

WASM link error: function signature mismatch


I've been using WASM via emscripten for a few weeks now and had been making good progress, until this error:

exception thrown: RuntimeError: function signature mismatch,RuntimeError: function signature mismatch

This started happening in code that previously worked and seems to be something to do with WASMs lack of support for 64bit integers in javascript and the offsets used in file management. I've made an isolated case:

#include <iostream>

int main(int argc, char const *argv[])
{   
    char test[30];
    std::cout << __LINE__ << std::endl;
    FILE *f = fopen("minimal_call_dispatch.cpp","ra");
    std::cout << __LINE__ << std::endl; 
    fseek(f, 100, SEEK_SET);
    std::cout << __LINE__ << std::endl;
    fclose(f);
    std::cout << __LINE__ << std::endl; 
    return 0;
}

building with:

call emcc -o ./test.js test_file.cpp -s WASM=1 -s NO_EXIT_RUNTIME=1 -std=c++1z 

Which outputs '6\n8\n' before failing on 'fseek' due to the mentioned error.

Somewhere along the line, I suspect that wasm is trying to use headers that communicate with javascript that have 64bit integers instead of 32bit ones but I can't see how that could have happened. I'm going to try re-installing emscripten but even if that works, I'd like to get a better idea of what's causing this.

As a work around, does anybody know how to get emcc to ignore worries over 64bit integers and silently convert them to 32bit? I'm not looking to address more than 3gigs of ram, after all.


Solution

  • You got the error simply because your code is wrong.

    1. You really don't need to worry about 32bit/64bit stuff. The current WebAssembly spec do not have 64-bit machine and it only have 32-bit addresses (but it supports 64-bit integer i64 internally and calculates 64-bit integers well. It just doesn't have 64-bit addresses.) This means that the compiler is already knows the target machine is 32-bit and size_t will be 32-bit as will.
    2. In the comments, I asked if you want to open the file or want to create the file. This is important because using "ra" with fopen() will cause segmentation fault when the file doesn't exist because of r flag!

    Okay, lets try this in the native environment.

    g++ test_file.cpp -o test.out
    

    running ./test.out will prints the following if test_file.cpp doesn't exist:

    6
    8
    [2]    14464 segmentation fault (core dumped)  ./test.out
    

    So your code is wrong, but why Emscripten throws the error differently? When you use emcc without debugging flags like -g, it will have the minimized environment that doesn't catch errors like segfault because such smart runtime will increase the binary size which is critical in the web environment. As a result the runtime keeps running ignoring segfualt and it will end up with a random error. So function signature mismatch doesn't mean anything.

    You can build it using debugging-related options:

    emcc -o ./test.html  test_file.cpp -s WASM=1 -std=c++1z \
        -g4 -s ASSERTIONS=2 -s SAFE_HEAP=1 -s STACK_OVERFLOW_CHECK=1 \
        --source-map-base http://your_server_ip:port/
    

    Then opening test.html you will see the correct error now:

    exception thrown: RuntimeError: abort(segmentation fault) at Error
    

    Now you won't get the misleading errors like signature mismatch :)

    By the way, replace fopen("minimal_call_dispatch.cpp","ra"); with fopen("minimal_call_dispatch.cpp","a"); will fix the error.

    With the correct --source-map-base server IP settings, you will have a better debugging experience. For example, you will get the source test_file.cpp in the browser so that you can set breakpoints to the .cpp file.

    Have fun with debugging :)