[英]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.