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

发布于 2024-10-26 04:51:07 字数 614 浏览 6 评论 0原文

我正在使用 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() 的东西?

问候 - 乔金·霍夫

I am wrapping a shared library (written in C) with Java using JNA. The shared library is written internally, but that library uses functions from another external library, which again depends another external library. So the situation is something like this:

ext1 <- ext2 <- internal

I.e. the internal uses external library ext2 which again uses external library ext1. What I have tried is:

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

This approach fails with "UnresolvedException" when loading the library "ext2"; the linker complains about symbols which are indeed present in the library "ext1". So it semmes that the System.loadLibrary() function does not make the symbols from "ext1" globally available? When using the stdlib function dlopen() as:

handle = dlopen( lib_name , RTLD_GLOBAL );

All the symbols found in @lib_name will be available for symbol resolution in subsequent loads; I guess what I would like was something similar for the java variety System.loadLibrary()?

Regards - Joakim Hove

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

蓝海似她心 2024-11-02 04:51:07

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

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

首先,指向 JNA bug 的链接: 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 中适用于我,我想如果设置了正确的库路径,这也应该适用于其他操作系统。

It's an old question, but I've found an acceptable solution, which should also be portable, and I thought I should post an answer. The solution is to use JNA's NativeLibrary#getInstance(), because on Linux this will pass RTLD_GLOBAL to dlopen() (and on Windows this is not needed).

Now, if you are using this library to implement a Java native method, you will also need to call System.load() (or Sysem.loadLibrary()) on the same library, after calling NativeLibrary#getInstance().

First, a link to a JNA bug: JNA-61

A comment in there says that basically one should load dependencies before the actual library to use from within JNA, not the standard Java way. I'll just copy-paste my code, it's a typical scenario:

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

I've written a small library to provide OCR capability to my Java app using Tesseract. Tesseract dependes on Leptonica, so to use my library, I need to load libraries lept and tesseract first. Loading the libraries with the standard means (System.load() and System.loadLibrary()) doesn't do the trick, neither does setting properties jna.library.path or java.library.path. Obviously, JNA likes to load libraries its own way.

This works for me in Linux, I guess if one sets the proper library path, this should work in other OSs as well.

无畏 2024-11-02 04:51:07

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

There is yet another solution for that. You can dlopen directly inside JNI code, like this:

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

...
...

loadLibrary();

This way, you will open library with RTLD_GLOBAL.

You can find detailed description here: http://www.owsiak.org/?p=3640

坚持沉默 2024-11-02 04:51:07

好的;

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

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

它实际上似乎有效:-)

OK;

I have found an acceptable solution in the end, but not without significant amount of hoops. What I do is

  1. Use the normal JNA mechanism to map the dlopen() function from the dynamic linking library (libdl.so).
  2. Use the dlopen() function mapped in with JNA to load external libraries "ext1" and "ext2" with the option RTLD_GLOBAL set.

It actually seems to work :-)

窝囊感情。 2024-11-02 04:51:07

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

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

As described at http://www.owsiak.org/?p=3640, an easy but crude solution on Linux is to use LD_PRELOAD.

If that's not acceptable, then I'd recommend the answer by Oo.oO: dlopen the library with RTLD_GLOBAL within JNI code.

农村范ル 2024-11-02 04:51:07

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

In order to fix your issue you can use this package: https://github.com/victor-paltz/global-load-library. It loads the libraries directly with the RTLD_GLOBAL flag.

Here is an example:

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

It is using the same dlopen() trick as the previous answers, but it is packaged in a standalone code.

黯然#的苍凉 2024-11-02 04:51:07

尝试一下,将此函数添加到您的代码中。在加载 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;
    }
}

Try this, add this function to your code. Call it before you load your dlls. For the parameter, use the location of your dlls.


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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文