生活随笔
收集整理的這篇文章主要介紹了
静态库和动态库的分析
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
?在Linux操作系統(tǒng)中,普遍使用ELF格式作為可執(zhí)行程序或者程序生成過程中的中間格式。ELF(Executable and Linking Format,可執(zhí)行連接格式)。
???? ELF文件格式包括三種主要的類型:可執(zhí)行文件、可重定向文件、共享庫:
1、可執(zhí)行文件(應用程序)
可執(zhí)行文件包含了代碼和數(shù)據(jù),是可以直接運行的程序。
2、可重定向文件(*.o)
可重定向文件又稱為目標文件,它包含了代碼和數(shù)據(jù)(這些數(shù)據(jù)是和其他重定位文件和共享的object文件一起連接時使用的)。
*.o文件參與程序的連接(創(chuàng)建一個程序)和程序的執(zhí)行(運行一個程序),它提供了一個方便有效的方法來用并行的視角看待文件的內(nèi)容,這些*.o文件的活動可以反映出不同的需要。
Linux下,我們可以用gcc -c編譯源文件時可將其編譯成*.o格式。
3、共享文件(*.so)
也稱為動態(tài)庫文件,它包含了代碼和數(shù)據(jù)(這些數(shù)據(jù)是在連接時候被連接器ld和運行時動態(tài)連接器使用的)。動態(tài)連接器可能稱為ld.so.1,libc.so.1或者 ld-linux.so.1。
那么到底什么是庫呢?
庫從本質(zhì)上來說是一種可執(zhí)行代碼的二進制格式,可以被載入內(nèi)存中執(zhí)行。庫分靜態(tài)庫和動態(tài)庫兩種。二者的不同點在于代碼被載入的時刻不同。
靜態(tài)庫:這類庫的名字一般是libxxx.a,xxx為庫的名字。靜態(tài)庫在程序編譯時會被連接到目標代碼中,利用靜態(tài)函數(shù)庫編譯成的文件比較大,因為整個函數(shù)庫的所有數(shù)據(jù)都會被整合進目標代碼中,他的優(yōu)點就顯而易見了,即編譯后的執(zhí)行程序不需要外部的函數(shù)庫支持,因為所有使用的函數(shù)都已經(jīng)被編譯進去了。當然這也會成為他的缺點,因為如果靜態(tài)函數(shù)庫改變了,那么你的程序必須重新編譯。
動態(tài)庫:這類庫的名字一般是libxxx.M.N.so,同樣的xxx為庫的名字,M是庫的主版本號,N是庫的副版本號。當然也可以不要版本號,但名字必須有。相對于靜態(tài)函數(shù)庫,動態(tài)函數(shù)庫在編譯的時候并沒有被編譯進目標代碼中,你的程序執(zhí)行到相關(guān)函數(shù)時才調(diào)用該函數(shù)庫里的相應函數(shù),因此動態(tài)函數(shù)庫所產(chǎn)生的可執(zhí)行文件比較小。由于函數(shù)庫沒有被整合進你的程序,而是程序運行時動態(tài)的申請并調(diào)用,所以程序的運行環(huán)境中必須提供相應的庫。動態(tài)函數(shù)庫的改變并不影響你的程序,所以動態(tài)函數(shù)庫的升級比較方便。linux系統(tǒng)有幾個重要的目錄存放相應的函數(shù)庫,如/lib /usr/lib。
當要使用靜態(tài)的程序庫時,連接器會找出程序所需的函數(shù),然后將它們拷貝到執(zhí)行文件,由于這種拷貝是完整的,所以一旦連接成功,靜態(tài)程序庫也就不再需要了。然而,對動態(tài)庫而言,就不是這樣。動態(tài)庫會在執(zhí)行程序內(nèi)留下一個標記指明當程序執(zhí)行時,首先必須載入這個庫。由于動態(tài)庫節(jié)省空間,linux下進行連接的缺省操作是首先連接動態(tài)庫,也就是說,如果同時存在靜態(tài)和動態(tài)庫,不特別指定的話,將與動態(tài)庫相連接。
一、靜態(tài)庫
??????? 前面已經(jīng)介紹了靜態(tài)庫的概念,下面我們通過一個實例來看靜態(tài)庫的使用
1、生成靜態(tài)庫
1)這里準備了兩個庫的源碼st1 、st2用它們來制作庫libmytest.a,如下
[cpp]?view plaincopy
fs@ubuntu:~/qiang/stalib$?ls?-l?? total?12?? -rw-rw-r--?1?fs?fs?98?Jan??5?19:56?main.c?? -rw-rw-r--?1?fs?fs?69?Jan??5?19:55?st1.c?? -rw-rw-r--?1?fs?fs?68?Jan??5?19:55?st2.c?? fs@ubuntu:~/qiang/stalib$?cat?st1.c?? #include?<stdio.h>?? ?? void?print1()?? {?? ????printf("Hello!I?am?st1!\n?");?? }?? fs@ubuntu:~/qiang/stalib$?cat?st2.c?? #include?<stdio.h>?? ?? void?print2()?? {?? ????printf("Hello!I?am?st2!\n");?? }?? fs@ubuntu:~/qiang/stalib$?cat?main.c?? #include?<stdio.h>?? ?? int?main(int?argc,?const?char?*argv[])?? {?? ????print1();?? ????print2();?? ?????? ????return?0;?? }?? fs@ubuntu:~/qiang/stalib$???
2) 生成libmytest.a文件
創(chuàng)建靜態(tài)庫用ar命令,它將很多.o轉(zhuǎn)換成.a
[cpp]?view plaincopy
fs@ubuntu:~/qiang/stalib$?gcc?-c?st1.c?st2.c?? fs@ubuntu:~/qiang/stalib$?ar?crs?libmytest.a?st1.o?st2.o?? fs@ubuntu:~/qiang/stalib$?ls?-l?? total?24?? -rw-rw-r--?1?fs?fs?2262?Jan??5?20:04?libmytest.a?? -rw-rw-r--?1?fs?fs???98?Jan??5?19:56?main.c?? -rw-rw-r--?1?fs?fs???69?Jan??5?20:03?st1.c?? -rw-rw-r--?1?fs?fs?1024?Jan??5?20:04?st1.o?? -rw-rw-r--?1?fs?fs???69?Jan??5?20:03?st2.c?? -rw-rw-r--?1?fs?fs?1024?Jan??5?20:04?st2.o?? fs@ubuntu:~/qiang/stalib$?file?libmytest.a??? libmytest.a:?current?ar?archive?? fs@ubuntu:~/qiang/stalib$???
靜態(tài)庫文件libmytest.a已經(jīng)生成,用file命令查看其屬性,發(fā)現(xiàn)它確實是歸檔壓縮文件。用ar -t libmytest.a可以查看一個靜態(tài)庫包含了那些obj文件:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/stalib$?ar?-t?libmytest.a??? st1.o?? st2.o?? fs@ubuntu:~/qiang/stalib$???
2、使用靜態(tài)庫
前面我們已經(jīng)寫好了main.c,現(xiàn)在測試一下
[cpp]?view plaincopy
fs@ubuntu:~/qiang/stalib$?gcc?-o?test?main.c?-L.?-lmytest?? fs@ubuntu:~/qiang/stalib$?ls?-l?? total?32?? -rw-rw-r--?1?fs?fs?2262?Jan??5?20:04?libmytest.a?? -rw-rw-r--?1?fs?fs???98?Jan??5?19:56?main.c?? -rw-rw-r--?1?fs?fs???69?Jan??5?20:03?st1.c?? -rw-rw-r--?1?fs?fs?1024?Jan??5?20:04?st1.o?? -rw-rw-r--?1?fs?fs???69?Jan??5?20:03?st2.c?? -rw-rw-r--?1?fs?fs?1024?Jan??5?20:04?st2.o?? -rwxrwxr-x?1?fs?fs?7248?Jan??5?20:07?test?? fs@ubuntu:~/qiang/stalib$?./test??? Hello!?I?am?st1!?? Hello!?I?am?st2!?? fs@ubuntu:~/qiang/stalib$???
這里gcc的參數(shù)-L是告訴編譯器庫文件的路徑是當前目錄,-l是告訴編譯器要使用的庫的名字叫mytest。
二、動態(tài)庫
動態(tài)庫的基本概念
1、動態(tài)鏈接庫是程序運行時加載的庫,當動態(tài)鏈接庫正確安裝后,所有的程序都可以使用動態(tài)庫來運行程序。動態(tài)鏈接庫是目標文件的集合,目標文件在動態(tài)鏈接庫中的組織方式是按照特殊方式形成的。庫中函數(shù)和變量的地址是相對地址,不是絕對地址,其真實地址在調(diào)用動態(tài)庫的程序加載時形成。
2、動態(tài)鏈接庫的名稱有別名(soname), 真名(realname)和鏈接名(linker name)。別名由一個前綴lib,然后是庫的名字,再加上一個后綴“.so”構(gòu)成("libxxx.so")。真名是動態(tài)鏈接庫真實名稱,一般總是在別名的基礎加上一個小版本號,發(fā)布版本等構(gòu)成。除此之外,還有一個鏈接名,即程序鏈接時使用的庫的名字。
3、在動態(tài)鏈接庫安裝的時候,總是復制文件到某個目錄下,然后用一個軟連接生成別名,在庫文件進行更新的時候,僅僅更新軟鏈接即可。
下面我們通過一個實例來學習如何生成動態(tài)庫和使用動態(tài)庫
1、生成動態(tài)庫
1)當前文件夾下有下面四個文件
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?ls?-l?? total?16?? -rw-rw-r--?1?fs?fs?73?Jan??5?18:11?dy1.c?? -rw-rw-r--?1?fs?fs?74?Jan??5?18:11?dy2.c?? -rw-rw-r--?1?fs?fs?90?Jan??5?18:11?main.c?? -rw-rw-r--?1?fs?fs?95?Jan??5?18:10?mylib.h?? fs@ubuntu:~/qiang/lib/dylib$???
文件內(nèi)容分別為:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?cat?mylib.h??? #ifndef?_MYLIB_H_?? #define?_MYLIB_H_?? ?? #include?<stdio.h>?? ?? void?print1();?? void?print2();?? ?? #endif?? fs@ubuntu:~/qiang/lib/dylib$?cat?dy1.c?? #include?"mylib.h"?? ?? void?print1()?? {?? ????printf("My?first?shared?lib!\n");?? }?? fs@ubuntu:~/qiang/lib/dylib$?cat?dy2.c?? #include?"mylib.h"?? ?? void?print2()?? {?? ????printf("My?second?shared?lib!\n");?? }?? fs@ubuntu:~/qiang/lib/dylib$?cat?main.c?? #include?"mylib.h"?? ?? int?main(int?argc,?char?*argv[])?? {?? ????print1();?? ????print2();?? ????return?0;?? }?? fs@ubuntu:~/qiang/lib/dylib$???
2)這里我們將dy1.c與dy2.c用來創(chuàng)建動態(tài)庫
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?gcc?-fPIC?-Wall?-c?dy1.c?dy2.c?? fs@ubuntu:~/qiang/lib/dylib$?gcc?-shared?-o?libtest.so?dy1.o?dy2.o?? fs@ubuntu:~/qiang/lib/dylib$???
這里 -fPIC 創(chuàng)建與地址無關(guān)的編譯程序,-shared指定生成動態(tài)鏈接庫。
我們也可以一步到位
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?gcc?-o?libtest.so?-fPIC?-shared?dy1.c?dy2.c?? fs@ubuntu:~/qiang/lib/dylib$???
我們可以看到下面已經(jīng)生成了一個libtest.so
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?ls?-l?? total?32?? -rw-rw-r--?1?fs?fs???73?Jan??5?18:11?dy1.c?? -rw-rw-r--?1?fs?fs?1332?Jan??5?18:29?dy1.o?? -rw-rw-r--?1?fs?fs???74?Jan??5?18:11?dy2.c?? -rw-rw-r--?1?fs?fs?1336?Jan??5?18:29?dy2.o?? -rwxrwxr-x?1?fs?fs?6798?Jan??5?18:29?libtest.so?? -rw-rw-r--?1?fs?fs???90?Jan??5?18:11?main.c?? -rw-rw-r--?1?fs?fs???95?Jan??5?18:10?mylib.h?? fs@ubuntu:~/qiang/lib/dylib$???
2、使用動態(tài)鏈接庫
????? 在編譯程序時,使用動態(tài)鏈接庫和靜態(tài)庫是一致的,使用”-l庫名”的方式,在生成可執(zhí)行文件的時候會鏈接庫文件。使用如下命令:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?gcc?-o?test?main.c?-L.?-ltest?? fs@ubuntu:~/qiang/lib/dylib$?ls?? dy1.c??dy2.c??libtest.so??main.c??mylib.h??test??
這里 -L 指定動態(tài)鏈接庫的路勁,-ldtest鏈接庫函數(shù)test 。-ltest是動態(tài)庫的調(diào)用規(guī)則。Linux系統(tǒng)下的動態(tài)庫命名方式是lib*.so,而在鏈接時表示位-l*,*是自己命名的庫名。
我們可以看到這里已經(jīng)生成了test可執(zhí)行文件,我們可以執(zhí)行一下:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?./test?? ./test:?error?while?loading?shared?libraries:?libtest.so:?cannot?open??? shared?object?file:?No?such?file?or?directory?? fs@ubuntu:~/qiang/lib/dylib$??
可以發(fā)現(xiàn)發(fā)生了錯誤,這是因為程序運行時沒有找到動態(tài)鏈接庫造成的。程序編譯時鏈接動態(tài)庫和運行時使用動態(tài)鏈接庫的概念是不同的,在運行時,程序鏈接的動態(tài)鏈接庫需要在系統(tǒng)目錄下才行。
這就到了動態(tài)庫的路徑問題,有三種方法:
1)把庫拷貝到/usr/lib和/lib目錄下:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?sudo?cp?libtest.so?/lib?? [sudo]?password?for?fs:???
這里要超級用戶權(quán)限sudo,我們看一下執(zhí)行結(jié)果:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?./test??? My?first?shared?lib!?? My?second?shared?lib!?? fs@ubuntu:~/qiang/lib/dylib$???
這里執(zhí)行結(jié)果正確。
2)在?LD_LIBRARY_PATH 環(huán)境變量中加上庫所在路徑
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?export?LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH?? fs@ubuntu:~/qiang/lib/dylib$???
我們可以看到:libtest.so 的路徑已經(jīng)存在
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?ldd?test?? ????linux-gate.so.1?=>??(0xb774b000)?? ????libtest.so?=>?/home/fs/qiang/lib/dylib/libtest.so?(0xb7746000)?? ????libc.so.6?=>?/lib/i386-linux-gnu/libc.so.6?(0xb758d000)?? ????/lib/ld-linux.so.2?(0xb774c000)?? fs@ubuntu:~/qiang/lib/dylib$???
我們可以看一下結(jié)果:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?./test??? My?first?shared?lib!?? My?second?shared?lib!?? fs@ubuntu:~/qiang/lib/dylib$???
也能得到正確結(jié)果。
3)添加/etc/ld.so.conf.d/*.conf文件。把庫所在的路徑加到文件末尾,并執(zhí)行l(wèi)dconfig刷新。
[cpp]?view plaincopy
fs@ubuntu:~/qiang/lib/dylib$?cd?/etc/ld.so.conf.d/?? fs@ubuntu:/etc/ld.so.conf.d$?ls?? i386-linux-gnu_GL.conf??libc.conf?? i686-linux-gnu.conf?????vmware-tools-libraries.conf?? fs@ubuntu:/etc/ld.so.conf.d$?sudo?vi?my.conf?? [sudo]?password?for?fs:??? fs@ubuntu:/etc/ld.so.conf.d$?cat?my.conf??? /home/fs/qiang/lib/dylib/libtest.so?? fs@ubuntu:/etc/ld.so.conf.d$???
在/etc/ld.so.conf.d/下建立 my.conf? 里面只有一句/home/fs/qiang/lib/dylib/libtest.so 即libtest.so的路徑,然后執(zhí)行l(wèi)dconfig刷新即可。
總結(jié)
以上是生活随笔為你收集整理的静态库和动态库的分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。