Skip to content

Commit 87c9fe7

Browse files
committed
Fix nested dynamic library loading
See upstream PR: emscripten-core/emscripten#25694 This fixes shapely.
1 parent 8b2fa75 commit 87c9fe7

5 files changed

+131
-96
lines changed

emsdk/patches/0001-Add-useful-error-when-symbol-resolution-fails.patch

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
From 4aa8434d294dea2c98225a6a36f4300f279ec2bd Mon Sep 17 00:00:00 2001
1+
From bbfcf45d1a257b4cb69356d48bdeed93430b60a5 Mon Sep 17 00:00:00 2001
22
From: Hood Chatham <roberthoodchatham@gmail.com>
33
Date: Fri, 19 May 2023 12:19:00 -0700
4-
Subject: [PATCH 1/4] Add useful error when symbol resolution fails
4+
Subject: [PATCH 1/3] Add useful error when symbol resolution fails
55

66
Currently if symbol resolution fails, we get:
77
```js
@@ -19,7 +19,7 @@ symbol.
1919
1 file changed, 3 insertions(+)
2020

2121
diff --git a/src/lib/libdylink.js b/src/lib/libdylink.js
22-
index 491b62735..3e6839be9 100644
22+
index fdfe2e608..737a9773e 100644
2323
--- a/src/lib/libdylink.js
2424
+++ b/src/lib/libdylink.js
2525
@@ -736,6 +736,9 @@ var LibraryDylink = {

emsdk/patches/0002-Don-t-check-exports-for-being-valid-C-C-identifiers-.patch

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
From e16f400d460cfd921192b5d32a52329bfe037f96 Mon Sep 17 00:00:00 2001
1+
From 3db1a0fe9d6d6697b6af78f178aedc58c7a280b8 Mon Sep 17 00:00:00 2001
22
From: Hood Chatham <roberthoodchatham@gmail.com>
33
Date: Sat, 17 May 2025 15:15:02 -0400
4-
Subject: [PATCH 2/4] Don't check exports for being valid C/C++ identifiers in
4+
Subject: [PATCH 2/3] Don't check exports for being valid C/C++ identifiers in
55
side modules
66

77
This unbreaks Rust side modules, which have been broken since
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
From 1a07b14227c65ae49d933584bb8760f3980edfed Mon Sep 17 00:00:00 2001
2+
From: Hood Chatham <roberthoodchatham@gmail.com>
3+
Date: Fri, 31 Oct 2025 15:29:33 -0700
4+
Subject: [PATCH 3/3] Fix nested dynamic library loading via RPATH
5+
6+
There is a bug with dynamic library loading. Suppose
7+
liba.so is on LD_LIBRARY_PATH and it has an RPATH of `$ORIGIN/other_dir`
8+
and loads `libb.so` from other_dir. Then suppose `libb.so` has an RPATH of
9+
`$ORIGIN` and wants to load `libc.so` also from `other_dir`.
10+
11+
Before this PR this doesn't work. The problem is that `flags.rpath.parentLibPath`
12+
is set to `libb.so` and we replace $ORIGIN with `PATH.dirname(parentLibName)`
13+
which is `.`. So unless `other_dir` is on the `LD_LIBRARY_PATH` or is the
14+
current working directory, loading will fail.
15+
16+
The fix: if `findLibraryFS()` returns a value that is not `undefined`, replace
17+
`libName` with the returned value.
18+
---
19+
src/lib/libdylink.js | 4 ++-
20+
test/test_other.py | 61 ++++++++++++++++++++++++++++++++++++++++++++
21+
2 files changed, 64 insertions(+), 1 deletion(-)
22+
23+
diff --git a/src/lib/libdylink.js b/src/lib/libdylink.js
24+
index 737a9773e..c1d2c72d9 100644
25+
--- a/src/lib/libdylink.js
26+
+++ b/src/lib/libdylink.js
27+
@@ -990,7 +990,7 @@ var LibraryDylink = {
28+
#endif
29+
}
30+
var rpathResolved = (rpath?.paths || []).map((p) => replaceORIGIN(rpath?.parentLibPath, p));
31+
- return withStackSave(() => {
32+
+ var result = withStackSave(() => {
33+
// In dylink.c we use: `char buf[2*NAME_MAX+2];` and NAME_MAX is 255.
34+
// So we use the same size here.
35+
var bufSize = 2*255 + 2;
36+
@@ -1000,6 +1000,7 @@ var LibraryDylink = {
37+
var resLibNameC = __emscripten_find_dylib(buf, rpathC, libNameC, bufSize);
38+
return resLibNameC ? UTF8ToString(resLibNameC) : undefined;
39+
});
40+
+ return FS.lookupPath(result).path;
41+
},
42+
#endif // FILESYSTEM
43+
44+
@@ -1107,6 +1108,7 @@ var LibraryDylink = {
45+
dbg(`checking filesystem: ${libName}: ${f ? 'found' : 'not found'}`);
46+
#endif
47+
if (f) {
48+
+ libName = f;
49+
var libData = FS.readFile(f, {encoding: 'binary'});
50+
return flags.loadAsync ? Promise.resolve(libData) : libData;
51+
}
52+
diff --git a/test/test_other.py b/test/test_other.py
53+
index 57ceb9f09..1baca1251 100644
54+
--- a/test/test_other.py
55+
+++ b/test/test_other.py
56+
@@ -2381,6 +2381,67 @@ Module['postRun'] = () => {
57+
self.assertEqual(get_runtime_paths('libside1.so'), ['$ORIGIN'])
58+
self.assertEqual(get_runtime_paths('a.out.wasm'), ['$ORIGIN'])
59+
60+
+ def test_dylink_dependencies_rpath_nested(self):
61+
+ create_file('pre.js', r'''
62+
+ Module.preRun.push(() => {
63+
+ Module.ENV.LD_LIBRARY_PATH = "/lib1";
64+
+ });
65+
+ ''')
66+
+ create_file('side1.c', r'''
67+
+ #include <stdio.h>
68+
+
69+
+ void side2();
70+
+
71+
+ void side1() {
72+
+ printf("side1\n");
73+
+ side2();
74+
+ }
75+
+ ''')
76+
+ create_file('side2.c', r'''
77+
+ #include <stdio.h>
78+
+ void side3();
79+
+
80+
+ void side2() {
81+
+ printf("side2\n");
82+
+ side3();
83+
+ }
84+
+ ''')
85+
+ create_file('side3.c', r'''
86+
+ #include <stdio.h>
87+
+
88+
+ void side3() {
89+
+ printf("side3\n");
90+
+ }
91+
+ ''')
92+
+ create_file('main.c', r'''
93+
+ #include <dlfcn.h>
94+
+ #include <stdio.h>
95+
+
96+
+ typedef void (*F)(void);
97+
+
98+
+ int main() {
99+
+ void* handle = dlopen("libside1.so", RTLD_NOW);
100+
+ F side1 = (F)dlsym(handle, "side1");
101+
+
102+
+ printf("main\n");
103+
+ side1();
104+
+ return 0;
105+
+ }
106+
+ ''')
107+
+ os.mkdir('libs')
108+
+ os.mkdir('lib1')
109+
+ self.emcc('side3.c', ['-fPIC', '-sSIDE_MODULE', '-olibs/libside3.so'])
110+
+ self.emcc('side2.c', ['-fPIC', '-sSIDE_MODULE', '-olibs/libside2.so', '-Wl,-rpath,$ORIGIN', 'libs/libside3.so'])
111+
+ self.emcc('side1.c', ['-fPIC', '-sSIDE_MODULE', '-Wl,-rpath,$ORIGIN/../libs', '-olib1/libside1.so', 'libs/libside2.so'])
112+
+ settings = ['-sMAIN_MODULE=2', '-sDYLINK_DEBUG', "-sEXPORTED_FUNCTIONS=[_printf,_main]", "-sEXPORTED_RUNTIME_METHODS=ENV"]
113+
+ preloads = []
114+
+ for file in ['lib1/libside1.so', 'libs/libside2.so', 'libs/libside3.so']:
115+
+ preloads += ['--preload-file', file]
116+
+ cmd = [EMCC, 'main.c', '-fPIC', '--pre-js', 'pre.js'] + settings + preloads
117+
+ self.run_process(cmd)
118+
+
119+
+ self.run_js('a.out.js')
120+
+
121+
def test_dylink_LEGACY_GL_EMULATION(self):
122+
# LEGACY_GL_EMULATION wraps JS library functions. This test ensure that when it does
123+
# so it preserves the `.sig` attributes needed by dynamic linking.
124+
--
125+
2.34.1
126+

emsdk/patches/0003-dylink-Fix-rpath-calculation-in-nested-dependencies.patch

Lines changed: 0 additions & 55 deletions
This file was deleted.

emsdk/patches/0004-Fix-promise-order.patch

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)