2016-06-01 8 views
0

Ich versuche zum ersten Mal, eine JVM von C++ über JNI zu laden, aber ich kann nicht scheinen, es zu funktionieren. Ich erhalte einen segfault, wenn ich JNI_CreateJavaVM aufruft.Segfault auf JNI_CreateJavaVM

Der Code ist ziemlich geradlinig (meist aus einem Online-Beispiel kopiert):

#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"); 
} 

Mein LD_LIBRARY_PATH /usr/java/jdk1.6.0_45/jre/lib/amd64/server enthält, welche der Pfad enthält eine Bibliothek libjvm.so. Es gibt ein paar andere libjvm.so Bibliotheken auf meinem System, aber die meisten von ihnen sind für Java 1.4. Es gibt nur noch eine andere jdk 1.6 libjvm.so und ich habe es auch mit den gleichen Ergebnissen versucht.

ich kompilieren mit:

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 

Und wenn ich es innerhalb gdb laufen lasse, erhalte ich die ersten drei Druckanweisungen, gefolgt von:

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 

Meine Vermutung ist, dass das Problem mehr hat tun, wie meine Umgebung eingerichtet ist oder wie ich die ausführbare Datei anstelle des Codes erstellte. Es ist ein paar Jahre her, dass ich mich wirklich damit beschäftigt habe, gemeinsame Bibliotheken zu verlinken, also ist es definitiv möglich, dass ich etwas vermasselt habe.

Irgendwelche Ideen, was ich falsch machen könnte?

Update Ich versuchte, die Bibliothek mit dlopen stattdessen zu laden (weil ich es in einigen Linux-Code sah, der JNI verwendet). Es machte keinen Unterschied, aber ich dachte, ich würde es hier einbeziehen, um zu sehen, ob es jemanden einen Hinweis darauf gibt, was ich falsch machen könnte.

Noch einmal kopiere ich das von Hand von einem System, das nicht mit dem Internet verbunden ist, so dass es einige Tippfehler geben kann.

#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"); 
} 

Der Ausgang (ähnlich wie zuvor) zeigt, dass alles, was erfolgreich ist, bis er eine segfault von innerhalb der JNI_CreateJavaVM Methode löst.

So scheint es, dass zumindest das Programm die Bibliothek/Funktion gut finden kann. Aber etwas schief geht, wenn es heißt. Für mich deutet dies darauf hin, dass es sich entweder nur um ein einfaches API-Missverständnis handeln könnte (ich übergebe es etwas anderes als das, was ich sein sollte) oder dass es etwas Seltsames mit der gemeinsam genutzten Bibliothek gibt. Könnte dies möglicherweise verursacht werden, wenn die gemeinsam genutzte Bibliothek für eine Architektur/Wortgröße kompiliert wurde, für die mein Programm nicht kompiliert wurde? Wenn ja, wie kann ich die Zielarchitektur sowohl für mein Programm als auch für die Bibliothek überprüfen?

+0

mit segfault Ich habe das Gefühl, der Code, den Sie verwenden, hat einen logischen Fehler. da ein segfault versucht, auf Speicher zuzugreifen, der nicht dort ist, wie wenn Sie über die Grenzen eines Arrays gehen. Es ist auch sinnvoll, die ersten drei Druckanweisungen zu erhalten, da die Zeile 'jint rc = JNI_CreateJavaVM (& jvm, (void **) && env, & vm_args) fehlschlägt. ' – jgr208

+0

Welchen Befehl verwenden Sie, um das Programm auszuführen? kompiliere auch mit -Wall und -Werror – jgr208

+0

Ich laufe nur mit ./exe/jniExp Oder mit gdb, gdb exe/jniExp gefolgt von run – NateW

Antwort

0

Gut, das ist peinlich ...

Ich habe endlich mein Problem herausgefunden und es gibt keine Möglichkeit, dass irgendjemand auf dem Stack-Überlauf es hätte beheben können, weil der Code, den ich in der Frage geschrieben habe, tatsächlich korrekt ist, aber es ist nicht derselbe Code wie das, was ich gerade ausgeführt habe.

Wie ich bereits erwähnt habe, verwende ich diesen Code auf einem Standalone-System ohne Internetzugang, also musste ich den Code von Hand kopieren. Indem ich es kopiert habe, habe ich meinen Fehler korrigiert.

Der Code, der auf meinem Standalone-System ist, enthält die folgenden Zeilen:

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; 

Welche Sie ist nicht das gleiche wie der Code feststellen, werde ich in der Frage gepostet. Weil es die Zeile fehlt:

vm_args.options = options; 

So wäre es natürlich eine segfault verursachen, weil vm_args.options nie etwas gesetzt wurde.

0

Der Fehler Sie ist auf dieser Linie bekommen:

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

Sie sind für die Adresse der Adresse eines Zeigers auf die env zu fragen; während, wenn Sie bei der Referenz aussehen 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(); 

sollten Sie den Code ändern zu lesen:

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

Ups, das war eigentlich ein Tippfehler. Die Maschine, auf der ich arbeite, hat keinen Internetzugang, also musste ich den Code manuell kopieren und ich habe es vermasselt. Auf der Maschine, auf der ich das eigentlich ausführen möchte, gibt es nur das eine '&'. Ich werde es in meinem Post reparieren. Du hast absolut Recht, basierend auf dem was ich gepostet habe. – NateW

+0

Wenn es nur ein Tippfehler ist, dann sollte der Code wie er sollte funktionieren. Es scheitert weder auf dem Mac noch auf dem Linux, auf dem ich es ausprobiert habe - beide laufen allerdings mit Java 8. – Petesh

+0

Hmm ... Nun, ich denke, wenn es mit Java 8 funktioniert, dann würde es mit Java 6 funktionieren (da das Beispiel, das ich kopiert habe, für Java 6 erstellt wurde). Ich denke, es muss etwas über die Art und Weise sein, wie meine Umgebung aufgebaut ist. Vielen Dank, dass Sie es sich angesehen haben. – NateW