在替換so檔案時,如果在不停程式的情況下,直接用 cp new.so old.so 的方式替換程式使用的動態庫檔案會導致正在執行中的程式崩潰。解決的辦法是採用「rm+cp」 或「mv+cp」 來替代直接「cp」 的操作方法。linux系統的動態庫有兩種使用方法:執行時動態鏈結庫,動態載入庫並在程式控制之下使用。
1、為什麼在不停程式的情況下,直接用 cp 命令替換程式使用的 so 檔案,會使程式崩潰?
很多同學在工作中遇到過這樣乙個問題,在替換 so 檔案時,如果在不停程式的情況下,直接用cp new.so old.so的方式替換程式使用的動態庫檔案會導致正在執行中的程式崩潰,退出。這與 cp 命令的實現有關,cp 並不改變目標檔案的 inode,cp 的目標檔案會繼承被覆蓋檔案的屬性而非原始檔。實際上它是這樣實現的:
strace cp libnew.so libold.so 2>&1 |grep open.*lib.*.so
open("libnew.so", o_rdonly|o_largefile) = 3
open("libold.so", o_wronly|o_trunc|o_largefile) = 4
在 cp 使用「o_wronly|o_trunc」 開啟目標檔案時,原 so 檔案的映象被意外的破壞了。這樣動態鏈結器 ld.so 不能訪問到 so 檔案中的函式入口。從而導致 segmentation fault,程式崩潰。ld.so 載入 so 檔案及「再定位」的機制比較複雜,詳情可參見參考文獻2。
2、怎樣在不停止程式的情況下替換so檔案,並且保證程式不會崩潰?
答案是採用「rm+cp」 或「mv+cp」 來替代直接「cp」 的操作方法。
在用新的so檔案 libnew.so 替換舊的so檔案 libold.so 時,如果採用如下方法:
rm libold.so
cp libnew.so libold.so
採用這種方法,目標檔案 libold.so 的 inode 其實已經改變了,原來的 libold.so 檔案雖然不能用 」ls」檢視到,但其 inode 並沒有被真正刪除,直到核心釋放對它的引用。同理,mv只是改變了檔名,其 inode 不變,新檔案使用了新的 inode。這樣動態鏈結器 ld.so 仍然使用原來檔案的 inode 訪問舊的 so 檔案。因而程式依然能正常執行。
到這裡,我們回想在上線操作中在替換可執行程式時,為什麼直接使用「cp new old」這樣的命令時,系統會禁止這樣的操作,並且給出這樣的提示「cp: cannot create regular file `old': text file busy」。這時,我們採用的辦法仍然是用「rm+cp」或者「mv+cp」來替代直接「cp」,這跟以上提到的so檔案的替換有同樣的道理。
但是,為什麼系統會阻止 cp 覆蓋可執行程式,而不阻止覆蓋 so 檔案呢?這是因為 linux 有個 demand paging 機制,所謂「demand paging」,簡單的說,就是系統為了節約物理記憶體開銷,並不會程式執行時就將所有頁(page)都載入到記憶體中,而只有在系統有訪問需求時才將其載入。
「demand paging」要求正在執行中的程式映象(注意,並非檔案本身)不被意外修改,因此核心在啟動程式後會鎖定這個程式映象的 inode。對於 so 檔案,它是靠 ld.so 載入的,而ld.so畢竟也是使用者態程式,沒有權利去鎖定inode,也不應與核心的檔案系統底層實現耦合。
3、linux動態庫的使用方法 linux 支援兩種型別的庫靜態庫和動態庫。 靜態庫包含在編譯時靜態繫結到乙個程式的函式。我們這裡關心的是動態庫。動態庫是在載入應用程式時被載入的,而且它與應用程式是在執行時繫結的。
程式使用動態庫的方法有兩種:
(1)、在執行時動態鏈結庫
這種方法是動態的將程式和共享庫鏈結並讓 linux 在執行時載入庫。
例:fc@fengchun~/project/so$ cat foo.c
#include
void foo()
printf("infoo\n");
cat test1.c
#include
int main(int argc, char * ar**)
loop:
foo();
sleep(1);
goto loop;
return 0;
首先將檔案 foo.c 編譯成動態庫 libtest.so:
gcc foo.c -fpic -shared -o libtest.so
然後將檔案 test1.c 編譯可執行程式:
gcc test1.c -l. -ltest -o test1
編譯引數 「-l.」 指定編譯器在當前目錄(.)查詢動態庫檔案,編譯引數 「-ltest」指定編譯器連線庫檔案 libtest.so。
執行可執行程式 test1
./tsest1
程式將在螢幕上每間隔1秒列印1次「infoo」,此時可以償試執行一下用直接cp覆蓋的方法替換libtest.so檔案。
cp libtest.so libtest1.so #即使用完全相同的so檔案覆蓋
cp libtest1.so libtest.so
可以看到程式立即崩潰退出。
fc@fengchun~/project/so$ ./test1
infoo
infoo
segmentation fault
(2)、在執行時動態載入庫並在程式控制之下使用它們。
通過這種方法使用動態庫,對於庫檔案本身的編寫和編譯與方法1是相同的。但在可執行程式中需要使用乙個稱為動態載入的過程,這樣程式可以有選擇地呼叫庫中的函式。動態載入(dynamic loading,dl)api 就是為了動態載入而存在的,它允許共享庫對使用者空間程式可用。儘管非常小,但是這個 api 提供了所有需要的東西,而且很多困難的工作是在後台完成的。
動態載入api主要包括以下函式:
#include
void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
以下是乙個使用動態載入api的例子:
fc@fengchun~/project/so$ cat foo.c
#include
void foo()
printf("infoo\n");
fc@fengchun~/project/so$ cat test2.c
#include
#include
#include
int main( )
void *dl_handle;
float (*func)();
char *error;
/* open the shared object */
dl_handle = dlopen( "./libtest.so", rtld_lazy );
if (dl_handle) {
printf( "!!! %s\n", dlerror() );
return;
while (1) {
/* resolve the symbol (method) from the object */
func = dlsym( dl_handle, "foo" );
error = dlerror();
if (error = null) {
printf( "!!! %s\n", error );
return;
/* call the resolved method and print the result */
(*func)();
sleep(1);
/* close the object */
dlclose( dl_handle );
return;
首先將檔案 foo.c 編譯成動態庫 libtest.so:
gcc foo.c -fpic -shared -o libtest.so
然後將檔案 test2.c 編譯可執行程式:
gcc -rdynamic -o test2 test2.c -ldl
編譯引數「-rdynamic」 用來通知鏈結器將所有符號新增到動態符號表中(目的是能夠通過使用 dlopen 來實現向後跟蹤),編譯引數 「-ldl」指定編譯器連線庫 libdl
執行可執行程式 test2:
./tsest2
程式將在螢幕上每間隔1秒列印1次「infoo」,此時可以償試執行一下用直接cp覆蓋的方法替換libtest.so檔案。
cp libtest.so libtest1.so #即使用完全相同的so檔案覆蓋
cp libtest1.so libtest.so
可以看到程式立即崩潰退出。
fc@fengchun~/project/so$ ./test2
infoo
infoo
segmentation fault
動態so庫修改方法
對於linux和android開發者,有時可能需要檢視或者修改.so檔案,下面來講述如何檢視或者修改so檔案。ida pro 010 editor 注意安裝完ida後通常會生成兩個快捷圖示32位和64位,這個是對應於so的,如果so是32位的,則開啟32位的32位的快捷圖示 開啟之後,載入so,通常...
IDA動態除錯so檔案
1 將ida安裝目錄的 dbgsrc android server或者 android server nonpie複製到虛擬機器的data資料夾 2 使用ida開啟so檔案 4 配置虛擬機器端的監聽 5 使用adb forward命令進行埠的 將裝置被除錯端的埠 到遠端除錯端中 6 設定ida的de...
C linux動態庫so匯出及使用
存在不相容的函式 控制linux動態庫的匯出函式 使用linux動態庫 靜態載入 其他問題 第一次嘗試匯出linux動態庫,包裝log4cpp,遇到的問題做個記錄。在官網上下下來包過後,官網的安裝說明不全 stdcall cdecl if defined msc ver defined win32 ...