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

时间:2013-04-25 12:48:52

标签: java java-native-interface loadlibrary unsatisfiedlinkerror java.library.path

我有java程序Client.class,它通过JNI使用cpp共享库libclient.so。 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
  3. 第一种方式正常。

    export LD_LIBRARY_PATH = /home/client/lib64

    java -classpath ./bin Client

    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 =&gt;未找到 如果我设置export LD_LIBRARY_PATH = / home / client / lib64,那么我看到:libhttp.so =&gt; /home/client/lib64/libhttp.so

3 个答案:

答案 0 :(得分:10)

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

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

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

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

-rpath '$ORIGIN'

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

-Wl,-rpath,'$ORIGIN'

有关此内容的更多信息,请参阅man page for ld.so

答案 1 :(得分:2)

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

但我发现了几个好方法。

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

答案 2 :(得分:0)

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

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

你可以从Java打电话:

System.loadLibrary( "http" );