,我發現自己與移植C++的GNU/Linux的應用程序在Windows的不值得羨慕的任務。這個應用程序所做的一件事就是搜索特定路徑上的共享庫,然後使用posix dlopen()和dlsym()調用動態加載它們。我們有一個非常好的理由來做這種裝載,我不會在這裏進入。
問題:
動態地發現由C++編譯器使用dlsym()或GetProcAddress的產生的符號()它們必須使用一個extern 「C」 鏈接塊中未重整。例如:
#include <list>
#include <string>
using std::list;
using std::string;
extern "C" {
list<string> get_list()
{
list<string> myList;
myList.push_back("list object");
return myList;
}
}
此代碼是完全有效的C++,可編譯並在Linux和Windows上的許多編譯器上運行。但是,它不會使用MSVC進行編譯,因爲「返回類型無效C」。我們已經想出瞭解決辦法是改變函數返回一個指針列表,而不是列表對象:
#include <list>
#include <string>
using std::list;
using std::string;
extern "C" {
list<string>* get_list()
{
list<string>* myList = new list<string>();
myList->push_back("ptr to list");
return myList;
}
}
我一直試圖找到的GNU/Linux加載程序的最佳解決方案,既可以使用新功能,也可以使用舊功能原型,或者至少檢測何時遇到棄用功能併發出警告。對於我們的用戶來說,如果代碼在嘗試使用舊庫時只是暫停了,那將是不合時宜的。我最初的想法是在調用get_list期間設置一個SIGSEGV信號處理程序(我知道這是icky - 我願意接受更好的想法)。所以只是爲了確認加載一箇舊的庫會發生段錯誤,我認爲它會使用舊的函數原型(返回一個列表對象)通過新的加載代碼運行一個庫(它期望一個指向列表的指針),令我驚訝的是剛剛工作。我的問題是爲什麼?
以下加載代碼適用於上面列出的兩個函數原型。我已經證實它可以在Fedora 12,RedHat 5.5和RedHawk 5.1上使用gcc版本4.1.2和4.4.4。使用g ++和-shared和-fPIC編譯庫,並且需要將可執行文件鏈接到dl(-ldl)。
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <string>
using std::list;
using std::string;
int main(int argc, char **argv)
{
void *handle;
list<string>* (*getList)(void);
char *error;
handle = dlopen("library path", RTLD_LAZY);
if (!handle)
{
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror();
*(void **) (&getList) = dlsym(handle, "get_list");
if ((error = dlerror()) != NULL)
{
printf("%s\n", error);
exit(EXIT_FAILURE);
}
list<string>* libList = (*getList)();
for(list<string>::iterator iter = libList->begin();
iter != libList->end(); iter++)
{
printf("\t%s\n", iter->c_str());
}
dlclose(handle);
exit(EXIT_SUCCESS);
}
因爲你很幸運。我懷疑如果你用更復雜的程序來嘗試這種事情,你會開始看到一個被搗毀的堆棧或類似的效果。 – aschepler 2011-02-23 21:31:24
我發佈的代碼被簡化了。實際的應用程序大約有10萬行代碼,我運行了一些相當廣泛的測試用例,這些測試用例似乎都可以工作。我同意,但這不應該工作,除非在這種情況下有GCC的一些怪癖。 – bckohan 2011-02-23 21:42:57
我不確定'他們必須解開'是否屬實。如果你問dlsym()這個錯位的名字,它會找不到它。 – 2011-02-23 22:28:02