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

问题描述 投票:7回答:5

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

ext1 < - ext2 < - 内部

即内部使用外部库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

java shared-libraries jna
5个回答
4
投票

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

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

首先,链接到JNA错误:JNA-61

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

String libPath =
        "/path/to/my/lib:" + // My library file
        "/usr/local/lib:" +  // Libraries lept and tesseract
        System.getProperty("java.library.path");

System.setProperty("jna.library.path", libPath);

NativeLibrary.getInstance("lept");
NativeLibrary.getInstance("tesseract");
OcrTesseractInterf ocrInstance = (OcrTesseractInterf)
        Native.loadLibrary(OcrTesseractInterf.JNA_LIBRARY_NAME, OcrTesseractInterf.class);

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

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


2
投票

好;

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

  1. 使用常规JNA机制从动态链接库(libdl.so)映射dlopen()函数。
  2. 使用与JNA映射的dlopen()函数加载外部库“ext1”和“ext2”,并设置选项RTLD_GLOBAL。

它实际上似乎工作:-)


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


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


0
投票

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

如果这是不可接受的,那么我建议Oo.oO的答案:dlopen在JNI代码中使用RTLD_GLOBAL的库。

© www.soinside.com 2019 - 2024. All rights reserved.