Skip to content

Commit

Permalink
Disable DL_LOAD_PATH prepending for @-paths on Darwin (#42721)
Browse files Browse the repository at this point in the history
* Disable `DL_LOAD_PATH` prepending for `@`-paths on Darwin

Many thanks to Randy Rucker from Apple for pointing out that we were
technically relying on undefined behavior in `dyld` for loading things
via `jl_dlopen("@loader_path/@rpath/libfoo.dylib")`, due to the
composition of `dlopen("@rpath/libfoo.dylib")` in our Julia code, and
the `DL_LOAD_PATH` prepending we do in `jl_load_dynamic_library()`.

This PR uses a slightly modified version of a patch emailed to me by
Randy, and should solve
JuliaLang/Downloads.jl#149 on macOS Monterey
where the undefined behavior in `dyld` has changed.

* Apply suggestions from code review

Co-authored-by: Jameson Nash <vtjnash@gmail.com>

* Use `[]` instead of `*`

Co-authored-by: Jameson Nash <vtjnash@gmail.com>
  • Loading branch information
staticfloat and vtjnash authored Oct 21, 2021
1 parent b2d15f0 commit 76c2431
Showing 1 changed file with 16 additions and 1 deletion.
17 changes: 16 additions & 1 deletion src/dlload.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags,
uv_stat_t stbuf;
void *handle;
int abspath;
int is_atpath;
// number of extensions to try — if modname already ends with the
// standard extension, then we don't try adding additional extensions
int n_extensions = endswith_extension(modname) ? 1 : N_EXTENSIONS;
Expand All @@ -181,16 +182,30 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags,
}

abspath = jl_isabspath(modname);
is_atpath = 0;

// Detect if our `modname` is something like `@rpath/libfoo.dylib`
#ifdef _OS_DARWIN_
size_t nameLen = strlen(modname);
const char *const atPaths[] = {"@executable_path/", "@loader_path/", "@rpath/"};
for (i = 0; i < sizeof(atPaths)/sizeof(char*); ++i) {
size_t atLen = strlen(atPaths[i]);
if (nameLen >= atLen && 0 == strncmp(modname, atPaths[i], atLen)) {
is_atpath = 1;
}
}
#endif

/*
this branch permutes all base paths in DL_LOAD_PATH with all extensions
note: skip when !jl_base_module to avoid UndefVarError(:DL_LOAD_PATH),
and also skip for absolute paths
and also skip for `@`-paths on macOS
We also do simple string replacement here for elements starting with `@executable_path/`.
While these exist as OS concepts on Darwin, we want to use them on other platforms
such as Windows, so we emulate them here.
*/
if (!abspath && jl_base_module != NULL) {
if (!abspath && !is_atpath && jl_base_module != NULL) {
jl_binding_t *b = jl_get_module_binding(jl_base_module, jl_symbol("DL_LOAD_PATH"));
jl_array_t *DL_LOAD_PATH = (jl_array_t*)(b ? jl_atomic_load_relaxed(&b->value) : NULL);
if (DL_LOAD_PATH != NULL) {
Expand Down

0 comments on commit 76c2431

Please sign in to comment.