Must call RegisterNatives after getMethodID, else get UnsatisfiedLinkError

Martin Buchholz martinrb at google.com
Wed Jun 9 19:39:49 UTC 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 core-libs-dev mailing list