emscripten-core/emscripten

Duplicate symbols using EM_JS in single header

Closed this issue · 7 comments

Hi,

I am dealing with a library which make a single header and a regular static library. Declaring function in header and implementing them with EM_JS in c source file work well. But when I merge the header and the c file to make a single header, __em_js_X global variables are declared multiple time (once per single header inclusion).

For my need, I made a INLINE_EM_JS macro which add the inline keyword to both __em_js_X global variables. But it is not working for both cases. I replaced EM_JS by INLINE_EM_JS when I make the single header.

#define _INLINE_EM_JS(ret, c_name, js_name, params, code)                                     \
    _EM_BEGIN_CDECL                                                                           \
    ret c_name params EM_IMPORT(js_name);                                                     \
    __attribute__((visibility("hidden"))) inline void* __em_js_ref_##c_name = (void*)&c_name; \
    EMSCRIPTEN_KEEPALIVE                                                                      \
    __attribute__((section("em_js"), aligned(1))) inline char __em_js__##js_name[] =          \
        #params "<::>" code;                                                                  \
    _EM_END_CDECL
#define INLINE_EM_JS(ret, name, params, ...) _INLINE_EM_JS(ret, name, name, params, #__VA_ARGS__)

Do you think there is another smarter way?

Regards,
Theo



Please include the following in your bug report:

Version of emscripten/emsdk:
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 4.0.10 (b7dc6e5)
clang version 21.0.0git (https:/github.com/llvm/llvm-project 8f7e57485ee73205e108d74abb5565d5c63beaca)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /home/****/Dev/emscripten-sdk/upstream/bin

Failing command line in full:

[ 66%] Linking CXX executable inline_em_js.js
wasm-ld: error: duplicate symbol: __em_js_ref_js_foo
>>> defined in CMakeFiles/inline_em_js.dir/test.cpp.o
>>> defined in CMakeFiles/inline_em_js.dir/main.cpp.o

wasm-ld: error: duplicate symbol: __em_js__js_foo
>>> defined in CMakeFiles/inline_em_js.dir/test.cpp.o
>>> defined in CMakeFiles/inline_em_js.dir/main.cpp.o
em++: error: '/home/****/Dev/emscripten-sdk/upstream/bin/wasm-ld -o inline_em_js.wasm CMakeFiles/inline_em_js.dir/test.cpp.o CMakeFiles/inline_em_js.dir/main.cpp.o -L/home/****/Dev/emscripten-sdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten -L/home/****/Dev/emscripten-sdk/upstream/emscripten/src/lib -lGL-getprocaddr -lal -lhtml5 -lstubs-debug -lnoexit -lc-debug -ldlmalloc-debug -lcompiler_rt -lc++-noexcept -lc++abi-debug-noexcept -lsockets -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr /tmp/tmpcufv9h7vlibemscripten_js_symbols.so --strip-debug --export=emscripten_stack_get_end --export=emscripten_stack_get_free --export=emscripten_stack_get_base --export=emscripten_stack_get_current --export=emscripten_stack_init --export=_emscripten_stack_alloc --export=__wasm_call_ctors --export=_emscripten_stack_restore --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=main --export-if-defined=__main_argc_argv --export-if-defined=fflush --export-table -z stack-size=65536 --no-growable-memory --initial-heap=16777216 --no-entry --stack-first --table-base=1' failed (returned 1)
gmake[2]: *** [CMakeFiles/inline_em_js.dir/build.make:117: inline_em_js.js] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:87: CMakeFiles/inline_em_js.dir/all] Error 2
gmake: *** [Makefile:91: all] Error 2

Full link command and output with -v appended:

[ 33%] Linking CXX executable inline_em_js.js
 /home/****/Dev/emscripten-sdk/upstream/bin/clang --version
 /home/****Dev/emscripten-sdk/upstream/bin/wasm-ld -o inline_em_js.wasm CMakeFiles/inline_em_js.dir/test.cpp.o CMakeFiles/inline_em_js.dir/main.cpp.o -L/home/****/Dev/emscripten-sdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten -L/home/****/Dev/emscripten-sdk/upstream/emscripten/src/lib -lGL-getprocaddr -lal -lhtml5 -lstubs-debug -lnoexit -lc-debug -ldlmalloc-debug -lcompiler_rt -lc++-noexcept -lc++abi-debug-noexcept -lsockets -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr /tmp/tmpwik2u1t7libemscripten_js_symbols.so --strip-debug --export=emscripten_stack_get_end --export=emscripten_stack_get_free --export=emscripten_stack_get_base --export=emscripten_stack_get_current --export=emscripten_stack_init --export=_emscripten_stack_alloc --export=__wasm_call_ctors --export=_emscripten_stack_restore --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=main --export-if-defined=__main_argc_argv --export-if-defined=fflush --export-table -z stack-size=65536 --no-growable-memory --initial-heap=16777216 --no-entry --stack-first --table-base=1
wasm-ld: error: duplicate symbol: __em_js_ref_js_foo
>>> defined in CMakeFiles/inline_em_js.dir/test.cpp.o
>>> defined in CMakeFiles/inline_em_js.dir/main.cpp.o

wasm-ld: error: duplicate symbol: __em_js__js_foo
>>> defined in CMakeFiles/inline_em_js.dir/test.cpp.o
>>> defined in CMakeFiles/inline_em_js.dir/main.cpp.o
em++: error: '/home/****/Dev/emscripten-sdk/upstream/bin/wasm-ld -o inline_em_js.wasm CMakeFiles/inline_em_js.dir/test.cpp.o CMakeFiles/inline_em_js.dir/main.cpp.o -L/home/****/Dev/emscripten-sdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten -L/home/****/Dev/emscripten-sdk/upstream/emscripten/src/lib -lGL-getprocaddr -lal -lhtml5 -lstubs-debug -lnoexit -lc-debug -ldlmalloc-debug -lcompiler_rt -lc++-noexcept -lc++abi-debug-noexcept -lsockets -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr /tmp/tmpwik2u1t7libemscripten_js_symbols.so --strip-debug --export=emscripten_stack_get_end --export=emscripten_stack_get_free --export=emscripten_stack_get_base --export=emscripten_stack_get_current --export=emscripten_stack_init --export=_emscripten_stack_alloc --export=__wasm_call_ctors --export=_emscripten_stack_restore --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=main --export-if-defined=__main_argc_argv --export-if-defined=fflush --export-table -z stack-size=65536 --no-growable-memory --initial-heap=16777216 --no-entry --stack-first --table-base=1' failed (returned 1)
gmake[2]: *** [CMakeFiles/inline_em_js.dir/build.make:117: inline_em_js.js] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:87: CMakeFiles/inline_em_js.dir/all] Error 2
gmake: *** [Makefile:91: all] Error 2

Note: Where possible, please avoid attaching screen shots of code or console
logs. Instead, please include them as text so that they may be copied /
searched. To make code blocks more readable and syntax highlighted, please
escape them with three backticks before and after, like this:

inline_em_js.zip

related to #21364

EM_JS symbols are always global symbols, since they live in JS. Its not possible today to declare the same EM_JS symbols in two different object files.

We maybe be able to add weak or static variant which is probably what you want here. Can you static first, which seems safer?

with static or static inline. It seems that error was debated in previous issues.

error: undefined symbol: js_foo (referenced by root reference (e.g. compiled C/C++ code))
warning: To disable errors for undefined symbols use `-sERROR_ON_UNDEFINED_SYMBOLS=0`
warning: _js_foo may need to be added to EXPORTED_FUNCTIONS if it arrives from a system library
Error: Aborting compilation due to previous errors
em++: error: '/home/****/Dev/emscripten-sdk/node/20.18.0_64bit/bin/node /home/****/Dev/emscripten-sdk/upstream/emscripten/tools/compiler.mjs -' failed (returned 1)
gmake[2]: *** [CMakeFiles/inline_em_js.dir/build.make:133: inline_em_js.js] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:87: CMakeFiles/inline_em_js.dir/all] Error 2
gmake: *** [Makefile:91: all] Error 2

The best would be to declare the symbol in one inclusion and to declare it extern in another. But I don't find a way yet :).

The inline keyword works in my little example. How to make it fail?

I saw this technique but it seems fragile : https://developer.arm.com/documentation/ka002428/latest

I didn't know the weak attribute. This is working too! It also work when symbols are declare in a c file.

#define _WEAK_EM_JS(ret, c_name, js_name, params, code)                  \
    _EM_BEGIN_CDECL                                                      \
    ret c_name params EM_IMPORT(js_name);                                \
    __attribute__((weak, visibility("hidden")))                          \
    void* __em_js_ref_##c_name = (void*)&c_name;                         \
    EMSCRIPTEN_KEEPALIVE                                                 \
    __attribute__((weak, section("em_js"), aligned(1)))                  \
    char __em_js__##js_name[] = #params "<::>" code;                     \
    _EM_END_CDECL
#define WEAK_EM_JS(ret, name, params, ...) _WEAK_EM_JS(ret, name, name, params, #__VA_ARGS__)

Nice! Glad to hear that works

I will make more test about inline and weak symbols. Thanks for your help.

You can close the issue, I don't think it is a good idea to make the EM_JS symbols weak in emscripten since it allows an override and probably takes more time to compile.