I have a JNI DLL, that crashes when using GetFieldID() on a class object, that was passed into a function. The library is working fine on Linux with 32-bit and 64-bit JVMs and only crashes when using 32-bit under Windows - 64-bit is fine.
The original DLL was cross-compiled on ubuntu 13.10 x86_64 using MinGW-w64 GCC 4.6.3, but I also compiled it natively under Windows using MinGW-w64 GCC 4.6.3 and I still got the same crash. Using ubuntu 14.04 with MinGW-w64 4.8.2 still produces the same error.
It appears there is some memory corruption going on since when I use an unoptimized DLL the crash doesn't happen an the first call on GetFieldID(), but on a later one (the original DLL has way more code than the stripped down example below) or even after the function finished somewhere in the JVM garbage collection.
The JVM I am using is Java 7u60, but I also tried it with 8u5 and got the same results. I tested it with the 32-bit JVM on a 64-bit and 32-bit systems as I came across an article, that said, that a 32-bit JVM might not be reliable on 64-bit Windows operating systems (sounded a bit bogus to me, but just to be sure).
Also there are other JNI DLLs, that don't utilize GetFieldID() at all and they are working just fine with 32-bit.
The crash data from the hs_err_pid.log
Current thread (0x00d5e000): JavaThread "main" [_thread_in_native, id=1104, stack(0x00dd0000,0x00e20000)]
siginfo: ExceptionCode=0xc0000005, ExceptionInformation=0x00000008 0x3462c9e8
Registers:
EAX=0x00000000, EBX=0x00e1f1fc, ECX=0x97254d7c, EDX=0x00d5eac4
ESP=0x00e1f1dc, EBP=0x00e1f1ec, ESI=0x3462c6e8, EDI=0x00d5e000
EIP=0x3462c9e8, EFLAGS=0x00010246
Top of Stack: (sp=0x00e1f1dc)
0x00e1f1dc: 00000000 3462c6e8 00000000 00e1f1fc
0x00e1f1ec: 00e1f224 025f334f 246970c0 025f88c9
0x00e1f1fc: 24695668 2460b700 00e1f204 34628d1b
0x00e1f20c: 00e1f22c 34628ee8 00000000 34628d40
0x00e1f21c: 00e1f1fc 00e1f22c 00e1f25c 025f3207
0x00e1f22c: 24693760 24693760 00000001 24693758
0x00e1f23c: 00e1f234 34628c56 00e1f264 34628ee8
0x00e1f24c: 00000000 34628c88 00e1f22c 00e1f268
Instructions: (pc=0x3462c9e8)
0x3462c9c8: 78 bc 62 34 50 bb 62 34 c0 bd 62 34 30 bd 62 34
0x3462c9d8: 00 00 00 00 00 00 00 00 0c 00 00 00 02 00 00 00
0x3462c9e8: 01 00 00 00 60 f9 5f 39 02 00 00 00 a0 b9 62 34
0x3462c9f8: 0a 00 b8 00 10 d6 00 39 00 00 00 00 01 00 40 80
Register to memory mapping:
EAX=0x00000000 is an unknown value
EBX=0x00e1f1fc is pointing into the stack for thread: 0x00d5e000
ECX=0x97254d7c is an unknown value
EDX=0x00d5eac4 is an unknown value
ESP=0x00e1f1dc is pointing into the stack for thread: 0x00d5e000
EBP=0x00e1f1ec is pointing into the stack for thread: 0x00d5e000
ESI=0x3462c6e8 is an oop
{method}
- klass: {other class}
EDI=0x00d5e000 is a thread
Stack: [0x00dd0000,0x00e20000], sp=0x00e1f1dc, free space=316k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C 0x3462c9e8
j jnitest.JNIClass.<init>()V+27
j jnitest.JNIClass.getInstance()Ljnitest/JNIClass;+22
j jnitest.Program.main([Ljava/lang/String;)V+0
v ~StubRoutines::call_stub
V [jvm.dll+0x140e6a]
V [jvm.dll+0x20529e]
V [jvm.dll+0x140eed]
V [jvm.dll+0x14d2ee]
V [jvm.dll+0x14d515]
V [jvm.dll+0xf1f99]
C [java.dll+0x7d82]
j sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87
j sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6
j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+57
j com.intellij.rt.execution.application.AppMain.main([Ljava/lang/String;)V+163
v ~StubRoutines::call_stub
V [jvm.dll+0x140e6a]
V [jvm.dll+0x20529e]
V [jvm.dll+0x140eed]
V [jvm.dll+0xca5c5]
V [jvm.dll+0xd5267]
C [java.exe+0x2063]
C [java.exe+0xa5d1]
C [java.exe+0xa65b]
C [kernel32.dll+0x1338a]
C [ntdll.dll+0x39f72]
C [ntdll.dll+0x39f45]
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j jnitest.JNIWrapper.createUuid(Ljnitest/JNIWrapper$sender_id_t;)I+25
j jnitest.JNIClass.<init>()V+27
j jnitest.JNIClass.getInstance()Ljnitest/JNIClass;+22
j jnitest.Program.main([Ljava/lang/String;)V+0
v ~StubRoutines::call_stub
j sun.reflect.NativeMethodAccessorImpl.invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+0
j sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87
j sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6
j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+57
j com.intellij.rt.execution.application.AppMain.main([Ljava/lang/String;)V+163
v ~StubRoutines::call_stub
The Java class:
package jnitest;
public class JNIClass {
static final Object _mutex = new Object();
static JNIClass _instance = null;
public static JNIClass getInstance()
{
if (_instance == null)
{
synchronized (_mutex)
{
if (_instance == null)
_instance = new JNIClass();
}
}
return _instance;
}
JNIWrapper.sender_id_t sid = null;
JNIClass() {
//create uuid
sid = new JNIWrapper.sender_id_t();
System.out.print(JNIWrapper.createUuid(sid));
}
}
The JNI wrapper class:
package jnitest;
public final class JNIWrapper {
static {
System.loadLibrary("JNIWrapper");
}
public static class sender_id_t
{
public long phy_idx;
}
public static native int createUuid(JNIWrapper.sender_id_t id);
}
The application:
package jnitest;
public class Program
{
public static void main(String[] args)
{
JNIClass.getInstance();
System.exit(0);
}
}
The auto-generated JNI DLL header:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jnitest_JNIWrapper */
#ifndef _Included_jnitest_JNIWrapper
#define _Included_jnitest_JNIWrapper
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: jnitest_JNIWrapper
* Method: createUuid
* Signature: (Ljnitest/JNIWrapper/sender_id_t;)I
*/
JNIEXPORT jint JNICALL Java_jnitest_JNIWrapper_createUuid
(JNIEnv *, jclass, jobject);
#ifdef __cplusplus
}
#endif
#endif
The JNI DLL implementation (updated to be able to use either C or C++ interface):
#include "jnitest_JNIWrapper.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
#define JNIFUNC(e,f) e->f()
#define JNIFUNCV(e,f,...) e->f(__VA_ARGS__)
#else
#define JNIFUNC(e,f) (*e)->f(e)
#define JNIFUNCV(e,f,...) (*e)->f(e,__VA_ARGS__)
#endif
JNIEXPORT jint JNICALL Java_jnitest_JNIWrapper_createUuid(JNIEnv *env, jclass clazz, jobject sid)
{
(void)clazz;
jclass cls = JNIFUNCV(env,GetObjectClass, sid);
jfieldID phyID = JNIFUNCV(env,GetFieldID, cls, "phy_idx", "J");
(void)phyID;
if (JNIFUNC(env,ExceptionCheck))
return 100;
return 0;
}
#ifdef __cplusplus
}
#endif
Update:
The compilation command:
i686-w64-mingw32-gcc -std=c99 -O3 -s -Wall -Wextra -Werror -o ../bin/JNIWrapper.dll -shared -Wl,--subsystem,windows dllmain.c JNIWrapper.c -I /usr/lib/jvm/java-7-openjdk-amd64/include