Description
Version of emscripten/emsdk:
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.26 (8eaf19f)
clang version 16.0.0 (https://github.com/llvm/llvm-project f81f0cb75a2808a67d2662f044ad07628fc9d900)
loadDynamicLibrary
, which is internally used in dlopen
, loads a dynamic library and its dependencies.
When loading dependencies, loadDynamicLibrary
propagates flags
parameter as-is. So if a target library is loaded locally (e.g. loadDynamicLibrary(..., {global: false})
or dlopen(..., RTLD_NOW | RTLD_LOCAL)
) it also loads its dependencies locally.
This is problematic because normally a shared library needs to use symbols from its dependencies.
Edited
The problem is that currently, a side module cannot see symbols of its dependencies if they are loaded locally. For example, when a.so
depends on b.so
, if we load a.so
locally via dlopen("a.so", RTLD_LOCAL)
, a.so
can't use symbols from b.so
.
How to reproduce
main.c
depends on dep.so
and dep.so
depends on dep2.so
. In native (linux), this works fine.
#!/bin/bash
set -e
cat > dep2.h << EOF
int _add(int a);
EOF
cat > dep2.c << EOF
int _add(int a) { return a; }
EOF
cat > dep.h << EOF
int add(int a);
EOF
cat > dep.c << EOF
#include "dep2.h"
int add(int a) { return _add(a) + 1; }
EOF
cat > main.c << EOF
#include<dlfcn.h>
#include<stdio.h>
#include "dep.h"
int main() {
void *lib_handle = dlopen("./dep.so", RTLD_NOW | RTLD_LOCAL);
int (*add)(int) = (int (*)(int))dlsym(lib_handle, "add");
printf("3 + 1 = %d\n", add(3));
}
EOF
rm -rf dep.o dep2.o dep.so dep2.so main
gcc dep2.c -shared -o dep2.so
gcc dep.c dep2.so -shared -o dep.so
gcc main.c -ldl -o main
LD_LIBRARY_PATH=$(pwd) ./main
While in emscripten, the same example raises error,
#!/bin/bash
set -e
cat > dep2.h << EOF
int _add(int a);
EOF
cat > dep2.c << EOF
int _add(int a) { return a; }
EOF
cat > dep.h << EOF
int add(int a);
EOF
cat > dep.c << EOF
#include "dep2.h"
int add(int a) { return _add(a) + 1; }
EOF
cat > main.c << EOF
#include<dlfcn.h>
#include<stdio.h>
#include "dep.h"
int main() {
void *lib_handle = dlopen("./dep.so", RTLD_NOW | RTLD_LOCAL);
int (*add)(int) = (int (*)(int))dlsym(lib_handle, "add");
printf("3 + 1 = %d\n", add(3));
}
EOF
rm -rf dep.o dep2.o dep.so dep2.so main
emcc -c dep.c
emcc -c dep2.c
emcc dep2.o -shared -o dep2.so -sSIDE_MODULE
emcc dep.o dep2.so -shared -o dep.so -sSIDE_MODULE
emcc main.c -sMAIN_MODULE -o main
node main
Aborted(Assertion failed: undefined symbol `_add`. perhaps a side module was not linked in?
if this global was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment)