簡體   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