Skip to content

Reimplement resolve_path in JS Take 2 NFC #23958

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 16 commits into from
Closed
92 changes: 91 additions & 1 deletion src/lib/libdylink.js
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ var LibraryDylink = {
}
#if DYLINK_DEBUG
dbg(`loadModule: memory[${memoryBase}:${memoryBase + metadata.memorySize}]` +
` table[${tableBasex}:${tableBase + metadata.tableSize}]`);
` table[${tableBase}:${tableBase + metadata.tableSize}]`);
#endif

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

$locateLibraryFromFS__deps: ['$FS'],
$locateLibraryFromFS: (filename, searchDirs, maxLength = Infinity) => {
// Find the library in the filesystem.
// returns null if not found.
if (typeof FS.lookupPath !== 'function') {
// wasmfs does not implement FS.lookupPath
#if DYLINK_DEBUG
dbg("locateLibraryFromFS: FS.lookupPath not implemented");
#endif
return null;
}

var candidates = [];
if (filename.charAt(0) === '/') { // abs path
candidates.push(filename);
} else if (searchDirs) {
for (var dir of searchDirs) {
// PATH.join does not work well with symlinks
candidates.push(dir + '/' + filename);
}
} else {
return null;
}

#if DYLINK_DEBUG
dbg("locateLibraryFromFS: candidates " + candidates);
#endif

for (var path of candidates) {
try {
var res = FS.lookupPath(path);
if (res.node.isDir || res.node.isDevice) {
continue
}

if (res.path.length >= maxLength) {
continue
}
#if DYLINK_DEBUG
dbg(`locateLibraryFromFS: found ${res.path} for (${filename})`);
#endif
return res.path;
} catch(e) {
#if DYLINK_DEBUG
dbg(`locateLibraryFromFS: ${path} not found: ${e}`);
#endif
// do nothing is file is not found
}
}

return null;
},

$getDefaultLibDirs__deps: ['$ENV'],
$getDefaultLibDirs__proxy: 'sync',
$getDefaultLibDirs: () => {
var ldLibraryPath = ENV['LD_LIBRARY_PATH']
#if DYLINK_DEBUG
dbg(`getDefaultLibDirs: LD_LIBRARY_PATH=${ldLibraryPath}`);
#endif
return ldLibraryPath?.split(':') ?? [];
},

_dylink_resolve_path_js__deps: ['$UTF8ToString', '$stringToUTF8', '$locateLibraryFromFS', '$getDefaultLibDirs'],
_dylink_resolve_path_js__proxy: 'sync',
_dylink_resolve_path_js: (cbuf, cfile, buflen) => {
var cfilePtr = cfile;

#if MEMORY64
cfilePtr = Number(cfilePtr);
buflen = Number(buflen)
#endif

var file = UTF8ToString(cfilePtr);

if (file.startsWith("/")) {
return cfile;
}

var res = locateLibraryFromFS(file, getDefaultLibDirs(), buflen - 1);
if (!res) {
#if DYLINK_DEBUG
dbg("_dylink_resolve_path_js: fail to locate " + file);
#endif
return cfile;
}
stringToUTF8(res, cbuf, buflen);
return cbuf;
},

// Async version of dlopen.
_emscripten_dlopen_js__deps: ['$dlopenInternal', '$callUserCallback', '$dlSetError'],
_emscripten_dlopen_js: (handle, onsuccess, onerror, user_data) => {
Expand Down
1 change: 1 addition & 0 deletions src/lib/libsigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ sigs = {
_dlopen_js__sig: 'pp',
_dlsym_catchup_js__sig: 'ppi',
_dlsym_js__sig: 'pppp',
_dylink_resolve_path_js__sig: 'pppp',
_embind_create_inheriting_constructor__sig: 'pppp',
_embind_finalize_value_array__sig: 'vp',
_embind_finalize_value_object__sig: 'vp',
Expand Down
52 changes: 7 additions & 45 deletions system/lib/libc/dynlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -483,49 +483,6 @@ static void dlopen_onerror(struct dso* dso, void* user_data) {
free(data);
}

// Modified version of path_open from musl/ldso/dynlink.c
static int path_find(const char *name, const char *s, char *buf, size_t buf_size) {
size_t l;
int fd;
for (;;) {
s += strspn(s, ":\n");
l = strcspn(s, ":\n");
if (l-1 >= INT_MAX) return -1;
if (snprintf(buf, buf_size, "%.*s/%s", (int)l, s, name) < buf_size) {
dbg("dlopen: path_find: %s", buf);
struct stat statbuf;
if (stat(buf, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) {
return 0;
}
switch (errno) {
case ENOENT:
case ENOTDIR:
case EACCES:
case ENAMETOOLONG:
break;
default:
dbg("dlopen: path_find failed: %s", strerror(errno));
/* Any negative value but -1 will inhibit
* futher path search. */
return -2;
}
}
s += l;
}
}

// Resolve filename using LD_LIBRARY_PATH
static const char* resolve_path(char* buf, const char* file, size_t buflen) {
if (!strchr(file, '/')) {
const char* env_path = getenv("LD_LIBRARY_PATH");
if (env_path && path_find(file, env_path, buf, buflen) == 0) {
dbg("dlopen: found in LD_LIBRARY_PATH: %s", buf);
return buf;
}
}
return file;
}

// Search for library name to see if it's already loaded
static struct dso* find_existing(const char* file) {
for (struct dlevent* e = head; e; e = e->next) {
Expand Down Expand Up @@ -553,7 +510,9 @@ static struct dso* _dlopen(const char* file, int flags) {
do_write_lock();

char buf[2*NAME_MAX+2];
file = resolve_path(buf, file, sizeof buf);

dbg("calling _dylink_resolve_path_js %s", file);
file = _dylink_resolve_path_js(buf, file, sizeof buf);

struct dso* p = find_existing(file);
if (p) {
Expand Down Expand Up @@ -593,7 +552,10 @@ void emscripten_dlopen(const char* filename, int flags, void* user_data,
}
do_write_lock();
char buf[2*NAME_MAX+2];
filename = resolve_path(buf, filename, sizeof buf);

dbg("calling _dylink_resolve_path_js %s", filename);
filename = _dylink_resolve_path_js(buf, filename, sizeof buf);

struct dso* p = find_existing(filename);
if (p) {
onsuccess(user_data, p);
Expand Down
1 change: 1 addition & 0 deletions system/lib/libc/emscripten_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ void _emscripten_dlopen_js(struct dso* handle,
dlopen_callback_func onsuccess,
dlopen_callback_func onerror,
void* user_data);
const char* _dylink_resolve_path_js(char* buf, const char* file, size_t buflen);
void* _dlsym_catchup_js(struct dso* handle, int sym_index);

int _setitimer_js(int which, double timeout);
Expand Down