Skip to content

Commit 3b98113

Browse files
committed
Proxy dlopen file operations back to the main thread. NFC
Fixes: #19245
1 parent 1386520 commit 3b98113

File tree

2 files changed

+92
-39
lines changed

2 files changed

+92
-39
lines changed

src/library_dylink.js

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,37 @@ var LibraryDylink = {
896896
return dso;
897897
},
898898

899+
$isValidFile__proxy: 'sync',
900+
$isValidFile__deps: ['$FS'],
901+
$isValidFile: function(filenamePtr) {
902+
var filename = UTF8ToString(filenamePtr);
903+
#if DYLINK_DEBUG
904+
dbg('isValidFile: ' + filename);
905+
#endif
906+
var target = FS.findObject(filename);
907+
return target && !target.isFolder && !target.isDevice;
908+
},
909+
910+
$readFileProxied__proxy: 'sync',
911+
$readFileProxied__deps: ['$FS'],
912+
$readFileProxied: function(filenamePtr, outPtr) {
913+
var filename = UTF8ToString(filenamePtr);
914+
#if DYLINK_DEBUG
915+
dbg('readFileProxied: ' + filename);
916+
#endif
917+
if (!FS.findObject(filename)) {
918+
return 0;
919+
}
920+
var fileData = FS.readFile(filename, {encoding: 'binary'});
921+
if (!(fileData instanceof Uint8Array)) {
922+
fileData = new Uint8Array(fileData);
923+
}
924+
var mem = _malloc(fileData.length)
925+
HEAP8.set(fileData, mem);
926+
{{{ makeSetValue('outPtr', 0, 'mem', '*') }}};
927+
return fileData.length;
928+
},
929+
899930
// loadDynamicLibrary loads dynamic library @ lib URL / path and returns
900931
// handle for loaded DSO.
901932
//
@@ -919,7 +950,7 @@ var LibraryDylink = {
919950
// Once a library becomes "global" or "nodelete", it cannot be removed or unloaded.
920951
$loadDynamicLibrary__deps: ['$LDSO', '$loadWebAssemblyModule',
921952
'$isInternalSym', '$mergeLibSymbols', '$newDSO',
922-
'$asyncLoad', '$preloadedWasm'],
953+
'$asyncLoad', '$preloadedWasm', '$readFileProxied'],
923954
$loadDynamicLibrary__docs: `
924955
/**
925956
* @param {number=} handle
@@ -965,12 +996,23 @@ var LibraryDylink = {
965996
// libName -> libData
966997
function loadLibData() {
967998
// for wasm, we can use fetch for async, but for fs mode we can only imitate it
968-
if (flags.fs && flags.fs.findObject(libName)) {
969-
var libData = flags.fs.readFile(libName, {encoding: 'binary'});
970-
if (!(libData instanceof Uint8Array)) {
971-
libData = new Uint8Array(libData);
999+
if (flags.fs) {
1000+
var libData = withStackSave(() => {
1001+
var addrPtr = stackAlloc({{{ POINTER_SIZE }}});
1002+
var len = readFileProxied(stringToUTF8OnStack(libName), addrPtr);
1003+
#if DYLINK_DEBUG
1004+
dbg('readFileProxied done: ' + len);
1005+
#endif
1006+
if (len) {
1007+
var addr = {{{ makeGetValue('addrPtr', 0, '*') }}}
1008+
var rtn = HEAP8.slice(addr, addr + len);
1009+
_free(addr);
1010+
return rtn;
1011+
}
1012+
});
1013+
if (libData) {
1014+
return flags.loadAsync ? Promise.resolve(libData) : libData;
9721015
}
973-
return flags.loadAsync ? Promise.resolve(libData) : libData;
9741016
}
9751017

9761018
var libFile = locateFile(libName);
@@ -992,7 +1034,7 @@ var LibraryDylink = {
9921034
// lookup preloaded cache first
9931035
if (preloadedWasm[libName]) {
9941036
#if DYLINK_DEBUG
995-
err('using preloaded module for: ' + libName);
1037+
dbg('using preloaded module for: ' + libName);
9961038
#endif
9971039
var libModule = preloadedWasm[libName];
9981040
return flags.loadAsync ? Promise.resolve(libModule) : libModule;
@@ -1064,35 +1106,33 @@ var LibraryDylink = {
10641106
},
10651107

10661108
// void* dlopen(const char* filename, int flags);
1067-
$dlopenInternal__deps: ['$FS', '$ENV', '$dlSetError', '$PATH'],
1109+
$dlopenInternal__deps: ['$ENV', '$dlSetError', '$PATH', '$isValidFile'],
10681110
$dlopenInternal: function(handle, jsflags) {
10691111
// void *dlopen(const char *file, int mode);
10701112
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html
1071-
var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}});
1113+
var filenamePtr = handle + {{{ C_STRUCTS.dso.name }}};
1114+
var filename = UTF8ToString(filenamePtr);
10721115
var flags = {{{ makeGetValue('handle', C_STRUCTS.dso.flags, 'i32') }}};
10731116
#if DYLINK_DEBUG
10741117
dbg('dlopenInternal: ' + filename);
10751118
#endif
10761119
filename = PATH.normalize(filename);
10771120
var searchpaths = [];
10781121

1079-
var isValidFile = (filename) => {
1080-
var target = FS.findObject(filename);
1081-
return target && !target.isFolder && !target.isDevice;
1082-
};
1083-
1084-
if (!isValidFile(filename)) {
1122+
if (!isValidFile(filenamePtr)) {
10851123
if (ENV['LD_LIBRARY_PATH']) {
10861124
searchpaths = ENV['LD_LIBRARY_PATH'].split(':');
10871125
}
10881126

1089-
for (var ident in searchpaths) {
1090-
var searchfile = PATH.join2(searchpaths[ident], filename);
1091-
if (isValidFile(searchfile)) {
1092-
filename = searchfile;
1093-
break;
1127+
withStackSave(() => {
1128+
for (var ident in searchpaths) {
1129+
var searchfile = PATH.join2(searchpaths[ident], filename);
1130+
if (isValidFile(stringToUTF8OnStack(searchfile))) {
1131+
filename = searchfile;
1132+
break;
1133+
}
10941134
}
1095-
}
1135+
});
10961136
}
10971137

10981138
var global = Boolean(flags & {{{ cDefs.RTLD_GLOBAL }}});
@@ -1130,15 +1170,15 @@ var LibraryDylink = {
11301170
return Asyncify.handleSleep((wakeUp) => {
11311171
var jsflags = {
11321172
loadAsync: true,
1133-
fs: FS, // load libraries from provided filesystem
1173+
fs: true, // load libraries from filesystem
11341174
}
11351175
var promise = dlopenInternal(handle, jsflags);
11361176
promise.then(wakeUp).catch(() => wakeUp(0));
11371177
});
11381178
#else
11391179
var jsflags = {
11401180
loadAsync: false,
1141-
fs: FS, // load libraries from provided filesystem
1181+
fs: true, // load libraries from filesystem
11421182
}
11431183
return dlopenInternal(handle, jsflags);
11441184
#endif
@@ -1149,8 +1189,8 @@ var LibraryDylink = {
11491189
_emscripten_dlopen_js: function(handle, onsuccess, onerror, user_data) {
11501190
/** @param {Object=} e */
11511191
function errorCallback(e) {
1152-
var filename = UTF8ToString({{{ makeGetValue('handle', C_STRUCTS.dso.name, '*') }}});
1153-
dlSetError('Could not load dynamic lib: ' + filename + '\n' + e);
1192+
var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}});
1193+
dlSetError('Could not load dynamic libX: ' + filename + '\n' + e);
11541194
{{{ runtimeKeepalivePop() }}}
11551195
callUserCallback(() => {{{ makeDynCall('vpp', 'onerror') }}}(handle, user_data));
11561196
}
@@ -1160,7 +1200,7 @@ var LibraryDylink = {
11601200
}
11611201

11621202
{{{ runtimeKeepalivePush() }}}
1163-
var promise = dlopenInternal(handle, { loadAsync: true });
1203+
var promise = dlopenInternal(handle, { loadAsync: true, fs: true });
11641204
if (promise) {
11651205
promise.then(successCallback, errorCallback);
11661206
} else {

test/test_other.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6371,7 +6371,11 @@ def test_RUNTIME_LINKED_LIBS(self):
63716371

63726372
self.assertBinaryEqual('main.wasm', 'main2.wasm')
63736373

6374-
def test_ld_library_path(self):
6374+
@parameterized({
6375+
'': ([],),
6376+
'pthread': (['-g', '-pthread', '-Wno-experimental', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'],),
6377+
})
6378+
def test_ld_library_path(self, args):
63756379
create_file('hello1.c', r'''
63766380
#include <stdio.h>
63776381

@@ -6447,17 +6451,17 @@ def test_ld_library_path(self):
64476451
return 0;
64486452
}
64496453
''')
6450-
self.run_process([EMCC, '-o', 'libhello1.wasm', 'hello1.c', '-sSIDE_MODULE'])
6451-
self.run_process([EMCC, '-o', 'libhello2.wasm', 'hello2.c', '-sSIDE_MODULE'])
6452-
self.run_process([EMCC, '-o', 'libhello3.wasm', 'hello3.c', '-sSIDE_MODULE'])
6453-
self.run_process([EMCC, '-o', 'libhello4.wasm', 'hello4.c', '-sSIDE_MODULE'])
6454+
self.run_process([EMCC, '-o', 'libhello1.wasm', 'hello1.c', '-sSIDE_MODULE'] + args)
6455+
self.run_process([EMCC, '-o', 'libhello2.wasm', 'hello2.c', '-sSIDE_MODULE'] + args)
6456+
self.run_process([EMCC, '-o', 'libhello3.wasm', 'hello3.c', '-sSIDE_MODULE'] + args)
6457+
self.run_process([EMCC, '-o', 'libhello4.wasm', 'hello4.c', '-sSIDE_MODULE'] + args)
64546458
self.run_process([EMCC, '--profiling-funcs', '-o', 'main.js', 'main.c', '-sMAIN_MODULE=2', '-sINITIAL_MEMORY=32Mb',
64556459
'--embed-file', 'libhello1.wasm@/lib/libhello1.wasm',
64566460
'--embed-file', 'libhello2.wasm@/usr/lib/libhello2.wasm',
64576461
'--embed-file', 'libhello3.wasm@/libhello3.wasm',
64586462
'--embed-file', 'libhello4.wasm@/usr/local/lib/libhello4.wasm',
64596463
'libhello1.wasm', 'libhello2.wasm', 'libhello3.wasm', 'libhello4.wasm', '-sNO_AUTOLOAD_DYLIBS',
6460-
'--pre-js', 'pre.js'])
6464+
'--pre-js', 'pre.js'] + args)
64616465
out = self.run_js('main.js')
64626466
self.assertContained('Hello1', out)
64636467
self.assertContained('Hello2', out)
@@ -13390,7 +13394,13 @@ def test_windows_batch_file_dp0_expansion_bug(self):
1339013394
create_file('build_with_quotes.bat', f'@"emcc" {test_file("hello_world.c")}')
1339113395
self.run_process(['build_with_quotes.bat'])
1339213396

13393-
def test_preload_module(self):
13397+
@parameterized({
13398+
'': ([],),
13399+
'pthread': (['-g', '-pthread', '-Wno-experimental', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'],),
13400+
})
13401+
def test_preload_module(self, args):
13402+
if args:
13403+
self.setup_node_pthreads()
1339413404
# TODO(sbc): This test is copyied from test_browser.py. Perhaps find a better way to
1339513405
# share code between them.
1339613406
create_file('library.c', r'''
@@ -13399,17 +13409,20 @@ def test_preload_module(self):
1339913409
return 42;
1340013410
}
1340113411
''')
13402-
self.run_process([EMCC, 'library.c', '-sSIDE_MODULE', '-o', 'library.so'])
13412+
self.run_process([EMCC, 'library.c', '-sSIDE_MODULE', '-o', 'library.so'] + args)
1340313413
create_file('main.c', r'''
1340413414
#include <assert.h>
1340513415
#include <dlfcn.h>
1340613416
#include <stdio.h>
1340713417
#include <emscripten.h>
13418+
#include <emscripten/threading.h>
1340813419
int main() {
13409-
int found = EM_ASM_INT(
13410-
return preloadedWasm['/library.so'] !== undefined;
13411-
);
13412-
assert(found);
13420+
if (emscripten_is_main_runtime_thread()) {
13421+
int found = EM_ASM_INT(
13422+
return preloadedWasm['/library.so'] !== undefined;
13423+
);
13424+
assert(found);
13425+
}
1341313426
void *lib_handle = dlopen("/library.so", RTLD_NOW);
1341413427
assert(lib_handle);
1341513428
typedef int (*voidfunc)();
@@ -13420,4 +13433,4 @@ def test_preload_module(self):
1342013433
return 0;
1342113434
}
1342213435
''')
13423-
self.do_runf('main.c', 'done\n', emcc_args=['-sMAIN_MODULE=2', '--preload-file', '.@/', '--use-preload-plugins'])
13436+
self.do_runf('main.c', 'done\n', emcc_args=['-sMAIN_MODULE=2', '--preload-file', '.@/', '--use-preload-plugins'] + args)

0 commit comments

Comments
 (0)