United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6493522 JNI_RegisterNatives fails to bind a method of an uninitialized class
JDK-6493522 : JNI_RegisterNatives fails to bind a method of an uninitialized class

Details
Type:
Bug
Submit Date:
2006-11-14
Status:
Resolved
Updated Date:
2013-09-17
Project Name:
JDK
Resolved Date:
2013-09-17
Component:
hotspot
OS:
generic
Sub-Component:
runtime
CPU:
generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
6
Fixed Versions:
hs25

Related Reports

Sub Tasks

Description
If an unitialized class instance is passed to the JNI_RegisterNatives function for binding, it fails to bind requested methods correctly, although reporting about success.

Testcase is attached.
Some users encountered this issue in JDK 6 release.
The link on a related discussion:
    http://forum.java.sun.com/thread.jspa?messageID=9412706
Issue re-discovered. Martin Buchholz reports:

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@1f7182c1
Successfully called registered native method
back to Java

Thanks,

Martin

[Edited to fix typo in name]

                                    

Comments
WORK AROUND

Ensure the class is initialized first. For example, GetMethodID will trigger initialization and so can be used from native code before calling JNI_RegisterNatives.

Of course it might not be correct to trigger class initialization from this native context.
                                     
2010-06-09
I was able to reproduce this bug in JDK 6 and JDK 7, but not in JDK 8 (running on Linux 32 and 64 bit).  So, I'm marking this bug as fixed.
                                     
2013-09-17



Hardware and Software, Engineered to Work Together