簡體   English   中英

C++ 中 dlsym() 和 dlopen() 的替代方案

[英]Alternatives to dlsym() and dlopen() in C++

我有一個應用程序,其中一部分使用共享庫。 這些庫在編譯時鏈接。
在運行時,加載程序希望共享對象在LD_LIBRARY_PATH ,如果沒有找到,整個應用程序崩潰並顯示錯誤“無法加載共享庫”。請注意,不能保證客戶端將擁有該庫,在這種情況下我想要應用程序要留下合適的錯誤消息,獨立部分也應該正常工作。

為此,我使用dlsym()dlopen()來使用共享庫中的 API。 問題是如果我在 API 中有很多函數,我必須使用dlsym()和 ptr 單獨訪問它們,在我的情況下這會導致內存損壞和代碼崩潰。

有沒有其他選擇?

您的問題的常見解決方案是聲明一個函數指針表,執行單個 dlsym() 來查找它,然后通過指向該表的指針調用所有其他函數。 示例(未經測試):

// libfoo.h
struct APIs {
   void  (*api1)(void);
   void *(*api2)(int);
   long  (*api3)(int, void *);
};

// libfoo.cc
void fn1(void) { ... }
void *fn2(int) { ... }
long fn3(int, void *) { ... }

APIs api_table = { fn1, fn2, fn3 };


// client.cc
#include "libfoo.h"
...
  void *foo_handle = dlopen("libfoo.so", RTLD_LAZY);
  if (!foo_handle) {
     return false;            // library not present
  }
  APIs *table = dlsym(foo_handle, "api_table");
  table->api1();              // calls fn1
  void *p = table->api2(42);  // calls fn2
  long x = table->api3(1, p); // calls fn3

PS 使用 dlsym 和指針單獨訪問您的 API 函數本身不會導致內存損壞和崩潰。 很可能你只是有錯誤。

編輯:
您可以將這種完全相同的技術用於第 3 方庫。 創建一個libdrmaa_wrapper.so並將api_table放入其中。 將包裝器直接鏈接到libdrmaa.so

在主可執行文件中, dlopen("libdrmaa_wrapper.so", RTLD_NOW) 當(且僅當) libdrmaa.so在運行時存在並提供您在api_table使用的所有 API 函數時,此dlopen才會成功。 如果成功,一個dlsym調用將讓您訪問整個 API。

您可以用另一個應用程序包裝您的應用程序,該應用程序首先檢查所有必需的庫,如果缺少某些內容,它會很好地出錯,但如果一切正常,它就會執行真正的應用程序。

使用以下類型的代碼

Class DynLib
{
    /* All your functions */
    void fun1() {};
    void fun2() {};
    .
    .
    .
}

DynLib* getDynLibPointer()
{
    DynLib* x = new Dynlib;
    return x;
}

使用dlopen()在運行時加載這個庫。 並使用dlsym()並調用返回 DynLib 對象的getDynLibPointer() 從這個對象你可以訪問所有的函數 jst 作為obj.fun1() .....

這當然是早先提出的 C++ 風格的 struct 方法。

您可能正在 Linux 上尋找某種形式的延遲庫加載。 它不是開箱即用的,但您可以通過創建一個小的靜態存根庫來輕松模仿它,該庫將嘗試在第一次調用它的任何函數時dlopen所需的庫(發出診斷消息並在dlopen失敗時終止)然后將所有呼叫轉發給它。

這樣的存根庫可以手工編寫,由項目/庫特定腳本生成或由通用工具Implib.so生成:

$ implib-gen.py libxyz.so
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...

您的問題是未解析符號的解析很早就完成了 - 在 Linux 上我相信數據符號在進程啟動時解析,而函數符號是懶惰地完成的。 因此,根據您未解析的符號以及您正在進行的靜態初始化類型 - 您可能沒有機會進入您的代碼。

我的建議是使用一個包裝應用程序來捕獲返回代碼/錯誤字符串“無法加載共享庫”,然后將其轉換為更有意義的內容。 如果這是通用的,則每次添加新共享庫時都不需要更新。

或者,您可以讓您的包裝器腳本運行ldd ,然后解析輸出, ldd將報告未為您的特定應用程序找到的所有庫。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM