in erts/emulator/beam/erl_nif.c [4341:4648]
Eterm erts_load_nif(Process *c_p, BeamInstr *I, Eterm filename, Eterm args)
{
static const char bad_lib[] = "bad_lib";
static const char upgrade[] = "upgrade";
char* lib_name = NULL;
void* handle = NULL;
void* init_func = NULL;
ErlNifEntry* entry = NULL;
ErlNifEnv env;
int i, err, encoding;
Module* module_p;
Eterm mod_atom;
const Atom* mod_atomp;
Eterm f_atom;
ErtsCodeMFA* caller;
ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT;
Eterm ret = am_ok;
int veto;
int taint = 1;
struct erl_module_nif* lib = NULL;
struct erl_module_instance* this_mi;
struct erl_module_instance* prev_mi;
encoding = erts_get_native_filename_encoding();
if (encoding == ERL_FILENAME_WIN_WCHAR) {
/* Do not convert the lib name to utf-16le yet, do that in win32 specific code */
/* since lib_name is used in error messages */
encoding = ERL_FILENAME_UTF8;
}
lib_name = erts_convert_filename_to_encoding(filename, NULL, 0,
ERTS_ALC_T_TMP, 1, 0, encoding,
NULL, 0);
if (!lib_name) {
return THE_NON_VALUE;
}
/* Find calling module */
caller = erts_find_function_from_pc(I);
ASSERT(caller != NULL);
mod_atom = caller->module;
ASSERT(is_atom(mod_atom));
module_p = erts_get_module(mod_atom, erts_active_code_ix());
ASSERT(module_p != NULL);
mod_atomp = atom_tab(atom_val(mod_atom));
{
ErtsStaticNifEntry* sne;
sne = erts_static_nif_get_nif_init((char*)mod_atomp->name, mod_atomp->len);
if (sne != NULL) {
init_func = sne->nif_init;
handle = init_func;
taint = sne->taint;
}
}
this_mi = &module_p->curr;
prev_mi = &module_p->old;
if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) {
ret = load_nif_error(c_p, "old_code", "Calling load_nif from old "
"module '%T' not allowed", mod_atom);
goto error;
} else if (module_p->on_load) {
ASSERT(module_p->on_load->code_hdr->on_load_function_ptr);
if (module_p->curr.code_hdr) {
prev_mi = &module_p->curr;
} else {
prev_mi = &module_p->old;
}
this_mi = module_p->on_load;
}
if (this_mi->nif != NULL) {
ret = load_nif_error(c_p,"reload","NIF library already loaded"
" (reload disallowed since OTP 20).");
}
else if (init_func == NULL &&
(err=erts_sys_ddll_open(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) {
const char slogan[] = "Failed to load NIF library";
if (strstr(errdesc.str, lib_name) != NULL) {
ret = load_nif_error(c_p, "load_failed", "%s: '%s'", slogan, errdesc.str);
}
else {
ret = load_nif_error(c_p, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str);
}
}
else if (init_func == NULL &&
erts_sys_ddll_load_nif_init(handle, &init_func, &errdesc) != ERL_DE_NO_ERROR) {
ret = load_nif_error(c_p, bad_lib, "Failed to find library init"
" function: '%s'", errdesc.str);
}
else if ((taint ? erts_add_taint(mod_atom) : 0,
(entry = erts_sys_ddll_call_nif_init(init_func)) == NULL)) {
ret = load_nif_error(c_p, bad_lib, "Library init-call unsuccessful");
}
else if (entry->major > ERL_NIF_MAJOR_VERSION
|| (entry->major == ERL_NIF_MAJOR_VERSION
&& entry->minor > ERL_NIF_MINOR_VERSION)) {
char* fmt = "That '%T' NIF library needs %s or newer. Either try to"
" recompile the NIF lib or use a newer erts runtime.";
ret = load_nif_error(c_p, bad_lib, fmt, mod_atom, entry->min_erts);
}
else if (entry->major < ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD
|| (entry->major==2 && entry->minor == 5)) { /* experimental maps */
char* fmt = "That old NIF library (%d.%d) is not compatible with this "
"erts runtime (%d.%d). Try recompile the NIF lib.";
ret = load_nif_error(c_p, bad_lib, fmt, entry->major, entry->minor,
ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION);
}
else if (AT_LEAST_VERSION(entry, 2, 1)
&& sys_strcmp(entry->vm_variant, ERL_NIF_VM_VARIANT) != 0) {
ret = load_nif_error(c_p, bad_lib, "Library (%s) not compiled for "
"this vm variant (%s).",
entry->vm_variant, ERL_NIF_VM_VARIANT);
}
else if (!erts_is_atom_str((char*)entry->name, mod_atom, 1)) {
ret = load_nif_error(c_p, bad_lib, "Library module name '%s' does not"
" match calling module '%T'", entry->name, mod_atom);
}
else {
lib = create_lib(entry);
entry = &lib->entry; /* Use a guaranteed modern lib entry from now on */
lib->handle = handle;
erts_refc_init(&lib->refc, 2); /* Erlang code + NIF code */
erts_refc_init(&lib->dynlib_refc, 1);
ASSERT(opened_rt_list == NULL);
lib->mod = module_p;
#ifdef BEAMASM
lib->finish = erts_alloc(ERTS_ALC_T_NIF_FINISH,
sizeof_ErtsNifFinish(entry->num_of_funcs));
#else
lib->finish = erts_alloc(ERTS_ALC_T_NIF,
sizeof_ErtsNifFinish(entry->num_of_funcs));
#endif
lib->finish->nstubs_hashed = 0;
erts_rwmtx_rwlock(&erts_nif_call_tab_lock);
for (i=0; i < entry->num_of_funcs; i++) {
ErtsCodeInfo** ci_pp;
ErtsCodeInfo* ci;
ErlNifFunc* f = &entry->funcs[i];
ErtsNifBeamStub* stub = &lib->finish->beam_stubv[i];
stub->code_info = NULL; /* end marker in case we fail */
if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1)
|| (ci_pp = get_func_pp(this_mi->code_hdr, f_atom, f->arity))==NULL) {
ret = load_nif_error(c_p,bad_lib,"Function not found %T:%s/%u",
mod_atom, f->name, f->arity);
break;
}
ci = *ci_pp;
if (f->flags != 0 &&
f->flags != ERL_NIF_DIRTY_JOB_IO_BOUND &&
f->flags != ERL_NIF_DIRTY_JOB_CPU_BOUND) {
ret = load_nif_error(c_p, bad_lib, "Illegal flags field value %d for NIF %T:%s/%u",
f->flags, mod_atom, f->name, f->arity);
break;
}
ASSERT(erts_codeinfo_to_code(ci_pp[1]) - erts_codeinfo_to_code(ci_pp[0])
>= BEAM_NATIVE_MIN_FUNC_SZ);
ERTS_CT_ASSERT(BEAM_NATIVE_MIN_FUNC_SZ*8 >= sizeof(stub->beam));
stub->code_info = ci;
stub->info = *ci;
if (hash_put(&erts_nif_call_tab, stub) != stub) {
ret = load_nif_error(c_p, bad_lib, "Duplicate NIF entry for %T:%s/%u",
mod_atom, f->name, f->arity);
break;
}
lib->finish->nstubs_hashed++;
#ifdef BEAMASM
{
/* See beam_asm.h for details on how the nif load trampoline works */
void* normal_fptr, *dirty_fptr;
if (f->flags) {
if (f->flags == ERL_NIF_DIRTY_JOB_IO_BOUND)
normal_fptr = static_schedule_dirty_io_nif;
else
normal_fptr = static_schedule_dirty_cpu_nif;
dirty_fptr = f->fptr;
} else {
dirty_fptr = NULL;
normal_fptr = f->fptr;
}
beamasm_emit_call_nif(
ci, normal_fptr, lib, dirty_fptr,
(char*)&stub->info,
sizeof(stub->info) + sizeof(stub->prologue) + sizeof(stub->beam));
}
#else
stub->beam[0] = BeamOpCodeAddr(op_call_nif_WWW);
stub->beam[2] = (BeamInstr) lib;
if (f->flags) {
stub->beam[3] = (BeamInstr) f->fptr;
stub->beam[1] = (f->flags == ERL_NIF_DIRTY_JOB_IO_BOUND) ?
(BeamInstr) static_schedule_dirty_io_nif :
(BeamInstr) static_schedule_dirty_cpu_nif;
}
else
stub->beam[1] = (BeamInstr) f->fptr;
#endif
}
erts_rwmtx_rwunlock(&erts_nif_call_tab_lock);
ASSERT(lib->finish->nstubs_hashed == lib->entry.num_of_funcs);
}
if (ret != am_ok) {
goto error;
}
/* Call load or upgrade:
*/
env.mod_nif = lib;
lib->priv_data = NULL;
if (prev_mi->nif != NULL) { /**************** Upgrade ***************/
void* prev_old_data = prev_mi->nif->priv_data;
if (entry->upgrade == NULL) {
ret = load_nif_error(c_p, upgrade, "Upgrade not supported by this NIF library.");
goto error;
}
/*
* Go single scheduler during upgrade callback.
* Todo: Fix better solution with suspending callers and waiting for
* all calls to return (including dirty).
* Note that erts_thr_progress_block() will not block dirty NIFs.
*/
erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
erts_thr_progress_block();
erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
erts_pre_nif(&env, c_p, lib, NULL);
veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, args);
erts_post_nif(&env);
erts_thr_progress_unblock();
if (veto) {
prev_mi->nif->priv_data = prev_old_data;
ret = load_nif_error(c_p, upgrade, "Library upgrade-call unsuccessful (%d).", veto);
}
}
else if (entry->load != NULL) { /********* Initial load ***********/
erts_pre_nif(&env, c_p, lib, NULL);
veto = entry->load(&env, &lib->priv_data, args);
erts_post_nif(&env);
if (veto) {
ret = load_nif_error(c_p, "load", "Library load-call unsuccessful (%d).", veto);
}
}
if (ret == am_ok) {
/*
* Everything ok, make NIF code callable.
*/
this_mi->nif = lib;
prepare_opened_rt(lib);
/*
* The call table lock will make sure all NIFs and callbacks in module
* are made accessible atomically.
*/
erts_rwmtx_rwlock(&erts_nif_call_tab_lock);
commit_opened_rt();
patch_call_nif_early(entry, this_mi);
erts_rwmtx_rwunlock(&erts_nif_call_tab_lock);
cleanup_opened_rt();
/*
* Now we wait thread progress, to make sure no process is still
* executing the beam code of the NIFs, before we can patch in the
* final fast multi word call_nif_WWW instructions.
*/
erts_refc_inc(&lib->refc, 2);
erts_schedule_thr_prgr_later_op(load_nif_1st_finisher, lib,
&lib->lop);
}
else {
error:
rollback_opened_resource_types();
ASSERT(ret != am_ok);
if (lib != NULL) {
if (lib->finish != NULL) {
erase_hashed_stubs(lib->finish);
#ifdef BEAMASM
erts_free(ERTS_ALC_T_NIF_FINISH, lib->finish);
#else
erts_free(ERTS_ALC_T_NIF, lib->finish);
#endif
}
erts_free(ERTS_ALC_T_NIF, lib);
}
if (handle != NULL && !erts_is_static_nif(handle)) {
erts_sys_ddll_close(handle);
}
erts_sys_ddll_free_error(&errdesc);
}
erts_free(ERTS_ALC_T_TMP, lib_name);
BIF_RET(ret);
}