Skip to content

Commit 78de8b2

Browse files
committed
Fix nested dynamic library loading via RPATH
There is a bug with dynamic library loading. Suppose liba.so is on LD_LIBRARY_PATH and it has an RPATH of `$ORIGIN/other_dir` and loads `libb.so` from other_dir. Then suppose `libb.so` has an RPATH of `$ORIGIN` and wants to load `libc.so` also from `other_dir`. Before this PR this doesn't work. The problem is that `flags.rpath.parentLibPath` is set to `libb.so` and we replace $ORIGIN with `PATH.dirname(parentLibName)` which is `.`. So unless `other_dir` is on the `LD_LIBRARY_PATH` or is the current working directory, loading will fail. The fix: if `findLibraryFS()` returns a value that is not `undefined`, replace `libName` with the returned value.
1 parent a45c791 commit 78de8b2

File tree

3 files changed

+65
-2
lines changed

3 files changed

+65
-2
lines changed

src/lib/libdylink.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,7 @@ var LibraryDylink = {
10511051
#endif
10521052
}
10531053
var rpathResolved = (rpath?.paths || []).map((p) => replaceORIGIN(rpath?.parentLibPath, p));
1054-
return withStackSave(() => {
1054+
var result = withStackSave(() => {
10551055
// In dylink.c we use: `char buf[2*NAME_MAX+2];` and NAME_MAX is 255.
10561056
// So we use the same size here.
10571057
var bufSize = 2*255 + 2;
@@ -1061,6 +1061,7 @@ var LibraryDylink = {
10611061
var resLibNameC = __emscripten_find_dylib(buf, rpathC, libNameC, bufSize);
10621062
return resLibNameC ? UTF8ToString(resLibNameC) : undefined;
10631063
});
1064+
return FS.lookupPath(result).path;
10641065
},
10651066
#endif // FILESYSTEM
10661067

@@ -1168,6 +1169,7 @@ var LibraryDylink = {
11681169
dbg(`checking filesystem: ${libName}: ${f ? 'found' : 'not found'}`);
11691170
#endif
11701171
if (f) {
1172+
libName = f;
11711173
var libData = FS.readFile(f, {encoding: 'binary'});
11721174
return flags.loadAsync ? Promise.resolve(libData) : libData;
11731175
}

test/test_other.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2437,6 +2437,67 @@ def get_runtime_paths(path):
24372437
self.assertEqual(get_runtime_paths('libside1.so'), ['$ORIGIN'])
24382438
self.assertEqual(get_runtime_paths('a.out.wasm'), ['$ORIGIN'])
24392439

2440+
def test_dylink_dependencies_rpath_nested(self):
2441+
create_file('pre.js', r'''
2442+
Module.preRun.push(() => {
2443+
Module.ENV.LD_LIBRARY_PATH = "/lib1";
2444+
});
2445+
''')
2446+
create_file('side1.c', r'''
2447+
#include <stdio.h>
2448+
2449+
void side2();
2450+
2451+
void side1() {
2452+
printf("side1\n");
2453+
side2();
2454+
}
2455+
''')
2456+
create_file('side2.c', r'''
2457+
#include <stdio.h>
2458+
void side3();
2459+
2460+
void side2() {
2461+
printf("side2\n");
2462+
side3();
2463+
}
2464+
''')
2465+
create_file('side3.c', r'''
2466+
#include <stdio.h>
2467+
2468+
void side3() {
2469+
printf("side3\n");
2470+
}
2471+
''')
2472+
create_file('main.c', r'''
2473+
#include <dlfcn.h>
2474+
#include <stdio.h>
2475+
2476+
typedef void (*F)(void);
2477+
2478+
int main() {
2479+
void* handle = dlopen("libside1.so", RTLD_NOW);
2480+
F side1 = (F)dlsym(handle, "side1");
2481+
2482+
printf("main\n");
2483+
side1();
2484+
return 0;
2485+
}
2486+
''')
2487+
os.mkdir('libs')
2488+
os.mkdir('lib1')
2489+
self.emcc('side3.c', ['-fPIC', '-sSIDE_MODULE', '-olibs/libside3.so'])
2490+
self.emcc('side2.c', ['-fPIC', '-sSIDE_MODULE', '-olibs/libside2.so', '-Wl,-rpath,$ORIGIN', 'libs/libside3.so'])
2491+
self.emcc('side1.c', ['-fPIC', '-sSIDE_MODULE', '-Wl,-rpath,$ORIGIN/../libs', '-olib1/libside1.so', 'libs/libside2.so'])
2492+
settings = ['-sMAIN_MODULE=2', '-sDYLINK_DEBUG', "-sEXPORTED_FUNCTIONS=[_printf,_main]", "-sEXPORTED_RUNTIME_METHODS=ENV"]
2493+
preloads = []
2494+
for file in ['lib1/libside1.so', 'libs/libside2.so', 'libs/libside3.so']:
2495+
preloads += ['--preload-file', file]
2496+
cmd = [EMCC, 'main.c', '-fPIC', '--pre-js', 'pre.js'] + settings + preloads
2497+
self.run_process(cmd)
2498+
2499+
self.run_js('a.out.js')
2500+
24402501
def test_dylink_LEGACY_GL_EMULATION(self):
24412502
# LEGACY_GL_EMULATION wraps JS library functions. This test ensure that when it does
24422503
# so it preserves the `.sig` attributes needed by dynamic linking.

0 commit comments

Comments
 (0)