Search code examples
cstructrustjava-native-interfaceunsafe

Strange memory layout with Option<unsafe fn ...> within a struct


I'm using JNI definitions from here. I create a JNINativeInterface_ with most members initialized to None. I then run native code which uses the RegisterNatives field of the aforementioned struct. I initialized RegisterNatives and surrounding fields as such:

SetDoubleArrayRegion: unsafe { transmute(0xdeadbeaf as u64) },
RegisterNatives: Some(register_natives),
UnregisterNatives: unsafe { transmute(0xdeadbeaf as u64) },

register_natives is defined like so(this matches the library type exactly):

unsafe extern "system" fn register_natives(env: *mut sys::JNIEnv,
                                           clazz: jclass,
                                           methods: *const JNINativeMethod,
                                           nMethods: jint) -> jint {
    unimplemented!()
}

The native code that uses the struct segfaults(and seems to get a null ptr instead of register_natives).

The relevant part of the struct looks like so under GDB:

0x7ffcf5f4a5b8: 0x0 0x0 0x0 0x0
0x7ffcf5f4a5c8: 0xdeadbeaf  0x0 0x43fd9950  0x55ea
0x7ffcf5f4a5d8: 0xdeadbeaf  0x0 0x0 0x0
0x7ffcf5f4a5e8: 0x0 0x0 0x0 0x0

I'm confused as to exactly what I am looking at since I was expecting 0xdeadbeaf , followed by a 64 bit pointer, followed by 0xdeadbeaf, but as you can see that is not what I get. Am I wrong about my assumptions as to how option will be represented behind the scenes? Why does bindgen/the aformentioned library seem to thing that Option will lead to a compatible interface?


Solution

  • [...] I was expecting 0xdeadbeaf, followed by a 64 bit pointer, followed by 0xdeadbeaf, but as you can see that is not what I get.

    We must not be seeing the same thing, because I do see that.

    0x7ffcf5f4a5c8: 0xdeadbeaf  0x0 0x43fd9950  0x55ea
    0x7ffcf5f4a5d8: 0xdeadbeaf  0x0 0x0 0x0
    

    Each hex number is a 32-bit integer, so you have to take two of them to make a 64-bit integer. The first is 0x00000000deadbeaf, the second is 0x000055ea43fd9950 (your register_natives function, presumably) and the third is 0x00000000deadbeaf again. (It's also "obvious" from the addresses: a 64-bit integer takes 8 bytes, so it takes two to take 0x10 bytes. Therefore, there are two 64-bit integer per line.)

    The reason the program segfaults may be because letting a panic unwind through foreign code is undefined behavior. Try changing your register_natives function to something that doesn't panic.