Skip to content

Commit 02a7ec1

Browse files
committed
Add internal dlvsym support for ccall's
This change lays the necessary groundwork to support performing versioned symbol lookups using `dlvsym`. Since there's no way to ask for a versioned symbol from `ccall` this code is currently unused, but it is a pre-requisite to add symbol versioning to Julia's internal libraries.
1 parent 7ba7e32 commit 02a7ec1

File tree

7 files changed

+139
-23
lines changed

7 files changed

+139
-23
lines changed

src/ccall.cpp

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ static Value *runtime_sym_lookup(
120120
IRBuilder<> &irbuilder,
121121
jl_codectx_t *ctx,
122122
PointerType *funcptype, const char *f_lib, jl_value_t *lib_expr,
123-
const char *f_name, Function *f,
123+
const char *f_name, const char *f_version, Function *f,
124124
GlobalVariable *libptrgv,
125125
GlobalVariable *llvmgv, bool runtime_lib)
126126
{
@@ -158,8 +158,14 @@ static Value *runtime_sym_lookup(
158158
Value *nameval = stringConstPtr(emission_context, irbuilder, f_name);
159159
if (lib_expr) {
160160
jl_cgval_t libval = emit_expr(*ctx, lib_expr);
161-
llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jllazydlsym_func),
162-
{ boxed(*ctx, libval), nameval });
161+
if (f_version != NULL) {
162+
Value *versionval = stringConstPtr(emission_context, irbuilder, f_version);
163+
llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jllazydlvsym_func),
164+
{ boxed(*ctx, libval), nameval, versionval });
165+
} else {
166+
llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jllazydlsym_func),
167+
{ boxed(*ctx, libval), nameval });
168+
}
163169
}
164170
else {
165171
Value *libname;
@@ -170,8 +176,14 @@ static Value *runtime_sym_lookup(
170176
// f_lib is actually one of the special sentinel values
171177
libname = ConstantExpr::getIntToPtr(ConstantInt::get(getSizeTy(irbuilder.getContext()), (uintptr_t)f_lib), getInt8PtrTy(irbuilder.getContext()));
172178
}
173-
llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jldlsym_func),
174-
{ libname, nameval, libptrgv });
179+
if (f_version != NULL) {
180+
Value *versionval = stringConstPtr(emission_context, irbuilder, f_version);
181+
llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jldlvsym_func),
182+
{ libname, nameval, versionval, libptrgv });
183+
} else {
184+
llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jldlsym_func),
185+
{ libname, nameval, libptrgv });
186+
}
175187
}
176188
StoreInst *store = irbuilder.CreateAlignedStore(llvmf, llvmgv, Align(sizeof(void*)));
177189
store->setAtomic(AtomicOrdering::Release);
@@ -188,18 +200,18 @@ static Value *runtime_sym_lookup(
188200
static Value *runtime_sym_lookup(
189201
jl_codectx_t &ctx,
190202
PointerType *funcptype, const char *f_lib, jl_value_t *lib_expr,
191-
const char *f_name, Function *f,
203+
const char *f_name, const char *f_version, Function *f,
192204
GlobalVariable *libptrgv,
193205
GlobalVariable *llvmgv, bool runtime_lib)
194206
{
195207
return runtime_sym_lookup(ctx.emission_context, ctx.builder, &ctx, funcptype, f_lib, lib_expr,
196-
f_name, f, libptrgv, llvmgv, runtime_lib);
208+
f_name, f_version, f, libptrgv, llvmgv, runtime_lib);
197209
}
198210

199211
static Value *runtime_sym_lookup(
200212
jl_codectx_t &ctx,
201213
PointerType *funcptype, const char *f_lib, jl_value_t *lib_expr,
202-
const char *f_name, Function *f)
214+
const char *f_name, const char *f_version, Function *f)
203215
{
204216
auto T_pvoidfunc = JuliaType::get_pvoidfunc_ty(ctx.builder.getContext());
205217
GlobalVariable *libptrgv;
@@ -223,15 +235,16 @@ static Value *runtime_sym_lookup(
223235
libptrgv = prepare_global_in(jl_Module, libptrgv);
224236
}
225237
llvmgv = prepare_global_in(jl_Module, llvmgv);
226-
return runtime_sym_lookup(ctx, funcptype, f_lib, lib_expr, f_name, f, libptrgv, llvmgv, runtime_lib);
238+
return runtime_sym_lookup(ctx, funcptype, f_lib, lib_expr, f_name, f_version, f, libptrgv, llvmgv, runtime_lib);
227239
}
228240

229241
// Emit a "PLT" entry that will be lazily initialized
230242
// when being called the first time.
231243
static GlobalVariable *emit_plt_thunk(
232244
jl_codectx_t &ctx,
233245
FunctionType *functype, const AttributeList &attrs,
234-
CallingConv::ID cc, const char *f_lib, const char *f_name,
246+
CallingConv::ID cc,
247+
const char *f_lib, const char *f_name, const char *f_version,
235248
GlobalVariable *libptrgv, GlobalVariable *llvmgv,
236249
bool runtime_lib)
237250
{
@@ -256,8 +269,8 @@ static GlobalVariable *emit_plt_thunk(
256269
fname);
257270
BasicBlock *b0 = BasicBlock::Create(M->getContext(), "top", plt);
258271
IRBuilder<> irbuilder(b0);
259-
Value *ptr = runtime_sym_lookup(ctx.emission_context, irbuilder, NULL, funcptype, f_lib, NULL, f_name, plt, libptrgv,
260-
llvmgv, runtime_lib);
272+
Value *ptr = runtime_sym_lookup(ctx.emission_context, irbuilder, NULL, funcptype,
273+
f_lib, NULL, f_name, f_version, plt, libptrgv, llvmgv, runtime_lib);
261274
StoreInst *store = irbuilder.CreateAlignedStore(irbuilder.CreateBitCast(ptr, T_pvoidfunc), got, Align(sizeof(void*)));
262275
store->setAtomic(AtomicOrdering::Release);
263276
SmallVector<Value*, 16> args;
@@ -303,7 +316,8 @@ static Value *emit_plt(
303316
jl_codectx_t &ctx,
304317
FunctionType *functype,
305318
const AttributeList &attrs,
306-
CallingConv::ID cc, const char *f_lib, const char *f_name)
319+
CallingConv::ID cc,
320+
const char *f_lib, const char *f_name, const char *f_version)
307321
{
308322
++PLT;
309323
assert(ctx.emission_context.imaging);
@@ -320,7 +334,7 @@ static Value *emit_plt(
320334
GlobalVariable *&sharedgot = pltMap[key];
321335
if (!sharedgot) {
322336
sharedgot = emit_plt_thunk(ctx,
323-
functype, attrs, cc, f_lib, f_name, libptrgv, llvmgv, runtime_lib);
337+
functype, attrs, cc, f_lib, f_name, f_version, libptrgv, llvmgv, runtime_lib);
324338
}
325339
GlobalVariable *got = prepare_global_in(jl_Module, sharedgot);
326340
LoadInst *got_val = ctx.builder.CreateAlignedLoad(got->getValueType(), got, Align(sizeof(void*)));
@@ -565,6 +579,7 @@ typedef struct {
565579
void (*fptr)(void); // if the argument is a constant pointer
566580
const char *f_name; // if the symbol name is known
567581
const char *f_lib; // if a library name is specified
582+
const char *f_version;
568583
jl_value_t *lib_expr; // expression to compute library path lazily
569584
jl_value_t *gcroot;
570585
} native_sym_arg_t;
@@ -576,6 +591,8 @@ static void interpret_symbol_arg(jl_codectx_t &ctx, native_sym_arg_t &out, jl_va
576591
void (*&fptr)(void) = out.fptr;
577592
const char *&f_name = out.f_name;
578593
const char *&f_lib = out.f_lib;
594+
const char *&f_version = out.f_version;
595+
f_version = NULL;
579596

580597
jl_value_t *ptr = static_eval(ctx, arg);
581598
if (ptr == NULL) {
@@ -709,20 +726,29 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg
709726
}
710727
else {
711728
if (sym.lib_expr) {
712-
res = runtime_sym_lookup(ctx, cast<PointerType>(getInt8PtrTy(ctx.builder.getContext())), NULL, sym.lib_expr, sym.f_name, ctx.f);
729+
res = runtime_sym_lookup(ctx, cast<PointerType>(getInt8PtrTy(ctx.builder.getContext())), NULL,
730+
sym.lib_expr, sym.f_name, sym.f_version, ctx.f);
713731
}
714732
else if (ctx.emission_context.imaging) {
715-
res = runtime_sym_lookup(ctx, cast<PointerType>(getInt8PtrTy(ctx.builder.getContext())), sym.f_lib, NULL, sym.f_name, ctx.f);
733+
res = runtime_sym_lookup(ctx, cast<PointerType>(getInt8PtrTy(ctx.builder.getContext())),
734+
sym.f_lib, NULL, sym.f_name, sym.f_version, ctx.f);
716735
res = ctx.builder.CreatePtrToInt(res, lrt);
717736
}
718737
else {
719738
void *symaddr;
720739

721740
void* libsym = jl_get_library_(sym.f_lib, 0);
722-
if (!libsym || !jl_dlsym(libsym, sym.f_name, &symaddr, 0)) {
741+
int symbol_found = 0;
742+
if (sym.f_version != NULL) {
743+
symbol_found = jl_dlvsym(libsym, sym.f_name, sym.f_version, &symaddr, 0);
744+
} else {
745+
symbol_found = jl_dlsym(libsym, sym.f_name, &symaddr, 0);
746+
}
747+
if (!libsym || !symbol_found) {
723748
// Error mode, either the library or the symbol couldn't be find during compiletime.
724749
// Fallback to a runtime symbol lookup.
725-
res = runtime_sym_lookup(ctx, cast<PointerType>(getInt8PtrTy(ctx.builder.getContext())), sym.f_lib, NULL, sym.f_name, ctx.f);
750+
res = runtime_sym_lookup(ctx, cast<PointerType>(getInt8PtrTy(ctx.builder.getContext())),
751+
sym.f_lib, NULL, sym.f_name, sym.f_version, ctx.f);
726752
res = ctx.builder.CreatePtrToInt(res, lrt);
727753
} else {
728754
// since we aren't saving this code, there's no sense in
@@ -2043,25 +2069,31 @@ jl_cgval_t function_sig_t::emit_a_ccall(
20432069
PointerType *funcptype = PointerType::get(functype, 0);
20442070
if (symarg.lib_expr) {
20452071
++DeferredCCallLookups;
2046-
llvmf = runtime_sym_lookup(ctx, funcptype, NULL, symarg.lib_expr, symarg.f_name, ctx.f);
2072+
llvmf = runtime_sym_lookup(ctx, funcptype, NULL, symarg.lib_expr, symarg.f_name, symarg.f_version, ctx.f);
20472073
}
20482074
else if (ctx.emission_context.imaging) {
20492075
++DeferredCCallLookups;
20502076
// vararg requires musttail,
20512077
// but musttail is incompatible with noreturn.
20522078
if (functype->isVarArg())
2053-
llvmf = runtime_sym_lookup(ctx, funcptype, symarg.f_lib, NULL, symarg.f_name, ctx.f);
2079+
llvmf = runtime_sym_lookup(ctx, funcptype, symarg.f_lib, NULL, symarg.f_name, symarg.f_version, ctx.f);
20542080
else
2055-
llvmf = emit_plt(ctx, functype, attributes, cc, symarg.f_lib, symarg.f_name);
2081+
llvmf = emit_plt(ctx, functype, attributes, cc, symarg.f_lib, symarg.f_name, symarg.f_version);
20562082
}
20572083
else {
20582084
void *symaddr;
20592085
void *libsym = jl_get_library_(symarg.f_lib, 0);
2060-
if (!libsym || !jl_dlsym(libsym, symarg.f_name, &symaddr, 0)) {
2086+
int symbol_found = 0;
2087+
if (symarg.f_version != NULL) {
2088+
symbol_found = jl_dlvsym(libsym, symarg.f_name, symarg.f_version, &symaddr, 0);
2089+
} else {
2090+
symbol_found = jl_dlsym(libsym, symarg.f_name, &symaddr, 0);
2091+
}
2092+
if (!libsym || !symbol_found) {
20612093
++DeferredCCallLookups;
20622094
// either the library or the symbol could not be found, place a runtime
20632095
// lookup here instead.
2064-
llvmf = runtime_sym_lookup(ctx, funcptype, symarg.f_lib, NULL, symarg.f_name, ctx.f);
2096+
llvmf = runtime_sym_lookup(ctx, funcptype, symarg.f_lib, NULL, symarg.f_name, symarg.f_version, ctx.f);
20652097
} else {
20662098
++LiteralCCalls;
20672099
// since we aren't saving this code, there's no sense in

src/codegen.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,12 +1030,24 @@ static const auto jldlsym_func = new JuliaFunction{
10301030
{getInt8PtrTy(C), getInt8PtrTy(C), PointerType::get(getInt8PtrTy(C), 0)}, false); },
10311031
nullptr,
10321032
};
1033+
static const auto jldlvsym_func = new JuliaFunction{
1034+
XSTR(jl_load_and_lookup_with_ver),
1035+
[](LLVMContext &C) { return FunctionType::get(JuliaType::get_pvoidfunc_ty(C),
1036+
{getInt8PtrTy(C), getInt8PtrTy(C), getInt8PtrTy(C), PointerType::get(getInt8PtrTy(C), 0)}, false); },
1037+
nullptr,
1038+
};
10331039
static const auto jllazydlsym_func = new JuliaFunction{
10341040
XSTR(jl_lazy_load_and_lookup),
10351041
[](LLVMContext &C) { return FunctionType::get(JuliaType::get_pvoidfunc_ty(C),
10361042
{JuliaType::get_prjlvalue_ty(C), getInt8PtrTy(C)}, false); },
10371043
nullptr,
10381044
};
1045+
static const auto jllazydlvsym_func = new JuliaFunction{
1046+
XSTR(jl_lazy_load_and_lookup_with_ver),
1047+
[](LLVMContext &C) { return FunctionType::get(JuliaType::get_pvoidfunc_ty(C),
1048+
{JuliaType::get_prjlvalue_ty(C), getInt8PtrTy(C), getInt8PtrTy(C)}, false); },
1049+
nullptr,
1050+
};
10391051
static const auto jltypeassert_func = new JuliaFunction{
10401052
XSTR(jl_typeassert),
10411053
[](LLVMContext &C) {
@@ -8743,6 +8755,7 @@ static void init_jit_functions(void)
87438755
add_named_global(jl_write_barrier_func, (void*)NULL);
87448756
add_named_global(jl_write_barrier_binding_func, (void*)NULL);
87458757
add_named_global(jldlsym_func, &jl_load_and_lookup);
8758+
add_named_global(jldlvsym_func, &jl_load_and_lookup_with_ver);
87468759
add_named_global("jl_adopt_thread", &jl_adopt_thread);
87478760
add_named_global(jlgetcfunctiontrampoline_func, &jl_get_cfunction_trampoline);
87488761
add_named_global(jlgetnthfieldchecked_func, &jl_get_nth_field_checked);

src/dlload.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,43 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags,
381381
return handle;
382382
}
383383

384+
JL_DLLEXPORT int jl_dlvsym(void *handle, const char *symbol, const char *version, void ** value, int throw_err) JL_NOTSAFEPOINT
385+
{
386+
#if defined(_OS_WINDOWS_) || !defined(__USE_GNU)
387+
if (throw_err) {
388+
jl_errorf("could not load symbol \"%s\" (version \"%s\"):\ndlvsym is not available on this platform", symbol, version);
389+
}
390+
return 0;
391+
#else
392+
int symbol_found = 0;
393+
394+
/* First, get the symbol value */
395+
*value = dlvsym(handle, symbol, version);
396+
397+
/* Next, check for errors. On Windows, a NULL pointer means the symbol was
398+
* not found. On everything else, we can have NULL symbols, so we check for
399+
* non-NULL returns from dlerror(). Since POSIX doesn't require `dlerror`
400+
* to be implemented safely, FreeBSD doesn't (unlike everyone else, who
401+
* realized decades ago that threads are here to stay), so we avoid calling
402+
* `dlerror` unless we need to get the error message.
403+
* https://github.com/freebsd/freebsd-src/blob/12db51d20823a5e3b9e5f8a2ea73156fe1cbfc28/libexec/rtld-elf/rtld.c#L198
404+
*/
405+
symbol_found = *value != NULL;
406+
const char *err = "";
407+
if (!symbol_found) {
408+
dlerror(); /* Reset error status. */
409+
*value = dlvsym(handle, symbol, version);
410+
err = dlerror();
411+
symbol_found = *value != NULL || err == NULL;
412+
}
413+
414+
if (!symbol_found && throw_err) {
415+
jl_errorf("could not load symbol \"%s\" (version \"%s\"):\n%s", symbol, version, err);
416+
}
417+
return symbol_found;
418+
#endif
419+
}
420+
384421
JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int throw_err) JL_NOTSAFEPOINT
385422
{
386423
int symbol_found = 0;

src/jl_exported_funcs.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
XX(jl_dlclose) \
117117
XX(jl_dlopen) \
118118
XX(jl_dlsym) \
119+
XX(jl_dlvsym) \
119120
XX(jl_dump_host_cpu) \
120121
XX(jl_check_pkgimage_clones) \
121122
XX(jl_egal) \
@@ -301,10 +302,12 @@
301302
XX(jl_is_unary_and_binary_operator) \
302303
XX(jl_is_unary_operator) \
303304
XX(jl_lazy_load_and_lookup) \
305+
XX(jl_lazy_load_and_lookup_with_ver) \
304306
XX(jl_lisp_prompt) \
305307
XX(jl_load) \
306308
XX(jl_load_) \
307309
XX(jl_load_and_lookup) \
310+
XX(jl_load_and_lookup_with_ver) \
308311
XX(jl_load_dynamic_library) \
309312
XX(jl_load_file_string) \
310313
XX(jl_lookup_code_address) \

src/julia.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,6 +1819,7 @@ JL_DLLEXPORT jl_libhandle jl_load_dynamic_library(const char *fname, unsigned fl
18191819
JL_DLLEXPORT jl_libhandle jl_dlopen(const char *filename, unsigned flags) JL_NOTSAFEPOINT;
18201820
JL_DLLEXPORT int jl_dlclose(jl_libhandle handle) JL_NOTSAFEPOINT;
18211821
JL_DLLEXPORT int jl_dlsym(jl_libhandle handle, const char *symbol, void ** value, int throw_err) JL_NOTSAFEPOINT;
1822+
JL_DLLEXPORT int jl_dlvsym(jl_libhandle handle, const char *symbol, const char *version, void ** value, int throw_err) JL_NOTSAFEPOINT;
18221823

18231824
// evaluation
18241825
JL_DLLEXPORT jl_value_t *jl_toplevel_eval(jl_module_t *m, jl_value_t *v);

src/julia_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,7 +1286,9 @@ void win32_formatmessage(DWORD code, char *reason, int len) JL_NOTSAFEPOINT;
12861286
JL_DLLEXPORT void *jl_get_library_(const char *f_lib, int throw_err);
12871287
#define jl_get_library(f_lib) jl_get_library_(f_lib, 1)
12881288
JL_DLLEXPORT void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *hnd);
1289+
JL_DLLEXPORT void *jl_load_and_lookup_with_ver(const char *f_lib, const char *f_name, const char *f_version, _Atomic(void*) *hnd);
12891290
JL_DLLEXPORT void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name);
1291+
JL_DLLEXPORT void *jl_lazy_load_and_lookup_with_ver(jl_value_t *lib_val, const char *f_name, const char *f_version);
12901292
JL_DLLEXPORT jl_value_t *jl_get_cfunction_trampoline(
12911293
jl_value_t *fobj, jl_datatype_t *result, htable_t *cache, jl_svec_t *fill,
12921294
void *(*init_trampoline)(void *tramp, void **nval),

src/runtime_ccall.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *
6464
return ptr;
6565
}
6666

67+
extern "C" JL_DLLEXPORT
68+
void *jl_load_and_lookup_with_ver(const char *f_lib, const char *f_name, const char *f_version, _Atomic(void*) *hnd)
69+
{
70+
void *handle = jl_atomic_load_acquire(hnd);
71+
if (!handle)
72+
jl_atomic_store_release(hnd, (handle = jl_get_library(f_lib)));
73+
void * ptr;
74+
jl_dlvsym(handle, f_name, f_version, &ptr, 1);
75+
return ptr;
76+
}
77+
6778
// jl_load_and_lookup, but with library computed at run time on first call
6879
extern "C" JL_DLLEXPORT
6980
void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name)
@@ -81,6 +92,23 @@ void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name)
8192
return ptr;
8293
}
8394

95+
// jl_load_and_lookup_with_ver, but with library computed at run time on first call
96+
extern "C" JL_DLLEXPORT
97+
void *jl_lazy_load_and_lookup_with_ver(jl_value_t *lib_val, const char *f_name, const char *f_version)
98+
{
99+
char *f_lib;
100+
101+
if (jl_is_symbol(lib_val))
102+
f_lib = jl_symbol_name((jl_sym_t*)lib_val);
103+
else if (jl_is_string(lib_val))
104+
f_lib = jl_string_data(lib_val);
105+
else
106+
jl_type_error("ccall", (jl_value_t*)jl_symbol_type, lib_val);
107+
void *ptr;
108+
jl_dlvsym(jl_get_library(f_lib), f_name, f_version, &ptr, 1);
109+
return ptr;
110+
}
111+
84112
// miscellany
85113
std::string jl_get_cpu_name_llvm(void)
86114
{

0 commit comments

Comments
 (0)