Skip to content

Commit f32da50

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 f32da50

File tree

8 files changed

+94
-40
lines changed

8 files changed

+94
-40
lines changed

src/ccall.cpp

Lines changed: 36 additions & 24 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
{
@@ -129,7 +129,7 @@ static Value *runtime_sym_lookup(
129129
// global HMODULE *libptrgv
130130
// global void **llvmgv
131131
// if (*llvmgv == NULL) {
132-
// *llvmgv = jl_load_and_lookup(f_lib, f_name, libptrgv);
132+
// *llvmgv = jl_load_and_lookup(f_lib, f_name, f_version, libptrgv);
133133
// }
134134
// return (*llvmgv)
135135
auto T_pvoidfunc = JuliaType::get_pvoidfunc_ty(irbuilder.getContext());
@@ -158,8 +158,9 @@ 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+
Value *versionval = nullableStringConstPtr(emission_context, irbuilder, f_version);
162+
llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jllazydlvsym_func),
163+
{ boxed(*ctx, libval), nameval, versionval });
163164
}
164165
else {
165166
Value *libname;
@@ -170,8 +171,9 @@ static Value *runtime_sym_lookup(
170171
// f_lib is actually one of the special sentinel values
171172
libname = ConstantExpr::getIntToPtr(ConstantInt::get(getSizeTy(irbuilder.getContext()), (uintptr_t)f_lib), getInt8PtrTy(irbuilder.getContext()));
172173
}
173-
llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jldlsym_func),
174-
{ libname, nameval, libptrgv });
174+
Value *versionval = nullableStringConstPtr(emission_context, irbuilder, f_version);
175+
llvmf = irbuilder.CreateCall(prepare_call_in(jl_builderModule(irbuilder), jldlvsym_func),
176+
{ libname, nameval, versionval, libptrgv });
175177
}
176178
StoreInst *store = irbuilder.CreateAlignedStore(llvmf, llvmgv, Align(sizeof(void*)));
177179
store->setAtomic(AtomicOrdering::Release);
@@ -188,18 +190,18 @@ static Value *runtime_sym_lookup(
188190
static Value *runtime_sym_lookup(
189191
jl_codectx_t &ctx,
190192
PointerType *funcptype, const char *f_lib, jl_value_t *lib_expr,
191-
const char *f_name, Function *f,
193+
const char *f_name, const char *f_version, Function *f,
192194
GlobalVariable *libptrgv,
193195
GlobalVariable *llvmgv, bool runtime_lib)
194196
{
195197
return runtime_sym_lookup(ctx.emission_context, ctx.builder, &ctx, funcptype, f_lib, lib_expr,
196-
f_name, f, libptrgv, llvmgv, runtime_lib);
198+
f_name, f_version, f, libptrgv, llvmgv, runtime_lib);
197199
}
198200

199201
static Value *runtime_sym_lookup(
200202
jl_codectx_t &ctx,
201203
PointerType *funcptype, const char *f_lib, jl_value_t *lib_expr,
202-
const char *f_name, Function *f)
204+
const char *f_name, const char *f_version, Function *f)
203205
{
204206
auto T_pvoidfunc = JuliaType::get_pvoidfunc_ty(ctx.builder.getContext());
205207
GlobalVariable *libptrgv;
@@ -223,15 +225,16 @@ static Value *runtime_sym_lookup(
223225
libptrgv = prepare_global_in(jl_Module, libptrgv);
224226
}
225227
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);
228+
return runtime_sym_lookup(ctx, funcptype, f_lib, lib_expr, f_name, f_version, f, libptrgv, llvmgv, runtime_lib);
227229
}
228230

229231
// Emit a "PLT" entry that will be lazily initialized
230232
// when being called the first time.
231233
static GlobalVariable *emit_plt_thunk(
232234
jl_codectx_t &ctx,
233235
FunctionType *functype, const AttributeList &attrs,
234-
CallingConv::ID cc, const char *f_lib, const char *f_name,
236+
CallingConv::ID cc,
237+
const char *f_lib, const char *f_name, const char *f_version,
235238
GlobalVariable *libptrgv, GlobalVariable *llvmgv,
236239
bool runtime_lib)
237240
{
@@ -256,8 +259,8 @@ static GlobalVariable *emit_plt_thunk(
256259
fname);
257260
BasicBlock *b0 = BasicBlock::Create(M->getContext(), "top", plt);
258261
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);
262+
Value *ptr = runtime_sym_lookup(ctx.emission_context, irbuilder, NULL, funcptype,
263+
f_lib, NULL, f_name, f_version, plt, libptrgv, llvmgv, runtime_lib);
261264
StoreInst *store = irbuilder.CreateAlignedStore(irbuilder.CreateBitCast(ptr, T_pvoidfunc), got, Align(sizeof(void*)));
262265
store->setAtomic(AtomicOrdering::Release);
263266
SmallVector<Value*, 16> args;
@@ -303,7 +306,8 @@ static Value *emit_plt(
303306
jl_codectx_t &ctx,
304307
FunctionType *functype,
305308
const AttributeList &attrs,
306-
CallingConv::ID cc, const char *f_lib, const char *f_name)
309+
CallingConv::ID cc,
310+
const char *f_lib, const char *f_name, const char *f_version)
307311
{
308312
++PLT;
309313
assert(ctx.emission_context.imaging);
@@ -320,7 +324,7 @@ static Value *emit_plt(
320324
GlobalVariable *&sharedgot = pltMap[key];
321325
if (!sharedgot) {
322326
sharedgot = emit_plt_thunk(ctx,
323-
functype, attrs, cc, f_lib, f_name, libptrgv, llvmgv, runtime_lib);
327+
functype, attrs, cc, f_lib, f_name, f_version, libptrgv, llvmgv, runtime_lib);
324328
}
325329
GlobalVariable *got = prepare_global_in(jl_Module, sharedgot);
326330
LoadInst *got_val = ctx.builder.CreateAlignedLoad(got->getValueType(), got, Align(sizeof(void*)));
@@ -565,6 +569,7 @@ typedef struct {
565569
void (*fptr)(void); // if the argument is a constant pointer
566570
const char *f_name; // if the symbol name is known
567571
const char *f_lib; // if a library name is specified
572+
const char *f_version;
568573
jl_value_t *lib_expr; // expression to compute library path lazily
569574
jl_value_t *gcroot;
570575
} native_sym_arg_t;
@@ -576,6 +581,8 @@ static void interpret_symbol_arg(jl_codectx_t &ctx, native_sym_arg_t &out, jl_va
576581
void (*&fptr)(void) = out.fptr;
577582
const char *&f_name = out.f_name;
578583
const char *&f_lib = out.f_lib;
584+
const char *&f_version = out.f_version;
585+
f_version = NULL;
579586

580587
jl_value_t *ptr = static_eval(ctx, arg);
581588
if (ptr == NULL) {
@@ -709,20 +716,24 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg
709716
}
710717
else {
711718
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);
719+
res = runtime_sym_lookup(ctx, cast<PointerType>(getInt8PtrTy(ctx.builder.getContext())), NULL,
720+
sym.lib_expr, sym.f_name, sym.f_version, ctx.f);
713721
}
714722
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);
723+
res = runtime_sym_lookup(ctx, cast<PointerType>(getInt8PtrTy(ctx.builder.getContext())),
724+
sym.f_lib, NULL, sym.f_name, sym.f_version, ctx.f);
716725
res = ctx.builder.CreatePtrToInt(res, lrt);
717726
}
718727
else {
719728
void *symaddr;
720729

721730
void* libsym = jl_get_library_(sym.f_lib, 0);
722-
if (!libsym || !jl_dlsym(libsym, sym.f_name, &symaddr, 0)) {
731+
int symbol_found = jl_dlvsym(libsym, sym.f_name, sym.f_version, &symaddr, 0);
732+
if (!libsym || !symbol_found) {
723733
// Error mode, either the library or the symbol couldn't be find during compiletime.
724734
// 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);
735+
res = runtime_sym_lookup(ctx, cast<PointerType>(getInt8PtrTy(ctx.builder.getContext())),
736+
sym.f_lib, NULL, sym.f_name, sym.f_version, ctx.f);
726737
res = ctx.builder.CreatePtrToInt(res, lrt);
727738
} else {
728739
// since we aren't saving this code, there's no sense in
@@ -2043,25 +2054,26 @@ jl_cgval_t function_sig_t::emit_a_ccall(
20432054
PointerType *funcptype = PointerType::get(functype, 0);
20442055
if (symarg.lib_expr) {
20452056
++DeferredCCallLookups;
2046-
llvmf = runtime_sym_lookup(ctx, funcptype, NULL, symarg.lib_expr, symarg.f_name, ctx.f);
2057+
llvmf = runtime_sym_lookup(ctx, funcptype, NULL, symarg.lib_expr, symarg.f_name, symarg.f_version, ctx.f);
20472058
}
20482059
else if (ctx.emission_context.imaging) {
20492060
++DeferredCCallLookups;
20502061
// vararg requires musttail,
20512062
// but musttail is incompatible with noreturn.
20522063
if (functype->isVarArg())
2053-
llvmf = runtime_sym_lookup(ctx, funcptype, symarg.f_lib, NULL, symarg.f_name, ctx.f);
2064+
llvmf = runtime_sym_lookup(ctx, funcptype, symarg.f_lib, NULL, symarg.f_name, symarg.f_version, ctx.f);
20542065
else
2055-
llvmf = emit_plt(ctx, functype, attributes, cc, symarg.f_lib, symarg.f_name);
2066+
llvmf = emit_plt(ctx, functype, attributes, cc, symarg.f_lib, symarg.f_name, symarg.f_version);
20562067
}
20572068
else {
20582069
void *symaddr;
20592070
void *libsym = jl_get_library_(symarg.f_lib, 0);
2060-
if (!libsym || !jl_dlsym(libsym, symarg.f_name, &symaddr, 0)) {
2071+
int symbol_found = jl_dlvsym(libsym, symarg.f_name, symarg.f_version, &symaddr, 0);
2072+
if (!libsym || !symbol_found) {
20612073
++DeferredCCallLookups;
20622074
// either the library or the symbol could not be found, place a runtime
20632075
// lookup here instead.
2064-
llvmf = runtime_sym_lookup(ctx, funcptype, symarg.f_lib, NULL, symarg.f_name, ctx.f);
2076+
llvmf = runtime_sym_lookup(ctx, funcptype, symarg.f_lib, NULL, symarg.f_name, symarg.f_version, ctx.f);
20652077
} else {
20662078
++LiteralCCalls;
20672079
// since we aren't saving this code, there's no sense in

src/cgutils.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,17 @@ static Value *stringConstPtr(
125125
Args);
126126
}
127127

128+
static Value *nullableStringConstPtr(
129+
jl_codegen_params_t &emission_context,
130+
IRBuilder<> &irbuilder,
131+
const char *txt)
132+
{
133+
if (txt == NULL)
134+
return ConstantPointerNull::get(PointerType::get(Type::getInt8Ty(irbuilder.getContext()), 0));
135+
136+
return stringConstPtr(emission_context, irbuilder, txt);
137+
}
138+
128139

129140
// --- MDNode ---
130141
Metadata *to_md_tree(jl_value_t *val, LLVMContext &ctxt) {

src/codegen.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,16 +1024,16 @@ static const auto memcmp_func = new JuliaFunction{
10241024
None); },
10251025
// TODO: inferLibFuncAttributes(*memcmp_func, TLI);
10261026
};
1027-
static const auto jldlsym_func = new JuliaFunction{
1027+
static const auto jldlvsym_func = new JuliaFunction{
10281028
XSTR(jl_load_and_lookup),
10291029
[](LLVMContext &C) { return FunctionType::get(JuliaType::get_pvoidfunc_ty(C),
1030-
{getInt8PtrTy(C), getInt8PtrTy(C), PointerType::get(getInt8PtrTy(C), 0)}, false); },
1030+
{getInt8PtrTy(C), getInt8PtrTy(C), getInt8PtrTy(C), PointerType::get(getInt8PtrTy(C), 0)}, false); },
10311031
nullptr,
10321032
};
1033-
static const auto jllazydlsym_func = new JuliaFunction{
1033+
static const auto jllazydlvsym_func = new JuliaFunction{
10341034
XSTR(jl_lazy_load_and_lookup),
10351035
[](LLVMContext &C) { return FunctionType::get(JuliaType::get_pvoidfunc_ty(C),
1036-
{JuliaType::get_prjlvalue_ty(C), getInt8PtrTy(C)}, false); },
1036+
{JuliaType::get_prjlvalue_ty(C), getInt8PtrTy(C), getInt8PtrTy(C)}, false); },
10371037
nullptr,
10381038
};
10391039
static const auto jltypeassert_func = new JuliaFunction{
@@ -8742,7 +8742,7 @@ static void init_jit_functions(void)
87428742
add_named_global(jl_typeof_func, (void*)NULL);
87438743
add_named_global(jl_write_barrier_func, (void*)NULL);
87448744
add_named_global(jl_write_barrier_binding_func, (void*)NULL);
8745-
add_named_global(jldlsym_func, &jl_load_and_lookup);
8745+
add_named_global(jldlvsym_func, &jl_load_and_lookup);
87468746
add_named_global("jl_adopt_thread", &jl_adopt_thread);
87478747
add_named_global(jlgetcfunctiontrampoline_func, &jl_get_cfunction_trampoline);
87488748
add_named_global(jlgetnthfieldchecked_func, &jl_get_nth_field_checked);

src/dlload.c

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

384-
JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int throw_err) JL_NOTSAFEPOINT
384+
JL_DLLEXPORT int jl_dlvsym(void *handle, const char *symbol, const char *version, void ** value, int throw_err) JL_NOTSAFEPOINT
385385
{
386+
386387
int symbol_found = 0;
387388

388389
/* First, get the symbol value */
390+
if (version != NULL) {
391+
#if defined(_OS_WINDOWS_) || !defined(__USE_GNU)
392+
if (throw_err) {
393+
jl_errorf("could not load symbol \"%s\" (version \"%s\"):\ndlvsym is not available on this platform", symbol, version);
394+
}
395+
return 0;
396+
#else
397+
*value = dlvsym(handle, symbol, version);
398+
#endif
399+
} else {
389400
#ifdef _OS_WINDOWS_
390-
*value = GetProcAddress((HMODULE) handle, symbol);
401+
*value = GetProcAddress((HMODULE) handle, symbol);
391402
#else
392-
*value = dlsym(handle, symbol);
403+
*value = dlsym(handle, symbol);
393404
#endif
405+
}
394406

395407
/* Next, check for errors. On Windows, a NULL pointer means the symbol was
396408
* not found. On everything else, we can have NULL symbols, so we check for
@@ -405,7 +417,15 @@ JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int t
405417
const char *err = "";
406418
if (!symbol_found) {
407419
dlerror(); /* Reset error status. */
408-
*value = dlsym(handle, symbol);
420+
if (version != NULL) {
421+
#if !defined(__USE_GNU)
422+
// unreachable
423+
#else
424+
*value = dlvsym(handle, symbol, version);
425+
#endif
426+
} else {
427+
*value = dlsym(handle, symbol);
428+
}
409429
err = dlerror();
410430
symbol_found = *value != NULL || err == NULL;
411431
}
@@ -416,11 +436,20 @@ JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int t
416436
char err[256];
417437
win32_formatmessage(GetLastError(), err, sizeof(err));
418438
#endif
419-
jl_errorf("could not load symbol \"%s\":\n%s", symbol, err);
439+
if (version != NULL) {
440+
jl_errorf("could not load symbol \"%s\" (version \"%s\"):\n%s", symbol, version, err);
441+
} else {
442+
jl_errorf("could not load symbol \"%s\":\n%s", symbol, err);
443+
}
420444
}
421445
return symbol_found;
422446
}
423447

448+
JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int throw_err) JL_NOTSAFEPOINT
449+
{
450+
return jl_dlvsym(handle, symbol, NULL, value, throw_err);
451+
}
452+
424453
#ifdef _OS_WINDOWS_
425454
//Look for symbols in win32 libraries
426455
JL_DLLEXPORT const char *jl_dlfind_win32(const char *f_name)

src/jl_exported_funcs.inc

Lines changed: 1 addition & 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) \

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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,8 +1285,8 @@ void win32_formatmessage(DWORD code, char *reason, int len) JL_NOTSAFEPOINT;
12851285

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)
1288-
JL_DLLEXPORT void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *hnd);
1289-
JL_DLLEXPORT void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name);
1288+
JL_DLLEXPORT void *jl_load_and_lookup(const char *f_lib, const char *f_name, const char *f_version, _Atomic(void*) *hnd);
1289+
JL_DLLEXPORT void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name, const char *f_version);
12901290
JL_DLLEXPORT jl_value_t *jl_get_cfunction_trampoline(
12911291
jl_value_t *fobj, jl_datatype_t *result, htable_t *cache, jl_svec_t *fill,
12921292
void *(*init_trampoline)(void *tramp, void **nval),

src/runtime_ccall.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,19 @@ void *jl_get_library_(const char *f_lib, int throw_err)
5454
}
5555

5656
extern "C" JL_DLLEXPORT
57-
void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *hnd)
57+
void *jl_load_and_lookup(const char *f_lib, const char *f_name, const char *f_version, _Atomic(void*) *hnd)
5858
{
5959
void *handle = jl_atomic_load_acquire(hnd);
6060
if (!handle)
6161
jl_atomic_store_release(hnd, (handle = jl_get_library(f_lib)));
6262
void * ptr;
63-
jl_dlsym(handle, f_name, &ptr, 1);
63+
jl_dlvsym(handle, f_name, f_version, &ptr, 1);
6464
return ptr;
6565
}
6666

6767
// jl_load_and_lookup, but with library computed at run time on first call
6868
extern "C" JL_DLLEXPORT
69-
void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name)
69+
void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name, const char *f_version)
7070
{
7171
char *f_lib;
7272

@@ -77,7 +77,7 @@ void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name)
7777
else
7878
jl_type_error("ccall", (jl_value_t*)jl_symbol_type, lib_val);
7979
void *ptr;
80-
jl_dlsym(jl_get_library(f_lib), f_name, &ptr, 1);
80+
jl_dlvsym(jl_get_library(f_lib), f_name, f_version, &ptr, 1);
8181
return ptr;
8282
}
8383

0 commit comments

Comments
 (0)