Skip to Main Content

Java HotSpot Virtual Machine

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

[Win32/Linux] Spawning JVM from native C program

843811Sep 13 2004 — edited Oct 4 2004
Hi,

I'm trying to spawn the Java Virtual Machine from my own C program so users who aren't used to .jar files can just click on the supplied .exe or execute the linux binary of my project. I found a solution that works in Linux even though I've got to manually set the LD_LIBRARY_PATH environment variable for it to work. If I don't set it, the program complains about missin HotSpot stuff. Well that's not a big problem at all, because the binary on Linux can spawn the JVM without any further problems. Here's the C code (works perfectly fine:
#include <stdlib.h>
#include <jni.h>
#include "Prog.h"

#ifdef _WIN32
#define PATH_SEPARATOR ';'
#else /* UNIX */
#define PATH_SEPARATOR ':'
#endif

#define USER_CLASSPATH "." /* where Prog.class is */

int main(int argc, char *argv[])
{
	JNIEnv *env;
	JavaVM *jvm;
	jint res;
	jclass cls;
	jmethodID mid;
	jstring jstr;
	jobjectArray args;

	JavaVMInitArgs vm_args;
	JavaVMOption options[4];

	options[0].optionString = "-Djava.compiler=NONE";	/* disable JIT */
	options[1].optionString = "-Djava.class.path=/home/voice/devel/jni";	/* user classes */
	options[2].optionString = "-Djava.library.path=./";	/* set native library path */
	options[3].optionString = "-verbose:jni";		/* print JNI-related messages */

	vm_args.version = JNI_VERSION_1_4;
	
	vm_args.options = options;
	vm_args.nOptions = 4;
	vm_args.ignoreUnrecognized = 1;

	/* Note that in the Java 2 SDK, there is no longer any need to call 
	* JNI_GetDefaultJavaVMInitArgs. 
	*/
	res = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
	
	if (res < 0) {
		fprintf(stderr, "Can't create Java VM\n");
		exit(1);
	}

	cls = (*env)->FindClass(env, "Prog");
	if (cls == 0) {
		fprintf(stderr, "Can't find Prog class\n");
		exit(1);
	}
 
	mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
	if (mid == 0) {
		fprintf(stderr, "Can't find Prog.main\n");
		exit(1);
	}

	jstr = (*env)->NewStringUTF(env, " from C!");
	if (jstr == 0) {
		fprintf(stderr, "Out of memory\n");
		exit(1);
	}
	args = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), jstr);
	if (args == 0) {
		fprintf(stderr, "Out of memory\n");
		exit(1);
	}
	(*env)->CallStaticVoidMethod(env, cls, mid, args);

	(*jvm)->DestroyJavaVM(jvm);

	return 0;
}
I'm using something very similar for the Windows32 Version of my JVM Launcher. The difference lies only in loading the jvm.dll and finding the ProcAdress of JNI_CreateJavaVM manually, because I want to be able to use a DLL of my own choice (e.g. c:\myProject\myExe.exe uses> c:\myProject\dlls\jvm.dll).
There's no problem in finding the DLL or the ProcAdress. Both works very well (I have checked on this while debugging the project), but something very weird happens after the call to JNI_CreateJavaVM. Have a look at the code first:
#include <stdlib.h>
#include <jni.h>
#include <windows.h>
 
#ifdef _WIN32
#define PATH_SEPARATOR ';'
#else /* UNIX */
#define PATH_SEPARATOR ':'
#endif

#define USER_CLASSPATH "." /* where Prog.class is */
 /* Win32 version */
void *JNU_FindCreateJavaVM(char *vmlibpath)
{
    HINSTANCE hVM = LoadLibrary(vmlibpath);
    if (hVM == NULL) {
        return NULL;
    }
     
    return GetProcAddress(hVM, "JNI_CreateJavaVM");
}

int main(int argc, char *argv[])
{
	JNIEnv *env;
	JavaVM *jvm;
	jint res;
	jclass cls;
	jmethodID mid;
	jstring jstr;
	jobjectArray args;

	JavaVMInitArgs vm_args;
	JavaVMOption options[4];
	
        jint (*pfnCreateVM)(JavaVM **pvm, void **penv, void *args) = NULL;

	options[0].optionString = "-Djava.compiler=NONE";	/* disable JIT */
	options[1].optionString = "-Djava.class.path=z:\\jni\\";	/* user classes */
	options[2].optionString = "-Djava.library.path=z:\\jni\\";	/* set native library path */
	options[3].optionString = "-verbose:jni";		/* print JNI-related messages */
 
	vm_args.version = JNI_VERSION_1_4;
	vm_args.options = options;
	vm_args.nOptions = 4;
	vm_args.ignoreUnrecognized = 1;
 
	/* Note that in the Java 2 SDK, there is no longer any need to call 
	* JNI_GetDefaultJavaVMInitArgs. 
	*/
	pfnCreateVM = JNU_FindCreateJavaVM("jvm.dll");
 	if(pfnCreateVM == NULL)
	{
	    fprintf(stderr, "Shit happens!\n");
	    exit(1);
	}    	   
 
       printf("JNI_CreateJavaVM Proc: %X\n", pfnCreateVM);  
 	
       res = pfnCreateVM(&jvm, (void**)&env, &vm_args);
	
	if (res < 0) {
		fprintf(stderr, "Can't create Java VM\n");
		exit(1);
	}
 
	cls = (*env)->FindClass(env, "Prog");
 	if (cls == 0) {
 		fprintf(stderr, "Can't find Prog class\n");
 		exit(1);
	}
  
 	mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
 	if (mid == 0) {
 		fprintf(stderr, "Can't find Prog.main\n");
 		exit(1);
 	}
 
 	jstr = (*env)->NewStringUTF(env, " from C!");
 	if (jstr == 0) {
 		fprintf(stderr, "Out of memory\n");
 		exit(1);
 	}
 	args = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), jstr);
 	if (args == 0) {
 		fprintf(stderr, "Out of memory\n");
		exit(1);
 	}
 	(*env)->CallStaticVoidMethod(env, cls, mid, args);
 
 	(*jvm)->DestroyJavaVM(jvm);
 
 	return 0;
}
(This will compile without errors/warnings with Microsoft Visual C++ 6.0)

Now, if I execute the program, it crashes during the execution of pfnCreateVM which is funcpointer to JNI_CreateJavaVM. Here's everything I have found out by debugging the project: At first, I have set a break point at the line res = pfnCreateVM(&jvm, (void**)&env, &vm_args);. After that, I told the debugger to execute the assembler instructions step-by-step. The program crashes here:

0817A03B call esi
0817A03D lea edx,[esp+24h]
0817A041 push 5Ch
0817A043 push edx
0817A044 mov byte ptr [eax],0
0817A047 call esi
0817A049 add esp,18h
0817A04C mov byte ptr [eax],0 <-- Access violation

And then, the callstack looks like this:
JVM! 0817a04c()
baadf00d()

The baadf00d think looks like a joke to me, but maybe the Java Developers want to tell my something with this strange callstack. Maybe the supplied parameters were bad or anything... I don't know what to do at this point, but I hope someone else can help me.

Environment Specs:
OS: Linux (program works)[]:
JDK: Standard Edition 1.4.2_05
Compiler: gcc 3.3.4
Code: see above

Compiling and linking:
gcc -Wall -g -DDEBUG=1\
-I/opt/sun-jdk-1.4.2.04/include\
-I/opt/sun-jdk-1.4.2.04/include/linux\
-L/opt/sun-jdk-1.4.2.04/jre/lib/i386\
-L/opt/sun-jdk-1.4.2.04/jre/lib/i386/server/\
-ljava\
-ljvm\
-lverify\
invoke.c -o prog


[i]OS: Windows (program does not work):

JDK: Standard Edition 1.4.2_05
Compiler: Microsoft Visual C++ 6.0
Code: see above

Compiling and linking:
SET JDK=C:\j2sdk1.4.2_05
cl /TC /Gd -I%JDK%\include -I%JDK%\include\win32 -MD invoke.c -link %JDK%\lib\jvm.lib


I hope you can help me. Thanks.

c ya,
Frank.
Comments
Locked Post
New comments cannot be posted to this locked post.
Post Details
Locked on Nov 1 2004
Added on Sep 13 2004
9 comments
328 views