Skip to content
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

Rework dlsym(), dlopen(), jl_dlsym() and jl_load_dynamic_library() APIs #28888

Merged
merged 8 commits into from
Aug 29, 2018
26 changes: 16 additions & 10 deletions src/ccall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ static bool runtime_sym_gvs(const char *f_lib, const char *f_name, MT &&M,
llvmgv = new GlobalVariable(*M, T_pvoidfunc, false,
GlobalVariable::ExternalLinkage, NULL, name);
llvmgv = global_proto(llvmgv);
void *addr = jl_dlsym_e(libsym, f_name);
void *addr;
jl_dlsym(libsym, f_name, &addr, 0);
(*symMap)[f_name] = std::make_pair(llvmgv, addr);
if (symaddr)
*symaddr = addr;
Expand Down Expand Up @@ -776,8 +777,8 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg
res = ctx.builder.CreatePtrToInt(res, lrt);
}
else {
void *symaddr = jl_dlsym_e(jl_get_library(sym.f_lib), sym.f_name);
if (symaddr == NULL) {
void *symaddr;
if (!jl_dlsym(jl_get_library(sym.f_lib), sym.f_name, &symaddr, 0)) {
std::stringstream msg;
msg << "cglobal: could not find symbol ";
msg << sym.f_name;
Expand Down Expand Up @@ -1490,14 +1491,19 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs)
};
#define is_libjulia_func(name) _is_libjulia_func((uintptr_t)&(name), #name)

static jl_ptls_t (*ptls_getter)(void) = [] {
// directly accessing the address of an ifunc can cause compile-time linker issues
// on some configurations (e.g. AArch64 + -Bsymbolic-functions), so we guard the
// `&jl_get_ptls_states` within this `#ifdef` guard, and use a more roundabout
// method involving `jl_dlsym()` on Linux platforms instead.
#ifdef _OS_LINUX_
Copy link
Contributor

@yuyichao yuyichao Aug 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually makes the static useless. The performance isn't super critical here so if you want to just get rid of the static that's fine. Otherwise you can write it as.

static auto ptls_getter = [] {
#ifdef _OS_LINUX_
    jl_ptls_t (*p)(void);
    jl_dlsym(....);
    return p;
#else
    return jl_get_ptls_states;
#endif
}();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that's available even all the way back to GCC 4.8.5. I'm amazed. And yes, you're right this completely defeats static, but I like this fancy lambda initialization. I'm gonna go with that since compiler support is good enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is standard C++11 feature and we require C++11 so that shouldn't be very surprising......

// directly accessing the address of an ifunc can cause linker issue on
// some configurations (e.g. AArch64 + -Bsymbolic-functions).
static const auto ptls_getter = jl_dlsym_e(jl_dlopen(nullptr, 0),
"jl_get_ptls_states");
jl_ptls_t (*p)(void);
jl_dlsym(jl_dlopen(nullptr, 0), "jl_get_ptls_states", (void **)&p, 0);
return p;
#else
static const auto ptls_getter = &jl_get_ptls_states;
return &jl_get_ptls_states;
#endif
}();

// emit arguments
jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nccallargs);
Expand Down Expand Up @@ -1967,8 +1973,8 @@ jl_cgval_t function_sig_t::emit_a_ccall(
llvmf = emit_plt(ctx, functype, attributes, cc, symarg.f_lib, symarg.f_name);
}
else {
void *symaddr = jl_dlsym_e(jl_get_library(symarg.f_lib), symarg.f_name);
if (symaddr == NULL) {
void *symaddr;
if (!jl_dlsym(jl_get_library(symarg.f_lib), symarg.f_name, &symaddr, 0)) {
std::stringstream msg;
msg << "ccall: could not find function ";
msg << symarg.f_name;
Expand Down
74 changes: 41 additions & 33 deletions src/dlload.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,18 @@ extern char *julia_bindir;

#define JL_RTLD(flags, FLAG) (flags & JL_RTLD_ ## FLAG ? RTLD_ ## FLAG : 0)

static void JL_NORETURN jl_dlerror(const char *fmt, const char *sym)
static const char * jl_dlerror(void)
{
#ifdef _OS_WINDOWS_
CHAR reason[256];
static JL_THREAD_LOCAL CHAR reason[256];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change needed but note that the "more complex" tls implementation we have should actually be simpler and faster.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simpler than just writing JL_THREAD_LOCAL? How would I use it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simpler in the code generated that is.

FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(),
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
reason, sizeof(reason) / sizeof(reason[0]), NULL);
return (const char *)&reason[0];
#else
const char *reason = dlerror();
return dlerror();
#endif
jl_errorf(fmt, sym, reason);
}

JL_DLLEXPORT void *jl_dlopen(const char *filename, unsigned flags)
Expand Down Expand Up @@ -117,7 +117,7 @@ JL_DLLEXPORT int jl_dlclose(void *handle)
#endif
}

static void *jl_load_dynamic_library_(const char *modname, unsigned flags, int throw_err)
JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, int throw_err)
{
char path[PATHBUF];
int i;
Expand Down Expand Up @@ -192,56 +192,64 @@ static void *jl_load_dynamic_library_(const char *modname, unsigned flags, int t
}

notfound:
if (throw_err)
jl_dlerror("could not load library \"%s\"\n%s", modname);
if (throw_err) {
const char * reason = jl_dlerror();
jl_errorf("could not load library \"%s\"\n%s", modname, reason);
}
return NULL;

done:
return handle;
}

JL_DLLEXPORT void *jl_load_dynamic_library_e(const char *modname, unsigned flags)
{
return jl_load_dynamic_library_(modname, flags, 0);
}

JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags)
JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int throw_err)
{
return jl_load_dynamic_library_(modname, flags, 1);
}
int symbol_found = 0;

JL_DLLEXPORT void *jl_dlsym_e(void *handle, const char *symbol)
{
/* First, get the symbol value */
#ifdef _OS_WINDOWS_
void *ptr = GetProcAddress((HMODULE) handle, symbol);
*value = GetProcAddress((HMODULE) handle, symbol);
#else
dlerror(); /* Reset error status. */
void *ptr = dlsym(handle, symbol);
*value = dlsym(handle, symbol);
#endif
return ptr;
}

JL_DLLEXPORT void *jl_dlsym(void *handle, const char *symbol)
{
void *ptr = jl_dlsym_e(handle, symbol);
if (!ptr)
jl_dlerror("could not load symbol \"%s\":\n%s", symbol);
return ptr;
/* Next, check for errors. On Windows, a NULL pointer means the symbol
* was not found. On everything else, we can have NULL symbols, so we check
* for non-NULL returns from dlerror(). Note that we unconditionally call
* jl_dlerror() on POSIX systems, but on Windows systems we only call it
* when we have been returned a NULL symbol.*/
const char * err = NULL;
#ifdef _OS_WINDOWS_
symbol_found = *value != NULL;
#else
err = jl_dlerror();
symbol_found = err == NULL;
#endif

if (!symbol_found && throw_err) {
#ifdef _OS_WINDOWS_
err = jl_dlerror();
#endif
jl_errorf("could not load symbol \"%s\":\n%s", symbol, err);
}
return symbol_found;
}

#ifdef _OS_WINDOWS_
//Look for symbols in win32 libraries
const char *jl_dlfind_win32(const char *f_name)
{
if (jl_dlsym_e(jl_exe_handle, f_name))
void * dummy;
if (jl_dlsym(jl_exe_handle, f_name, &dummy, 0))
return JL_EXE_LIBNAME;
if (jl_dlsym_e(jl_dl_handle, f_name))
if (jl_dlsym(jl_dl_handle, f_name, &dummy, 0))
return JL_DL_LIBNAME;
if (jl_dlsym_e(jl_kernel32_handle, f_name))
if (jl_dlsym(jl_kernel32_handle, f_name, &dummy, 0))
return "kernel32";
if (jl_dlsym_e(jl_ntdll_handle, f_name))
if (jl_dlsym(jl_ntdll_handle, f_name, &dummy, 0))
return "ntdll";
if (jl_dlsym_e(jl_crtdll_handle, f_name))
if (jl_dlsym(jl_crtdll_handle, f_name, &dummy, 0))
#if defined(_MSC_VER)
#if _MSC_VER == 1800
return "msvcr120";
Expand All @@ -251,7 +259,7 @@ const char *jl_dlfind_win32(const char *f_name)
#else
return "msvcrt";
#endif
if (jl_dlsym_e(jl_winsock_handle, f_name))
if (jl_dlsym(jl_winsock_handle, f_name, &dummy, 0))
return "ws2_32";
// additional common libraries (libc?) could be added here, but in general,
// it is better to specify the library explicitly in the code. This exists
Expand Down
4 changes: 2 additions & 2 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ void _julia_init(JL_IMAGE_SEARCH rel)
}
jl_arr_xtralloc_limit = total_mem / 100; // Extra allocation limited to 1% of total RAM
jl_find_stack_bottom();
jl_dl_handle = jl_load_dynamic_library(NULL, JL_RTLD_DEFAULT);
jl_dl_handle = jl_load_dynamic_library(NULL, JL_RTLD_DEFAULT, 1);
#ifdef _OS_WINDOWS_
jl_ntdll_handle = jl_dlopen("ntdll.dll", 0); // bypass julia's pathchecking for system dlls
jl_kernel32_handle = jl_dlopen("kernel32.dll", 0);
Expand All @@ -661,7 +661,7 @@ void _julia_init(JL_IMAGE_SEARCH rel)
needsSymRefreshModuleList = 0;
HMODULE jl_dbghelp = (HMODULE) jl_dlopen("dbghelp.dll", 0);
if (jl_dbghelp)
hSymRefreshModuleList = (BOOL (WINAPI*)(HANDLE)) jl_dlsym(jl_dbghelp, "SymRefreshModuleList");
jl_dlsym(jl_dbghelp, "SymRefreshModuleList", (void **)&hSymRefreshModuleList, 1);
#else
jl_exe_handle = jl_dlopen(NULL, JL_RTLD_NOW);
#ifdef RTLD_DEFAULT
Expand Down
8 changes: 5 additions & 3 deletions src/jitlayers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,16 @@ static uint64_t resolve_atomic(const char *name)
#elif defined(_OS_WINDOWS_)
static const char *const libatomic = "libatomic-1.dll";
#endif
static void *atomic_hdl = jl_load_dynamic_library_e(libatomic,
JL_RTLD_LOCAL);
static void *atomic_hdl = jl_load_dynamic_library(libatomic,
JL_RTLD_LOCAL, 0);
static const char *const atomic_prefix = "__atomic_";
if (!atomic_hdl)
return 0;
if (strncmp(name, atomic_prefix, strlen(atomic_prefix)) != 0)
return 0;
return (uintptr_t)jl_dlsym_e(atomic_hdl, name);
uintptr_t value;
jl_dlsym(atomic_hdl, name, (void **)&value, 0);
return value;
}
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/jlapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ JL_DLLEXPORT void jl_init(void)
{
char *libbindir = NULL;
#ifdef _OS_WINDOWS_
void *hdl = (void*)jl_load_dynamic_library_e(NULL, JL_RTLD_DEFAULT);
void *hdl = (void*)jl_load_dynamic_library(NULL, JL_RTLD_DEFAULT, 0);
if (hdl) {
char *to_free = (char*)jl_pathname_for_handle(hdl);
if (to_free) {
Expand Down
9 changes: 5 additions & 4 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
# define JL_CONST_FUNC __attribute__((const))
# define JL_USED_FUNC __attribute__((used))
# define JL_SECTION(name) __attribute__((section(name)))
# define JL_THREAD_LOCAL __thread
#elif defined(_COMPILER_MICROSOFT_)
# define JL_NORETURN __declspec(noreturn)
// This is the closest I can find for __attribute__((const))
Expand All @@ -53,10 +54,12 @@
# define JL_USED_FUNC
// TODO: Figure out what to do on MSVC
# define JL_SECTION(x)
# define JL_THREAD_LOCAL __declspec(threaD)
#else
# define JL_NORETURN
# define JL_CONST_FUNC
# define JL_USED_FUNC
# define JL_THREAD_LOCAL
#endif

#define container_of(ptr, type, member) \
Expand Down Expand Up @@ -1498,12 +1501,10 @@ enum JL_RTLD_CONSTANT {
#define JL_RTLD_DEFAULT (JL_RTLD_LAZY | JL_RTLD_DEEPBIND)

typedef void *jl_uv_libhandle; // compatible with dlopen (void*) / LoadLibrary (HMODULE)
JL_DLLEXPORT jl_uv_libhandle jl_load_dynamic_library(const char *fname, unsigned flags);
JL_DLLEXPORT jl_uv_libhandle jl_load_dynamic_library_e(const char *fname, unsigned flags);
JL_DLLEXPORT jl_uv_libhandle jl_load_dynamic_library(const char *fname, unsigned flags, int throw_err);
JL_DLLEXPORT jl_uv_libhandle jl_dlopen(const char *filename, unsigned flags);
JL_DLLEXPORT int jl_dlclose(jl_uv_libhandle handle);
JL_DLLEXPORT void *jl_dlsym_e(jl_uv_libhandle handle, const char *symbol);
JL_DLLEXPORT void *jl_dlsym(jl_uv_libhandle handle, const char *symbol);
JL_DLLEXPORT int jl_dlsym(jl_uv_libhandle handle, const char *symbol, void ** value, int throw_err);

// compiler
JL_DLLEXPORT jl_value_t *jl_toplevel_eval(jl_module_t *m, jl_value_t *v);
Expand Down
30 changes: 20 additions & 10 deletions src/processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,23 +621,33 @@ template<typename F>
static inline jl_sysimg_fptrs_t parse_sysimg(void *hdl, F &&callback)
{
jl_sysimg_fptrs_t res = {nullptr, 0, nullptr, 0, nullptr, nullptr};
char * data_base;

// .data base
auto data_base = (char*)jl_dlsym(hdl, "jl_sysimg_gvars_base");
jl_dlsym(hdl, "jl_sysimg_gvars_base", (void **)&data_base, 1);
// .text base
res.base = (const char*)jl_dlsym(hdl, "jl_sysimg_fvars_base");
auto offsets = ((const int32_t*)jl_dlsym(hdl, "jl_sysimg_fvars_offsets")) + 1;
uint32_t nfunc = ((const uint32_t*)offsets)[-1];
res.offsets = offsets;
jl_dlsym(hdl, "jl_sysimg_fvars_base", (void **)&res.base, 1);

const int32_t * offsets;
jl_dlsym(hdl, "jl_sysimg_fvars_offsets", (void **)&offsets, 1);
uint32_t nfunc = offsets[0];
res.offsets = offsets + 1;

void *ids = jl_dlsym(hdl, "jl_dispatch_target_ids");
void *ids;
jl_dlsym(hdl, "jl_dispatch_target_ids", &ids, 1);
uint32_t target_idx = callback(ids);

auto reloc_slots = ((const int32_t*)jl_dlsym(hdl, "jl_dispatch_reloc_slots")) + 1;
auto nreloc = ((const uint32_t*)reloc_slots)[-1];
auto clone_idxs = (const uint32_t*)jl_dlsym(hdl, "jl_dispatch_fvars_idxs");
auto clone_offsets = (const int32_t*)jl_dlsym(hdl, "jl_dispatch_fvars_offsets");
const int32_t * reloc_slots;
jl_dlsym(hdl, "jl_dispatch_reloc_slots",(void **) &reloc_slots, 1);
const uint32_t nreloc = reloc_slots[0];
reloc_slots += 1;
const uint32_t * clone_idxs;
const int32_t * clone_offsets;
jl_dlsym(hdl, "jl_dispatch_fvars_idxs", (void **)&clone_idxs, 1);
jl_dlsym(hdl, "jl_dispatch_fvars_offsets", (void **)&clone_offsets, 1);
uint32_t tag_len = clone_idxs[0];
clone_idxs += 1;

assert(tag_len & jl_sysimg_tag_mask);
std::vector<const int32_t*> base_offsets = {res.offsets};
// Find target
Expand Down
22 changes: 8 additions & 14 deletions src/processor_arm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,17 +499,6 @@ static constexpr size_t ncpu_names = sizeof(cpus) / sizeof(cpus[0]);
#endif

#if defined(DYN_GETAUXVAL)
static bool getauxval_dlsym(unsigned long type, unsigned long *val)
{
static auto getauxval_p = (unsigned long (*)(unsigned long))
jl_dlsym_e(jl_dlopen(nullptr, JL_RTLD_LOCAL), "getauxval");
if (getauxval_p) {
*val = getauxval_p(type);
return true;
}
return false;
}

static unsigned long getauxval_procfs(unsigned long type)
{
int fd = open("/proc/self/auxv", O_RDONLY);
Expand All @@ -531,9 +520,14 @@ static unsigned long getauxval_procfs(unsigned long type)

static inline unsigned long jl_getauxval(unsigned long type)
{
unsigned long val;
if (getauxval_dlsym(type, &val))
return val;
// First, try resolving getauxval in libc
auto libc = jl_dlopen(nullptr, JL_RTLD_LOCAL);
static (unsigned long (*)(unsigned long) getauxval_p;
if (jl_dlsym(libc, "getauxval", &getauxval_p, 0) {
return getauxval_p(type);
}

// If we couldn't resolve it, use procfs.
return getauxval_procfs(type);
}
#else
Expand Down
6 changes: 4 additions & 2 deletions src/runtime_ccall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void *jl_get_library(const char *f_lib)
if (hnd != NULL)
return hnd;
// We might run this concurrently on two threads but it doesn't matter.
hnd = jl_load_dynamic_library(f_lib, JL_RTLD_DEFAULT);
hnd = jl_load_dynamic_library(f_lib, JL_RTLD_DEFAULT, 1);
if (hnd != NULL)
jl_atomic_store_release(map_slot, hnd);
return hnd;
Expand All @@ -57,7 +57,9 @@ void *jl_load_and_lookup(const char *f_lib, const char *f_name, void **hnd)
void *handle = jl_atomic_load_acquire(hnd);
if (!handle)
jl_atomic_store_release(hnd, (handle = jl_get_library(f_lib)));
return jl_dlsym(handle, f_name);
void * ptr;
jl_dlsym(handle, f_name, &ptr, 1);
return ptr;
}

// miscellany
Expand Down
3 changes: 2 additions & 1 deletion src/runtime_intrinsics.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ JL_DLLEXPORT jl_value_t *jl_cglobal(jl_value_t *v, jl_value_t *ty)
f_lib = (char*)jl_dlfind_win32(f_name);
#endif

void *ptr = jl_dlsym(jl_get_library(f_lib), f_name);
void *ptr;
jl_dlsym(jl_get_library(f_lib), f_name, &ptr, 1);
jl_value_t *jv = jl_gc_alloc_1w();
jl_set_typeof(jv, rt);
*(void**)jl_data_ptr(jv) = ptr;
Expand Down
3 changes: 1 addition & 2 deletions src/signals-win.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@ void restore_signals(void)
// turn on ctrl-c handler
SetConsoleCtrlHandler(NULL, 0);
// see if SetThreadStackGuarantee exists
pSetThreadStackGuarantee = (BOOL (*)(PULONG)) jl_dlsym_e(jl_kernel32_handle,
"SetThreadStackGuarantee");
jl_dlsym(jl_kernel32_handle, "SetThreadStackGuarantee", (const void **)&pSetThreadStackGuarantee, 0);
}

void jl_throw_in_ctx(jl_value_t *excpt, CONTEXT *ctxThread, int bt)
Expand Down
Loading