Skip to content

Bad function pointer using fmt in a side-module. #22587

Closed
@jdumas

Description

@jdumas

Version of emscripten/emsdk:

I'm on macOS 14.5.

❯ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.65-git
clang version 20.0.0git
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /opt/homebrew/Cellar/emscripten/3.1.65/libexec/llvm/bin

Failing command line in full:

Consider the following project:

CMakeLists.txt

# ------------------------------------------------------------------------------
# CMAKE SETUP
# ------------------------------------------------------------------------------

cmake_minimum_required(VERSION 3.25)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Enable/Disable output of compile commands during generation.")

project("Wasm Side Module")

# ------------------------------------------------------------------------------
# OPTIONS
# ------------------------------------------------------------------------------

option(USE_EXCEPTIONS "Use Exceptions" ON)
option(USE_WASM_EXCEPTIONS "Use WASM Exceptions" OFF)

set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard version to compile with")
set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Whether the C++ standard version is a requirement")
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Whether compiler specific extensions are requested")

# ------------------------------------------------------------------------------
# DEPENDENCIES
# ------------------------------------------------------------------------------

if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build" FORCE)
endif()

include(FetchContent)

# Need dependencies to get them
add_compile_options(
    -fPIC
    -flto
    -fsanitize=undefined
    -Werror=bad-function-cast
    -Werror=cast-function-type
    -fno-omit-frame-pointer
)
add_link_options(
    -fPIC
    -flto
    -fsanitize=undefined
)
if(EMSCRIPTEN)
    add_link_options(
        --profiling-funcs
        -sSTACK_OVERFLOW_CHECK=2
        -sDYNCALLS=1
        -sEXCEPTION_STACK_TRACES=1
        -sSAFE_HEAP
        -sASSERTIONS=2
    )
endif()

if(USE_EXCEPTIONS)
    add_compile_options(-fexceptions)
    add_link_options(-fexceptions)
elseif(USE_WASM_EXCEPTIONS)
    add_compile_options(-fwasm-exceptions)
    add_link_options(-fwasm-exceptions)
endif()

FetchContent_Declare(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG ed8f8be70d82b0a081286e262867956e565c1bf6
)
FetchContent_MakeAvailable(
    fmt
)

if(EMSCRIPTEN)

    # Side module
    add_executable(side_module side_module.cpp)
    target_link_options(side_module PRIVATE -sSIDE_MODULE=2)

    target_link_libraries(side_module PRIVATE fmt::fmt)
    set_target_properties(side_module PROPERTIES
        PREFIX ""
        SUFFIX ".wasm"
        ENABLE_EXPORTS ON
    )

    # Main module
    add_executable(main_module main_module.cpp)
    target_link_options(main_module PRIVATE
        -sMAIN_MODULE=2
        -sNO_AUTOLOAD_DYLIBS
        $<TARGET_FILE:side_module>
    )

    target_link_libraries(main_module PRIVATE side_module)
    set_target_properties(main_module PROPERTIES SUFFIX ".cjs")

endif()

main_module.cpp

#include <iostream>

#include <dlfcn.h>

int main(int argc, char** argv)
{

    void* handle = dlopen("side_module.wasm", RTLD_NOW);

    if (!handle) {
        std::cout << "failed to dlopen" << std::endl;
        return 1;
    }

    typedef void (*func)();

    func test_func = (func)dlsym(handle, "my_function");

    {
        const char* error = dlerror();
        if (error || !test_func) {
            std::cout << "Failed to load symbol err = " << error << "\n";
            return 1;
        }
    }

    std::cout << "calling test_func" << std::endl;
    test_func();

    return 0;
}

side_module.cpp

#include <emscripten.h>
#include <fmt/format.h>

#include <iostream>
#include <map>
#include <string_view>

class MyObject
{
    struct AttributeManager
    {
        int create(std::string_view name)
        {
            std::string key(name);
            auto [it, insertion_successful] = m_name_to_id.emplace(key, m_name_to_id.size());
            return it->second;
        }

        int get_id(std::string_view name) const
        {
            std::string key(name);
            auto it = m_name_to_id.find(key);
            if (it != m_name_to_id.end()) {
                return it->second;
            } else {
                return -1;
            }
        }

    protected:
        std::map<std::string, int> m_name_to_id;
    };

    AttributeManager m_manager;

public:
    int get_attribute_id(std::string_view name) const
    {
        auto ret = m_manager.get_id(name);
        if (ret == -1) {
            throw std::runtime_error(fmt::format("Attribute '{}' does not exist.", name));
        }
        return ret;
    }

    int create_attribute(std::string_view name) { return m_manager.create(name); }
};

extern "C" {

EMSCRIPTEN_KEEPALIVE
void my_function()
{
    MyObject foo;
    int x = foo.create_attribute("foo");
    int y = foo.create_attribute("bar");

    std::cout << foo.get_attribute_id("foo") << std::endl;
    std::cout << foo.get_attribute_id("bar") << std::endl;
}

}

Steps to reproduce:

Compile with the following:

emcmake cmake ..
cmake --build . -j 12
node main_module.cjs

Failing output:

❯ node main_module.cjs
calling test_func
Aborted(Assertion failed: bad function pointer type - dynCall function not found for sig 'iij')
/Users/jedumas/sandbox/wasm/build/main_module.cjs:638
  /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what);
                                        ^

RuntimeError: Aborted(Assertion failed: bad function pointer type - dynCall function not found for sig 'iij')
    at abort (/Users/jedumas/sandbox/wasm/build/main_module.cjs:638:41)
    at assert (/Users/jedumas/sandbox/wasm/build/main_module.cjs:370:5)
    at dynCallLegacy (/Users/jedumas/sandbox/wasm/build/main_module.cjs:1268:3)
    at dynCall (/Users/jedumas/sandbox/wasm/build/main_module.cjs:1297:13)
    at /Users/jedumas/sandbox/wasm/build/main_module.cjs:1805:12
    at stubs.<computed> (/Users/jedumas/sandbox/wasm/build/main_module.cjs:1955:20)
    at side_module.wasm.legalfunc$invoke_iij (wasm://wasm/side_module.wasm-002f3df6:wasm-function[213]:0x21ab1)
    at side_module.wasm.my_function (wasm://wasm/side_module.wasm-002f3df6:wasm-function[72]:0x7691)
    at main_module.wasm.main (wasm://wasm/main_module.wasm-00f4246e:wasm-function[398]:0x23b11)
    at callMain (/Users/jedumas/sandbox/wasm/build/main_module.cjs:6651:15)

Node.js v22.7.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions