繁体   English   中英

Java:加载具有依赖项的共享库

[英]Java: load shared libraries with dependencies

我正在使用 JNA 用 Ja​​va 包装一个共享库(用 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() 的东西?

问候 - 乔金霍夫

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

现在,如果您使用这个库来实现 Java native方法,您还需要在调用NativeLibrary#getInstance()之后在同一个库上调用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,所以要使用我的库,我需要先加载库lepttesseract 使用标准方法(System.load() 和 System.loadLibrary())加载库并不能解决问题,设置属性jna.library.pathjava.library.path也不行。 显然,JNA 喜欢以自己的方式加载库。

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

还有另一种解决方案。 您可以直接在 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

好的;

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

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

它实际上似乎有效:-)

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

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

试试这个,将此功能添加到您的代码中。 在加载 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;
    }
}

为了解决您的问题,您可以使用此包: 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() 技巧,但它打包在独立代码中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM