2

我想从其他 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程序加载这个静态块的动态库时,它给出了以下错误:

线程“主”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 。

提前致谢!

4

2 回答 2

1

假设您控制共享对象的位置,liba.so并使用嵌入libb.so集编译您的libb.so共享对象,以便它可以定位RPATHliba.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/.设置一个,以便运行时链接器搜索位于同一目录中的。RPATHlibb.solibb.soliba.so

于 2019-11-13T17:13:40.663 回答
1

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

正如您所指出的,在 Linux 库中,搜索路径可以由 LD_LIBRARY_PATH 环境变量设置,但无法修改已经运行的 JVM 进程的环境变量(请参阅此处,其中包含关于使用 gdb 的 hack的建议,对于非-调试案例)。

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

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

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

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

在多个依赖库和更深的依赖层次结构的情况下,手动将所有依赖库以相反的顺序一个接一个地加载。

于 2019-11-12T18:49:59.483 回答