Linux .a文件.o文件.la文件.lo文件和.so文件

.o 就相當於Windows裏的obj文件 
.a 是好多個.o合在一起,用於靜態連接 
.so 是shared object,用於動態連接的,和dll差不多


 


.o文件是鏈接文件,.a是靜態庫文件,靠.o文件生成,作爲一個庫爲外部程序提供函數,接口。


生成.o文件:
gcc -c test.o test.c


生成.a文件:
ar cqs test.a test.o


 


 


o: 編譯的目標文件
a: 靜態庫,其實就是把若干o文件打了個包
so: 動態鏈接庫(共享庫)


lo: 使用libtool編譯出的目標文件,其實就是在o文件中添加了一些信息
la: 使用libtool編譯出的庫文件,其實是個文本文件,記錄同名動態庫和靜態庫的相關信息


1 libtool的工作原理
libtool 是一個通用庫支持腳本,將使用動態庫的複雜性隱藏在統一、可移植的接口中;使用libtool的標準方法,可以在不同平臺上創建並調用動態庫。可以認爲libtool是gcc的一個抽象,其包裝了gcc(或者其他的編譯器),用戶無需知道細節,只要告訴libtool需要編譯哪些庫即可,libtool將處理庫的依賴等細節。libtool只與後綴名爲lo、la爲的libtool文件打交道。


libtool主要的一個作用是在編譯大型軟件的過程中解決了庫的依賴問題;將繁重的庫依賴關係的維護工作承擔下來,從而釋放了程序員的人力資源。libtool提供統一的接口,隱藏了不同平臺間庫的名稱的差異等細節,生成一個抽象的後綴名爲la高層庫libxx.la(其實是個文本文件),並將該庫對其它庫的依賴關係,都寫在該la的文件中。該文件中的dependency_libs記錄該庫依賴的所有庫(其中有些是以.la文件的形式加入的);libdir則指出了庫的安裝位置;library_names記錄了共享庫的名字;old_library記錄了靜態庫的名字。


當編譯過程到link階段的時候,如果有下面的命令:


$libtool --mode=link gcc -o myprog -rpath /usr/lib –L/usr/lib –la


libtool會到/usr/lib路徑下去尋找liba.la,然後從中讀取實際的共享庫的名字(library_names中記錄了該名字,比如liba.so)和路徑(lib_dir中記錄了,比如libdir=’/usr/lib’),返回諸如/usr/lib/liba.so的參數給激發出的gcc命令行。


如果liba.so依賴於庫/usr/lib/libb.so,則在liba.la中將會有dependency_libs=’-L/usr/lib -lb’或者dependency_libs=’/usr/lib/libb.la’的行,如果是前者,其將直接把“-L/usr/lib –lb”當作參數傳給gcc命令行;如果是後者,libtool將從/usr/lib/libb.la中讀取實際的libb.so的庫名稱和路徑,然後組合成參數“/usr/lib/libb.so”傳遞給gcc命令行。


當要生成的文件是諸如libmylib.la的時候,比如:


$libtool --mode=link gcc -o libmylib.la -rpath /usr/lib –L/usr/lib –la


其依賴的庫的搜索基本類似,只是在這個時候會根據相應的規則生成相應的共享庫和靜態庫。


注意:libtool在鏈接的時候只會涉及到後綴名爲la的libtool文件;實際的庫文件名稱和庫安裝路徑以及依賴關係是從該文件中讀取的。


2 爲何使用 -Wl,--rpath-link -Wl,DIR?
使用libtool解決編譯問題看上去沒什麼問題:庫的名稱、路徑、依賴都得到了很好的解決。但下結論不要那麼着急,一個顯而易見的問題就是:並不是所有的庫都是用libtool編譯的。


比如上面那個例子,


$libtool --mode=link gcc -o myprog -rpath /usr/lib –L/usr/lib –la


如果liba.so不是使用libtool工具生成的,則libtool此時根本找不到liba.la文件(不存在該文件)。這種情況下,libtool只會把“–L/usr/lib –la”當作參數傳遞給gcc命令行。


考慮以下情況:要從myprog.o文件編譯生成myprog,其依賴於庫liba.so(使用libtool生成),liba.so又依賴於libb.so(libb.so的生成不使用libtool),而且由於某種原因,a對b的依賴並沒有寫入到liba.la中,那麼如果用以下命令編譯:


$libtool --mode=link gcc -o myprog -rpath /usr/lib –L/usr/lib –la


激發出的gcc命令行類似於下面:


gcc –o myprog /usr/lib/liba.so


由於liba.so依賴於libb.so(這種依賴可以用readelf讀liba.so的ELF文件看到),而上面的命令行中,並沒有出現libb.so,於是,可能會出現問題。


說“可能”,是因爲如果在本地編譯的情況下,gcc在命令行中找不到一個庫(比如上面的liba.so)依賴的其它庫(比如libb.so),鏈接器會按照某種策略到某些路徑下面去尋找需要的共享庫:


1. 所有由'-rpath-link'選項指定的搜索路徑.


2. 所有由'-rpath'指定的搜索路徑. '-rpath'跟'-rpath_link'的不同之處在於,由'-rpath'指定的路徑被包含在可執行文件中,並在運行時使用, 而'-rpath-link'選項僅僅在連接時起作用.


3. 在一個ELF系統中, 如果'-rpath'和'rpath-link'選項沒有被使用, 會搜索環境變量'LD_RUN_PATH'的內容.它也只對本地連接器起作用.


4. 在SunOS上, '-rpath'選項不使用, 只搜索所有由'-L'指定的目錄.


5. 對於一個本地連接器,環境變量'LD_LIBRARY_PATH'的內容被搜索.


6. 對於一個本地ELF連接器,共享庫中的`DT_RUNPATH'和`DT_RPATH'操作符會被需要它的共享庫搜索. 如果'DT_RUNPATH'存在了, 那'DT_RPATH'就會被忽略.


7. 缺省目錄, 常規的,如'/lib'和'/usr/lib'.


8. 對於ELF系統上的本地連接器, 如果文件'/etc/ld.so.conf'存在, 這個文件中有的目錄會被搜索.


從以上可以看出,在使用本地工具鏈進行本地編譯情況下,只要庫存在於某個位置,gcc總能通過如上策略找到需要的共享庫。但在交叉編譯下,上述八種策略,可以使用的僅僅有兩個:-rpath-link,-rpath。這兩個選項在上述八種策略當中優先級最高,當指定這兩個選項時,如果鏈接需要的共享庫找不到,鏈接器會優先到這兩個選項指定的路徑下去搜索需要的共享庫。通過上面的描述可以看到:-rpath指定的路徑將被寫到可執行文件中;-rpath-link則不會;我們當然不希望交叉編譯情況下使用的路徑信息被寫進最終的可執行文件,所以我們選擇使用選項-rpath-link。


gcc的選項“-Wl,--rpath-link –Wl,DIR”會把-rpath-link選項及路徑信息傳遞給鏈接器。回到上面那個例子,如果命令行中沒有出現libb.so,但gcc指定了“-Wl,--rpath-link –Wl,DIR”,則鏈接器找不到libb.so的時候,會首先到後面-rpath-link指定的路徑去尋找其依賴的庫。此處我們使用的編譯命令的示例是使用unicore平臺的工具鏈。


$ unicore32-linux-gcc –o myprog /usr/lib/liba.so \


-Wl,--rpath-link -Wl,/home/UNITY_float/install/usr/lib


這樣,編譯器會首先到“/home/UNITY_float/install/usr/lib”下面去搜索libb.so


libtool如何把選項“-Wl,--rpath-link –Wl,DIR”傳遞給gcc?libtool中有一個變量“hardcode_libdir_flag_spec”,該變量本來是傳遞“-rpath”選項的,但我們可以修改它,添加我們需要的路徑,傳遞給unicore32-linux-gcc。


“hardcode_libdir_flag_spec”原來的定義如下:


hardcode_libdir_flag_spec="\${wl}--rpath \${wl}\$libdir"


我們修改後的定義如下:


hardcode_libdir_flag_spec="\${wl}—rpath-link \${wl}\$libdir \


-Wl,--rpath-link -Wl,/home/UNITY_float/install/usr/lib \


-Wl,--rpath-link -Wl,/home/UNITY_float/install/usr/X11R6/lib "


這樣,當libtool在“--mode=link”的模式下,就會把選項“-Wl,--rpath-link –Wl,DIR”傳遞給gcc編譯器了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章