diff -r ee8cb854ed24 -r f43284a4103a erts/emulator/beam/atom.names
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -78,8 +78,12 @@ atom double
 atom double
 atom longdouble
 atom pointer
+atom cstring
+atom nonnull
 atom size_t
 atom ssize_t
+atom off_t
+atom pid_t
 atom void
 
 #
diff -r ee8cb854ed24 -r f43284a4103a erts/emulator/beam/bif.tab
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -690,5 +690,7 @@ bif erlang:old_binary_to_term/1
 #
 # FFI BIFs
 #
+bif ffi:raw_cstring_to_binary/1
+bif ffi:raw_buffer_to_binary/2
 bif ffi:raw_call/3
 bif ffi:raw_call/2
diff -r ee8cb854ed24 -r f43284a4103a erts/emulator/beam/erl_bif_ffi.c
--- a/erts/emulator/beam/erl_bif_ffi.c
+++ b/erts/emulator/beam/erl_bif_ffi.c
@@ -57,7 +57,37 @@ BIF_RETTYPE ffi_raw_call_2(BIF_ALIST_2)
     BIF_ERROR(BIF_P, BADARG);
 }
 
+BIF_RETTYPE ffi_raw_cstring_to_binary_1(BIF_ALIST_1)
+{
+    /* FIXME: a more descriptive error is definitely required */
+    BIF_ERROR(BIF_P, BADARG);
+}
+
+BIF_RETTYPE ffi_raw_buffer_to_binary_2(BIF_ALIST_2)
+{
+    /* FIXME: a more descriptive error is definitely required */
+    BIF_ERROR(BIF_P, BADARG);
+}
+
 #else /* HAVE_LIBFFI */
+
+/*
+ * Some "special" types, that need particular care by the FFI.
+ * 
+ * We need a libffi type structure that is actually equal to
+ * ffi_type_pointer, but with a different address (later we will use
+ * it for distinguishing among types).  The initialization will be
+ * performed in erts_init_ffi() */
+ffi_type erl_ffi_type_binary;
+ffi_type erl_ffi_type_cstring;
+ffi_type erl_ffi_type_nonnull;
+
+void erts_init_ffi(void)
+{
+    erl_ffi_type_binary = ffi_type_pointer;
+    erl_ffi_type_cstring = ffi_type_pointer;
+    erl_ffi_type_nonnull = ffi_type_pointer;
+}
 
 /*
  * FFI type conversion table
@@ -67,14 +97,16 @@ typedef struct {
     ffi_type *ftype;
 } FFIConversion;
 
-/* Floating point and unsized types come first (it's some sort of
- * optimization for frequent usages) */
 static FFIConversion conversion_table[] = {
+    /* Standard C types.  Floating point and unsized types come first
+     * (it's some sort of optimization for frequent usages) */
     {am_float, &ffi_type_float},
     {am_double, &ffi_type_double},
     {am_uint, &ffi_type_uint},
     {am_sint, &ffi_type_sint},
     {am_pointer, &ffi_type_pointer},
+    {am_cstring, &erl_ffi_type_cstring},
+    {am_nonnull, &erl_ffi_type_nonnull},
     {am_ushort, &ffi_type_ushort},
     {am_sshort, &ffi_type_sshort},
     {am_ulong, &ffi_type_ulong},
@@ -92,20 +124,30 @@ static FFIConversion conversion_table[] 
     {am_sint64, &ffi_type_sint64},
     {am_longdouble, &ffi_type_longdouble},
 
-    /* We could create new FFI types for size_t et al., but that's easier */
+     /* Other types derived from the standard ones. We could create
+      * new FFI types for size_t et al., but that's easier */
     {am_size_t, ((sizeof(size_t) == sizeof(Uint32))
                 ? &ffi_type_uint32 : &ffi_type_uint64)},
     {am_ssize_t, ((sizeof(ssize_t) == sizeof(Sint32))
                  ? &ffi_type_sint32 : &ffi_type_sint64)},
+    {am_off_t, ((sizeof(off_t) == sizeof(Sint32))
+                ? &ffi_type_sint32 : &ffi_type_sint64)},
+    {am_pid_t, ((sizeof(pid_t) == sizeof(Sint32))
+		? &ffi_type_sint32 : &ffi_type_sint64)},
 
-    {am_false, NULL} /* Keep this at the end of the array */
+    /* Erlang types */
+    {am_binary, &erl_ffi_type_binary},
+
+    /* Keep this at the end of the array */
+    {am_false, NULL}
 };
 
 /*
  * Forward declarations
  */
 static int build_ffi_args_array(Eterm args[], ffi_type *ffi_types[], byte *buf,
-                                void *call_ptrs[], Uint n);
+                                void *call_ptrs[], void *tmp_allocs[],
+				ErlDrvBinary *drv_allocs[], Uint n);
 static Eterm build_ffi_return_term(Process *p, ffi_type *rtype, byte *retval);
 static char *pick_list_or_atom(Eterm name_term);
 static Port *id_or_name2port(Process *c_p, Eterm id);
@@ -170,20 +212,25 @@ BIF_RETTYPE ffi_raw_call_3(BIF_ALIST_3)
         goto error;
 
     {
+	Uint nargs_1 = nargs - 1;
         Sint64 retval[2]; /* FFI call return value (must be large & aligned) */
         ffi_cif cif;
         ffi_type* ftypes[nargs]; /* Return type + call arguments types */
-        void* avalues[nargs - 1]; /* Pointers to arguments data */
+        void* avalues[nargs_1]; /* Pointers to arguments data */
+        void* tmp_allocs[nargs_1]; /* Buffers allocated with ERTS_ALC_T_TMP */
+        ErlDrvBinary* drv_allocs[nargs_1]; /* Driver bins that need free()d */
         byte buf[ERL_FFI_ARGS_BUF_SIZE]; /* Buffer for FFI arguments */
+	ErlDrvBinary** b;
+	void** p;
 
         /* Prepare the FFI function call... */
         if ((status = erl_ffi_build_types_array(typestp, ftypes, nargs)))
             goto late_error;
         if ((status = build_ffi_args_array(calltp+1, ftypes+1, buf, avalues,
-                                            nargs-1)))
+                                           tmp_allocs, drv_allocs, nargs-1)))
             goto late_error;
 
-        if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, nargs - 1,
+        if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, nargs_1,
                          ftypes[0], ftypes+1) != FFI_OK) {
 	    status = ERL_FFI_STATUS_FFI_ERROR;
             goto late_error;
@@ -193,6 +240,17 @@ BIF_RETTYPE ffi_raw_call_3(BIF_ALIST_3)
 
         /* Now figure out what to do with the return value */
         res = build_ffi_return_term(BIF_P, ftypes[0], (byte*)retval);
+
+	/* Finally free the temporary data */
+	for (p = tmp_allocs; *p != NULL; ++p) {
+	    erts_free(ERTS_ALC_T_TMP, *p);
+	}
+	for (b = drv_allocs; *b != NULL; ++b) {
+	    /* We have created the binary for the sake of the FFI call, and we
+	     * are now removing our own reference.  If the called C function
+	     * didn't increase the refcount, the binary will be freed */
+	    driver_free_binary(*b);
+	}
     }
 
     BIF_RET(res);
@@ -276,20 +334,36 @@ BIF_RETTYPE ffi_raw_call_2(BIF_ALIST_2)
 	goto error;
 
     {
+	Uint nargs_1 = nargs - 1;
         Sint64 retval[2]; /* FFI call return value (must be large & aligned) */
-        void* avalues[nargs - 1]; /* Pointers to arguments data */
+        void* avalues[nargs_1]; /* Pointers to arguments data */
+        void* tmp_allocs[2*nargs_1]; /* Buffers allocated with ERTS_ALC_T_TMP */
+        ErlDrvBinary* drv_allocs[nargs_1]; /* Driver bins that need free()d */
         byte buf[ERL_FFI_ARGS_BUF_SIZE]; /* Buffer for FFI arguments */
+	void** p;
 
         /* Prepare the FFI function call... */
         if ((status = build_ffi_args_array(calltp+1,
 					   preload->ftypes + 1, buf, avalues,
-					   nargs - 1)))
+					   tmp_allocs, drv_allocs, nargs_1)))
             goto late_error;
         /* ...and actually perform the call */
         ffi_call(&preload->cif, preload->function, &retval, avalues);
 
         /* Now figure out what to do with the return value */
         res = build_ffi_return_term(BIF_P, preload->ftypes[0], (byte*)retval);
+
+	/* Finally free the temporary data */
+	for (p = tmp_allocs; *p != NULL; ++p) {
+	    erts_free(ERTS_ALC_T_TMP, *p);
+	}
+	for (p = (void**)drv_allocs; *p != NULL; ++p) {
+	    /* We have created the binary for the sake of the FFI
+	     * call, and we are now removing our own reference.  If
+	     * the called C function didn't increase the refcount, or
+	     * didn't return it, the binary will be freed */
+	    driver_free_binary((ErlDrvBinary*) *p);
+	}
     }
 
     BIF_RET(res);
@@ -308,7 +382,63 @@ BIF_RETTYPE ffi_raw_call_2(BIF_ALIST_2)
             /* It should never happen */
             BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
         }
+    } else {
+	/* This should never happen, but we shut up compiler warnings */
+	BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
     }
+}
+
+/* Return a new binary with a copy of the given NULL-terminated string
+ * (a pointer possibly obtained with a FFI call) */
+BIF_RETTYPE ffi_raw_cstring_to_binary_1(BIF_ALIST_1)
+{
+    char* ptr;
+    Eterm res;
+
+    if (is_big(BIF_ARG_1))
+	ptr = *((char**)big_v(BIF_ARG_1));
+    else if (is_small(BIF_ARG_1))
+	ptr = (char*)unsigned_val(BIF_ARG_1);
+    else {
+    error:
+	BIF_ERROR(BIF_P, BADARG);
+    }
+    
+    if (ptr == NULL)
+	goto error;
+
+    res = new_binary(BIF_P, (byte*)ptr, sys_strlen(ptr) + 1); /* Include '\0'*/
+
+    BIF_RET(res);
+}
+
+/* Return a new binary with a copy of BIF_ARG_2 bytes from the given
+ * buffer (a pointer possibly obtained with a FFI call) */
+BIF_RETTYPE ffi_raw_buffer_to_binary_2(BIF_ALIST_2)
+{
+    char* ptr;
+    int size;
+    Eterm res;
+
+    if (is_big(BIF_ARG_1))
+	ptr = *((char**)big_v(BIF_ARG_1));
+    else if (is_small(BIF_ARG_1))
+	ptr = (char*)unsigned_val(BIF_ARG_1);
+    else {
+    error:
+	BIF_ERROR(BIF_P, BADARG);
+    }
+    
+    if (ptr == NULL)
+	goto error;
+
+    if (is_not_small(BIF_ARG_2))
+	BIF_ERROR(BIF_P, BADARG);
+    size = (int)unsigned_val(BIF_ARG_2);
+
+    res = new_binary(BIF_P, (byte*)ptr, size);
+
+    BIF_RET(res);
 }
 
 /* Fill the given array of ffi_type pointers with proper values, based
@@ -344,20 +474,36 @@ int erl_ffi_build_types_array(Eterm type
 /* Fill 'call_ptrs' with proper pointers, based on the given array of
  * Erlang terms representing FFI call arguments.  'n' is the number of
  * elements of the arrays.  'buf' is used as storage for data that
- * needs to be copied.  Return 0 on success, something else on
- * error. */
-static int build_ffi_args_array(Eterm args[], ffi_type *ffi_types[],
-                                byte *buf, void *call_ptrs[], Uint n)
+ * needs to be copied.
+ *
+ * 'tmp_allocs' and 'drv_allocs' are NULL-terminated array of pointers
+ * to (respectively) temporary buffers (ERTS_ALC_T_TMP) and
+ * ErlDrvBinary's that should be freed after the function returns.
+ * Their size should be at least n, or (3*n) in the case of
+ * 'tmp_allocs'.
+ *
+ * Return 0 on success, something else on error. */
+static int build_ffi_args_array(Eterm args[], ffi_type *ffi_types[], byte *buf,
+				void *call_ptrs[], void *tmp_allocs[],
+				ErlDrvBinary *drv_allocs[], Uint n)
 {
     Uint i, offset = 0;
     FloatDef f;
     ffi_type* ftype;
-    Eterm arg, unused1, unused2;
+    Eterm arg, bitoffs, bitsize;
+    Eterm *binval;
     byte* ptr;
     int tmpint;
     long tmplong;
     Sint32 tmps32;
     Sint64 tmps64;
+    Binary *bin;
+    ErlDrvBinary *drvbin;
+
+    /* Mark the end of the allocations arrays (it will be moved at
+     * each actual allocation */
+    *tmp_allocs = NULL;
+    *drv_allocs = NULL;
 
     for (i = 0; i < n; ++i) {
         
@@ -412,16 +558,22 @@ static int build_ffi_args_array(Eterm ar
             call_ptrs[i] = buf;
             offset += sizeof(int);
             buf += sizeof(int);
-        } else if (ftype == &ffi_type_pointer) {
+        } else if (ftype == &ffi_type_pointer
+		   || ftype == &erl_ffi_type_cstring
+		   || ftype == &erl_ffi_type_nonnull) {
             /* Pointers may be taken either from binaries, smalls or bigs */
             if (is_binary(arg))
-                ERTS_GET_BINARY_BYTES(arg, ptr, unused1, unused2);
+                ERTS_GET_BINARY_BYTES(arg, ptr, bitoffs, bitsize);
             else if (is_big(arg))
                 ptr = *((void**)big_v(arg));
             else if (is_small(arg))
                 ptr = (void*)unsigned_val(arg);
             else
                 return ERL_FFI_STATUS_BADARG;
+	    if ((ftype != &ffi_type_pointer) && (ptr == NULL)) {
+		/* 'cstring' and 'nonnull' may not be NULL */
+		return ERL_FFI_STATUS_BADARG;
+	    }
             *((void**)buf) = ptr;
             call_ptrs[i] = buf;
             offset += sizeof(void*);
@@ -568,6 +720,40 @@ static int build_ffi_args_array(Eterm ar
             call_ptrs[i] = buf;
             offset += sizeof(long double);
             buf += sizeof(long double);
+	} else if (ftype == &erl_ffi_type_binary) {
+            if (is_not_binary(arg))
+                return ERL_FFI_STATUS_BADARG;
+
+	    ERTS_GET_BINARY_BYTES(arg, ptr, bitoffs, bitsize);
+	    binval = binary_val(arg);	    
+	    if ((thing_subtag(*binval) == HEAP_BINARY_SUBTAG)
+		|| (thing_subtag(*binval) == SUB_BINARY_SUBTAG)) {
+		/* We need to create a new refcounted binary with the
+		 * original contents */
+		bin = erts_bin_drv_alloc_fnf(binary_size(arg));
+		if (bin == NULL) {
+		    erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY,
+				      sizeof(Binary) + binary_size(arg));
+		}
+		bin->flags = BIN_FLAG_DRV;
+		erts_refc_init(&bin->refc, 1);
+		bin->orig_size = (long) binary_size(arg);
+		sys_memcpy(bin->orig_bytes, ptr, bin->orig_size);
+
+		/* Mark the new binary to be freed */
+		*drv_allocs = Binary2ErlDrvBinary(bin);
+		*(++drv_allocs) = NULL;
+	    } else {
+		/* This is a refcounted binary, we just use it
+		 * directly */
+		bin = ((ProcBin*) binval)->val;
+	    }
+	    /* Turn the binary into an ErlDrvBinary and use it */
+	    drvbin = Binary2ErlDrvBinary(bin);
+            *((ErlDrvBinary**)buf) = drvbin;
+            call_ptrs[i] = buf;
+            offset += sizeof(ErlDrvBinary*);
+            buf += sizeof(ErlDrvBinary*);
         } else {
             /* It should never happen */
             return ERL_FFI_STATUS_BUG;
@@ -578,7 +764,7 @@ static int build_ffi_args_array(Eterm ar
 }
 
 /* Build an Erlang Term compatible with the given FFI return type and
- * value */
+ * value. */
 static Eterm build_ffi_return_term(Process* p, ffi_type* rtype, byte* retval)
 {
     FloatDef f;
@@ -588,6 +774,9 @@ static Eterm build_ffi_return_term(Proce
     unsigned long tmpulong;
     Sint64 tmps64;
     Uint64 tmpu64;
+    ProcBin *pb;
+    Binary *bin;
+    byte *ptr;
 
     if (rtype == &ffi_type_void) {
         return am_void;
@@ -605,8 +794,15 @@ static Eterm build_ffi_return_term(Proce
         res = erts_make_integer(*((unsigned int*) retval), p);
     } else if (rtype == &ffi_type_sint) {
         res = erts_make_signed_integer(*((int*) retval), p);
-    } else if (rtype == &ffi_type_pointer) {
+    } else if (rtype == &ffi_type_pointer
+	       || rtype == &erl_ffi_type_cstring
+	       || rtype == &erl_ffi_type_nonnull) {
         hp = ArithAlloc(p, 1 + WSIZE(sizeof(void*)));
+	if ((rtype != &ffi_type_pointer) && (*((byte**)retval) == NULL)) {
+	    /* 'cstring' and 'nonnull' may not be NULL */
+	    /* FIXME: a more descriptive error is definitely needed */
+	    BIF_ERROR(p, BADARG);
+	}
         res = bytes_to_big((byte*)retval, sizeof(void*), 0, hp);
     } else if (rtype == &ffi_type_ushort) {
         res = make_small(*((unsigned short*) retval));
@@ -660,6 +856,52 @@ static Eterm build_ffi_return_term(Proce
         hp = HAlloc(p, FLOAT_SIZE_OBJECT);
         res = make_float(hp);
         PUT_DOUBLE(f, hp);
+    } else if (rtype == &erl_ffi_type_binary) {
+	/* FIXME: check that the handling of returned binaries is OK
+	 *
+	 * More in detail, there are three cases (always requiring a
+	 * refcount increment):
+	 *
+	 *   1. the returned driver binary may have been autogenerated
+	 *      by the FFI (and it is, thus, pointed in the 'drv_allocs'
+	 *      array), and used as C function call argument.  We must
+	 *      increase its refcount, in order to prevent its
+	 *      deallocation (because when we return, a
+	 *      driver_free_binary() will be issued);
+	 *
+	 *   2. the driver binary may have been originated by the
+	 *      Erlang VM, passed as C function call argument, and
+	 *      returned.  Now it is possibly going to be bound to a
+	 *      (new?) variable.  Should we increase its refcount?
+	 *      Yes (but deeper review is required);
+	 *
+	 *   3. the driver binary was created by the C side.  If the
+	 *      developer followed the rules about refcount
+	 *      management, everything is OK and we can increase the
+	 *      refcount.  In this case the original refcount could be
+	 *      0 (if the calling function didn't want to track the
+	 *      binary after return, thus calling
+	 *      driver_binary_dec_refc()).
+	 */
+	ptr = *((byte**)retval);
+	if (ptr == NULL) {
+	    /* A binary may not be NULL */
+	    /* FIXME: a more descriptive error is definitely needed */
+	    BIF_ERROR(p, BADARG);
+	}
+	bin = ErlDrvBinary2Binary((ErlDrvBinary*) ptr);
+	erts_refc_inc(&bin->refc, 1);
+
+	pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE);	
+	pb->thing_word = HEADER_PROC_BIN;
+	pb->size = bin->orig_size;
+	pb->next = MSO(p).mso;
+	MSO(p).mso = pb;
+	pb->val = bin;
+	pb->bytes = (byte*) bin->orig_bytes;
+	MSO(p).overhead += (bin->orig_size / BINARY_OVERHEAD_FACTOR
+			    / sizeof(Eterm));
+	res = make_binary(pb);
     } else {
         /* It should never happen */
         BIF_ERROR(p, EXC_INTERNAL_ERROR);
diff -r ee8cb854ed24 -r f43284a4103a erts/emulator/beam/erl_bif_ffi.h
--- a/erts/emulator/beam/erl_bif_ffi.h
+++ b/erts/emulator/beam/erl_bif_ffi.h
@@ -42,6 +42,7 @@
 #include <ffi.h>
 
 /* Function prototypes */
+void erts_init_ffi(void);
 int erl_ffi_build_types_array(Eterm types[], ffi_type *ftypes[],Uint n);
 
 #endif /* HAVE_LIBFFI */
diff -r ee8cb854ed24 -r f43284a4103a erts/emulator/beam/erl_init.c
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -39,6 +39,10 @@
 #include "erl_instrument.h"
 #include "erl_printf_term.h"
 #include "erl_misc_utils.h"
+
+#ifdef HAVE_LIBFFI
+#include "erl_bif_ffi.h"
+#endif
 
 #ifdef HIPE
 #include "hipe_mode_switch.h"	/* for hipe_mode_switch_init() */
@@ -252,6 +256,9 @@ erl_init(void)
     erts_init_bif();
     erts_init_obsolete();
     erts_delay_trap = erts_export_put(am_erlang, am_delay_trap, 2);
+#ifdef HAVE_LIBFFI
+    erts_init_ffi();
+#endif
 #if HAVE_ERTS_MSEG
     erts_mseg_late_init(); /* Must be after timer (init_time()) and thread
 			      initializations */
diff -r ee8cb854ed24 -r f43284a4103a erts/emulator/beam/io.c
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -3265,21 +3265,21 @@ driver_binary_get_refc(ErlDrvBinary *dbp
 driver_binary_get_refc(ErlDrvBinary *dbp)
 {
     Binary* bp = ErlDrvBinary2Binary(dbp);
-    return erts_refc_read(&bp->refc, 1);
+    return erts_refc_read(&bp->refc, 0);
 }
 
 long
 driver_binary_inc_refc(ErlDrvBinary *dbp)
 {
     Binary* bp = ErlDrvBinary2Binary(dbp);
-    return erts_refc_inctest(&bp->refc, 2);
+    return erts_refc_inctest(&bp->refc, 1);
 }
 
 long
 driver_binary_dec_refc(ErlDrvBinary *dbp)
 {
     Binary* bp = ErlDrvBinary2Binary(dbp);
-    return erts_refc_dectest(&bp->refc, 1);
+    return erts_refc_dectest(&bp->refc, 0);
 }
 
 
