1. 程式人生 > >靜態庫、共享庫

靜態庫、共享庫

std 技術 都是 應用程序 rtl 解析 efi 插件 目錄

一、什麽是庫 本質上來說庫是一種可執行代碼的二進制形式,可以被操作系統載入內存執行。由於windows和linux的本質不同,因此二者庫的二進制是不兼容的。 Linux操作系統支持的函數庫分為靜態庫和動態庫,動態庫又稱共享庫。Linux系統有幾個重要的目錄存放相應的函數庫,如/lib /usr/lib。 二、靜態函數庫、動態函數庫 A. 這類庫的名字一般是libxxx.a;利用靜態函數庫編譯成的文件比較大,因為整個函數庫的所有數據都被整合進目標代碼中,他的優點就顯而易見了,即編譯後的執行程序不需要外部的函數庫支持,因為所有使用的函數都已經被編譯進可執行文件了。當然這也會稱為它的缺點,因為如果靜態函數庫改變了,那麽你的程序必須重新編譯,而且體積也較大。 B.這類庫德名字一般是libxxx.so,動態庫又稱共享庫;相對於靜態函數庫,動態函數庫在編譯的時候並沒有被編譯進目標代碼中,你的程序執行到相關函數時才調用函數庫裏的相應函數,因此動態函數庫所產生的可執行文件比較小。由於函數庫沒有被整合進你的程序,而是程序運行時動態申請並調用,所以程序的運行環境中必須提供相應的庫。動態函數庫的改變並不影響你的程序,所以動態函數庫的升級比較方便。而且如果多個應用程序都要使用同一函數庫,動態庫就非常適合,可以減少應用程序的體積。
註意:不管是靜態函數庫還是動態函數庫,都是由*.o目標文件生成。 三、函數庫的創建 A.靜態函數庫的創建 ar -cr libname.a test1.o test2.o ar:靜態函數庫創建的命令 -c :create的意思 -r :replace的意思,表示當前插入的模塊名已經在庫中存在,則替換同名的模塊。如果若幹模塊中有一個模塊在庫中不存在,ar顯示一個錯誤信息,並不替換其他同名的模塊。默認的情況下,新的成員增加在庫德結尾處。 B.動態函數庫的創建 gcc -shared -fpic -o libname.so test1.c test2.c -fpic:產生代碼位置無關代碼

技術分享圖片


-shared :生成共享庫 四、靜態庫和動態庫的使用 案例: add.c #include <stdio.h> int add(int a,int b) { return a + b; } sub.c #include <stdio.h> int sub(int a,int b) { return a - b; } head.h #ifndef _HEAD_H_ #define _HEAD_H_ extern int add(int a,int b); extern int sub(int a,int b); #endif
main.c #include <stdio.h> int main(int argc,char *argv[]) { int a,b; if(argc < 3) { fprintf(stderr,"Usage : %s argv[1] argv[2].\n",argv[0]); return -1; } a = atoi(argv[1]); b = atoi(argv[2]); printf("a + b = %d\n",add(a,b)); printf("a - b = %d\n",sub(a,b)); return 0; } 生成靜態庫

技術分享圖片

生成動態庫:

技術分享圖片

使用生成的生成的庫:

技術分享圖片

其中 -L 指定函數庫查找的位置,註意L後面還有‘.‘,表示在當前目錄下查找 -l則指定函數庫名,其中的lib和.a(.so)省略。 註意:-L是指定查找位置,-l指定需要操作的庫名。 從上面的運行結果中,我們可以看到: A.當動態庫和靜態庫同時存在的時候,gcc默認使用的是動態庫。如果強制使用靜態庫則需要加-static選項支持。 B.動態庫生成的可執行文件,test1不能正常的運行。 C.鏈接靜態庫的可執行程序明顯比鏈接動態庫的可執行文件大。 五、讓鏈接動態庫的可執行程序正常運行。 當系統加載可執行代碼時候,能夠知道其所依賴的庫的名字,但是還需要知道絕對路勁。此時就需要系統動態載入器(dynamic linker/loader)。 對於elf格式的可執行程序,是由ld-linux.so*來完成的,它先後搜索elf文件的DT_RPATH段---環境變量LD_LIBRARY_PATH、/etc/ld.so.cache文件列表、/usr/lib、/lib目錄找到庫文件後將其載入內存。 A.一種最直接的方法,就是把生成的動態庫拷貝到/usr/lib或/lib中去。

技術分享圖片 B.使用LD_LIBRARY_PATH環境變量,這個環境變量在ubuntu操作系統中默認沒有,需要手動添加

技術分享圖片

C.動態在安裝在其他目錄下,如果想操作系統能找到它,可以通過一下步驟 <1>新建並編輯/etc/ld.so.conf.d/my.conf文件,加入庫所在目錄的路徑 <2>執行ldconfig命令更新ld.so.cache文件

技術分享圖片
此時,在執行鏈接動態庫的可執行文件則可以正常運行。 六、查看庫中的符號 A.nm命令可以打印出庫中涉及到的所有符號。庫既可以是靜態庫也可以是動態的。 常見的三種符號: <1>在庫中被調用,但沒有在庫中定義(表明需要其他庫支持),用U表示 <2>在庫中定義的函數,用T表示 <3>“弱態”符號,他們雖然在庫中被定義,但是可能被其他庫中同名的符號覆蓋,用W表示。

技術分享圖片

B.ldd命令可以查看一個可執行程序依賴的共享庫

技術分享圖片

七、動態加載庫 用gcc -shared生成的我們稱為動態庫(共享庫),其中動態庫在運行的過程中有兩種方式 A.動態鏈接 這種方式下,可執行程序只是做一個動態的鏈接,當需要用到動態庫中的函數時,有加載器隱士的加載。 B.動態加載 這種方式下,在可執行程序的內部,我們可以用dlopen()這樣的函數,手動進行加載,dlsym()函數找到我們想要調用函數的入口地址,然後進行調用。這種方式,在寫插件程序中得到廣泛應用。 相關的API:

技術分享圖片
<1>dlopen()打開一個新的動態庫,並把它裝入內存。該函數主要用來記載庫中的符號,這些符號在編譯的時候是不知道的。 dlopen()函數需要兩個參數:一個文件名和一個標誌。 A.文件名是我們之前接觸過的動態庫的名字,如果它是一個絕對路徑,如:/home/cyg/worddir/libname.so,此時dlopen直接到指定的路徑下打開動態庫。 如果沒有指定路徑,僅僅指定了一個動態庫的名字,此時dlopen將它先後搜索elf文件的DT_RPATH段、環境變量LD_LIBRARY_PATH、/etc/ld.so.cache文件列表、/lib、/usr/lib目錄找到庫文件後將其載入內存。 B.標誌指明是否立刻計算庫的依賴性。
常常一個庫中還依賴別的庫,就是這個函數實現的時候,調用了別的庫函數。不是在這個庫中實現的函數我們稱為位定義的符號。
如果將標誌 設置為RTLD_NOW的話,則會在dlopen函數返回前,將這些未定義的符號解析出來。如果設置為RTLD_LAZY,則會在需要的時候才會去解析。 返回值:dlopen()函數會返回一個句柄作為dlsym()函數的第一個參數,以獲得符號在庫中的地址。使用這個地址,就可以獲得庫中特定函數的指針,並且調用裝載庫中的相應函數。 <2>dlerror() 當動態鏈接庫操作函數執行失敗時,dlerror可以返回出錯信息,返回值為NULL時表示操作函數執行成功。 <3>void *dlsym(void *handle,char *symbol); dlsym根據動態鏈接庫操作句柄(handle)與符號(symbol),返回符號對應的函數的執行代碼地址。由此地址,可以帶參數執行相應的函數。 如程序代碼 :int (*add)(int x,int y);//函數指針 handle = dlopen("xxx.so",RTLD_LAZY);//打開共享庫 add = dlsym(handle,"add");//獲取add函數在共享庫的地址 value = add(12,34);//調用add函數 <4>int dlclose(void *handle); dlclose用於關閉指定句柄的動態鏈接庫,只有當此動態鏈接庫的使用計數為0時,才會真正被系統卸載。 案例: #include <stdio.h> #include <string.h> #include <errno.h> #include <dlfcn.h> int test_dl(char *pso,char *pfu) { void *handle; int (*ptest)(int x,int y); if((handle = dlopen(pso,RTLD_LAZY)) == NULL) { printf("%s.\n",dlerror()); return -1; } if((ptest = dlsym(handle,pfu)) == NULL) { printf("%s.\n",dlerror()); return -1; } printf("ptest complete : %d.\n",ptest(12,13)); dlclose(handle); return 0; } int main(int argc,char *argv[]) { char buf[100]; char *pso,*pfun; printf("Input xxx.so:function_name.\n"); while(1) { printf(">"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1] = 0; pso = strdup(strtok(buf,":")); pfun = strdup(strtok(NULL,":")); test_dl(pso,pfun); } return 0; } 運行結果:

技術分享圖片

靜態庫、共享庫