Must call RegisterNatives after getMethodID, else get UnsatisfiedLinkError
Martin Buchholz
martinrb at google.com
Wed Jun 9 12:39:49 PDT 2010
David, Thanks for the bug archaeology. Judging by the other
commenters, this bug is a regression from jdk5, and has cost many
other users hours of frustrating debugging time, so I think an
increase to P2 is in order. Since it used to work, it shouldn't be
too hard to fix - all you need to do is to properly track registered
native methods.
Sorry for not doing this kind of research myself - it's just too
painful without my beloved jbugs script :-(
Here's an improved version of our test case, with
-XX:+PrintJNIResolving output added,
that you might add to the bug report.
$ cat Test.java; echo ---------------------; cat jvm.cc; echo
--------------------------; JDK=`jver 1.7.0-b96`; g++ -I$JDK/include
-I$JDK/include/linux -ldl -DJDK=\"$JDK\" jvm.cc && ./a.out
public class Test {
public Test() {
System.out.println("My class loader is " + getClass().getClassLoader());
testNative();
System.out.println("back to Java");
}
public static native void testNative();
}
---------------------
#include <cstdio>
#include <cstdlib>
#include <dlfcn.h>
#include <jni.h>
JavaVM* jvm;
JNIEnv* env;
const char* libjvm = JDK "/jre/lib/amd64/server/libjvm.so";
const char* loader_url = "file://./";
const char* class_name = "Test";
#define countof(array) (sizeof(array)/sizeof(array[0]))
void InitializeJVM() {
JavaVMOption options[] = {
{ (char*)"-Djava.class.path=.", NULL },
{ (char*)"-XX:+PrintJNIResolving", NULL },
//{ (char*)"-verbose:jni", NULL },
};
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = countof(options);
vm_args.ignoreUnrecognized = JNI_TRUE;
void* handle = dlopen(libjvm, RTLD_LAZY);
if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(1); }
jint JNICALL (*func_create_java_vm)(JavaVM**, void**, void*) =
reinterpret_cast<jint JNICALL (*)(JavaVM**, void**, void*)>
(dlsym(handle, "JNI_CreateJavaVM"));
if (!func_create_java_vm) { fprintf(stderr, "%s\n", dlerror()); exit(1); }
jint result = (*func_create_java_vm)(&jvm, (void**)(&env), &vm_args);
}
void TestNative(JNIEnv* env, jclass cls) {
printf("Successfully called registered native method\n");
}
void RegisterNativeMethod(jclass cls) {
static const JNINativeMethod jni_method =
{ (char*)"testNative",
(char*)"()V",
(void*)&TestNative };
env->RegisterNatives(cls, &jni_method, 1);
if (env->ExceptionCheck()) env->ExceptionDescribe();
}
void Test() {
// URL[] urls = {new URL(url)}
jclass cls = env->FindClass("java/net/URL");
jmethodID method = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
jstring jurl_str = env->NewStringUTF(loader_url);
jobject jurl = env->NewObject(cls, method, jurl_str);
jobjectArray jurls = env->NewObjectArray(1, cls, jurl);
// URLClassLoader loader = new URLClassLoaer(urls)
cls = env->FindClass("java/net/URLClassLoader");
method = env->GetMethodID(cls, "<init>", "([Ljava/net/URL;)V");
jobject jloader = env->NewObject(cls, method, jurls);
// Class cls = loader.loadClass(name)
method = env->GetMethodID(
cls, "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;");
jstring jname = env->NewStringUTF(class_name);
cls = (jclass)env->CallObjectMethod(jloader, method, jname, (jboolean) true);
// RegisterNatives must be called after GetMethodID.
// If the order is reversed, we get UnsatisfiedLinkError,
// which seems like a bug.
RegisterNativeMethod(cls);
method = env->GetMethodID(cls, "<init>", "()V");
if (env->ExceptionCheck()) env->ExceptionDescribe();
env->NewObject(cls, method);
if (env->ExceptionCheck()) env->ExceptionDescribe();
}
int main(int argc, char** argv) {
InitializeJVM();
Test();
return 0;
}
--------------------------
[Dynamic-linking native method java.lang.Object.registerNatives ... JNI]
[Registering JNI native method java.lang.Object.hashCode]
[Registering JNI native method java.lang.Object.wait]
[Registering JNI native method java.lang.Object.notify]
[Registering JNI native method java.lang.Object.notifyAll]
[Registering JNI native method java.lang.Object.clone]
[Dynamic-linking native method java.lang.System.registerNatives ... JNI]
[Registering JNI native method java.lang.System.currentTimeMillis]
[Registering JNI native method java.lang.System.nanoTime]
[Registering JNI native method java.lang.System.arraycopy]
[Dynamic-linking native method java.lang.Thread.registerNatives ... JNI]
[Registering JNI native method java.lang.Thread.start0]
[Registering JNI native method java.lang.Thread.stop0]
[Registering JNI native method java.lang.Thread.isAlive]
[Registering JNI native method java.lang.Thread.suspend0]
[Registering JNI native method java.lang.Thread.resume0]
[Registering JNI native method java.lang.Thread.setPriority0]
[Registering JNI native method java.lang.Thread.yield]
[Registering JNI native method java.lang.Thread.sleep]
[Registering JNI native method java.lang.Thread.currentThread]
[Registering JNI native method java.lang.Thread.countStackFrames]
[Registering JNI native method java.lang.Thread.interrupt0]
[Registering JNI native method java.lang.Thread.isInterrupted]
[Registering JNI native method java.lang.Thread.holdsLock]
[Registering JNI native method java.lang.Thread.getThreads]
[Registering JNI native method java.lang.Thread.dumpThreads]
[Dynamic-linking native method
java.security.AccessController.getStackAccessControlContext ... JNI]
[Dynamic-linking native method
java.security.AccessController.getInheritedAccessControlContext ...
JNI]
[Dynamic-linking native method java.lang.ClassLoader.registerNatives ... JNI]
[Registering JNI native method java.lang.ClassLoader.retrieveDirectives]
[Dynamic-linking native method
java.security.AccessController.doPrivileged ... JNI]
[Dynamic-linking native method java.lang.Class.registerNatives ... JNI]
[Registering JNI native method java.lang.Class.getName0]
[Registering JNI native method java.lang.Class.getSuperclass]
[Registering JNI native method java.lang.Class.getInterfaces]
[Registering JNI native method java.lang.Class.getClassLoader0]
[Registering JNI native method java.lang.Class.isInterface]
[Registering JNI native method java.lang.Class.getSigners]
[Registering JNI native method java.lang.Class.setSigners]
[Registering JNI native method java.lang.Class.isArray]
[Registering JNI native method java.lang.Class.isPrimitive]
[Registering JNI native method java.lang.Class.getComponentType]
[Registering JNI native method java.lang.Class.getModifiers]
[Registering JNI native method java.lang.Class.getDeclaredFields0]
[Registering JNI native method java.lang.Class.getDeclaredMethods0]
[Registering JNI native method java.lang.Class.getDeclaredConstructors0]
[Registering JNI native method java.lang.Class.getProtectionDomain0]
[Registering JNI native method java.lang.Class.setProtectionDomain0]
[Registering JNI native method java.lang.Class.getDeclaredClasses0]
[Registering JNI native method java.lang.Class.getDeclaringClass]
[Registering JNI native method java.lang.Class.getGenericSignature]
[Registering JNI native method java.lang.Class.getRawAnnotations]
[Registering JNI native method java.lang.Class.getConstantPool]
[Registering JNI native method java.lang.Class.desiredAssertionStatus0]
[Registering JNI native method java.lang.Class.getEnclosingMethod0]
[Dynamic-linking native method java.lang.Class.getPrimitiveClass ... JNI]
[Dynamic-linking native method java.lang.System.initProperties ... JNI]
[Dynamic-linking native method sun.misc.Unsafe.registerNatives ... JNI]
[Registering JNI native method sun.misc.Unsafe.getLoadAverage]
[Dynamic-linking native method java.lang.Throwable.fillInStackTrace ... JNI]
[Registering JNI native method sun.misc.Unsafe.copyMemory]
[Registering JNI native method sun.misc.Unsafe.setMemory]
[Registering JNI native method sun.misc.Unsafe.getObject]
[Registering JNI native method sun.misc.Unsafe.putObject]
[Registering JNI native method sun.misc.Unsafe.getObjectVolatile]
[Registering JNI native method sun.misc.Unsafe.putObjectVolatile]
[Registering JNI native method sun.misc.Unsafe.getBoolean]
[Registering JNI native method sun.misc.Unsafe.putBoolean]
[Registering JNI native method sun.misc.Unsafe.getBooleanVolatile]
[Registering JNI native method sun.misc.Unsafe.putBooleanVolatile]
[Registering JNI native method sun.misc.Unsafe.getByte]
[Registering JNI native method sun.misc.Unsafe.putByte]
[Registering JNI native method sun.misc.Unsafe.getByteVolatile]
[Registering JNI native method sun.misc.Unsafe.putByteVolatile]
[Registering JNI native method sun.misc.Unsafe.getShort]
[Registering JNI native method sun.misc.Unsafe.putShort]
[Registering JNI native method sun.misc.Unsafe.getShortVolatile]
[Registering JNI native method sun.misc.Unsafe.putShortVolatile]
[Registering JNI native method sun.misc.Unsafe.getChar]
[Registering JNI native method sun.misc.Unsafe.putChar]
[Registering JNI native method sun.misc.Unsafe.getCharVolatile]
[Registering JNI native method sun.misc.Unsafe.putCharVolatile]
[Registering JNI native method sun.misc.Unsafe.getInt]
[Registering JNI native method sun.misc.Unsafe.putInt]
[Registering JNI native method sun.misc.Unsafe.getIntVolatile]
[Registering JNI native method sun.misc.Unsafe.putIntVolatile]
[Registering JNI native method sun.misc.Unsafe.getLong]
[Registering JNI native method sun.misc.Unsafe.putLong]
[Registering JNI native method sun.misc.Unsafe.getLongVolatile]
[Registering JNI native method sun.misc.Unsafe.putLongVolatile]
[Registering JNI native method sun.misc.Unsafe.getFloat]
[Registering JNI native method sun.misc.Unsafe.putFloat]
[Registering JNI native method sun.misc.Unsafe.getFloatVolatile]
[Registering JNI native method sun.misc.Unsafe.putFloatVolatile]
[Registering JNI native method sun.misc.Unsafe.getDouble]
[Registering JNI native method sun.misc.Unsafe.putDouble]
[Registering JNI native method sun.misc.Unsafe.getDoubleVolatile]
[Registering JNI native method sun.misc.Unsafe.putDoubleVolatile]
[Registering JNI native method sun.misc.Unsafe.getByte]
[Registering JNI native method sun.misc.Unsafe.putByte]
[Registering JNI native method sun.misc.Unsafe.getShort]
[Registering JNI native method sun.misc.Unsafe.putShort]
[Registering JNI native method sun.misc.Unsafe.getChar]
[Registering JNI native method sun.misc.Unsafe.putChar]
[Registering JNI native method sun.misc.Unsafe.getInt]
[Registering JNI native method sun.misc.Unsafe.putInt]
[Registering JNI native method sun.misc.Unsafe.getLong]
[Registering JNI native method sun.misc.Unsafe.putLong]
[Registering JNI native method sun.misc.Unsafe.getFloat]
[Registering JNI native method sun.misc.Unsafe.putFloat]
[Registering JNI native method sun.misc.Unsafe.getDouble]
[Registering JNI native method sun.misc.Unsafe.putDouble]
[Registering JNI native method sun.misc.Unsafe.getAddress]
[Registering JNI native method sun.misc.Unsafe.putAddress]
[Registering JNI native method sun.misc.Unsafe.allocateMemory]
[Registering JNI native method sun.misc.Unsafe.reallocateMemory]
[Registering JNI native method sun.misc.Unsafe.freeMemory]
[Registering JNI native method sun.misc.Unsafe.objectFieldOffset]
[Registering JNI native method sun.misc.Unsafe.staticFieldOffset]
[Registering JNI native method sun.misc.Unsafe.staticFieldBase]
[Registering JNI native method sun.misc.Unsafe.ensureClassInitialized]
[Registering JNI native method sun.misc.Unsafe.arrayBaseOffset]
[Registering JNI native method sun.misc.Unsafe.arrayIndexScale]
[Registering JNI native method sun.misc.Unsafe.addressSize]
[Registering JNI native method sun.misc.Unsafe.pageSize]
[Registering JNI native method sun.misc.Unsafe.defineClass]
[Registering JNI native method sun.misc.Unsafe.defineClass]
[Registering JNI native method sun.misc.Unsafe.allocateInstance]
[Registering JNI native method sun.misc.Unsafe.monitorEnter]
[Registering JNI native method sun.misc.Unsafe.monitorExit]
[Registering JNI native method sun.misc.Unsafe.tryMonitorEnter]
[Registering JNI native method sun.misc.Unsafe.throwException]
[Registering JNI native method sun.misc.Unsafe.compareAndSwapObject]
[Registering JNI native method sun.misc.Unsafe.compareAndSwapInt]
[Registering JNI native method sun.misc.Unsafe.compareAndSwapLong]
[Registering JNI native method sun.misc.Unsafe.putOrderedObject]
[Registering JNI native method sun.misc.Unsafe.putOrderedInt]
[Registering JNI native method sun.misc.Unsafe.putOrderedLong]
[Registering JNI native method sun.misc.Unsafe.park]
[Registering JNI native method sun.misc.Unsafe.unpark]
[Dynamic-linking native method java.lang.Float.floatToRawIntBits ... JNI]
[Dynamic-linking native method java.lang.Double.doubleToRawLongBits ... JNI]
[Dynamic-linking native method sun.reflect.Reflection.getCallerClass ... JNI]
[Dynamic-linking native method java.lang.String.intern ... JNI]
[Dynamic-linking native method java.lang.Object.getClass ... JNI]
[Dynamic-linking native method java.lang.Class.forName0 ... JNI]
[Dynamic-linking native method
sun.reflect.Reflection.getClassAccessFlags ... JNI]
[Dynamic-linking native method
sun.reflect.NativeConstructorAccessorImpl.newInstance0 ... JNI]
[Dynamic-linking native method sun.misc.VM.initialize ... JNI]
[Dynamic-linking native method java.io.FileSystem.getFileSystem ... JNI]
[Dynamic-linking native method java.io.UnixFileSystem.initIDs ... JNI]
[Dynamic-linking native method java.lang.System.mapLibraryName ... JNI]
[Dynamic-linking native method
java.io.UnixFileSystem.getBooleanAttributes0 ... JNI]
[Dynamic-linking native method java.io.UnixFileSystem.canonicalize0 ... JNI]
[Dynamic-linking native method java.lang.ClassLoader$NativeLibrary.load ... JNI]
[Dynamic-linking native method java.io.FileInputStream.initIDs ... JNI]
[Dynamic-linking native method java.io.FileDescriptor.initIDs ... JNI]
[Dynamic-linking native method java.io.FileOutputStream.initIDs ... JNI]
[Dynamic-linking native method java.lang.System.setIn0 ... JNI]
[Dynamic-linking native method java.lang.System.setOut0 ... JNI]
[Dynamic-linking native method java.lang.System.setErr0 ... JNI]
[Dynamic-linking native method sun.misc.Signal.findSignal ... JNI]
[Dynamic-linking native method sun.misc.Signal.handle0 ... JNI]
[Dynamic-linking native method java.lang.Runtime.maxMemory ... JNI]
[Dynamic-linking native method java.lang.Compiler.registerNatives ... JNI]
[Registering JNI native method java.lang.Compiler.compileClass]
[Registering JNI native method java.lang.Compiler.compileClasses]
[Registering JNI native method java.lang.Compiler.command]
[Registering JNI native method java.lang.Compiler.enable]
[Registering JNI native method java.lang.Compiler.disable]
[Dynamic-linking native method java.lang.ClassLoader.getCaller ... JNI]
[Dynamic-linking native method java.lang.ClassLoader$NativeLibrary.find ... JNI]
[Dynamic-linking native method
java.security.AccessController.doPrivileged ... JNI]
[Dynamic-linking native method java.io.FileInputStream.open ... JNI]
[Dynamic-linking native method java.io.FileInputStream.readBytes ... JNI]
[Dynamic-linking native method java.io.FileInputStream.available ... JNI]
[Dynamic-linking native method java.lang.reflect.Array.newArray ... JNI]
[Dynamic-linking native method java.io.FileInputStream.close0 ... JNI]
[Dynamic-linking native method java.io.UnixFileSystem.list ... JNI]
[Dynamic-linking native method java.lang.ClassLoader.findLoadedClass0 ... JNI]
[Dynamic-linking native method java.lang.ClassLoader.findBootstrapClass ... JNI]
[Dynamic-linking native method
java.security.AccessController.doPrivileged ... JNI]
[Dynamic-linking native method java.io.UnixFileSystem.getLength ... JNI]
[Dynamic-linking native method sun.misc.Perf.registerNatives ... JNI]
[Registering JNI native method sun.misc.Perf.attach]
[Registering JNI native method sun.misc.Perf.detach]
[Registering JNI native method sun.misc.Perf.createLong]
[Registering JNI native method sun.misc.Perf.createByteArray]
[Registering JNI native method sun.misc.Perf.highResCounter]
[Registering JNI native method sun.misc.Perf.highResFrequency]
[Dynamic-linking native method java.lang.ClassLoader.defineClass1 ... JNI]
[Dynamic-linking native method java.lang.ClassLoader.resolveClass0 ... JNI]
[Registering JNI native method Test.testNative]
[Dynamic-linking native method java.io.FileOutputStream.writeBytes ... JNI]
My class loader is sun.misc.Launcher$AppClassLoader at b92d342
Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.testNative()V
[Dynamic-linking native method java.lang.Throwable.getStackTraceDepth ... JNI]
[Dynamic-linking native method java.lang.Throwable.getStackTraceElement ... JNI]
at Test.testNative(Native Method)
at Test.<init>(Test.java:4)
Martin
On Wed, Jun 9, 2010 at 03:25, David Holmes <David.Holmes at oracle.com> wrote:
> Hi Martin,
>
> Turns out this is a known (and old) issue (thanks Yuri!):
>
> CR 6493522 "JNI_RegisterNatives fails to bind a method of an uninitialized
> class"
>
> I'll add this email info to the CR.
>
> David
>
> Martin Buchholz said the following on 06/09/10 09:12:
>>
>> Hi ClassLoader maintainers,
>>
>> This is a bug report.
>>
>> (I think this is a hotspot bug, but it might be a core library
>> ClassLoader bug or even a URLClassLoader bug)
>>
>> If you use RegisterNatives with a class loaded using URLClassLoader,
>> you must call GetMethodID
>> before RegisterNatives, or you get an UnsatisfiedLinkError as in the
>> test case below
>>
>> (you will need to edit the below to fill in the location of your jni
>> include directory and your libjvm.so)
>>
>> $ cat Test.java; echo ---------------------; cat jvm.cc; echo
>> --------------------------; g++ -I$JDK/include -I$JDK/include/linux
>> -ldl jvm.cc && ./a.out
>> public class Test {
>> public Test() {
>> System.out.println("My class loader is " +
>> getClass().getClassLoader());
>> testNative();
>> System.out.println("back to Java");
>> }
>> public static native void testNative();
>> }
>> ---------------------
>> #include <cstdio>
>> #include <dlfcn.h>
>> #include <jni.h>
>>
>> JavaVM* jvm;
>> JNIEnv* env;
>> const char* libjvm =
>> "$JDKDIR/libjvm.so";
>> const char* loader_url = "file://./";
>> const char* class_name = "Test";
>>
>> void InitializeJVM() {
>> JavaVMOption options[1];
>> options[0].optionString = (char*)"-Djava.class.path=.";
>> //options[1].optionString = (char*)"-verbose:jni";
>>
>> JavaVMInitArgs vm_args;
>> vm_args.version = JNI_VERSION_1_2;
>> vm_args.options = options;
>> vm_args.nOptions = 2;
>> vm_args.ignoreUnrecognized = JNI_TRUE;
>>
>> void* handle = dlopen(libjvm, RTLD_LAZY);
>> if (handle == NULL) perror("dlopen");
>> jint JNICALL (*func_create_java_vm)(JavaVM**, void**, void*) =
>> reinterpret_cast<jint JNICALL (*)(JavaVM**, void**, void*)>
>> (dlsym(handle, "JNI_CreateJavaVM"));
>> if (func_create_java_vm == NULL) perror("dlsym");
>> jint result = (*func_create_java_vm)(&jvm, (void**)(&env), &vm_args);
>> }
>>
>> void TestNative(JNIEnv* env, jclass cls) {
>> printf("Successfully called registered native method\n");
>> }
>>
>> void RegisterNativeMethod(jclass cls) {
>> static const JNINativeMethod jni_method =
>> { (char*)"testNative",
>> (char*)"()V",
>> (void*)&TestNative };
>> env->RegisterNatives(cls, &jni_method, 1);
>> if (env->ExceptionCheck()) env->ExceptionDescribe();
>> }
>>
>> void Test() {
>> // URL[] urls = {new URL(url)}
>> jclass cls = env->FindClass("java/net/URL");
>> jmethodID method = env->GetMethodID(cls, "<init>",
>> "(Ljava/lang/String;)V");
>> jstring jurl_str = env->NewStringUTF(loader_url);
>> jobject jurl = env->NewObject(cls, method, jurl_str);
>> jobjectArray jurls = env->NewObjectArray(1, cls, jurl);
>>
>> // URLClassLoader loader = new URLClassLoaer(urls)
>> cls = env->FindClass("java/net/URLClassLoader");
>> method = env->GetMethodID(cls, "<init>", "([Ljava/net/URL;)V");
>> jobject jloader = env->NewObject(cls, method, jurls);
>>
>> // Class cls = loader.loadClass(name)
>> method = env->GetMethodID(
>> cls, "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;");
>> jstring jname = env->NewStringUTF(class_name);
>> cls = (jclass)env->CallObjectMethod(jloader, method, jname, (jboolean)
>> true);
>>
>> method = env->GetMethodID(cls, "<init>", "()V");
>> if (env->ExceptionCheck()) env->ExceptionDescribe();
>>
>> // RegisterNatives must be called after GetMethodID.
>> // If the order is reversed, we get UnsatisfiedLinkError,
>> // which seems like a bug.
>> RegisterNativeMethod(cls);
>>
>> env->NewObject(cls, method);
>> if (env->ExceptionCheck()) env->ExceptionDescribe();
>> }
>>
>> int main(int argc, char** argv) {
>> InitializeJVM();
>> Test();
>>
>> return 0;
>> }
>> --------------------------
>> My class loader is sun.misc.Launcher$AppClassLoader at 1f7182c1
>> Successfully called registered native method
>> back to Java
>>
>> Thanks,
>>
>> Martin
>
More information about the net-dev
mailing list