[英]Java: load shared libraries with dependencies
我正在使用 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() 的东西?
问候 - 乔金霍夫
这是一个老问题,但我找到了一个可以接受的解决方案,它也应该是可移植的,我想我应该发布一个答案。 解决方案是使用JNA的NativeLibrary#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,所以要使用我的库,我需要先加载库lept和tesseract 。 使用标准方法(System.load() 和 System.loadLibrary())加载库并不能解决问题,设置属性jna.library.path或java.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
好的;
我最终找到了一个可以接受的解决方案,但并非没有大量的箍。 我做的是
它实际上似乎有效:-)
如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.