Skip to content

Commit 66d2137

Browse files
authored
Reimplement resolve_path in JS NFC (#23935)
As discussed in #23872, this PR reimplements `resolve_path` function, which is used to locate the library using `LD_LIBRARY_PATH` in JavaScript, so that it can be reused inside `libdylink.js`.
1 parent 5c63a71 commit 66d2137

File tree

4 files changed

+100
-46
lines changed

4 files changed

+100
-46
lines changed

src/lib/libdylink.js

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ var LibraryDylink = {
656656
}
657657
#if DYLINK_DEBUG
658658
dbg(`loadModule: memory[${memoryBase}:${memoryBase + metadata.memorySize}]` +
659-
` table[${tableBasex}:${tableBase + metadata.tableSize}]`);
659+
` table[${tableBase}:${tableBase + metadata.tableSize}]`);
660660
#endif
661661

662662
// This is the export map that we ultimately return. We declare it here
@@ -1172,6 +1172,96 @@ var LibraryDylink = {
11721172
#endif
11731173
},
11741174

1175+
$locateLibraryFromFS__deps: ['$FS'],
1176+
$locateLibraryFromFS: (filename, searchDirs, maxLength = Infinity) => {
1177+
// Find the library in the filesystem.
1178+
// returns null if not found.
1179+
if (typeof FS.lookupPath !== 'function') {
1180+
// wasmfs does not implement FS.lookupPath
1181+
#if DYLINK_DEBUG
1182+
dbg("locateLibraryFromFS: FS.lookupPath not implemented");
1183+
#endif
1184+
return null;
1185+
}
1186+
1187+
var candidates = [];
1188+
if (filename.charAt(0) === '/') { // abs path
1189+
candidates.push(filename);
1190+
} else if (searchDirs) {
1191+
for (var dir of searchDirs) {
1192+
// PATH.join does not work well with symlinks
1193+
candidates.push(dir + '/' + filename);
1194+
}
1195+
} else {
1196+
return null;
1197+
}
1198+
1199+
#if DYLINK_DEBUG
1200+
dbg("locateLibraryFromFS: candidates " + candidates);
1201+
#endif
1202+
1203+
for (var path of candidates) {
1204+
try {
1205+
var res = FS.lookupPath(path);
1206+
if (res.node.isDir || res.node.isDevice) {
1207+
continue
1208+
}
1209+
1210+
if (res.path.length >= maxLength) {
1211+
continue
1212+
}
1213+
#if DYLINK_DEBUG
1214+
dbg(`locateLibraryFromFS: found ${res.path} for (${filename})`);
1215+
#endif
1216+
return res.path;
1217+
} catch(e) {
1218+
#if DYLINK_DEBUG
1219+
dbg(`locateLibraryFromFS: ${path} not found: ${e}`);
1220+
#endif
1221+
// do nothing is file is not found
1222+
}
1223+
}
1224+
1225+
return null;
1226+
},
1227+
1228+
$getDefaultLibDirs__deps: ['$ENV'],
1229+
$getDefaultLibDirs__proxy: 'sync',
1230+
$getDefaultLibDirs: () => {
1231+
var ldLibraryPath = ENV['LD_LIBRARY_PATH']
1232+
#if DYLINK_DEBUG
1233+
dbg(`getDefaultLibDirs: LD_LIBRARY_PATH=${ldLibraryPath}`);
1234+
#endif
1235+
return ldLibraryPath?.split(':') ?? [];
1236+
},
1237+
1238+
_dylink_resolve_path_js__deps: ['$UTF8ToString', '$stringToUTF8', '$locateLibraryFromFS', '$getDefaultLibDirs'],
1239+
_dylink_resolve_path_js__proxy: 'sync',
1240+
_dylink_resolve_path_js: (cbuf, cfile, buflen) => {
1241+
var cfilePtr = cfile;
1242+
1243+
#if MEMORY64
1244+
cfilePtr = Number(cfilePtr);
1245+
buflen = Number(buflen)
1246+
#endif
1247+
1248+
var file = UTF8ToString(cfilePtr);
1249+
1250+
if (file.startsWith("/")) {
1251+
return cfile;
1252+
}
1253+
1254+
var res = locateLibraryFromFS(file, getDefaultLibDirs(), buflen - 1);
1255+
if (!res) {
1256+
#if DYLINK_DEBUG
1257+
dbg("_dylink_resolve_path_js: fail to locate " + file);
1258+
#endif
1259+
return cfile;
1260+
}
1261+
stringToUTF8(res, cbuf, buflen);
1262+
return cbuf;
1263+
},
1264+
11751265
// Async version of dlopen.
11761266
_emscripten_dlopen_js__deps: ['$dlopenInternal', '$callUserCallback', '$dlSetError'],
11771267
_emscripten_dlopen_js: (handle, onsuccess, onerror, user_data) => {

src/lib/libsigs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ sigs = {
282282
_dlopen_js__sig: 'pp',
283283
_dlsym_catchup_js__sig: 'ppi',
284284
_dlsym_js__sig: 'pppp',
285+
_dylink_resolve_path_js__sig: 'pppp',
285286
_embind_create_inheriting_constructor__sig: 'pppp',
286287
_embind_finalize_value_array__sig: 'vp',
287288
_embind_finalize_value_object__sig: 'vp',

system/lib/libc/dynlink.c

Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -483,49 +483,6 @@ static void dlopen_onerror(struct dso* dso, void* user_data) {
483483
free(data);
484484
}
485485

486-
// Modified version of path_open from musl/ldso/dynlink.c
487-
static int path_find(const char *name, const char *s, char *buf, size_t buf_size) {
488-
size_t l;
489-
int fd;
490-
for (;;) {
491-
s += strspn(s, ":\n");
492-
l = strcspn(s, ":\n");
493-
if (l-1 >= INT_MAX) return -1;
494-
if (snprintf(buf, buf_size, "%.*s/%s", (int)l, s, name) < buf_size) {
495-
dbg("dlopen: path_find: %s", buf);
496-
struct stat statbuf;
497-
if (stat(buf, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) {
498-
return 0;
499-
}
500-
switch (errno) {
501-
case ENOENT:
502-
case ENOTDIR:
503-
case EACCES:
504-
case ENAMETOOLONG:
505-
break;
506-
default:
507-
dbg("dlopen: path_find failed: %s", strerror(errno));
508-
/* Any negative value but -1 will inhibit
509-
* futher path search. */
510-
return -2;
511-
}
512-
}
513-
s += l;
514-
}
515-
}
516-
517-
// Resolve filename using LD_LIBRARY_PATH
518-
static const char* resolve_path(char* buf, const char* file, size_t buflen) {
519-
if (!strchr(file, '/')) {
520-
const char* env_path = getenv("LD_LIBRARY_PATH");
521-
if (env_path && path_find(file, env_path, buf, buflen) == 0) {
522-
dbg("dlopen: found in LD_LIBRARY_PATH: %s", buf);
523-
return buf;
524-
}
525-
}
526-
return file;
527-
}
528-
529486
// Search for library name to see if it's already loaded
530487
static struct dso* find_existing(const char* file) {
531488
for (struct dlevent* e = head; e; e = e->next) {
@@ -553,7 +510,9 @@ static struct dso* _dlopen(const char* file, int flags) {
553510
do_write_lock();
554511

555512
char buf[2*NAME_MAX+2];
556-
file = resolve_path(buf, file, sizeof buf);
513+
514+
dbg("calling _dylink_resolve_path_js %s", file);
515+
file = _dylink_resolve_path_js(buf, file, sizeof buf);
557516

558517
struct dso* p = find_existing(file);
559518
if (p) {
@@ -593,7 +552,10 @@ void emscripten_dlopen(const char* filename, int flags, void* user_data,
593552
}
594553
do_write_lock();
595554
char buf[2*NAME_MAX+2];
596-
filename = resolve_path(buf, filename, sizeof buf);
555+
556+
dbg("calling _dylink_resolve_path_js %s", filename);
557+
filename = _dylink_resolve_path_js(buf, filename, sizeof buf);
558+
597559
struct dso* p = find_existing(filename);
598560
if (p) {
599561
onsuccess(user_data, p);

system/lib/libc/emscripten_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ void _emscripten_dlopen_js(struct dso* handle,
9090
dlopen_callback_func onsuccess,
9191
dlopen_callback_func onerror,
9292
void* user_data);
93+
const char* _dylink_resolve_path_js(char* buf, const char* file, size_t buflen);
9394
void* _dlsym_catchup_js(struct dso* handle, int sym_index);
9495

9596
int _setitimer_js(int which, double timeout);

0 commit comments

Comments
 (0)