diff options
Diffstat (limited to 'erts/emulator/beam/erl_nif.c')
-rw-r--r-- | erts/emulator/beam/erl_nif.c | 565 |
1 files changed, 457 insertions, 108 deletions
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 585a6c1fdf..1dd9c8bd4a 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -48,9 +48,23 @@ struct erl_module_nif { struct enif_entry_t* entry; erts_refc_t rt_cnt; /* number of resource types */ erts_refc_t rt_dtor_cnt; /* number of resource types with destructors */ - int is_orphan; /* if erlang module has been purged */ + Module* mod; /* Can be NULL if orphan with dtor-resources left */ }; +#ifdef DEBUG +# define READONLY_CHECK +#endif +#ifdef READONLY_CHECK +# define ADD_READONLY_CHECK(ENV,PTR,SIZE) add_readonly_check(ENV,PTR,SIZE) +static void add_readonly_check(ErlNifEnv*, unsigned char* ptr, unsigned sz); +#else +# define ADD_READONLY_CHECK(ENV,PTR,SIZE) ((void)0) +#endif + +#ifdef DEBUG +static int is_offheap(const ErlOffHeap* off_heap); +#endif + #define MIN_HEAP_FRAG_SZ 200 static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp); @@ -74,7 +88,8 @@ static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp) HEAP_TOP(env->proc) = env->hp; } else { - HRelease(env->proc, env->hp_end, env->hp); + env->heap_frag->used_size = hp - env->heap_frag->mem; + ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); } frag_sz = need + MIN_HEAP_FRAG_SZ; hp = erts_heap_alloc(env->proc, frag_sz); @@ -106,6 +121,13 @@ static void pre_nif_noproc(ErlNifEnv* env, struct erl_module_nif* mod_nif) env->tmp_obj_list = NULL; } +/* Temporary object header, auto-deallocated when NIF returns. */ +struct enif_tmp_obj_t { + struct enif_tmp_obj_t* next; + void (*dtor)(struct enif_tmp_obj_t*); + /*char data[];*/ +}; + static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env) { while (env->tmp_obj_list != NULL) { @@ -126,8 +148,9 @@ void erts_post_nif(ErlNifEnv* env) } else { ASSERT(env->hp_end != HEAP_LIMIT(env->proc)); - ASSERT(env->hp_end - env->hp <= env->heap_frag->size); - HRelease(env->proc, env->hp_end, env->hp); + ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); + env->heap_frag->used_size = env->hp - env->heap_frag->mem; + ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); } free_tmp_objs(env); } @@ -141,7 +164,7 @@ static void post_nif_noproc(ErlNifEnv* env) /* Flush out our cached heap pointers to allow an ordinary HAlloc */ -static void enable_halloc(ErlNifEnv* env) +static void flush_env(ErlNifEnv* env) { if (env->heap_frag == NULL) { ASSERT(env->hp_end == HEAP_LIMIT(env->proc)); @@ -151,14 +174,15 @@ static void enable_halloc(ErlNifEnv* env) } else { ASSERT(env->hp_end != HEAP_LIMIT(env->proc)); - ASSERT(env->hp_end - env->hp <= env->heap_frag->size); - HRelease(env->proc, env->hp_end, env->hp); + ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); + env->heap_frag->used_size = env->hp - env->heap_frag->mem; + ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); } } -/* Restore cached heap pointers +/* Restore cached heap pointers to allow alloc_heap again. */ -static void disable_halloc(ErlNifEnv* env) +static void cache_env(ErlNifEnv* env) { if (env->heap_frag == NULL) { ASSERT(env->hp_end == HEAP_LIMIT(env->proc)); @@ -168,35 +192,190 @@ static void disable_halloc(ErlNifEnv* env) } else { ASSERT(env->hp_end != HEAP_LIMIT(env->proc)); - ASSERT(env->hp_end - env->hp <= env->heap_frag->size); + ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); env->heap_frag = MBUF(env->proc); ASSERT(env->heap_frag != NULL); env->hp = env->heap_frag->mem + env->heap_frag->used_size; - env->hp_end = env->heap_frag->mem + env->heap_frag->size; + env->hp_end = env->heap_frag->mem + env->heap_frag->alloc_size; } } - - void* enif_priv_data(ErlNifEnv* env) { return env->mod_nif->priv_data; } -void* enif_alloc(ErlNifEnv* env, size_t size) +void* enif_alloc(size_t size) { return erts_alloc_fnf(ERTS_ALC_T_NIF, (Uint) size); } -void* enif_realloc(ErlNifEnv* env, void* ptr, size_t size) +void* enif_realloc(void* ptr, size_t size) { return erts_realloc_fnf(ERTS_ALC_T_NIF, ptr, size); } -void enif_free(ErlNifEnv* env, void* ptr) +void enif_free(void* ptr) { erts_free(ERTS_ALC_T_NIF, ptr); } +struct enif_msg_environment_t +{ + ErlNifEnv env; + Process phony_proc; +}; + +ErlNifEnv* enif_alloc_env(void) +{ + struct enif_msg_environment_t* msg_env = + erts_alloc_fnf(ERTS_ALC_T_NIF, sizeof(struct enif_msg_environment_t)); + Eterm* phony_heap = (Eterm*) msg_env; /* dummy non-NULL ptr */ + + msg_env->env.hp = phony_heap; + msg_env->env.hp_end = phony_heap; + msg_env->env.heap_frag = NULL; + msg_env->env.mod_nif = NULL; + msg_env->env.tmp_obj_list = (struct enif_tmp_obj_t*) 1; /* invalid non-NULL */ + msg_env->env.proc = &msg_env->phony_proc; + memset(&msg_env->phony_proc, 0, sizeof(Process)); + HEAP_START(&msg_env->phony_proc) = phony_heap; + HEAP_TOP(&msg_env->phony_proc) = phony_heap; + HEAP_LIMIT(&msg_env->phony_proc) = phony_heap; + HEAP_END(&msg_env->phony_proc) = phony_heap; + MBUF(&msg_env->phony_proc) = NULL; + msg_env->phony_proc.id = ERTS_INVALID_PID; +#ifdef FORCE_HEAP_FRAGS + msg_env->phony_proc.space_verified = 0; + msg_env->phony_proc.space_verified_from = NULL; +#endif + return &msg_env->env; +} +void enif_free_env(ErlNifEnv* env) +{ + enif_clear_env(env); + erts_free(ERTS_ALC_T_NIF, env); +} + +static ERTS_INLINE void clear_offheap(ErlOffHeap* oh) +{ + oh->first = NULL; + oh->overhead = 0; +} + +void enif_clear_env(ErlNifEnv* env) +{ + struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)env; + Process* p = &menv->phony_proc; + ASSERT(p == menv->env.proc); + ASSERT(p->id == ERTS_INVALID_PID); + ASSERT(MBUF(p) == menv->env.heap_frag); + if (MBUF(p) != NULL) { + erts_cleanup_offheap(&MSO(p)); + clear_offheap(&MSO(p)); + free_message_buffer(MBUF(p)); + MBUF(p) = NULL; + menv->env.heap_frag = NULL; + } + ASSERT(HEAP_TOP(p) == HEAP_END(p)); + menv->env.hp = menv->env.hp_end = HEAP_TOP(p); + + ASSERT(!is_offheap(&MSO(p))); +} +int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, + ErlNifEnv* msg_env, ERL_NIF_TERM msg) +{ + struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)msg_env; + ErtsProcLocks rp_locks = 0; + Process* rp; + Process* c_p; + ErlHeapFragment* frags; +#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) + ErtsProcLocks rp_had_locks; +#endif + Eterm receiver = to_pid->pid; + int flush_me = 0; + + if (env != NULL) { + c_p = env->proc; + if (receiver == c_p->id) { + rp_locks = ERTS_PROC_LOCK_MAIN; + flush_me = 1; + } + } + else { +#ifdef ERTS_SMP + c_p = NULL; +#else + erl_exit(ERTS_ABORT_EXIT,"enif_send: env==NULL on non-SMP VM"); +#endif + } + +#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP) + rp_had_locks = rp_locks; +#endif + rp = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN, + receiver, rp_locks, ERTS_P2P_FLG_SMP_INC_REFC); + if (rp == NULL) { + ASSERT(env == NULL || receiver != c_p->id); + return 0; + } + flush_env(msg_env); + frags = menv->env.heap_frag; + ASSERT(frags == MBUF(&menv->phony_proc)); + if (frags != NULL) { + /* Move all offheap's from phony proc to the first fragment. + Quick and dirty, but erts_move_msg_mbuf_to_heap doesn't care. */ + ASSERT(!is_offheap(&frags->off_heap)); + frags->off_heap = MSO(&menv->phony_proc); + clear_offheap(&MSO(&menv->phony_proc)); + menv->env.heap_frag = NULL; + MBUF(&menv->phony_proc) = NULL; + } + ASSERT(!is_offheap(&MSO(&menv->phony_proc))); + + if (flush_me) { + flush_env(env); /* Needed for ERTS_HOLE_CHECK */ + } + erts_queue_message(rp, &rp_locks, frags, msg, am_undefined); + if (rp_locks) { + ERTS_SMP_LC_ASSERT(rp_locks == (rp_had_locks | (ERTS_PROC_LOCK_MSGQ | + ERTS_PROC_LOCK_STATUS))); + erts_smp_proc_unlock(rp, (ERTS_PROC_LOCK_MSGQ | ERTS_PROC_LOCK_STATUS)); + } + erts_smp_proc_dec_refc(rp); + if (flush_me) { + cache_env(env); + } + return 1; +} + +ERL_NIF_TERM enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term) +{ + Uint sz; + Eterm* hp; + sz = size_object(src_term); + hp = alloc_heap(dst_env, sz); + return copy_struct(src_term, sz, &hp, &MSO(dst_env->proc)); +} + + +#ifdef DEBUG +static int is_offheap(const ErlOffHeap* oh) +{ + return oh->first != NULL; +} +#endif + +ErlNifPid* enif_self(ErlNifEnv* caller_env, ErlNifPid* pid) +{ + pid->pid = caller_env->proc->id; + return pid; +} +int enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid) +{ + return is_internal_pid(term) ? (pid->pid=term, 1) : 0; +} + int enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term) { return is_atom(term); @@ -232,9 +411,19 @@ int enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term) return is_ref(term); } +int enif_is_tuple(ErlNifEnv* env, ERL_NIF_TERM term) +{ + return is_tuple(term); +} + +int enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term) +{ + return is_list(term) || is_nil(term); +} + static void aligned_binary_dtor(struct enif_tmp_obj_t* obj) { - erts_free_aligned_binary_bytes((byte*)obj); + erts_free_aligned_binary_bytes_extra((byte*)obj,ERTS_ALC_T_TMP); } int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin) @@ -244,7 +433,7 @@ int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin) byte* raw_ptr; }u; u.tmp = NULL; - bin->data = erts_get_aligned_binary_bytes_extra(bin_term, &u.raw_ptr, + bin->data = erts_get_aligned_binary_bytes_extra(bin_term, &u.raw_ptr, ERTS_ALC_T_TMP, sizeof(struct enif_tmp_obj_t)); if (bin->data == NULL) { return 0; @@ -257,6 +446,7 @@ int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin) bin->bin_term = bin_term; bin->size = binary_size(bin_term); bin->ref_bin = NULL; + ADD_READONLY_CHECK(env, bin->data, bin->size); return 1; } @@ -293,10 +483,11 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin) bin->bin_term = THE_NON_VALUE; bin->ref_bin = NULL; io_list_to_buf(term, (char*) bin->data, sz); + ADD_READONLY_CHECK(env, bin->data, bin->size); return 1; } -int enif_alloc_binary(ErlNifEnv* env, unsigned size, ErlNifBinary* bin) +int enif_alloc_binary(size_t size, ErlNifBinary* bin) { Binary* refbin; @@ -315,7 +506,7 @@ int enif_alloc_binary(ErlNifEnv* env, unsigned size, ErlNifBinary* bin) return 1; } -int enif_realloc_binary(ErlNifEnv* env, ErlNifBinary* bin, unsigned size) +int enif_realloc_binary(ErlNifBinary* bin, size_t size) { if (bin->ref_bin != NULL) { Binary* oldbin; @@ -333,15 +524,15 @@ int enif_realloc_binary(ErlNifEnv* env, ErlNifBinary* bin, unsigned size) } else { unsigned char* old_data = bin->data; - unsigned cpy_sz = (size < bin->size ? size : bin->size); - enif_alloc_binary(env, size, bin); + size_t cpy_sz = (size < bin->size ? size : bin->size); + enif_alloc_binary(size, bin); sys_memcpy(bin->data, old_data, cpy_sz); } return 1; } -void enif_release_binary(ErlNifEnv* env, ErlNifBinary* bin) +void enif_release_binary(ErlNifBinary* bin) { if (bin->ref_bin != NULL) { Binary* refbin = bin->ref_bin; @@ -357,12 +548,21 @@ void enif_release_binary(ErlNifEnv* env, ErlNifBinary* bin) #endif } -int enif_is_identical(ErlNifEnv* env, Eterm lhs, Eterm rhs) +unsigned char* enif_make_new_binary(ErlNifEnv* env, size_t size, + ERL_NIF_TERM* termp) +{ + flush_env(env); + *termp = new_binary(env->proc, NULL, size); + cache_env(env); + return binary_bytes(*termp); +} + +int enif_is_identical(Eterm lhs, Eterm rhs) { return EQ(lhs,rhs); } -int enif_compare(ErlNifEnv* env, Eterm lhs, Eterm rhs) +int enif_compare(Eterm lhs, Eterm rhs) { return cmp(lhs,rhs); } @@ -425,13 +625,13 @@ Eterm enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin) 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->next = MSO(env->proc).first; + MSO(env->proc).first = (struct erl_off_heap_header*) pb; pb->val = bptr; pb->bytes = (byte*) bptr->orig_bytes; pb->flags = 0; - MSO(env->proc).overhead += pb->size / sizeof(Eterm); + OH_OVERHEAD(&(MSO(env->proc)), pb->size / sizeof(Eterm)); bin_term = make_binary(pb); if (erts_refc_read(&bptr->refc, 1) == 1) { /* Total ownership transfer */ @@ -441,15 +641,15 @@ Eterm enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin) return bin_term; } else { - enable_halloc(env); + flush_env(env); bin->bin_term = new_binary(env->proc, bin->data, bin->size); - disable_halloc(env); + cache_env(env); return bin->bin_term; } } Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, - unsigned pos, unsigned size) + size_t pos, size_t size) { ErlSubBin* sb; Eterm orig; @@ -479,9 +679,11 @@ Eterm enif_make_badarg(ErlNifEnv* env) BIF_ERROR(env->proc, BADARG); } -int enif_get_atom(ErlNifEnv* env, Eterm atom, char* buf, unsigned len) +int enif_get_atom(ErlNifEnv* env, Eterm atom, char* buf, unsigned len, + ErlNifCharEncoding encoding) { Atom* ap; + ASSERT(encoding == ERL_NIF_LATIN1); if (is_not_atom(atom)) { return 0; } @@ -496,9 +698,9 @@ int enif_get_atom(ErlNifEnv* env, Eterm atom, char* buf, unsigned len) int enif_get_int(ErlNifEnv* env, Eterm term, int* ip) { -#if SIZEOF_INT == SIZEOF_VOID_P +#if SIZEOF_INT == ERTS_SIZEOF_ETERM return term_to_Sint(term, (Sint*)ip); -#elif SIZEOF_LONG == SIZEOF_VOID_P +#elif SIZEOF_LONG == ERTS_SIZEOF_ETERM Sint i; if (!term_to_Sint(term, &i) || i < INT_MIN || i > INT_MAX) { return 0; @@ -512,9 +714,9 @@ int enif_get_int(ErlNifEnv* env, Eterm term, int* ip) int enif_get_uint(ErlNifEnv* env, Eterm term, unsigned* ip) { -#if SIZEOF_INT == SIZEOF_VOID_P +#if SIZEOF_INT == ERTS_SIZEOF_ETERM return term_to_Uint(term, (Uint*)ip); -#elif SIZEOF_LONG == SIZEOF_VOID_P +#elif SIZEOF_LONG == ERTS_SIZEOF_ETERM Uint i; if (!term_to_Uint(term, &i) || i > UINT_MAX) { return 0; @@ -526,8 +728,11 @@ int enif_get_uint(ErlNifEnv* env, Eterm term, unsigned* ip) int enif_get_long(ErlNifEnv* env, Eterm term, long* ip) { -#if SIZEOF_LONG == SIZEOF_VOID_P +#if SIZEOF_LONG == ERTS_SIZEOF_ETERM return term_to_Sint(term, ip); +#elif SIZEOF_INT == ERTS_SIZEOF_ETERM + Sint i; + return term_to_Sint(term, &i) ? (*ip = (long) i, 1) : 0; #else # error Unknown long word size #endif @@ -535,14 +740,29 @@ int enif_get_long(ErlNifEnv* env, Eterm term, long* ip) int enif_get_ulong(ErlNifEnv* env, Eterm term, unsigned long* ip) { -#if SIZEOF_LONG == SIZEOF_VOID_P +#if SIZEOF_LONG == ERTS_SIZEOF_ETERM return term_to_Uint(term, ip); +#elif SIZEOF_INT == ERTS_SIZEOF_ETERM + Uint u; + return term_to_Uint(term, &u) ? (*ip = (unsigned long) u, 1) : 0; #else # error Unknown long word size #endif } -int enif_get_double(ErlNifEnv* env, Eterm term, double* dp) +#if HAVE_INT64 && SIZEOF_LONG != 8 +int enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifSInt64* ip) +{ + return term_to_Sint64(term, ip); +} + +int enif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip) +{ + return term_to_Uint64(term, ip); +} +#endif /* HAVE_INT64 && SIZEOF_LONG != 8 */ + +int enif_get_double(ErlNifEnv* env, ERL_NIF_TERM term, double* dp) { FloatDef f; if (is_not_float(term)) { @@ -553,6 +773,17 @@ int enif_get_double(ErlNifEnv* env, Eterm term, double* dp) return 1; } +int enif_get_atom_length(ErlNifEnv* env, Eterm atom, unsigned* len, + ErlNifCharEncoding enc) +{ + Atom* ap; + ASSERT(enc == ERL_NIF_LATIN1); + if (is_not_atom(atom)) return 0; + ap = atom_tab(atom_val(atom)); + *len = ap->len; + return 1; +} + int enif_get_list_cell(ErlNifEnv* env, Eterm term, Eterm* head, Eterm* tail) { Eterm* val; @@ -563,29 +794,33 @@ int enif_get_list_cell(ErlNifEnv* env, Eterm term, Eterm* head, Eterm* tail) return 1; } +int enif_get_list_length(ErlNifEnv* env, Eterm term, unsigned* len) +{ + if (is_not_list(term) && is_not_nil(term)) return 0; + *len = list_length(term); + return 1; +} + ERL_NIF_TERM enif_make_int(ErlNifEnv* env, int i) { -#if SIZEOF_INT == SIZEOF_VOID_P +#if SIZEOF_INT == ERTS_SIZEOF_ETERM return IS_SSMALL(i) ? make_small(i) : small_to_big(i,alloc_heap(env,2)); -#elif SIZEOF_LONG == SIZEOF_VOID_P +#elif SIZEOF_LONG == ERTS_SIZEOF_ETERM return make_small(i); #endif } ERL_NIF_TERM enif_make_uint(ErlNifEnv* env, unsigned i) { -#if SIZEOF_INT == SIZEOF_VOID_P +#if SIZEOF_INT == ERTS_SIZEOF_ETERM return IS_USMALL(0,i) ? make_small(i) : uint_to_big(i,alloc_heap(env,2)); -#elif SIZEOF_LONG == SIZEOF_VOID_P +#elif SIZEOF_LONG == ERTS_SIZEOF_ETERM return make_small(i); #endif } ERL_NIF_TERM enif_make_long(ErlNifEnv* env, long i) { -#if SIZEOF_LONG != SIZEOF_VOID_P -# error Unknown long word size -#endif return IS_SSMALL(i) ? make_small(i) : small_to_big(i, alloc_heap(env,2)); } @@ -594,6 +829,26 @@ ERL_NIF_TERM enif_make_ulong(ErlNifEnv* env, unsigned long i) return IS_USMALL(0,i) ? make_small(i) : uint_to_big(i,alloc_heap(env,2)); } +#if HAVE_INT64 && SIZEOF_LONG != 8 +ERL_NIF_TERM enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i) +{ + Uint* hp; + Uint need = 0; + erts_bld_sint64(NULL, &need, i); + hp = alloc_heap(env, need); + return erts_bld_sint64(&hp, NULL, i); +} + +ERL_NIF_TERM enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i) +{ + Uint* hp; + Uint need = 0; + erts_bld_uint64(NULL, &need, i); + hp = alloc_heap(env, need); + return erts_bld_uint64(&hp, NULL, i); +} +#endif /* HAVE_INT64 && SIZEOF_LONG != 8 */ + ERL_NIF_TERM enif_make_double(ErlNifEnv* env, double d) { Eterm* hp = alloc_heap(env,FLOAT_SIZE_OBJECT); @@ -605,12 +860,25 @@ ERL_NIF_TERM enif_make_double(ErlNifEnv* env, double d) ERL_NIF_TERM enif_make_atom(ErlNifEnv* env, const char* name) { - return am_atom_put(name, sys_strlen(name)); + return enif_make_atom_len(env, name, sys_strlen(name)); } -int enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom) +ERL_NIF_TERM enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len) { - return erts_atom_get(name, sys_strlen(name), atom); + return am_atom_put(name, len); +} + +int enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom, + ErlNifCharEncoding enc) +{ + return enif_make_existing_atom_len(env, name, sys_strlen(name), atom, enc); +} + +int enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, + ERL_NIF_TERM* atom, ErlNifCharEncoding encoding) +{ + ASSERT(encoding == ERL_NIF_LATIN1); + return erts_atom_get(name, len, atom); } ERL_NIF_TERM enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...) @@ -689,11 +957,16 @@ ERL_NIF_TERM enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], ERL_NIF_TERM enif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding) -{ - Sint n = sys_strlen(string); - Eterm* hp = alloc_heap(env,n*2); +{ + return enif_make_string_len(env, string, sys_strlen(string), encoding); +} + +ERL_NIF_TERM enif_make_string_len(ErlNifEnv* env, const char* string, + size_t len, ErlNifCharEncoding encoding) +{ + Eterm* hp = alloc_heap(env,len*2); ASSERT(encoding == ERL_NIF_LATIN1); - return erts_bld_string_n(&hp,NULL,string,n); + return erts_bld_string_n(&hp,NULL,string,len); } ERL_NIF_TERM enif_make_ref(ErlNifEnv* env) @@ -764,7 +1037,8 @@ struct enif_resource_type_t ErlNifResourceDtor* dtor; /* user destructor function */ erts_refc_t refc; /* num of resources of this type (HOTSPOT warning) +1 for active erl_module_nif */ - char name[1]; + Eterm module; + Eterm name; }; /* dummy node in circular list */ @@ -782,14 +1056,14 @@ typedef struct enif_resource_t #define SIZEOF_ErlNifResource(SIZE) (offsetof(ErlNifResource,data) + (SIZE)) #define DATA_TO_RESOURCE(PTR) ((ErlNifResource*)((char*)(PTR) - offsetof(ErlNifResource,data))) -static ErlNifResourceType* find_resource_type(const char* name) +static ErlNifResourceType* find_resource_type(Eterm module, Eterm name) { ErlNifResourceType* type; for (type = resource_type_list.next; type != &resource_type_list; type = type->next) { - if (sys_strcmp(type->name, name) == 0) { + if (type->module == module && type->name == name) { return type; } } @@ -822,33 +1096,42 @@ static void steal_resource_type(ErlNifResourceType* type) if (type->dtor != NULL && erts_refc_dectest(&lib->rt_dtor_cnt, 0) == 0 - && lib->is_orphan) { + && lib->mod == NULL) { /* last type with destructor gone, close orphan lib */ close_lib(lib); } if (erts_refc_dectest(&lib->rt_cnt, 0) == 0 - && lib->is_orphan) { + && lib->mod == NULL) { erts_free(ERTS_ALC_T_NIF, lib); } } ErlNifResourceType* -enif_open_resource_type(ErlNifEnv* env, const char* type_name, - ErlNifResourceDtor* dtor, - enum ErlNifResourceFlags flags, - enum ErlNifResourceFlags* tried) +enif_open_resource_type(ErlNifEnv* env, + const char* module_str, + const char* name_str, + ErlNifResourceDtor* dtor, + ErlNifResourceFlags flags, + ErlNifResourceFlags* tried) { - ErlNifResourceType* type = find_resource_type(type_name); - enum ErlNifResourceFlags op = flags; + ErlNifResourceType* type = NULL; + ErlNifResourceFlags op = flags; + Eterm module_am, name_am; + ASSERT(erts_smp_is_system_blocked(0)); + ASSERT(module_str == NULL); /* for now... */ + module_am = make_atom(env->mod_nif->mod->module); + name_am = enif_make_atom(env, name_str); + + type = find_resource_type(module_am, name_am); if (type == NULL) { if (flags & ERL_NIF_RT_CREATE) { type = erts_alloc(ERTS_ALC_T_NIF, - sizeof(struct enif_resource_type_t) - + sys_strlen(type_name)); + sizeof(struct enif_resource_type_t)); type->dtor = dtor; - sys_strcpy(type->name, type_name); + type->module = module_am; + type->name = name_am; erts_refc_init(&type->refc, 1); type->owner = env->mod_nif; type->prev = &resource_type_list; @@ -896,13 +1179,13 @@ static void nif_resource_dtor(Binary* bin) if (erts_refc_dectest(&type->refc, 0) == 0) { ASSERT(type->next == NULL); ASSERT(type->owner != NULL); - ASSERT(type->owner->is_orphan); + ASSERT(type->owner->mod == NULL); steal_resource_type(type); erts_free(ERTS_ALC_T_NIF, type); } } -void* enif_alloc_resource(ErlNifEnv* env, ErlNifResourceType* type, unsigned size) +void* enif_alloc_resource(ErlNifResourceType* type, size_t size) { Binary* bin = erts_create_magic_binary(SIZEOF_ErlNifResource(size), &nif_resource_dtor); ErlNifResource* resource = ERTS_MAGIC_BIN_DATA(bin); @@ -915,7 +1198,7 @@ void* enif_alloc_resource(ErlNifEnv* env, ErlNifResourceType* type, unsigned siz return resource->data; } -void enif_release_resource(ErlNifEnv* env, void* obj) +void enif_release_resource(void* obj) { ErlNifResource* resource = DATA_TO_RESOURCE(obj); ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_DATA(resource); @@ -929,6 +1212,18 @@ void enif_release_resource(ErlNifEnv* env, void* obj) } } +void enif_keep_resource(void* obj) +{ + ErlNifResource* resource = DATA_TO_RESOURCE(obj); + ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_DATA(resource); + + ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor); +#ifdef DEBUG + erts_refc_inc(&resource->nif_refc, 1); +#endif + erts_refc_inc(&bin->binary.refc, 2); +} + ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj) { ErlNifResource* resource = DATA_TO_RESOURCE(obj); @@ -937,15 +1232,30 @@ ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj) return erts_mk_magic_binary_term(&hp, &MSO(env->proc), &bin->binary); } +ERL_NIF_TERM enif_make_resource_binary(ErlNifEnv* env, void* obj, + const void* data, size_t size) +{ + Eterm bin = enif_make_resource(env, obj); + ProcBin* pb = (ProcBin*) binary_val(bin); + pb->bytes = (byte*) data; + pb->size = size; + return bin; +} + int enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp) { + ProcBin* pb; Binary* mbin; ErlNifResource* resource; if (!ERTS_TERM_IS_MAGIC_BINARY(term)) { return 0; } - mbin = ((ProcBin*) binary_val(term))->val; + pb = (ProcBin*) binary_val(term); + /*if (pb->size != 0) { + return 0; / * Or should we allow "resource binaries" as handles? * / + }*/ + mbin = pb->val; resource = (ErlNifResource*) ERTS_MAGIC_BIN_DATA(mbin); if (ERTS_MAGIC_BIN_DESTRUCTOR(mbin) != &nif_resource_dtor || resource->type != type) { @@ -955,7 +1265,7 @@ int enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* typ return 1; } -unsigned enif_sizeof_resource(ErlNifEnv* env, void* obj) +size_t enif_sizeof_resource(void* obj) { ErlNifResource* resource = DATA_TO_RESOURCE(obj); Binary* bin = &ERTS_MAGIC_BIN_FROM_DATA(resource)->binary; @@ -967,37 +1277,22 @@ unsigned enif_sizeof_resource(ErlNifEnv* env, void* obj) ***************************************************************************/ -static Uint** get_func_pp(Eterm* mod_code, Eterm f_atom, unsigned arity) +static BeamInstr** get_func_pp(BeamInstr* 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)); + BeamInstr* code_ptr = (BeamInstr*) mod_code[MI_FUNCTIONS+j]; + ASSERT(code_ptr[0] == (BeamInstr) 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 (BeamInstr**) &mod_code[MI_FUNCTIONS+j]; } } return NULL; } -/*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->priv_data; - } -}*/ - static Eterm mkatom(const char *str) { return am_atom_put(str, sys_strlen(str)); @@ -1089,7 +1384,7 @@ 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? */ + char* lib_name = NULL; void* handle = NULL; void* init_func; ErlNifEntry* entry = NULL; @@ -1098,15 +1393,20 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) Module* mod; Eterm mod_atom; Eterm f_atom; - Eterm* caller; + BeamInstr* caller; ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT; Eterm ret = am_ok; int veto; struct erl_module_nif* lib = NULL; - 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);*/ + len = list_length(BIF_ARG_1); + if (len < 0) { + BIF_ERROR(BIF_P, BADARG); + } + lib_name = (char *) erts_alloc(ERTS_ALC_T_TMP, len + 1); + + if (intlist_to_buf(BIF_ARG_1, lib_name, len) != len) { + erts_free(ERTS_ALC_T_TMP, lib_name); BIF_ERROR(BIF_P, BADARG); } lib_name[len] = '\0'; @@ -1165,7 +1465,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) /*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; + BeamInstr** 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) { @@ -1195,7 +1495,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) lib->entry = entry; erts_refc_init(&lib->rt_cnt, 0); erts_refc_init(&lib->rt_dtor_cnt, 0); - lib->is_orphan = 0; + lib->mod = mod; env.mod_nif = lib; if (mod->nif != NULL) { /* Reload */ int k; @@ -1268,22 +1568,23 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) mod->nif = lib; for (i=0; i < entry->num_of_funcs; i++) { - Uint* code_ptr; + BeamInstr* 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); + code_ptr[5+0] = (BeamInstr) 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); + BpData** bps = (BpData**) code_ptr[1]; + BpData* bp = (BpData*) bps[bp_sched2ix()]; + bp->orig_instr = (BeamInstr) BeamOp(op_call_nif); } - code_ptr[5+1] = (Uint) entry->funcs[i].fptr; - code_ptr[5+2] = (Uint) lib; + code_ptr[5+1] = (BeamInstr) entry->funcs[i].fptr; + code_ptr[5+2] = (BeamInstr) lib; } } - else { + else { error: ASSERT(ret != am_ok); if (lib != NULL) { @@ -1297,6 +1598,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) erts_smp_release_system(); erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_free(ERTS_ALC_T_TMP, lib_name); BIF_RET(ret); } @@ -1308,7 +1610,7 @@ erts_unload_nif(struct erl_module_nif* lib) ErlNifResourceType* next; ASSERT(erts_smp_is_system_blocked(0)); ASSERT(lib != NULL); - ASSERT(!lib->is_orphan); + ASSERT(lib->mod != NULL); for (rt = resource_type_list.next; rt != &resource_type_list; rt = next) { @@ -1338,7 +1640,7 @@ erts_unload_nif(struct erl_module_nif* lib) else { ASSERT(erts_refc_read(&lib->rt_cnt, 1) > 0); } - lib->is_orphan = 1; + lib->mod = NULL; /* orphan lib */ } void erl_nif_init() @@ -1347,6 +1649,53 @@ void erl_nif_init() resource_type_list.prev = &resource_type_list; resource_type_list.dtor = NULL; resource_type_list.owner = NULL; - resource_type_list.name[0] = '\0'; + resource_type_list.module = THE_NON_VALUE; + resource_type_list.name = THE_NON_VALUE; } +#ifdef READONLY_CHECK +/* Use checksums to assert that NIFs do not write into inspected binaries +*/ +static void readonly_check_dtor(struct enif_tmp_obj_t*); +static unsigned calc_checksum(unsigned char* ptr, unsigned size); + +struct readonly_check_t +{ + struct enif_tmp_obj_t hdr; + unsigned char* ptr; + unsigned size; + unsigned checksum; +}; +static void add_readonly_check(ErlNifEnv* env, unsigned char* ptr, unsigned sz) +{ + struct readonly_check_t* obj = erts_alloc(ERTS_ALC_T_TMP, + sizeof(struct readonly_check_t)); + obj->hdr.next = env->tmp_obj_list; + env->tmp_obj_list = &obj->hdr; + obj->hdr.dtor = &readonly_check_dtor; + obj->ptr = ptr; + obj->size = sz; + obj->checksum = calc_checksum(ptr, sz); +} +static void readonly_check_dtor(struct enif_tmp_obj_t* o) +{ + struct readonly_check_t* obj = (struct readonly_check_t*) o; + unsigned chksum = calc_checksum(obj->ptr, obj->size); + if (chksum != obj->checksum) { + fprintf(stderr, "\r\nReadonly data written by NIF, checksums differ" + " %x != %x\r\nABORTING\r\n", chksum, obj->checksum); + abort(); + } + erts_free(ERTS_ALC_T_TMP, obj); +} +static unsigned calc_checksum(unsigned char* ptr, unsigned size) +{ + unsigned i, sum = 0; + for (i=0; i<size; i++) { + sum ^= ptr[i] << ((i % 4)*8); + } + return sum; +} + +#endif /* READONLY_CHECK */ + |