Search code examples
c++xcodejava-native-interface

Xcode gives Mach-O linker error: "_JNI_CreateJavaVM", referenced from: _main in main.o


I get this error from testing JNI:

Undefined symbols for architecture x86_64:
  "_JNI_CreateJavaVM", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Here is c++ code:

    #include <jni.h>
    #include <iostream>
    using namespace std;
    int main()
    {
        int res;
        JavaVMInitArgs vm_args;
        JavaVMOption options[3];
        JavaVM *jvm;
        JNIEnv *env;
        jmethodID mid;

        options[0].optionString = "-Djava.compiler=NONE";

        options[1].optionString = "-Djava.class.path = /Users/stephen/course/test/Test";
        options[2].optionString = "-verbose:NONE";

        vm_args.version = JNI_VERSION_1_8;
        vm_args.nOptions = 3;
        vm_args.options = options;
        vm_args.ignoreUnrecognized = JNI_TRUE;

       res = JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args);
       if(res == JNI_ERR){
           cout << "Error invoking the JVM";
           return 1;
       }
       cout <<"create JVM successfully!"<<endl;
       jclass cls = env->FindClass("/Users/stephen/course/Qt-project/test/Test");
       if(cls != 0){
           cout<<"find class successfully!" << endl;
       }
        mid = env->GetMethodID(cls,"sayHello","stephen");
        if(mid != 0){
            cout<<"Invoke method successfully!" << endl;
        }
        jvm->DestroyJavaVM();
        return 0;

    }

Here is java code:

public class Test
{
    public static void sayHello(String s){
        System.out.print("hello I am" + s + "\n");
    }

}

I add the include path of " jdk/include; jdk/include/darwin" the project, also I add lib path of " jdk/jre/lib/server" to the project to get the libjvm.dylib. The c++ standard library of my project is libstdc++(gnu c++ standard library. But I can't solve this problem as expected.


Solution

  • Take a look here for a sample code where JVM library is linked with your project:

    https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo028

    Take a look at Makefile. Especially, here:

    main: recipeNo028_main.o
        ld -o lib/recipeNo028_main -L${JAVA_HOME}/jre/lib/server/ \
        -ljvm \
            $(MAC_OS_FLAGS) \
        lib/recipeNo028_main.o
    

    where jvm lib is linked with the code, and here:

    CC=llvm-gcc
    MAC_OS_FLAGS=-rpath ${JAVA_HOME}/jre/lib/server -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -demangle -dynamic -arch x86_64 -macosx_version_min 10.12.0 -lSystem
    

    where all required libs are added to your code as well. It should work. Try to compile sample code. You can find more samples here: http://jnicookbook.owsiak.org

    Update

    How to use arbitrary JDK version for compilation.

    First, take a look at all installations you have

    /usr/libexec/java_home -V
    

    This will produce something like this

    /usr/libexec/java_home -V
    Matching Java Virtual Machines (4):
        9, x86_64:  "Java SE 9" /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home
        1.8.0_144, x86_64:  "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home
        1.8.0_111, x86_64:  "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home
        1.7.0_80, x86_64:   "Java SE 7" /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home
    

    Then, before running make, simply set JAVA_HOME to whatever you like

    export JAVA_HOME=$(/usr/libexec/java_home -v 9)
    

    Now, your code will use version that you have chosen.