天天看點

dlsym與-fPIC,以及objcopy

本文的copyleft歸[email protected]所有,使用GPL釋出,可以自由拷貝,轉載。但轉載請保持文檔的完整性,注明原作者及原連結,嚴禁用于任何商業用途。

作者:[email protected]

部落格:linuxfocus.blog.chinaunix.net    

這兩天做的工作當中,遇到了這樣一個需求。在Linux環境下,要為一個daemon程式的一個動态庫進行更新,不不打斷這個daemon的運作。這個動态庫的函數會被daemon的多個線程調用。在更新時,對于已經使用了這個動态庫的線程要毫無影響,直到這樣的線程再次調用動态庫的API時,再使用新的動态庫。換句話說,在更新時,新舊兩個動态庫可以同時被這個daemon的線程調用。

為了實作這個功能,毫無疑問,不能讓daemon自動加載動态庫,而是要通過使用dlopen和dlsym來加載動态庫,以及獲得相應的symbol。對于動态庫的句柄和指向其symbol的指針,需要使用引用計數,來檢查是否還有線程在引用它們。當更新動态庫時,對于其句柄和那些指針使用RCU機制進行釋放。這樣就保證了更新動态庫時,已被引用的舊的動态庫資源仍然可以繼續使用。直到所有的資源都被解引用時,資源才會被釋放。

為了驗證自己的思路,我寫了一個簡單的程式來測試同時加載兩個含有同樣symbol的動态庫,是否可以完全正常工作。下面是這個程式的簡單示意。

void *lib_handle1 = dlopen("libmy_lib.so", RTLD_NOW);

if (NULL == lib_handle1) {

//error handler

}

void *fp1 = dlsym(lib_handle1, "my_api");

if (NULL == fp1) {

void *lib_handle2 = dlopen("libmy_new_lib.so", RTLD_NOW);

if (NULL<b> </b>== lib_handle2) {

void *fp2 = dlsym(lib_handle2, "my_api");

if (NULL == fp2) {

首先要說明的是,第二次調用dlopen時,第二個動态的名字必須與第一個不同,否則程式會認為該動态庫已經加載,那麼第二個dlopen就會直接傳回lib_handle1的位址。這在man手冊中也有清晰的說明。

我這裡的第二個動态庫的名字已經與第一個有了差別。第二次dlopen傳回了一個新的加載位址,但是第二個dlsym卻傳回了與fp1相同的位址。也就是說,程式根本沒有取得libmy_new_lib.so中的my_api的位址。

我上網搜尋了一下,沒有找到準确的解釋。但是有人說因為symbol也已經被加載了,是以同名的symbol不會被第二次加載。

于是,我想将libmy_new_lib.so中的my_api改名——當然不能通過修改源代碼的方式。于是我使用objcopy來修改它的名字。具體指令是objcopy --redefine-sym my_api=my_new_api libmy_lib.so libmy_new_lib.so。修改後使用objdump檢視,發現已經成功修改了。

可是在測試中,卻發現dlsym失敗,報告找不到my_new_api。于是我又開始研究這個問題,最終發現objcopy的修改symbol名字的功能不支援動态庫,可man手冊上并沒有寫到。大家可以上網搜到,甚至可以找到objcopy的開發mailist中,有人就已經發現了這個問題,但是不知道為什麼一直沒有解決。

繞了半天,又回到了原點。最後又思索了半天,發現了最終的解決方案。在編譯參數中使用“-fPIC”即可解決這個問題。在man手冊中,對于-fPIC的解釋如下:

-fPIC

If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on the m68k, PowerPC and SPARC. Position-independent code requires special support, and therefore works only on certain machines.

When this flag is set, the macros "__pic__" and "__PIC__" are defined to 2.

當使用-fPIC時,生成的代碼時與位置無關的代碼。那麼在加載動态庫時,就不會加載到固定位置,那麼每個symbol都可以加載成功。當沒有-fPIC時,程式會發現該位置已有對應的symbol,自然就不會二次加載了。

這麼一個小問題也耽誤了我近一個下午的時間。原因有二:一是對gcc的編譯選項不夠熟悉;二是被objcopy的bug給耽誤了不少時間。

繼續閱讀