NateW
NateW

Reputation: 968

Segfault on JNI_CreateJavaVM

I am trying for the first time to load a JVM from C++ via JNI but I can't seem to get it to work. I get a segfault when I call JNI_CreateJavaVM.

The code is pretty straight forward (copied mostly from an online example):

#include<stdio.h>
#include<jni.h>

using namespace std;

int main(int argc, char** argv) {

    printf("Initializing JVM\n");
    JavaVM *jvm;
    JNIEnv *env;

    printf("Setting up args\n";
    JavaVMInitArgs vm_args;
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=.";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;

    printf("Attempting to create JVM\n");
    jint rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

    if(rc != JNI_OK) {
        printf("didn't work :(\n");
    }
    else {
        printf("JVM load succeeded!\n");
        jint ver = env->GetVersion();
        printf("Version: %i.%i\n", (ver>>16)&0x0f, ver&0x0f);

        printf("Cleaning up\n");
        delete options;
        jvm->DestroyJavaVM();
    }
    printf("Done\n");
}

My LD_LIBRARY_PATH contains /usr/java/jdk1.6.0_45/jre/lib/amd64/server which is the path containing a libjvm.so library. There are a few other libjvm.so libraries on my system, but most of them are for java 1.4. There is only one other jdk 1.6 libjvm.so and I tried using it as well with the same results.

I compile with:

g++ -g -c src/jniExpCppPart.cpp -I/usr/java/jdk1.6.0_45/include -I/usr/java/jdk1.6.0_45/include/linux -o obj/jniExpCppPart.o
g++ obj/jniExpCppPart.o -L/usr/java/jdk1.6.0_45/jre/lib/amd64/server -ljvm -o exe/jniExp

And when I run it within gdb, I get the first three print statements, followed by:

Program received signal SIGSEGV, Segmentation fault
0x0000003249479e27 in strncmp () from /lib64/libc.so.6
(gdb) bt
#0 0x0000003249479e27 in strncmp () from /lib64/libc.so.6
#1 0x00002aaaaacd8c10 in Arguments::process_sun_java_launcher_properties(JavaVMInitArgs*) () from /usr/java/jdk1.6.0_45/jre/lib/amd64/server/libjvm.so
#2 0x00002aaaab2cfe7d in Thread::create_vm(JavaVMInitArgs*, bool*) () from /usr/java/jdk1.6.0_45/jre/lib/amd64/server/libjvm.so
#3 0x00002aaaaafcc800 in JNI_CreateJavaVM () from /usr/java/jdk1.6.0_45/jre/lib/amd64/server/libjvm.so
#4 0x0000000000400761 in main(argc=1, argv=0x7fffffffe568) at src/jni/ExpCppPart.cpp:22

My guess is that the issue has more to do with how my environment is set up or how I built the executable rather than the code. Its been a few years since I really dealt with linking shared libraries, so its definitely possible that I messed something up.

Any ideas what I might be doing wrong?

Update I tried loading the library using dlopen instead (because I saw it in some Linux code that uses JNI). It didn't make difference, but I thought I'd include it here to see if it gives anyone a hint of what I may be doing wrong.

Once again I'm copying this by hand from a system that isn't connected to the internet, so there may be some typos.

#include<stdio.h>
#include<jni.h>
#include<dlfcn.h>

using namespace std;

//Create type for pointer to the JNI_CreateJavaVM function
typedef jint (*CreateJvmFuncPtr) (JavaVM**, void**, JavaVMInitArgs*);

//New method returns pointer to the JNI_CreateJavaVM function
CreateJvmFuncPtr findCreateJvm() {
    CreateJavaFuncPtr createJvm = NULL;

    void* jvmLib = dlopen("libjvm.so", RTLD_LAZY); //Get handle to jvm shared library
    char* error = dlerror(); //Check for errors on dlopen

    if(jvmLib = NULL || error != NULL) {
        printf("FailedToLoadJVM\n");
    }

    //Load pointer to the function within the shared library
    createJvm = (CreateJvmFuncPtr) dlsym(jvmLib, "JNI_CreateJavaVM");

    error = dlerror();
    if(error != NULL) {
        printf("Success\n");
    }

    return createJVM;
}

int main(int argc, char** argv) {

    printf("Initializing JVM\n");
    JavaVM *jvm;
    JNIEnv *env;

    printf("Setting up args\n";
    JavaVMInitArgs vm_args;
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=.";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;

    printf("Attempting to create JVM\n");
    //Old code: jint rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    //New code:
    CreateJvmFuncPtr createJVM = findCreateJvm();
    printf("findCreateJVM() returned 0x%x\n", createJVM);

    jint rc = createJVM(&jvm, (void**)&env, &vm_args);
    //End new code

    if(rc != JNI_OK) {
        printf("didn't work :(\n");
    }
    else {
        printf("JVM load succeeded!\n");
        jint ver = env->GetVersion();
        printf("Version: %i.%i\n", (ver>>16)&0x0f, ver&0x0f);

        printf("Cleaning up\n");
        delete options;
        jvm->DestroyJavaVM();
    }
    printf("Done\n");
}

The output (similar to before) shows that everything is successful until it throws a segfault from within the JNI_CreateJavaVM method.

Initializing JVM
Setting up args
Attempting to create JVM
Success!
findCreateJVM() returned 0xc1e70780
Segmentation fault

So it seems that at the very least the program can find the library/function just fine. But something goes wrong when it's called. To me this indicates that it could either be just a simple API misunderstanding (I'm passing it something other than what I should be) or there's something weird with the shared library. Could this be caused perhaps if the shared library was compiled for an architecture/word size different than my program was compiled for? If so, how can I check the target architecture for both my program and the library?

Upvotes: 2

Views: 2311

Answers (2)

NateW
NateW

Reputation: 968

Well this is embarrassing...

I finally found out my problem and there's no way anyone on stack overflow could have fixed it because the code I posted in the question is actually correct, but it is not the same code as what I was running.

As I mentioned, I am running this code on a standalone system without internet access, so I had to copy the code by hand. In copying it, I corrected my mistake.

The code that is on my standalone system, includes the following lines:

JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=.";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = false;

Which you'll notice is not the same as the code I posted in the question. Because it is missing the line:

vm_args.options = options;

So of course it would cause a segfault because vm_args.options was never set to anything.

Upvotes: 1

Anya Shenanigans
Anya Shenanigans

Reputation: 94654

The bug you're getting is on this line:

jint rc = JNI_CreateJavaVM(&jvm, (void**)&&env, &vm_args);

You're asking for the address of the address of a pointer to the env; whereas if you look at the reference example source:

#include <jni.h>       /* where everything is defined */
...
JavaVM *jvm;       /* denotes a Java VM */
JNIEnv *env;       //<!-- same as yours
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=/usr/lib/java";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
/* load and initialize a Java VM, return a JNI interface
 * pointer in env */
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); // <!-- NOT the same!
delete options;
/* invoke the Main.test method using the JNI */
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
env->CallStaticVoidMethod(cls, mid, 100);
/* We are done. */
jvm->DestroyJavaVM();

You should change the code to read:

jint rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

Upvotes: 0

Related Questions