aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/beam/erl_nif.c
blob: 2cb93112ae3ca0a6205cacf5f9fba8e2f2fec1ce (plain) (tree)




































                                                                         









































































                                                                   



                                                   




                                                              





                                                  


































                                                                          


















                                                                         



















                                                           





















                                                                        

























                                                            
                                      







                                                     
                                         




















                                                                 










                                                           
































                                                                            







                                                       





                                                             



                                                                                 














































                                                                      
                                    



                                                 
























                                                                                 
 
 
 



                                                                   

































                                                                             
                                                                    
















































                                                               











                                                                     
















                                                                                  

                                                                             



























































                                                                                         






                                                                                                     

























                                                                                                       

                                                                                 
























































































                                                                                                   
                                                                                            

























                                                                               
/*
 * %CopyrightBegin%
 * 
 * Copyright Ericsson AB 2009. All Rights Reserved.
 * 
 * The contents of this file are subject to the Erlang Public License,
 * Version 1.1, (the "License"); you may not use this file except in
 * compliance with the License. You should have received a copy of the
 * Erlang Public License along with this software. If not, it can be
 * retrieved online at http://www.erlang.org/.
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * %CopyrightEnd%
 */
/* Erlang Native InterFace
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include "erl_nif.h"

#include "sys.h"
#include "global.h"
#include "erl_binary.h"
#include "bif.h"
#include "error.h"
#include "big.h"
#include "beam_bp.h"

#include <limits.h>

#define MIN_HEAP_FRAG_SZ 200
static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need);

static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, unsigned need)
{
    Eterm* hp = env->hp;
    env->hp += need;
    if (env->hp <= env->hp_end) {
	return hp;
    }
    env->hp = hp;
    return alloc_heap_heavy(env,need);
}

static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need)
{
    Eterm* hp;

    if (env->heap_frag_sz == 0) {       
	ASSERT(HEAP_LIMIT(env->proc) == env->hp_end);
	HEAP_TOP(env->proc) = env->hp;	
	env->heap_frag_sz = need + MIN_HEAP_FRAG_SZ;
    }
    else {
	HRelease(env->proc, env->hp_end, env->hp);
	env->heap_frag_sz *= 2;
    }
    hp = erts_heap_alloc(env->proc, env->heap_frag_sz);
    env->hp = hp + need;
    env->hp_end = hp + env->heap_frag_sz;
    return hp;
}

void erts_pre_nif(ErlNifEnv* env, Process* p, void* nif_data)
{
    env->nif_data = nif_data;
    env->proc = p;
    env->hp = HEAP_TOP(p);
    env->hp_end = HEAP_LIMIT(p);
    env->heap_frag_sz = 0;
    env->fpe_was_unmasked = erts_block_fpe();
}

void erts_post_nif(ErlNifEnv* env)
{
    erts_unblock_fpe(env->fpe_was_unmasked);
    if (env->heap_frag_sz == 0) {
	ASSERT(env->hp_end == HEAP_LIMIT(env->proc));
	ASSERT(env->hp >= HEAP_TOP(env->proc));
	ASSERT(env->hp <= HEAP_LIMIT(env->proc));	
	HEAP_TOP(env->proc) = env->hp;
    }
    else {
	ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
	ASSERT(env->hp_end - env->hp <= env->heap_frag_sz);
	HRelease(env->proc, env->hp_end, env->hp);
    }
}

void* enif_get_data(ErlNifEnv* env)
{
    return env->nif_data;
}

void* enif_alloc(ErlNifEnv* env, size_t size)
{
    return erts_alloc_fnf(ERTS_ALC_T_NIF, (Uint) size);
}

void enif_free(ErlNifEnv* env, void* ptr)
{
    erts_free(ERTS_ALC_T_NIF, ptr);
}

int enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)
{
    return is_atom(term);
}

int enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)
{
    return is_binary(term) && (binary_bitsize(term) % 8 == 0);
}

int enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)
{
    return is_ref(term);
}


int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin)
{
    bin->tmp_alloc = NULL;
    bin->data = erts_get_aligned_binary_bytes(bin_term, &bin->tmp_alloc);
    if (bin->data == NULL) {
	return 0;
    }
    bin->bin_term = bin_term;
    bin->size = binary_size(bin_term);
    bin->ref_bin = NULL;
    return 1;
}


int enif_alloc_binary(ErlNifEnv* env, unsigned size, ErlNifBinary* bin)
{
    Binary* refbin;

    refbin = erts_bin_drv_alloc_fnf(size); /* BUGBUG: alloc type? */
    if (refbin == NULL) {
	return 0; /* The NIF must take action */
    }
    refbin->flags = BIN_FLAG_DRV; /* BUGBUG: Flag? */
    erts_refc_init(&refbin->refc, 1);
    refbin->orig_size = (long) size;

    bin->size = size;
    bin->data = (unsigned char*) refbin->orig_bytes;
    bin->bin_term = THE_NON_VALUE;
    bin->tmp_alloc = NULL;
    bin->ref_bin = refbin;
    return 1;
}

int enif_realloc_binary(ErlNifEnv* env, ErlNifBinary* bin, unsigned size)
{
    Binary* oldbin;
    Binary* newbin;
    ASSERT(bin->ref_bin != NULL);

    oldbin = (Binary*) bin->ref_bin; 
    newbin = (Binary *) erts_bin_realloc_fnf(oldbin, size);
    if (!newbin) {
	return 0;
    }    
    newbin->orig_size = size;
    bin->ref_bin = newbin;
    bin->data = (unsigned char*) newbin->orig_bytes;
    bin->size = size;
    return 1;
}


void enif_release_binary(ErlNifEnv* env, ErlNifBinary* bin)
{
    if (bin->ref_bin == NULL) {
	erts_free_aligned_binary_bytes(bin->tmp_alloc);
    }
    else {
	Binary* refbin = bin->ref_bin;
	ASSERT(bin->tmp_alloc == NULL);
	ASSERT(bin->bin_term == THE_NON_VALUE);
	if (erts_refc_dectest(&refbin->refc, 0) == 0) {
	    erts_bin_free(refbin);
	}
    }
#ifdef DEBUG
    bin->bin_term = THE_NON_VALUE;
    bin->tmp_alloc = NULL;
    bin->ref_bin = NULL;
#endif
}

int enif_is_identical(ErlNifEnv* env, Eterm lhs, Eterm rhs)
{
    return EQ(lhs,rhs);
}

int enif_compare(ErlNifEnv* env, Eterm lhs, Eterm rhs)
{
    return cmp(lhs,rhs);
}

int enif_get_tuple(ErlNifEnv* env, Eterm tpl, int* arity, Eterm** array)
{
    Eterm* ptr;
    if (is_not_tuple(tpl)) {
	return 0;
    }
    ptr = tuple_val(tpl);
    *arity = arityval(*ptr);
    *array = ptr+1;
    return 1;
}

Eterm enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)
{
    if (bin->ref_bin == NULL) {
	erts_free_aligned_binary_bytes(bin->tmp_alloc);
	return bin->bin_term;
    }
    else {
	Binary* bptr = bin->ref_bin;
	ProcBin* pb;
	ASSERT(bin->tmp_alloc == NULL);
	
	/* !! Copy-paste from new_binary() !! */
	pb = (ProcBin *) alloc_heap(env, PROC_BIN_SIZE);
	pb->thing_word = HEADER_PROC_BIN;
	pb->size = bptr->orig_size;
	pb->next = MSO(env->proc).mso;
	MSO(env->proc).mso = pb;
	pb->val = bptr;
	pb->bytes = (byte*) bptr->orig_bytes;
	pb->flags = 0;
	
	MSO(env->proc).overhead += pb->size / sizeof(Eterm);
	return make_binary(pb);
    }
}

Eterm enif_make_badarg(ErlNifEnv* env)
{
    BIF_ERROR(env->proc, BADARG);
}


int enif_get_int(ErlNifEnv* env, Eterm term, int* ip)
{
#if SIZEOF_INT == SIZEOF_VOID_P
    return term_to_Sint(term, (Sint*)ip);
#elif SIZEOF_LONG == SIZEOF_VOID_P
    Sint i;
    if (!term_to_Sint(term, &i) || i < INT_MIN || i > INT_MAX) {
	return 0;
    }
    *ip = (int) i;
    return 1;
#else
#  error Unknown word size 
#endif     
}

int enif_get_ulong(ErlNifEnv* env, Eterm term, unsigned long* ip)
{
#if SIZEOF_LONG == SIZEOF_VOID_P
    return term_to_Uint(term, ip);
#else
#  error Unknown long word size 
#endif     
}

int enif_get_double(ErlNifEnv* env, Eterm term, double* dp)
{
    FloatDef f;
    if (is_not_float(term)) {
	return 0;
    }
    GET_DOUBLE(term, f);
    *dp = f.fd;
    return 1;
}

int enif_get_list_cell(ErlNifEnv* env, Eterm term, Eterm* head, Eterm* tail)
{
    Eterm* val;
    if (is_not_list(term)) return 0;
    val = list_val(term);
    *head = CAR(val);
    *tail = CDR(val);
    return 1;
}

ERL_NIF_TERM enif_make_int(ErlNifEnv* env, int i)
{
#if SIZEOF_INT == SIZEOF_VOID_P
    return IS_SSMALL(i) ? make_small(i) : small_to_big(i,alloc_heap(env,2));
#elif SIZEOF_LONG == SIZEOF_VOID_P
    return make_small(i);
#endif
}

ERL_NIF_TERM enif_make_ulong(ErlNifEnv* env, unsigned long i)
{
#if SIZEOF_LONG == SIZEOF_VOID_P
    Eterm* hp;
    Uint sz = 0;
    erts_bld_uint(NULL, &sz, i);
    hp = alloc_heap(env,sz);
    return erts_bld_uint(&hp, NULL, i);
#else
#  error Unknown long word size 
#endif     

}

ERL_NIF_TERM enif_make_double(ErlNifEnv* env, double d)
{
    Eterm* hp = alloc_heap(env,FLOAT_SIZE_OBJECT);
    FloatDef f;
    f.fd = d;
    PUT_DOUBLE(f, hp);
    return make_float(hp);
}

ERL_NIF_TERM enif_make_atom(ErlNifEnv* env, const char* name)
{
    return am_atom_put(name, sys_strlen(name));
}

int enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom)
{
    return erts_atom_get(name, sys_strlen(name), atom);
}

ERL_NIF_TERM enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)
{
    Eterm* hp = alloc_heap(env,cnt+1);
    Eterm ret = make_tuple(hp);
    va_list ap;

    *hp++ = make_arityval(cnt);
    va_start(ap,cnt);
    while (cnt--) {
	*hp++ = va_arg(ap,Eterm);	   
    }
    va_end(ap);
    return ret;
}

ERL_NIF_TERM enif_make_list_cell(ErlNifEnv* env, Eterm car, Eterm cdr)
{
    Eterm* hp = alloc_heap(env,2);
    Eterm ret = make_list(hp);

    CAR(hp) = car;
    CDR(hp) = cdr;
    return ret;
}

ERL_NIF_TERM enif_make_list(ErlNifEnv* env, unsigned cnt, ...)
{
    Eterm* hp = alloc_heap(env,cnt*2);
    Eterm ret = make_list(hp);
    Eterm* last = &ret;
    va_list ap;

    va_start(ap,cnt);
    while (cnt--) {
	*last = make_list(hp);
	*hp = va_arg(ap,Eterm);
	last = ++hp;
	++hp;
    }
    va_end(ap);
    *last = NIL;
    return ret;
}

ERL_NIF_TERM enif_make_string(ErlNifEnv* env, const char* string)
{    
    Sint n = sys_strlen(string);    
    Eterm* hp = alloc_heap(env,n*2);
    return erts_bld_string_n(&hp,NULL,string,n); 
}

ERL_NIF_TERM enif_make_ref(ErlNifEnv* env)
{
    Eterm* hp = alloc_heap(env, REF_THING_SIZE);
    return erts_make_ref_in_buffer(hp);
}

#if 0 /* To be continued... */
typedef struct enif_handle_type_t
{
    struct enif_handle_type_t* next;
    struct enif_handle_type_t* prev;
    const char* name;
    void (*dtor)(void* obj);
    erts_smp_atomic_t ref_cnt;  /* num of handles of this type */
}ErlNifHandleType;

ErlNifHandleType*
enif_create_handle_type(ErlNifEnv* env, const char* type_name, 
			void (*dtor)(void *))
{

}

ERL_NIF_TERM enif_make_handle(ErlNifEnv* env, ErlNifHandleType* htype, void* obj)
{

}

int enif_get_handle(ErlNifEnv* env, ERL_NIF_TERM term, void** objp)
{
}
#endif

/***************************************************************************
 **                              load_nif/2                               **
 ***************************************************************************/


static Uint** get_func_pp(Eterm* mod_code, Eterm f_atom, unsigned arity)
{
    int n = (int) mod_code[MI_NUM_FUNCTIONS];
    int j;
    for (j = 0; j < n; ++j) {
	Uint* code_ptr = (Uint*) mod_code[MI_FUNCTIONS+j];
	ASSERT(code_ptr[0] == (Uint) BeamOp(op_i_func_info_IaaI));
	if (f_atom == ((Eterm) code_ptr[3])
	    && arity == ((unsigned) code_ptr[4])) {

	    return (Uint**) &mod_code[MI_FUNCTIONS+j];
	}
    }
    return NULL;
}

#define in_area(ptr,start,nbytes) \
    ((unsigned long)((char*)(ptr) - (char*)(start)) < (nbytes))

static void refresh_cached_nif_data(Eterm* mod_code,
				    struct erl_module_nif* mod_nif)
{
    int i;
    for (i=0; i < mod_nif->entry->num_of_funcs; i++) {
	Eterm f_atom;
	ErlNifFunc* func = &mod_nif->entry->funcs[i];	
	Uint* code_ptr;
	
	erts_atom_get(func->name, sys_strlen(func->name), &f_atom); 
	code_ptr = *get_func_pp(mod_code, f_atom, func->arity);
	code_ptr[5+2] = (Uint) mod_nif->data;
    }
}

static Eterm mkatom(const char *str)
{
    return am_atom_put(str, sys_strlen(str));
}

static struct tainted_module_t
{
    struct tainted_module_t* next;
    Eterm module_atom;
}*first_tainted_module = NULL;

static void add_taint(Eterm mod_atom)
{
    struct tainted_module_t* t;
    for (t=first_tainted_module ; t!=NULL; t=t->next) {
	if (t->module_atom == mod_atom) {
	    return;
	}
    }
    t = erts_alloc_fnf(ERTS_ALC_T_TAINT, sizeof(*t));
    if (t != NULL) {
	t->module_atom = mod_atom;
	t->next = first_tainted_module;
	first_tainted_module = t;
    }
}

Eterm erts_nif_taints(Process* p)
{
    struct tainted_module_t* t;
    unsigned cnt = 0;
    Eterm list = NIL;
    Eterm* hp;
    for (t=first_tainted_module ; t!=NULL; t=t->next) {
	cnt++;
    }
    hp = HAlloc(p,cnt*2);
    for (t=first_tainted_module ; t!=NULL; t=t->next) {
	list = CONS(hp, t->module_atom, list);
	hp += 2;
    }
    return list;
}

void erts_print_nif_taints(int to, void* to_arg)
{
    struct tainted_module_t* t;
    const char* delim = "";
    for (t=first_tainted_module ; t!=NULL; t=t->next) {
	const Atom* atom = atom_tab(atom_val(t->module_atom));
	erts_print(to,to_arg,"%s%.*s", delim, atom->len, atom->name);
	delim = ",";
    }
    erts_print(to,to_arg,"\n");
}


static Eterm load_nif_error(Process* p, const char* atom, const char* format, ...)
{
    erts_dsprintf_buf_t* dsbufp = erts_create_tmp_dsbuf(0);
    Eterm ret;
    Eterm* hp;
    Eterm** hpp = NULL;
    Uint sz = 0;
    Uint* szp = &sz;
    va_list arglist;   

    va_start(arglist, format);
    erts_vdsprintf(dsbufp, format, arglist);
    va_end(arglist);
    
    for (;;) {
	Eterm txt = erts_bld_string_n(hpp, &sz, dsbufp->str, dsbufp->str_len);
	ret = erts_bld_tuple(hpp, szp, 2, am_error,
			     erts_bld_tuple(hpp, szp, 2, mkatom(atom), txt));
	if (hpp != NULL) {
	    break;
	}
	hp = HAlloc(p,sz);
	hpp = &hp;
	szp = NULL;
    }
    erts_destroy_tmp_dsbuf(dsbufp);
    return ret;
}

BIF_RETTYPE load_nif_2(BIF_ALIST_2)
{
    static const char bad_lib[] = "bad_lib";
    static const char reload[] = "reload";
    static const char upgrade[] = "upgrade";
    char lib_name[256]; /* BUGBUG: Max-length? */
    void* handle = NULL;
    void* init_func;
    ErlNifEntry* entry = NULL;
    ErlNifEnv env;
    int len, i, err;
    Module* mod;
    Eterm mod_atom;
    Eterm f_atom;
    Eterm* caller;
    ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT;
    Eterm ret = am_ok;
    int veto;

    len = intlist_to_buf(BIF_ARG_1, lib_name, sizeof(lib_name)-1);
    if (len < 1) {
	/*erts_fprintf(stderr, "Invalid library path name '%T'\r\n", BIF_ARG_1);*/
	BIF_ERROR(BIF_P, BADARG);
    }
    lib_name[len] = '\0';

    /* Block system (is this the right place to do it?) */
    erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
    erts_smp_block_system(0);

    /* Find calling module */
    ASSERT(BIF_P->current != NULL);
    ASSERT(BIF_P->current[0] == am_erlang
	   && BIF_P->current[1] == am_load_nif 
	   && BIF_P->current[2] == 2);
    caller = find_function_from_pc(BIF_P->cp);
    ASSERT(caller != NULL);
    mod_atom = caller[0];
    ASSERT(is_atom(mod_atom));
    mod=erts_get_module(mod_atom);
    ASSERT(mod != NULL);

    if (!in_area(caller, mod->code, mod->code_length)) {
	ASSERT(in_area(caller, mod->old_code, mod->old_code_length));

	ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
			     "module '%T' not allowed", mod_atom);
    }    
    else if ((err=erts_sys_ddll_open2(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(BIF_P, "load_failed", "%s: '%s'", slogan, errdesc.str);
	}
	else {
	    ret = load_nif_error(BIF_P, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str);
	}
    }
    else if (erts_sys_ddll_load_nif_init(handle, &init_func, &errdesc) != ERL_DE_NO_ERROR) {
	ret  = load_nif_error(BIF_P, bad_lib, "Failed to find library init"
			      " function: '%s'", errdesc.str);
	
    }
    else if ((add_taint(mod_atom),
	      (entry = erts_sys_ddll_call_nif_init(init_func)) == NULL)) {
	ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful");
    }
    else if (entry->major != ERL_NIF_MAJOR_VERSION
	     || entry->minor > ERL_NIF_MINOR_VERSION) {
	
	ret = load_nif_error(BIF_P, bad_lib, "Library version (%d.%d) not compatible (with %d.%d).",
			     entry->major, entry->minor, ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION);
    }    
    else if (!erts_is_atom_str((char*)entry->name, mod_atom)) {
	ret = load_nif_error(BIF_P, bad_lib, "Library module name '%s' does not"
			     " match calling module '%T'", entry->name, mod_atom);
    }
    else {
	/*erts_fprintf(stderr, "Found module %T\r\n", mod_atom);*/
    
	for (i=0; i < entry->num_of_funcs && ret==am_ok; i++) {
	    Uint** code_pp;
	    ErlNifFunc* f = &entry->funcs[i];
	    if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom)
		|| (code_pp = get_func_pp(mod->code, f_atom, f->arity))==NULL) { 
		ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u",
				     mod_atom, f->name, f->arity);
	    }    
	    else if (code_pp[1] - code_pp[0] < (5+3)) {
		ret = load_nif_error(BIF_P,bad_lib,"No explicit call to load_nif"
				     " in module (%T:%s/%u to small)",
				     mod_atom, entry->funcs[i].name, entry->funcs[i].arity);
	    }
	    /*erts_fprintf(stderr, "Found NIF %T:%s/%u\r\n",
			 mod_atom, entry->funcs[i].name, entry->funcs[i].arity);*/
	}
    }

    if (ret != am_ok) {
	goto error;
    }

    /* Call load, reload or upgrade:
     */
    if (mod->nif.handle != NULL) { /* Reload */
	int k;
	ASSERT(mod->nif.entry != NULL);
	if (entry->reload == NULL) {
	    ret = load_nif_error(BIF_P,reload,"Reload not supported by this NIF library.");
	    goto error;
	}
	/* Check that no NIF is removed */
	for (k=0; k < mod->nif.entry->num_of_funcs; k++) {
	    ErlNifFunc* old_func = &mod->nif.entry->funcs[k];
	    for (i=0; i < entry->num_of_funcs; i++) {
		if (old_func->arity == entry->funcs[i].arity
		    && sys_strcmp(old_func->name, entry->funcs[i].name) == 0) {			   
		    break;
		}
	    }
	    if (i == entry->num_of_funcs) {
		ret = load_nif_error(BIF_P,reload,"Reloaded library missing "
				     "function %T:%s/%u\r\n", mod_atom,
				     old_func->name, old_func->arity);
		goto error;
	    }
	}       
	erts_pre_nif(&env, BIF_P, mod->nif.data);
	veto = entry->reload(&env, &env.nif_data, BIF_ARG_2);
	erts_post_nif(&env);
	if (veto) {
	    ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful.");
	}
	else {
	    erts_sys_ddll_close(mod->nif.handle);
	}
    }
    else {
	if (mod->old_nif.handle != NULL) { /* Upgrade */
	    void* prev_old_data = mod->old_nif.data;
	    if (entry->upgrade == NULL) {
		ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library.");
		goto error;
	    }
	    erts_pre_nif(&env, BIF_P, NULL);
	    veto = entry->upgrade(&env, &env.nif_data, &mod->old_nif.data, BIF_ARG_2);
	    erts_post_nif(&env);
	    if (veto) {
		mod->old_nif.data = prev_old_data;
		ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful.");
	    }
	    else if (mod->old_nif.data != prev_old_data) {
		refresh_cached_nif_data(mod->old_code, &mod->old_nif);
	    }
	}
	else if (entry->load != NULL) { /* Initial load */
	    erts_pre_nif(&env, BIF_P, NULL);
	    veto = entry->load(&env, &env.nif_data, BIF_ARG_2);
	    erts_post_nif(&env);
	    if (veto) {
		ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful.");
	    }
	}
    }
    if (ret == am_ok) {
	/*
	** Everything ok, patch the beam code with op_call_nif
	*/
	mod->nif.data = env.nif_data;
	mod->nif.handle = handle;
	mod->nif.entry = entry;
	for (i=0; i < entry->num_of_funcs; i++)
	{
	    Uint* code_ptr;
	    erts_atom_get(entry->funcs[i].name, sys_strlen(entry->funcs[i].name), &f_atom); 
	    code_ptr = *get_func_pp(mod->code, f_atom, entry->funcs[i].arity); 
	    
	    if (code_ptr[1] == 0) {
		code_ptr[5+0] = (Uint) BeamOp(op_call_nif);		
	    } else { /* Function traced, patch the original instruction word */
		BpData* bp = (BpData*) code_ptr[1];
	        bp->orig_instr = (Uint) BeamOp(op_call_nif); 
	    }	    
	    code_ptr[5+1] = (Uint) entry->funcs[i].fptr;
	    code_ptr[5+2] = (Uint) mod->nif.data;
	}
    }
    else {    
    error:
	ASSERT(ret != am_ok);
	if (handle != NULL) {
	    erts_sys_ddll_close(handle);
	}
	erts_sys_ddll_free_error(&errdesc);
    }

    erts_smp_release_system();
    erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
    BIF_RET(ret);
}