diff --git a/com.oracle.truffle.r.native/fficall/src/jni/userrng.c b/com.oracle.truffle.r.native/fficall/src/jni/userrng.c
new file mode 100644
index 0000000000000000000000000000000000000000..35d4e2bd9128da1be5cba8a53251026f43945d8d
--- /dev/null
+++ b/com.oracle.truffle.r.native/fficall/src/jni/userrng.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <rffiutils.h>
+
+typedef void (*call_init)(int seed);
+typedef double* (*call_rand)(void);
+typedef int* (*call_nSeed)(void);
+typedef int* (*call_seeds)(void);
+
+JNIEXPORT void JNICALL
+Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1UserRng_init(JNIEnv *env, jclass c, jlong address, jint seed) {
+	call_init f = (call_init) address;
+	f(seed);
+}
+
+JNIEXPORT double JNICALL
+Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1UserRng_rand(JNIEnv *env, jclass c, jlong address) {
+	call_rand f = (call_rand) address;
+	double* dp = f();
+	return *dp;
+}
+
+JNIEXPORT jint JNICALL
+Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1UserRng_nSeed(JNIEnv *env, jclass c, jlong address) {
+	call_nSeed f = (call_nSeed) address;
+	int *pn = f();
+	return *pn;
+}
+
+JNIEXPORT void JNICALL
+Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1UserRng_seeds(JNIEnv *env, jclass c, jlong address, jintArray seedsArray) {
+	call_seeds f = (call_seeds) address;
+	int *pseeds = f();
+	int seedslen = (*env)->GetArrayLength(env, seedsArray);
+	int *data = (*env)->GetIntArrayElements(env, seedsArray, NULL);
+	for (int i = 0; i < seedslen; i++) {
+		data[i] = pseeds[i];
+	}
+	(*env)->ReleaseIntArrayElements(env, seedsArray, data, 0);
+}
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_UserRng.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNI_UserRng.java
similarity index 50%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_UserRng.java
rename to com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNI_UserRng.java
index ff50fa1416cd3935c2afbe2f0b9915aeb257a3db..fcb6c4c8d6f3ed744a912944823950b842892264 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_UserRng.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNI_UserRng.java
@@ -22,82 +22,41 @@
  */
 package com.oracle.truffle.r.runtime.ffi.jnr;
 
+import static com.oracle.truffle.r.runtime.rng.user.UserRNG.Function;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.ffi.UserRngRFFI;
 
-import jnr.ffi.LibraryLoader;
-import jnr.ffi.Pointer;
-import jnr.ffi.annotations.In;
-
-//Checkstyle: stop method name
-public class JNR_UserRng implements UserRngRFFI {
-    public interface UserRng {
-        void user_unif_init(@In int seed);
-
-        Pointer user_unif_rand();
-
-        Pointer user_unif_nseed();
-
-        Pointer user_unif_seedloc();
-    }
-
-    private static class UserRngProvider {
-        private static String libPath;
-        private static UserRng userRng;
-
-        UserRngProvider(String libPath) {
-            UserRngProvider.libPath = libPath;
-        }
-
-        @TruffleBoundary
-        private static UserRng createAndLoadLib() {
-            return LibraryLoader.create(UserRng.class).load(libPath);
-        }
-
-        static UserRng userRng() {
-            if (userRng == null) {
-                userRng = createAndLoadLib();
-            }
-            return userRng;
-        }
-    }
-
-    private static UserRng userRng() {
-        return UserRngProvider.userRng();
-    }
-
-    @Override
-    @SuppressWarnings("unused")
-    public void setLibrary(String path) {
-        new UserRngProvider(path);
-
-    }
-
+public class JNI_UserRng implements UserRngRFFI {
     @Override
     @TruffleBoundary
     public void init(int seed) {
-        userRng().user_unif_init(seed);
+        init(Function.Init.getAddress(), seed);
+
     }
 
     @Override
     @TruffleBoundary
     public double rand() {
-        Pointer pDouble = userRng().user_unif_rand();
-        return pDouble.getDouble(0);
+        return rand(Function.Rand.getAddress());
     }
 
     @Override
     @TruffleBoundary
     public int nSeed() {
-        return userRng().user_unif_nseed().getInt(0);
+        return nSeed(Function.NSeed.getAddress());
     }
 
     @Override
     @TruffleBoundary
     public void seeds(int[] n) {
-        Pointer pInt = userRng().user_unif_seedloc();
-        for (int i = 0; i < n.length; i++) {
-            n[i] = pInt.getInt(i * 4);
-        }
+        seeds(Function.Seedloc.getAddress(), n);
     }
+
+    private static native void init(long address, int seed);
+
+    private static native double rand(long address);
+
+    private static native int nSeed(long address);
+
+    private static native void seeds(long address, int[] n);
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java
index 05a093a010bd91d13ab53b115f77be56f86458b5..19aba1169678bd8cdfffd5d9d8e2c4c5ed5aec24 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java
@@ -164,7 +164,7 @@ public class JNR_RFFIFactory extends RFFIFactory implements RFFI {
     public UserRngRFFI getUserRngRFFI() {
         if (userRngRFFI == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            userRngRFFI = new JNR_UserRng();
+            userRngRFFI = new JNI_UserRng();
         }
         return userRngRFFI;
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UserRngRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UserRngRFFI.java
index b3b7dc0dc3eb98c14bad2cc40154a93d2c95b23f..dc692727fcfe6b46100e854a859d63ec0abaa700 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UserRngRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UserRngRFFI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,13 +23,10 @@
 package com.oracle.truffle.r.runtime.ffi;
 
 /**
- * Explicit statically typed interface to user-supplied random number generators. TODO This could
- * eventually be subsumed by {@link CRFFI}.
+ * Explicit statically typed interface to user-supplied random number generators.
  */
 public interface UserRngRFFI {
 
-    void setLibrary(String path);
-
     void init(int seed);
 
     double rand();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java
index bc649e2415f24676d88609143feee3c1f02631ab..9e1f745d769433b28be59b45398e497348bf8cce 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java
@@ -35,42 +35,68 @@ import com.oracle.truffle.r.runtime.rng.RRNG.Kind;
  * Interface to a user-supplied RNG.
  */
 public final class UserRNG extends RNGInitAdapter {
-
-    private static final String USER_UNIF_RAND = "user_unif_rand";
-    private static final String USER_UNIF_INIT = "user_unif_init";
     private static final boolean OPTIONAL = true;
 
-    @SuppressWarnings("unused") private long userUnifRand;
-    @SuppressWarnings("unused") private long userUnifInit;
-    private long userUnifNSeed;
-    private long userUnifSeedloc;
+    public enum Function {
+        Rand(!OPTIONAL),
+        Init(OPTIONAL),
+        NSeed(OPTIONAL),
+        Seedloc(OPTIONAL);
+
+        private long address;
+        private final String symbol;
+        private final boolean optional;
+
+        Function(boolean optional) {
+            this.symbol = "user_unif_" + name().toLowerCase();
+            this.optional = optional;
+        }
+
+        private boolean isDefined() {
+            return address != 0;
+        }
+
+        public long getAddress() {
+            return address;
+        }
+
+        private void setAddress(DLLInfo dllInfo) {
+            this.address = findSymbol(symbol, dllInfo, optional);
+        }
+
+    }
+
     private UserRngRFFI userRngRFFI;
     private int nSeeds = 0;
 
     @Override
     @TruffleBoundary
     public void init(int seed) {
-        DLLInfo dllInfo = DLL.findLibraryContainingSymbol(USER_UNIF_RAND);
+        DLLInfo dllInfo = DLL.findLibraryContainingSymbol(Function.Rand.symbol);
         if (dllInfo == null) {
-            throw RError.error(RError.NO_CALLER, RError.Message.RNG_SYMBOL, USER_UNIF_RAND);
+            throw RError.error(RError.NO_CALLER, RError.Message.RNG_SYMBOL, Function.Rand.symbol);
+        }
+        for (Function f : Function.values()) {
+            f.setAddress(dllInfo);
         }
-        userUnifRand = findSymbol(USER_UNIF_RAND, dllInfo, !OPTIONAL);
-        userUnifInit = findSymbol(USER_UNIF_INIT, dllInfo, OPTIONAL);
-        userUnifNSeed = findSymbol(USER_UNIF_INIT, dllInfo, OPTIONAL);
-        userUnifSeedloc = findSymbol(USER_UNIF_INIT, dllInfo, OPTIONAL);
         userRngRFFI = RFFIFactory.getRFFI().getUserRngRFFI();
-        userRngRFFI.setLibrary(dllInfo.path);
-        userRngRFFI.init(seed);
-        if (userUnifSeedloc != 0 && userUnifNSeed == 0) {
+        if (Function.Init.isDefined()) {
+            userRngRFFI.init(seed);
+        }
+        if (Function.Seedloc.isDefined() && !Function.NSeed.isDefined()) {
             RError.warning(RError.NO_CALLER, RError.Message.RNG_READ_SEEDS);
         }
-        int ns = userRngRFFI.nSeed();
-        if (ns < 0 || ns > 625) {
-            RError.warning(RError.NO_CALLER, RError.Message.GENERIC, "seed length must be in 0...625; ignored");
-        } else {
-            nSeeds = ns;
-            // TODO: if we ever (initially) share iSeed (as GNU R does) we may need to assign this
-            // generator's iSeed here
+        if (Function.NSeed.isDefined()) {
+            int ns = userRngRFFI.nSeed();
+            if (ns < 0 || ns > 625) {
+                RError.warning(RError.NO_CALLER, RError.Message.GENERIC, "seed length must be in 0...625; ignored");
+            } else {
+                nSeeds = ns;
+                /*
+                 * TODO: if we ever (initially) share iSeed (as GNU R does) we may need to assign
+                 * this generator's iSeed here
+                 */
+            }
         }
     }
 
@@ -95,7 +121,7 @@ public final class UserRNG extends RNGInitAdapter {
 
     @Override
     public int[] getSeeds() {
-        if (userUnifSeedloc == 0) {
+        if (!Function.Seedloc.isDefined()) {
             return null;
         }
         int[] result = new int[nSeeds];