Java:使用依赖项加载共享库

时间:2011-03-24 20:21:29

标签: java shared-libraries jna

我使用JNA使用Java包装共享库(用C语言编写)。共享库是内部编写的,但该库使用来自另一个外部库的函数,这又取决于另一个外部库。所以情况是这样的:

ext1< - ext2< - internal

即。内部使用外部库ext2,它再次使用外部库ext1。我试过的是:

System.loadLibrary("ext1");
System.loadLibrary("ext2");
NativeLIbrary.loadLibrary("internal",xxx.class);  

加载库“ext2”时,此方法失败并显示“UnresolvedException”;链接器抱怨存在于库“ext1”中的符号。所以它认为System.loadLibrary()函数不会使“ext1”中的符号全局可用?使用stdlib函数dlopen()时:

handle = dlopen( lib_name , RTLD_GLOBAL );

@lib_name中的所有符号都可用于后续加载中的符号解析;我想我喜欢的东西类似于java品种System.loadLibrary()?

问候 - Joakim Hove

6 个答案:

答案 0 :(得分:4)

这是一个老问题,但我找到了一个可接受的解决方案,也应该是可移植的,我想我应该发布一个答案。解决方案是使用JNANativeLibrary#getInstance(),因为在Linux上,这会将RTLD_GLOBAL传递给dlopen()(在Windows上不需要这样做)。

现在,如果您使用此库来实现Java native方法,则在调用{{{{}}后,您还需要在同一个库上调用System.load()(或Sysem.loadLibrary()) 1}}。

首先,指向JNA错误的链接:JNA-61

那里的评论说,基本上应该在实际库之前加载依赖项以在JNA中使用,而不是标准的Java方式。我只是复制粘贴我的代码,这是典型的情形:

NativeLibrary#getInstance()

我编写了一个小型库,使用Tesseract为我的Java应用程序提供OCR功能。 Tesseract依赖于Leptonica,因此要使用我的库,我需要首先加载库 lept tesseract 。使用标准方法(System.load()和System.loadLibrary())加载库不起作用,设置属性 jna.library.path java.library也不起作用。 。路径。显然,JNA喜欢以自己的方式加载库。

这对我在Linux中有用,我想如果设置了正确的库路径,这也适用于其他操作系统。

答案 1 :(得分:2)

行;

我最终找到了一个可接受的解决方案,但并非没有大量的箍。我的工作是

  1. 使用普通的JNA机制从动态链接库(libdl.so)映射dlopen()函数。
  2. 使用与JNA映射的dlopen()函数加载外部库“ext1”和“ext2”,并设置选项RTLD_GLOBAL。
  3. 它实际上似乎有效: - )

答案 2 :(得分:2)

还有另一种解决方案。您可以直接在JNI代码中进行dlopen,如下所示:

void loadLibrary() {
  if(handle == NULL) {
    handle = dlopen("libname.so", RTLD_LAZY | RTLD_GLOBAL);
    if (!handle) {
      fprintf(stderr, "%s\n", dlerror());
      exit(EXIT_FAILURE);
    }
  }
}

...
...

loadLibrary();

这样,您将使用RTLD_GLOBAL打开库。

您可以在此处找到详细说明:http://www.owsiak.org/?p=3640

答案 3 :(得分:0)

试试这个,将此功能添加到您的代码中。在加载dll之前调用它。对于参数,请使用dll的位置。


    public boolean addDllLocationToPath(String dllLocation)
    {
        try
        {
            System.setProperty("java.library.path", System.getProperty("java.library.path") + ";" + dllLocation);
            Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
            fieldSysPath.setAccessible(true);
            fieldSysPath.set(null, null);
        }
        catch (Exception e)
        {
            System.err.println("Could not modify path");
            return false;
        }
        return true;
    }
}

答案 4 :(得分:0)

http://www.owsiak.org/?p=3640所述,在Linux上一个简单但粗略的解决方案是使用LD_PRELOAD

如果这是不可接受的,那么我建议Oo.oO回答:在JNI代码中将dlopenRTLD_GLOBAL一起使用。

答案 5 :(得分:0)

为了解决您的问题,您可以使用以下软件包:https://github.com/victor-paltz/global-load-library。它直接使用RTLD_GLOBAL标志加载库。

这里是一个例子:

import com.globalload.LibraryLoaderJNI;

public class HelloWorldJNI {
 
    static {
        // Loaded with RTLD_GLOBAL flag
        try {
            LibraryLoaderJNI.loadLibrary("/path/to/my_native_lib_A");
        } catch (UnsatisfiedLinkError e) {
            System.Println("Couldn't load my_native_lib_A");
            System.Println(e.getMessage());
            e.printStackTrace();
        }

        // Not loaded with RTLD_GLOBAL flag
        try {
            System.load("/path/to/my_native_lib_B");
        } catch (UnsatisfiedLinkError e) {
            System.Println("Couldn't load my_native_lib_B");
            System.Println(e.getMessage());
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        new HelloWorldJNI().sayHello();
    }
 
    private native void sayHello();
}

它使用与以前的答案相同的dlopen()技巧,但打包在独立代码中。