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

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

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

客户端可以加载库

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

第一种方式很好。

export LD_LIBRARY_PATH = /home/client/lib64

java -classpath ./bin客户端

secon方式失败了。

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 

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

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

我的加载代码:

  //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; } } } 

附加信息。

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

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

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

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

因此,如果您想在同一目录中安装libclient.solibhttp.so ,则需要使用

 -rpath '$ORIGIN' 

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

 -Wl,-rpath,'$ORIGIN' 

有关这方面的更多信息,请参见ld.so的手册页 。

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

但我找到了几个好方法。

  1. 将libhttp.so放入库的共享位置,例如/ usr / lib64。
  2. 将libhttp.so的路径放入LD_LIBRARY_PATH。
  3. 使用libhttp.so构建libclient.so。
  4. 在构建libclient.so时使用-rpath。

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

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

你可以从Java调用:

 System.loadLibrary( "http" );