Java System.loadLibrary 不起作用。链中第二个库的 UnsatisfiedLinkError

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16215197/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 05:49:48  来源:igfitidea点击:

System.loadLibrary does not work. UnsatisfiedLinkError for the second lib in chain

javajava-native-interfaceloadlibraryunsatisfiedlinkerrorjava.library.path

提问by degratnik

I have java program Client.class that uses cpp shared library libclient.so via JNI. libclient.so is built as shared and uses cpp shared library libhttp.so.

我有 java 程序 Client.class,它通过 JNI 使用 cpp 共享库 libclient.so。libclient.so 构建为共享并使用 cpp 共享库 libhttp.so。

libclient.so and libhttp.so are placed in folder /home/client/lib64
Client.class is placed in /home/client/bin

libclient.so 和 libhttp.so 放在文件夹/home/client/lib64
Client.class 放在/home/client/bin

Client can load library with

客户端可以加载库

  1. System.load and environment variable LD_LIBRARY_PATH
  2. System.loadLibrary and -Djava.library.path
  1. System.load 和环境变量 LD_LIBRARY_PATH
  2. System.loadLibrary 和 -Djava.library.path

The first way works fine.

第一种方式工作正常。

export LD_LIBRARY_PATH = /home/client/lib64

export LD_LIBRARY_PATH = /home/client/lib64

java -classpath ./bin Client

java -classpath ./bin 客户端

The secon way fails.

第二种方式失败。

java -classpath ./bin -Djava.library.path=./../lib64 Client

java -classpath ./bin -Djava.library.path=./../lib64 Client

java.lang.UnsatisfiedLinkError: /home/client/lib64/libclient.so: libhttp.so: cannot open shared object file: No such file or directory

When I put libhttp.so into /usr/lib64 the second way works fine.

当我将 libhttp.so 放入 /usr/lib64 时,第二种方式工作正常。

Why libclient.so is looking for libhttp.so in /usr/lib64 if I use System.loadLibrary? How can I fix it without coping libhttp.so into /usr/lib64?

如果我使用 System.loadLibrary,为什么 libclient.so 会在 /usr/lib64 中寻找 libhttp.so?如何在不将 libhttp.so 处理到 /usr/lib64 的情况下修复它?

My loading code:

我的加载代码:

    //Try load from -Djava.library.path        
    boolean found = false;
    String lib = "client";
    try {
       System.loadLibrary(lib);
       found = true;
    } catch (UnsatisfiedLinkError e) {
       e.printStackTrace();
    }
    //Try load from LD_LIBRARY_PATH
    if (!found) {
       lib = "libclient.so";
       String ld_lib_path = System.getenv("LD_LIBRARY_PATH");
       String[] paths = ld_lib_path.split(":");
       for(int i=0; i<paths.length; i++) {
          String p = paths[i];
          File x = new File(p, lib);
          if (x.exists()) {
             System.load(x.getAbsolutePath());
             found = true;
             break;
          }
       }
    }

Additional information.

附加信息。

If I test libclient.so with ldd then I see: libhttp.so => not found If I set export LD_LIBRARY_PATH = /home/client/lib64 then I see: libhttp.so => /home/client/lib64/libhttp.so

如果我用 ldd 测试 libclient.so 然后我看到: libhttp.so => not found 如果我设置 export LD_LIBRARY_PATH = /home/client/lib64 然后我看到: libhttp.so => /home/client/lib64/libhttp.so

采纳答案by Philipp Wendler

The reason for this is that libclient.so is loaded from your JVM, which looks in java.library.path. However, when libclient.so tries to load libhttp.so, it knows nothing about Java and just uses the regular Linux way of loading shared libraries (the dynamic linker ld.so), which looks in LD_LIBRARY_PATHand some common directories like /usr/lib64.

这样做的原因是 libclient.so 是从您的 JVM 加载的,它在java.library.path. 然而,当 libclient.so 尝试加载 libhttp.so 时,它对 Java 一无所知,只是使用加载共享库的常规 Linux 方式(动态链接器ld.so),它在LD_LIBRARY_PATH一些常见目录中查找,如/usr/lib64.

I would probably go with using LD_LIBRARY_PATHset from a start script of your Java application. If you don't want to use a start script, you could in theory set LD_LIBRARY_PATHfrom within the process itself. However, Java does not allow to do this (there is only System.getenv(), not System.setenv()), so you would need to write a small C library that is called from Java and calls putenv()setting LD_LIBRARY_PATH.

我可能会LD_LIBRARY_PATH从 Java 应用程序的启动脚本中使用set。如果您不想使用启动脚本,理论上您可以LD_LIBRARY_PATH从流程本身进行设置。但是,Java 不允许这样做(只有System.getenv(),没有System.setenv()),因此您需要编写一个从 Java 调用并调用putenv()setting的小型 C 库LD_LIBRARY_PATH

If you build libclient.soitself, you can use the -rpathlinker flag to specify a path where the dynamic linker should look for further required libraries. Be careful if you specify a relative path here, it will interpreted as relative to the current working directory of the running application, not relative to the location of libclient.so. To achieve this, you need to use $ORIGINas argument for -rpathand be careful that your shell does not expand this.

如果您libclient.so自行构建,则可以使用-rpath链接器标志指定动态链接器应在其中查找更多所需库的路径。如果您在此处指定相对路径,请小心,它将被解释为相对于正在运行的应用程序的当前工作目录,而不是相对于libclient.so. 要实现这一点,您需要使用$ORIGINas 参数-rpath并注意您的 shell 不会扩展它。

So, if you want to have libclient.soand libhttp.soin the same directory, you need to use

所以,如果你想拥有libclient.solibhttp.so在同一个目录中,你需要使用

-rpath '$ORIGIN'

as argument to the linker when building libclient.so. If you do not call the linker directly but let your compiler call it, you need to add the following to your compiler's command line:

作为构建时链接器的参数libclient.so。如果您不直接调用链接器而是让编译器调用它,则需要将以下内容添加到编译器的命令行中:

-Wl,-rpath,'$ORIGIN'

More information about this can be found in the man page for ld.so.

可以在 的手册页中ld.so找到有关这方面的更多信息。

回答by degratnik

I have no good answer for this question.

这个问题我没有很好的答案。

But I found several good ways.

但是我找到了几个好方法。

  1. Put libhttp.so into shared location for libraries, for example /usr/lib64.
  2. Put path to libhttp.so into LD_LIBRARY_PATH.
  3. Build libclient.so with libhttp.so inside.
  4. Use -rpath during building of libclient.so.
  1. 将 libhttp.so 放入库的共享位置,例如 /usr/lib64。
  2. 将 libhttp.so 的路径放入 LD_LIBRARY_PATH。
  3. 使用 libhttp.so 构建 libclient.so。
  4. 在构建 libclient.so 期间使用 -rpath。

回答by Alexander C.

For correct lookup of library (from java.library.path) for different OS's must have different names:

为了正确查找不同操作系统的库(来自java.library.path)必须具有不同的名称:

  • Linux: libhttp.so
  • Windows: http.dll
  • Linux:libhttp.so
  • Windows:http.dll

Than you can call from Java:

比您可以从 Java 调用:

System.loadLibrary( "http" );