通过JNI从其他共享库加载依赖的.so

时间:2019-01-25 21:50:01

标签: linux java-native-interface shared-libraries dynamic-loading java.library.path

我想从其他libb.so中调用liba.so的某些功能。 libb.so是动态的,因此该库实现了我在JNI中使用System.loadLibrary(“ b”)加载的本机方法。首先,我使用java.library.path为jni中的.so设置了完整路径,但是当我运行Java程序时,在加载共享库libb.so时, 它给出以下错误:

Exception in thread "main" java.lang.UnsatisfiedLinkError: x/y/z/libb.so: liba.so: cannot open shared object file: No such file or directory.
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
        at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1857)
        at java.lang.Runtime.loadLibrary0(Runtime.java:870)
        at java.lang.System.loadLibrary(System.java:1122)
  1. 首先,我在使用编译过程中已将共享库liba.so与其他共享库libb.so链接在一起

    g ++ -shared -o libb.so -fPIC b.cc -L / x / y / z -la

    (例如liba.so的完整路径为/ x / y / z)

  2. 在JNI中,我实用地设置了java.library.path,其中包含liba.so,libb.so的完整路径,然后我习惯将JNI本机库libb.so加载为

(假设libb.so的完整路径是a / b / c, liba.so的完整路径是x / y / z。)

String libpath = "x/y/z" + "a/b/c";
System.setProperty( "java.library.path", libpath);
    try {
        Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
        fieldSysPath.setAccessible( true );
        fieldSysPath.set( null, null );
       }
    catch (Exception e)
      {
        System.out.println(e);
      }

//在这里,我能够打印/获取正确的java.library.path。 (两个共享库的路径都正确保存到java.library.path中)

static {
    System.loadLibrary("b");
}

当我的Java程序加载此静态块动态库时,会出现以下错误:

线程“ main”中的异常java.lang.UnsatisfiedLinkError:x / y / z / liba.so:libb.so:无法打开共享对象文件:没有这样的文件或目录。

注意:当我在LD_LIBRARY_PATH中设置liba.so的路径时,此工作正常,没有任何错误。但我不想在SHELL中设置LD_LIBRARY_PATH。我只想在程序本身中设置java.library.path或LD_LIBRARY_PATH。

预先感谢!

2 个答案:

答案 0 :(得分:0)

从本机库加载从属库是纯粹的OS操作,因此不在Java控制范围之内。不幸的是,您无法使用Java提供的工具解决此问题。

如您所述,在Linux库中,搜索路径可以由LD_LIBRARY_PATH环境变量设置,但是无法修改已经运行的JVM进程的环境变量(请参阅here并建议使用{{ 3}}确实不适用于非调试情况。

您描述的问题是Windows hack using gdb的JDK错误,带有注释我们真的无能为力

但是,对于您的情况,应该有一个简单的解决方案:

在加载 libb.so 之前先显式加载依赖库 liba.so

    static {
        System.loadLibrary("a");
        System.loadLibrary("b");
    }

对于具有多个依赖库和更深层次的依赖层次结构的情况,请以相反的顺序手动加载所有依赖库。

答案 1 :(得分:0)

假设您控制共享对象liba.solibb.so的位置,请用embedded RPATH set so that it can locate liba.so编译libb.so共享对象。

如果两个共享对象位于shame目录中,则可以使用:

g++ -shared -o libb.so -fPIC b.cc -L/x/y/z -la -Wl,-rpath,'$ORIGIN/.'

$ORIGIN本身可以工作,但是我想使用$ORIGIN/.清楚地显示结果是一个目录。如果您有公用的binlib目录树,始终将$ORIGIN/../lib用于可执行文件和共享对象也是IMO的一个好主意。)

-Wl,-rpath,'$ORIGIN/.将在RPATH共享对象中设置libb.so,以便运行时链接程序在同一目录libb.so中搜索liba.so