Search code examples
javac++java-native-interfacejavaagentsjvmti

JNI passing a long value to a native method


So as described above I am trying to copy an int object into off-heap memory with the help of Unsafe. Here is my main function:

public static void main(String[] args) throws Exception {
    UnsafeHelper hlpr = new UnsafeHelper();

    int original = 100;
    long copyaddress = hlpr.shallowCopy(original);
    System.out.println("COPIED TO ADDRESS: " + copyaddress);
    int copy = (int) hlpr.fromAddress(copyaddress);

    getCopiedObject(copyaddress);
}

it provides me the address of the beginning of the copied object (copyaddress).

Next, i want to pass this address to the function defined in jni agent. Here is the declaration of the native function in the java class:

private static native void getCopiedObject(long address);

Here is the declaration of the function in agent.h:

JNIEXPORT void JNICALL Java_main_Main_getCopiedObject(JNIEnv *, jlong);

And here is where i get into the trouble... The long value which i get in the C++ function is different from the one which i pass in java...

JNIEXPORT void JNICALL Java_main_Main_getCopiedObject(JNIEnv *env, jlong address){
    ...
    unsigned long long a = address;
    signed long long b = address;
    printf("UNSIGNED LONG LONG %llu \n", a);
    printf("SIGNED LONG LONG %lld \n", b);
    ...
}

In my case java long's size is 64bit, so i use long long in my c++ code... I tried both the signed and unsigned conversions but it never worked...

So when i run this code i get following outputs:

JAVA:
COPIED TO ADDRESS: 1487190592 
AGENT:
UNSIGNED LONG LONG 37025744 
SIGNED LONG LONG 37025744 

According to the output, i am converting jlong to long in a wrong way...

Does anybody know how to get the correct long long value from a jlong ?


Solution

  • I seem to have figured out what the problem is. When you have a static native method there are 2 extra arguments besides the one you have in Java. The first is the JNIEnv and the second is a jclass.

    I don't know how exactly you generated your .h file, but when I use:

    javah -jni Main
    

    I get the signature:

    JNIEXPORT void JNICALL Java_Main_passLong(JNIEnv *, jclass, jlong);
    

    In the .h file. If I use it like this, it behaves as expected. So my solution would be to add a jclass parameter before the jlong.

    If I remove the jclass parameter, I see the wrong value. I guess the JVM does not check the signature of the native method when loading the library.