emscripten icon indicating copy to clipboard operation
emscripten copied to clipboard

Signature mismatch between side and main module with flto

Open gl84 opened this issue 1 year ago • 1 comments

Enabling LTO in the side module breaks the loading of the side module. To reproduce the error one can use the following code:

catch.hpp:

#include <string>
#include <sstream>
#include "emscripten.h"

// Part of catch2 lib that reproduces error
class StreamBufImpl : public std::streambuf {
    public:
        StreamBufImpl() = default;
};

class DebugOutStream {
    StreamBufImpl m_streamBuf;
public:
    DebugOutStream() :   m_streamBuf() {}
};

auto makeStream() -> auto const* {
    return new DebugOutStream();
}

main_catch2.cpp:

#include "catch.hpp"

EMSCRIPTEN_KEEPALIVE
int main( int argc, char* argv[] ) {
    return EXIT_SUCCESS;
}

Test_Impl.cpp:

#include "catch.hpp"

EMSCRIPTEN_KEEPALIVE extern "C" void func() {}

Compiling with em++ -fPIC -flto -O0 -o libmain_catch2.so -sSIDE_MODULE=1 main_catch2.cpp && em++ -flto -O0 -sMAIN_MODULE=2 -sEXPORTED_FUNCTIONS=_func -sASSERTIONS=2 Test_Impl.cpp -o Test.js libmain_catch2.so

leads to a runtime Error: [LinkError: WebAssembly.instantiate(): Import #8 module="env" function="_ZNSt3__215basic_streambufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj" error: imported function does not match the expected type].

em++ --version: 3.1.64 node --version: v18.20.3

gl84 avatar Aug 19 '24 10:08 gl84

I managed to reproduce and simplified a little:

main.c:

int main(int argc, char* argv[]) {
  return 0;
}

catch.cpp:

#include "catch.hpp"
$ em++ -flto -c -sSIDE_MODULE=1 catch.cpp
$ em++ -o libside.so -sSIDE_MODULE=1 catch.o
$ emcc -sMAIN_MODULE=2 -sASSERTIONS=2 main.c libside.so
$ node ./a.out.js
node:internal/process/promises:288
            triggerUncaughtException(err, true /* fromPromise */);
            ^

[LinkError: WebAssembly.instantiate(): Import #8 module="env" function="_ZNSt3__215basic_streambufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj" error: imported function does not match the expected type]

Node.js v18.20.3

It seems like somehow the signature of that function (which is std::__2::basic_streambuf<char, std::__2::char_traits<char> >::seekoff(long long, std::__2::ios_base::seekdir, unsigned int) by the way) is modified by the LTO process when linking the side module.

I don't know why that would be at this point I'm afraid.

sbc100 avatar Sep 07 '24 00:09 sbc100