开发者

Loading methods of a specific class from a C++ library using JNI

I have a C++ library xyz. It has many classes like xyzA, xyzB etc. I want to use the method getAge() from the class xyzA which is in the xyz library.

The xyz.so file already exists.

Steps I have followed:

  1. Created a Java class xyz.java

    class xyz {
    
        public native int getAge();
    
        public static void main(String[] args) {
            new xyz().getAge();
        }
        static {
            System.loadLibrary("xyz");
        }
    }
    
  2. Created the header for the Java class.

    /* DO NOT EDIT THIS FILE - it is machine ge开发者_运维问答nerated */
    #include <jni.h>
    /* Header for class xyz */
    
    #ifndef _Included_xyz
    #define _Included_xyz
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     xyz
     * Method:    getAge
     * Signature: ()I
     */
    JNIEXPORT jint JNICALL Java_xyz_getAge
      (JNIEnv *, jobject);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
  3. The cpp wrapper class looks like:

    #include <stdio.h>
    #include "xyz.h"
    #include <jni.h>
    
    JNIEXPORT jint JNICALL Java_xyz_getAge(JNIEnv *, jobject)
    {
        // some code
    }
    
  4. I successfully compile the class as follows:

    gcc -fPIC -shared -l stdc++ -I/grid/0/gs/java/jdk64/current/include -I/grid/0/gs/java/jdk64/current/include/linux xyz.cpp 
    
  5. Then run the Java prog as:

    java -Djava.library.path=/grid/0/tmp/direct/lib xyz
    

    I get the following error:

    Exception in thread "main" java.lang.UnsatisfiedLinkError: xyz.getAge()I
            at xyz.getAge(Native Method)
            at xyz.main(xyz.java:6)
    

It cannot find the method getAge() specific to the class xyzA. How can that method be accessed? Also, is the library getting linked through my wrapper class?

Any pointers would be appreciated.

Thanks.


If you are running on Unix, the shared library has to be named libxyz.so, not xyz.so.


This error normally means that the library has been successfully loaded, but that there is an inconsistency in the signature of the function. Globally, your code looks correct, but we do put an extern "C" in front of JNIEXPORT in our code base. We don't use the generated headers, so the definition of the function is the only place we could specify extern "C". In your case, I think that the compiler is supposed to recognize that the function declared in the header and the one defined in your .cpp are the same, and define the function as extern "C", but having never done it this way, I'm not 100% sure. You can verify by doing something like:

nm -C libxyz.so | egrep getAge

The function should appear as a C function, with a T in the second column; without the -C, it should appear unmangled. Note that the slightest difference in the definition and the declaration will mean that you're defining a different function; I don't see one in the code you've posted, but it's worth double checking.

(I'd also wrap the call to LoadLibrary in a try block, just to be sure.)

EDITED to add some additional information:

I'm not sure when and how Linux links the additional libraries needed; we've always loaded all of our libraries explicitly. You might try adding a call to dlopen in either JNI_OnLoad or the constructor of a static object. (I would recommend this anyway, in order to control the arguments to dlopen. We found that when loading several different .so, RTTI didn't work accross library boundaries if we didn't.) I would expect not doing so to result in a loader error, but it all depends on when Linux tries to load libxyz.so; if it only does so when the JVM calls dlsym, then you'll get the above error. (I think this depends on the arguments the JVM passes to dlopen when you call java.lang.System.LoadLibrary: if it passes RTLD_LAZY or RTLD_NOW. By forcing the load in JNI_OnLoad or the constructor of a static object, you more or less guarantee that loader errors appear as loader errors, and not link errors, later.)


Exported function names in C++ libraries are mangled: The plain function name is decorated with class and namespace names, parameter and return types. For instance, here is one of the methods from a boost library:

        ?estimate_max_state_count@?$perl_matcher@PB_WV?$alloca
tor@U?$sub_match@PB_W@boost@@@std@@U?$regex_traits@_WV?$w32_regex_traits@_W@boos
t@@@boost@@@re_detail@boost@@AAEXPAUrandom_access_iterator_tag@std@@@Z

The format of the name is not standardised and varies from compiler to compiler. The end result is that it is difficult to call an exported C++ member function unless you're writing another module in C++ and compiling it using the same compiler. Trying to call from Java is more trouble than it is worth.

Instead, if the C++ library is yours, export helper functions using the extern "C" calling convention:

Foo * foo;

extern "C"
{
  void myfunc()
  {
    foo->bar(); // Call C++ function within a C-style function.
  }
}

If the library is third-party, you should wrap it with your own library which exposes necessary functions using the C-style export, as per above.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜