Java:加载具有依赖项的共享库
我正在使用 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() 的东西?
问候 - 乔金·霍夫
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这是一个老问题,但我找到了一个可以接受的解决方案,它也应该是可移植的,我想我应该发布一个答案。解决方案是使用 JNA 的
NativeLibrary#getInstance()
,因为在 Linux 上这会通过RTLD_GLOBAL
到dlopen()
(在 Windows 上不需要)。现在,如果您使用此库来实现 Java
native
方法,您还需要调用System.load()
(或Sysem.loadLibrary()
)在同一个库上,调用NativeLibrary#getInstance()
后。首先,指向 JNA bug 的链接: JNA-61
那里的评论基本上说,应该在实际库之前加载依赖项,以便从 JNA 中使用,而不是标准的 Java 方式。我将复制粘贴我的代码,这是一种典型的场景:
我编写了一个小型库使用 Tesseract 为我的 Java 应用程序提供 OCR 功能。 Tesseract 依赖于 Leptonica,因此要使用我的库,我需要首先加载库 lept 和 tesseract。使用标准方法(System.load() 和 System.loadLibrary())加载库并不能解决问题,设置属性 jna.library.path 或 java.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 passRTLD_GLOBAL
todlopen()
(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 callSystem.load()
(orSysem.loadLibrary()
) on the same library, after callingNativeLibrary#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:
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.
还有另一种解决方案。您可以直接在 JNI 代码中 dlopen,如下所示:
这样,您将使用 RTLD_GLOBAL 打开库。
您可以在这里找到详细说明:http://www.owsiak.org/?p=3640
There is yet another solution for that. You can dlopen directly inside JNI code, like this:
This way, you will open library with RTLD_GLOBAL.
You can find detailed description here: http://www.owsiak.org/?p=3640
好的;
我最终找到了一个可以接受的解决方案,但并非没有大量的限制。我所做的是
它实际上似乎有效:-)
OK;
I have found an acceptable solution in the end, but not without significant amount of hoops. What I do is
It actually seems to work :-)
如 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 withRTLD_GLOBAL
within JNI code.为了解决您的问题,您可以使用此软件包: https://github.com/ victor-paltz/global-load-library。它使用 RTLD_GLOBAL 标志直接加载库。
这是一个示例:
它使用与前面的答案相同的 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:
It is using the same dlopen() trick as the previous answers, but it is packaged in a standalone code.
尝试一下,将此函数添加到您的代码中。在加载 dll 之前调用它。对于参数,请使用 dll 的位置。
Try this, add this function to your code. Call it before you load your dlls. For the parameter, use the location of your dlls.