Exception 0xC0000005 in jvm.dll when creating an instance via JNI NewObject

2

I am writing a plug-in for an existing application. Implementation language is C. However, the actual functionality is implemented in Java. For this reason, I am using Java Native Interface (JNI) to create a JVM instance from within C. I can find the appropriate Java class and create an instance. This is what the code looks like:

login(uintptr_t connection, const char* username, …) {
    …
    jmethodID constructor = (*env)->GetMethodID(env, ps->class, "<init>", "(JLjava/lang/String;)V");
    jstring jusername = (*env)->NewStringUTF(env, username);
    jobject instance = (*env)->NewObject(env, ps->class, constructor, connection, jusername);

Everything works just fine.

On Linux.

On Windows, it is a complete mess. As soon as I try to create an instance of the Java class, it throws a

EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0813751f, pid=8, tid=0x00000009

. More details are written to a log file, but the stack trace is not helpful other than pointing to somewhere in the jvm.dll. Stepping through with a debugger has not been insightful. Note this is not the same as this question.

java
c
java-native-interface
asked on Stack Overflow Mar 14, 2020 by Hermann • edited Mar 15, 2020 by Hermann

1 Answer

2

After days, I figured it out.

The constructor I am invoking expects a parameter. The type is long (Java) aka J (JNI Type Signature) aka jlong (corresponing C type). A C uintptr_t is compatible with a jlong.

On Linux, my uintptr_t is 8 bytes long, since I am in a amd64 environment with 64 bit applications. For Windows, the application was build in 32 bit. As a result uintptr_t is only 4 bytes long, but the JVM still expect a 8 byte jlong. However, NewObject is a variadic function, automatic promotion does not happen and type safety is not guaranteed.

login(uintptr_t connection, const char* username, …) {
    …
    jmethodID constructor = (*env)->GetMethodID(env, ps->class, "<init>", "(JLjava/lang/String;)V");
    jstring jusername = (*env)->NewStringUTF(env, username);
    jlong jconnection = connection;
    jobject instance = (*env)->NewObject(env, ps->class, constructor, jconnection, jusername);

A simple cast to the correct type was the solution. I expect this pitfall to exist with CallVoidMethod or any of the Call*Method mentioned in the documentation, too.

answered on Stack Overflow Mar 14, 2020 by Hermann • edited Mar 15, 2020 by Hermann

User contributions licensed under CC BY-SA 3.0