【问题标题】:Load dependent .so from other shared library via JNI通过 JNI 从其他共享库加载依赖的 .so
【发布时间】:2019-01-25 21:50:00
【问题描述】:

我想从其他 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。

提前致谢!

【问题讨论】:

  • @Oo.oO,当我在 LD_LIBRARY_PATH 中设置 liba.so 的路径时,它就可以工作了。但是当我尝试通过程序在 java.library.path 中设置依赖库(liba.so)路径时,它会出错。
  • 它不适用于 java.library.path。一旦进入 liba.so 你依赖于系统。
  • @Oo.oO,即使我在程序内部的 LD_LIBRARY_PATH 中设置 liba.so 的路径,它也不起作用。我认为一旦程序已经启动,您就无法更改程序内部正在运行的进程的 LD_LIBRARY_PATH 设置。我搜索了各种讨论,如果不在程序启动前在 SHELL 中设置 LD_LIBRARY_PATH,我无法找到克服上述错误的方法。

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


【解决方案1】:

从本机库加载依赖库是纯粹的操作系统操作,因此不受 Java 控制。遗憾的是,您无法使用 Java 提供的工具解决此问题。

正如您所指出的,在 Linux 中,库搜索路径可以通过 LD_LIBRARY_PATH 环境变量设置,但无法修改已经运行的 JVM 进程的环境变量(请参阅 here 并建议使用 hack using gdb not真正适用于非调试情况)。

您描述的问题被归档为 Windows https://bugs.openjdk.java.net/browse/JDK-8213772 的 JDK 错误,并带有注释我们真的无能为力

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

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

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

在依赖库多且依赖层次更深的情况下,手动将所有依赖库逆序一一加载。

【讨论】:

    【解决方案2】:

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

    如果这两个共享对象位于羞耻目录中,这将起作用:

    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/. 将在libb.so 共享对象中设置一个RPATH,以便运行时链接器在libb.so 所在的同一目录中搜索liba.so

    【讨论】:

      猜你喜欢
      • 2012-06-19
      • 2014-03-22
      • 1970-01-01
      • 1970-01-01
      • 2016-06-06
      • 2017-02-23
      • 2011-01-01
      • 2019-09-07
      • 1970-01-01
      相关资源
      最近更新 更多