Skip to content

Commit 9427f33

Browse files
authored
jl_dlfind: do not find symbols in library dependencies (#58815)
A `ccall` without a specific library finds one with `jl_dlfind`, which calls `jl_dlsym` on each of the following libraries, in order: - `libjulia-internal` - `libjulia` - The current executable - (On Windows): `kernel32`, `crtdll`, `ntdll`, `ws2_32` The semantics of using `dlsym` (this does not apply to `GetProcAddress`) with a specific handle are a little weird: the library and its dependencies are searched, even if the provided symbol would resolve somewhere else if used in the library. On macOS and Linux, this causes all of the calls to libc functions in Base go through the handle for `libjulia-internal`, making it difficult to hook functions. For example, `-fsanitize=thread` on clang intercepts malloc and free by defining them in the executable; calls to malloc from Julia code would go to the original libc version while calls to free in `libjulia-internal` would go to the hooked version. This change makes `jl_dlfind` return a handle only if the symbol is found in that library and not one of its dependencies. ## Example `a.c`: ```c void b_func(void); void c_func(void) { puts("c override from a"); } int main() { puts("a"); b_func(); } ``` `b.c`: ```c define _GNU_SOURCE #include <stdio.h> #include <dlfcn.h> void c_func(void); void b_func(void) { puts("b"); c_func(); /* How jl_libjulia_internal_handle is set by jl_find_dynamic_library_by_addr */ Dl_info info; dladdr(&b_func, &info); void *hdl = dlopen(info.dli_fname, RTLD_NOW | RTLD_NOLOAD | RTLD_LOCAL); void *(*dl_c_func)(void) = dlsym(hdl, "c_func"); dl_c_func(); } ``` `c.c`: ```c #include <stdio.h> void c_func(void) { puts("c"); } ``` ``` $ cc -g -fPIC -shared -o libc.so c.c $ cc -g -fPIC -shared -o libb.so b.c libc.so $ cc -Wl,-rpath,. -g -fPIC -o main a.c libb.so libc.so $ ./main a b c override from a c ```
1 parent d7d9115 commit 9427f33

14 files changed

+120
-49
lines changed

base/libdl.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ function dlsym(hnd::Ptr, s::Union{Symbol,AbstractString}; throw_error::Bool = tr
6060
hnd == C_NULL && throw(ArgumentError("NULL library handle"))
6161
val = Ref(Ptr{Cvoid}(0))
6262
symbol_found = ccall(:jl_dlsym, Cint,
63-
(Ptr{Cvoid}, Cstring, Ref{Ptr{Cvoid}}, Cint),
64-
hnd, s, val, Int64(throw_error)
63+
(Ptr{Cvoid}, Cstring, Ref{Ptr{Cvoid}}, Cint, Cint),
64+
hnd, s, val, Int64(throw_error), Int64(1)
6565
)
6666
if symbol_found == 0
6767
return nothing

src/ccall.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ static void interpret_symbol_arg(jl_codectx_t &ctx, native_sym_arg_t &out, jl_va
627627
void *symaddr;
628628
std::string iname("i");
629629
iname += f_name;
630-
if (jl_dlsym(jl_libjulia_internal_handle, iname.c_str(), &symaddr, 0)) {
630+
if (jl_dlsym(jl_libjulia_internal_handle, iname.c_str(), &symaddr, 0, 0)) {
631631
f_lib = JL_LIBJULIA_INTERNAL_DL_LIBNAME;
632632
f_name = jl_symbol_name(jl_symbol(iname.c_str()));
633633
}

src/dlload.c

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,8 @@ JL_DLLEXPORT int jl_dlclose(void *handle) JL_NOTSAFEPOINT
238238
#endif
239239
}
240240

241-
void *jl_find_dynamic_library_by_addr(void *symbol, int throw_err) {
241+
void *jl_find_dynamic_library_by_addr(void *symbol, int throw_err, int close) JL_NOTSAFEPOINT
242+
{
242243
void *handle;
243244
#ifdef _OS_WINDOWS_
244245
if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
@@ -255,14 +256,21 @@ void *jl_find_dynamic_library_by_addr(void *symbol, int throw_err) {
255256
jl_error("could not load base module");
256257
return NULL;
257258
}
259+
dlerror();
258260
handle = dlopen(info.dli_fname, RTLD_NOW | RTLD_NOLOAD | RTLD_LOCAL);
259-
#if !defined(__APPLE__)
261+
#if defined(_OS_FREEBSD_)
262+
// FreeBSD will not give you a handle for the executable if you dlopen() it
263+
// with RTLD_NOLOAD, so check jl_exe_handle.
264+
if (handle == NULL && dlerror() == NULL) {
265+
handle = jl_exe_handle;
266+
}
267+
#elif !defined(__APPLE__)
260268
if (handle == RTLD_DEFAULT && (RTLD_DEFAULT != NULL || dlerror() == NULL)) {
261269
// We loaded the executable but got RTLD_DEFAULT back, ask for a real handle instead
262270
handle = dlopen("", RTLD_NOW | RTLD_NOLOAD | RTLD_LOCAL);
263271
}
264272
#endif
265-
if (handle != NULL)
273+
if (handle != NULL && close)
266274
dlclose(handle); // Undo ref count increment from `dlopen`
267275
#endif
268276
return handle;
@@ -285,7 +293,7 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags,
285293

286294
// modname == NULL is a sentinel value requesting the handle of libjulia-internal
287295
if (modname == NULL)
288-
return jl_find_dynamic_library_by_addr(&jl_load_dynamic_library, throw_err);
296+
return jl_libjulia_internal_handle;
289297

290298
abspath = jl_isabspath(modname);
291299
is_atpath = 0;
@@ -407,13 +415,26 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags,
407415
return handle;
408416
}
409417

410-
JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int throw_err) JL_NOTSAFEPOINT
418+
/*
419+
* When search_deps is 1, act like dlsym and search both the library for the
420+
* handle and all its dependencies. Use this option only when compatibility
421+
* with dlsym(3) is required, thought this behaviour is not possible on Windows.
422+
*
423+
* At time of writing, only Base.dlsym() uses search_deps = 1.
424+
*/
425+
JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int throw_err, int search_deps) JL_NOTSAFEPOINT
411426
{
412427
int symbol_found = 0;
413428

414429
/* First, get the symbol value */
415-
#ifdef _OS_WINDOWS_
430+
#if defined(_OS_WINDOWS_)
416431
*value = GetProcAddress((HMODULE) handle, symbol);
432+
#elif defined(_OS_DARWIN_)
433+
/* When !search_deps and the handle isn't special, force RTLD_FIRST. */
434+
if (!search_deps && handle != RTLD_NEXT && handle != RTLD_DEFAULT &&
435+
handle != RTLD_SELF && handle != RTLD_MAIN_ONLY)
436+
handle = (void *)((uintptr_t)handle | 1);
437+
*value = dlsym(handle, symbol);
417438
#else
418439
*value = dlsym(handle, symbol);
419440
#endif
@@ -437,14 +458,29 @@ JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int t
437458
}
438459
#endif
439460

440-
if (!symbol_found && throw_err) {
461+
#if !defined(_OS_DARWIN_) && !defined(_OS_WINDOWS_)
462+
/*
463+
* Unlike GetProcAddress, dlsym will search the dependencies of the given
464+
* library, so we must check where the symbol came from.
465+
*/
466+
if (symbol_found && !search_deps && handle != jl_RTLD_DEFAULT_handle) {
467+
void *symbol_handle = jl_find_dynamic_library_by_addr(*value, 0, 1);
468+
symbol_found = handle == symbol_handle;
469+
}
470+
#endif
471+
472+
if (!symbol_found) {
473+
if (throw_err) {
441474
#ifdef _OS_WINDOWS_
442-
char err[256];
443-
win32_formatmessage(GetLastError(), err, sizeof(err));
475+
char err[256];
476+
win32_formatmessage(GetLastError(), err, sizeof(err));
444477
#endif
445-
jl_errorf("could not load symbol \"%s\":\n%s", symbol, err);
478+
jl_errorf("could not load symbol \"%s\":\n%s", symbol, err);
479+
}
480+
return 0;
446481
}
447-
return symbol_found;
482+
483+
return 1;
448484
}
449485

450486
// Look for symbols in internal libraries
@@ -455,23 +491,23 @@ JL_DLLEXPORT const char *jl_dlfind(const char *f_name)
455491
// https://cgit.freebsd.org/src/commit/?id=21a52f99440c9bec7679f3b0c5c9d888901c3694
456492
// (See https://github.com/JuliaLang/julia/issues/50846)
457493
if (strcmp(f_name, "dl_iterate_phdr") == 0)
458-
return JL_EXE_LIBNAME;
494+
return NULL;
459495
#endif
460496
void * dummy;
461-
if (jl_dlsym(jl_libjulia_internal_handle, f_name, &dummy, 0))
497+
if (jl_dlsym(jl_libjulia_internal_handle, f_name, &dummy, 0, 0))
462498
return JL_LIBJULIA_INTERNAL_DL_LIBNAME;
463-
if (jl_dlsym(jl_libjulia_handle, f_name, &dummy, 0))
499+
if (jl_dlsym(jl_libjulia_handle, f_name, &dummy, 0, 0))
464500
return JL_LIBJULIA_DL_LIBNAME;
465-
if (jl_dlsym(jl_exe_handle, f_name, &dummy, 0))
501+
if (jl_dlsym(jl_exe_handle, f_name, &dummy, 0, 0))
466502
return JL_EXE_LIBNAME;
467503
#ifdef _OS_WINDOWS_
468-
if (jl_dlsym(jl_kernel32_handle, f_name, &dummy, 0))
504+
if (jl_dlsym(jl_kernel32_handle, f_name, &dummy, 0, 0))
469505
return "kernel32";
470-
if (jl_dlsym(jl_crtdll_handle, f_name, &dummy, 0)) // Prefer crtdll over ntdll
506+
if (jl_dlsym(jl_crtdll_handle, f_name, &dummy, 0, 0)) // Prefer crtdll over ntdll
471507
return jl_crtdll_basename;
472-
if (jl_dlsym(jl_ntdll_handle, f_name, &dummy, 0))
508+
if (jl_dlsym(jl_ntdll_handle, f_name, &dummy, 0, 0))
473509
return "ntdll";
474-
if (jl_dlsym(jl_winsock_handle, f_name, &dummy, 0))
510+
if (jl_dlsym(jl_winsock_handle, f_name, &dummy, 0, 0))
475511
return "ws2_32";
476512
#endif
477513
// additional common libraries (libc?) could be added here, but in general,

src/init.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
#include <pthread_np.h>
2222
#endif
2323

24+
#if !defined(_OS_WINDOWS_)
25+
#include <dlfcn.h>
26+
#endif
27+
2428
#include "julia.h"
2529
#include "julia_internal.h"
2630
#include "builtin_proto.h"
@@ -723,26 +727,29 @@ JL_DLLEXPORT void jl_init_(jl_image_buf_t sysimage)
723727
void *stack_lo, *stack_hi;
724728
jl_init_stack_limits(1, &stack_lo, &stack_hi);
725729

726-
jl_libjulia_internal_handle = jl_find_dynamic_library_by_addr(&jl_load_dynamic_library, /* throw_err */ 1);
727-
jl_libjulia_handle = jl_find_dynamic_library_by_addr(&jl_any_type, /* throw_err */ 1);
730+
// Note that if we ever want to be able to unload Julia entirely, we will
731+
// have to dlclose() these handles.
732+
jl_libjulia_internal_handle = jl_find_dynamic_library_by_addr(&jl_load_dynamic_library, /* throw_err */ 1, 0);
733+
jl_libjulia_handle = jl_find_dynamic_library_by_addr(&jl_any_type, /* throw_err */ 1, 0);
728734
#ifdef _OS_WINDOWS_
735+
/* If this parameter is NULL, GetModuleHandle returns a handle to the file
736+
used to create the calling process (.exe file). */
729737
jl_exe_handle = GetModuleHandleA(NULL);
730-
jl_RTLD_DEFAULT_handle = jl_libjulia_internal_handle;
738+
jl_RTLD_DEFAULT_handle = NULL;
731739
jl_ntdll_handle = jl_dlopen("ntdll.dll", JL_RTLD_NOLOAD); // bypass julia's pathchecking for system dlls
732740
jl_kernel32_handle = jl_dlopen("kernel32.dll", JL_RTLD_NOLOAD);
733741
jl_crtdll_handle = jl_dlopen(jl_crtdll_name, JL_RTLD_NOLOAD);
734742
jl_winsock_handle = jl_dlopen("ws2_32.dll", JL_RTLD_NOLOAD);
735743
HMODULE jl_dbghelp = (HMODULE) jl_dlopen("dbghelp.dll", JL_RTLD_NOLOAD);
736744
needsSymRefreshModuleList = 0;
737745
if (jl_dbghelp)
738-
jl_dlsym(jl_dbghelp, "SymRefreshModuleList", (void **)&hSymRefreshModuleList, 1);
746+
jl_dlsym(jl_dbghelp, "SymRefreshModuleList", (void **)&hSymRefreshModuleList, 1, 0);
739747
#else
740-
jl_exe_handle = jl_dlopen(NULL, JL_RTLD_NOW);
741-
#ifdef RTLD_DEFAULT
748+
/* macOS dlopen(3): If path is NULL and the option RTLD_FIRST is used, the
749+
handle returned will only search the main executable. */
750+
jl_exe_handle = jl_dlopen(NULL, JL_RTLD_NOW | JL_RTLD_NOLOAD | JL_RTLD_LOCAL | JL_RTLD_FIRST);
751+
// RTLD_DEFAULT is mandatory on POSIX
742752
jl_RTLD_DEFAULT_handle = RTLD_DEFAULT;
743-
#else
744-
jl_RTLD_DEFAULT_handle = jl_exe_handle;
745-
#endif
746753
#endif
747754

748755
jl_init_rand();

src/jitlayers.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1722,7 +1722,7 @@ struct JuliaOJIT::DLSymOptimizer {
17221722

17231723
void *lookup_symbol(void *libhandle, const char *fname) JL_NOTSAFEPOINT {
17241724
void *addr;
1725-
jl_dlsym(libhandle, fname, &addr, 0);
1725+
jl_dlsym(libhandle, fname, &addr, 0, 0);
17261726
return addr;
17271727
}
17281728

src/julia.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2311,13 +2311,17 @@ enum JL_RTLD_CONSTANT {
23112311
/* MacOS X 10.5+: */
23122312
JL_RTLD_FIRST=128U
23132313
};
2314+
#ifdef _OS_DARWIN_
2315+
#define JL_RTLD_DEFAULT (JL_RTLD_LAZY | JL_RTLD_DEEPBIND | JL_RTLD_FIRST)
2316+
#else
23142317
#define JL_RTLD_DEFAULT (JL_RTLD_LAZY | JL_RTLD_DEEPBIND)
2318+
#endif
23152319

23162320
typedef void *jl_libhandle; // compatible with dlopen (void*) / LoadLibrary (HMODULE)
23172321
JL_DLLEXPORT jl_libhandle jl_load_dynamic_library(const char *fname, unsigned flags, int throw_err);
23182322
JL_DLLEXPORT jl_libhandle jl_dlopen(const char *filename, unsigned flags) JL_NOTSAFEPOINT;
23192323
JL_DLLEXPORT int jl_dlclose(jl_libhandle handle) JL_NOTSAFEPOINT;
2320-
JL_DLLEXPORT int jl_dlsym(jl_libhandle handle, const char *symbol, void ** value, int throw_err) JL_NOTSAFEPOINT;
2324+
JL_DLLEXPORT int jl_dlsym(jl_libhandle handle, const char *symbol, void ** value, int throw_err, int search_deps) JL_NOTSAFEPOINT;
23212325

23222326
// evaluation
23232327
JL_DLLEXPORT jl_value_t *jl_toplevel_eval(jl_module_t *m, jl_value_t *v);

src/julia_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,7 @@ void win32_formatmessage(DWORD code, char *reason, int len) JL_NOTSAFEPOINT;
16301630
#endif
16311631

16321632
JL_DLLEXPORT void *jl_get_library_(const char *f_lib, int throw_err);
1633-
void *jl_find_dynamic_library_by_addr(void *symbol, int throw_err);
1633+
void *jl_find_dynamic_library_by_addr(void *symbol, int throw_err, int close) JL_NOTSAFEPOINT;
16341634
#define jl_get_library(f_lib) jl_get_library_(f_lib, 1)
16351635
JL_DLLEXPORT void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *hnd);
16361636
JL_DLLEXPORT void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name);

src/processor_arm.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ static inline unsigned long jl_getauxval(unsigned long type)
779779
// First, try resolving getauxval in libc
780780
auto libc = jl_dlopen(nullptr, JL_RTLD_LOCAL);
781781
static unsigned long (*getauxval_p)(unsigned long) = NULL;
782-
if (getauxval_p == NULL && jl_dlsym(libc, "getauxval", (void **)&getauxval_p, 0)) {
782+
if (getauxval_p == NULL && jl_dlsym(libc, "getauxval", (void **)&getauxval_p, 0, 0)) {
783783
return getauxval_p(type);
784784
}
785785

src/runtime_ccall.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *
5858
if (!handle)
5959
jl_atomic_store_release(hnd, (handle = jl_get_library(f_lib)));
6060
void * ptr;
61-
jl_dlsym(handle, f_name, &ptr, 1);
61+
jl_dlsym(handle, f_name, &ptr, 1, 0);
6262
return ptr;
6363
}
6464

@@ -79,7 +79,7 @@ void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name)
7979
} else
8080
jl_type_error("ccall", (jl_value_t*)jl_symbol_type, lib_val);
8181
void *ptr;
82-
jl_dlsym(lib_ptr, f_name, &ptr, 1);
82+
jl_dlsym(lib_ptr, f_name, &ptr, 1, 0);
8383
return ptr;
8484
}
8585

src/runtime_intrinsics.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ JL_DLLEXPORT jl_value_t *jl_cglobal(jl_value_t *v, jl_value_t *ty)
669669
}
670670
else {
671671
void *handle = jl_get_library((char*)jl_dlfind(f_name));
672-
jl_dlsym(handle, f_name, &ptr, 1);
672+
jl_dlsym(handle, f_name, &ptr, 1, 0);
673673
}
674674
JL_GC_POP();
675675

0 commit comments

Comments
 (0)