天天看點

自己在linux上編譯、連結、動态庫和靜态庫的學習筆記

在平常的項目中,我們都是使用公司要求的makefile、makedebug一類的檔案,是以,在編譯、連結、生成和連結動态庫與靜态庫的時候,我們隻是簡單的使用一些已經設定的變量,隻是簡單的修改、添加一些檔案名,或許這次我們編譯通過了,但是,在某一個時候,可能出現了一個問題,無論簡單與否,因為平常沒有留意,導緻的結果可能是花了好長時間才能解決。而如果平常隻是簡單的留一下心,或許這些問題都是可以避免的。 

是以,今天我自己使用幾個檔案,編譯動态庫、靜态庫以及動态庫和靜态庫的嵌套調用等問題,盡量還原我在項目中遇到的問題,盡量讓自己明白平常沒有意識到的一些東西。 

需要用到的檔案清單,如下: 

1、編譯生成可執行檔案 

我們在單獨編譯的時候,隻需要檢查頭檔案,因為都放到了一個目錄,是以單獨編譯的時候,不需要依賴其他檔案,就可以生成目标檔案(*.o),如下: 

編譯完成之後,就生成了對應的目标檔案,如下: 

編譯的時候,沒有添加依賴的頭檔案,是因為預設編譯的時候,在目前目錄下去找。 

小測試1: 

如果我們把showpoint.cpp單獨移動到一個目錄下,再去編譯一下。 

通過上面可以确定的是,在編譯的時候,因為頭檔案裡包含了其他頭檔案“showcoor.h”,而該頭檔案不在目前目錄,而且不在系統指定的目錄,是以,才會提示找不到頭檔案的錯誤。這個時候,我們就需要手動的指定頭檔案所在的目錄,使用“-I路徑” 

通過生成的目标檔案,最後進行連結,就可以生成最終的可執行檔案。 

通過最後的連結,我們還可以看到函數的依賴關系。 

小測試2: 

因為showpoint.cpp會調用`showcoor(int)'函數,是以,缺少“showcoor.o”檔案時,連結的時候,就會提示找不到該函數。 

小測試3: 

因為main.cpp會首先調用`showpoint(int, int, int)'函數,是以缺少“showpoint.o”檔案時,連結的時候,就會提示找不到“showpoint”函數。 

很明顯,因為找不到“main”函數,無法生成最終的可執行檔案。 

2、生成靜态庫 

靜态函數庫: 

這類庫的名字一般是libxxx.a;利用靜态函數庫編譯成的檔案比較大,因為整個函數庫的所有資料都會被整合進目标代碼中,他的優點就顯而易見了,即編譯後的執行程式不需要外部的函數庫支援,因為所有使用的函數都已經被編譯進去了。當然這也會成為他的缺點,因為如果靜态函數庫改變了,那麼你的程式必須重新編譯。 

擴充閱讀: http://tech.ccidnet.com/art/2583/20080303/1378433_1.html  

生成靜态庫需要使用“ar -cr” 

c   Create the archive,也就是建立靜态庫 

r   Insert the files member... into archive (with replacement),也就是沒有生成,有的話替換。 

當然,我們還可以直接通過“.cpp”檔案直接生成靜态庫。如下: 

3、動态庫 

動态函數庫: 

這類庫的名字一般是libxxx.so;相對于靜态函數庫,動态函數庫在編譯的時候并沒有被編譯進目标代碼中,你的程式執行到相關函數時才調用該函數庫裡的相應函數,是以動态函數庫所産生的可執行檔案比較小。由于函數庫沒有被整合進你的程式,而是程式運作時動态的申請并調用,是以程式的運作環境中必須提供相應的庫。動态函數庫的改變并不影響你的程式,是以動态函數庫的更新比較友善。 

生成動态庫的方式是:g++ -shared -fPCI -o libXXX.so *.o  

-fpic 使輸出的對象子產品是按照可重定位位址方式生成的。 

-shared指定把對應的源檔案生成對應的動态連結庫檔案。 

下面是對于-fpic的詳細解釋: 

在 Linux 下制作動态連結庫,“标準” 的做法是編譯成位置無關代碼(Position Independent Code,PIC),然後連結成一個動态連結庫。 

擴充閱讀: http://www.linuxidc.com/Linux/2011-06/37268.htm  

上述方法是通過.cpp檔案直接生成動态庫。其實,也可以通過.o檔案生成動态庫,如下: 

上面錯誤産生的原因就是因為參數“-fPIC”的原因,因為最終生成該動态庫的時候,使用了參數“-fPIC”,表示生成的動态庫是“位置無關代碼”,而在生成“showpoint.o”的時候,是位置相關性的,是以,錯誤資訊也提示了,讓使用“-fPIC”重新編譯(recompile with -fPIC)。 

測試4: 

當然,我們通過上述方式,隻是把函數“showpoint”函數封裝成了,如果我們在main函數隻調用“libshowpoint.so”的話,會出現問題的,如下: 

-l庫名,動态庫的命名為libXXX.so的話,就可以使用-lXXX的方式引用該庫,如果是靜态庫,并且命名為libXXX.a,則同樣可以使用lXXX的方式引用該庫。 

這個時候,我們還需要使用函數“showcoor”所在的庫,是以,我們有必要把函數“showcoor”也封裝到動态庫中。 

當然,我們在最後生成可執行檔案的時候,也可以連結同時連結生成的動态庫和靜态庫,如下: 

我們可以看到,調用動态庫和靜态庫的方式是一樣的,都是“-L路徑 -l庫名”。 

4、庫的嵌套調用 

既然我們已經把函數“showcoor”封裝成了靜态庫,是以我們完全可以通過使用靜态庫“libshowcoor.a”來生成我們最終的動态庫“libshowpoint.so”,因為靜态庫可以了解成是“目标檔案(*.o)的打包”。 

測試5:動态庫調用靜态庫 

我們通過已有的靜态庫生成最終的動态庫 

上述錯誤提示和原來的沒有添加“-fPIC”生成的“*.o”檔案生成動态庫出現的錯誤相同,而且也給給出了解決方法,靜态庫的生成重新使用“-fPIC”進行編譯。 

這樣最終生成的動态庫,就同時包含“showpoint”函數和“showoccr”函數,調用如下: 

5、使用靜态庫調用的順序問題 

如果生成了多個動态庫,我們的調用順序對最終生成的檔案沒有影響,測試如下: 

但是,如果我們使用了靜态庫,調用順序就有影響了,否則的話,編譯的時候會因為找不到函數而出錯。 

測試6: 

調整靜态庫的調用順序: 

通過上面,我們可以知道,被調用的庫應該放到調用庫的後面。因為函數“showpoint”中調用了函數“showcoor”,是以,函數“showcoor”對應的庫應該放到後面才行。 

是以,如果遇到了程式需要調用A庫,而A庫調用B庫,那麼我們應該寫成“g++ -o main main.cpp -L./ -libA.so -libB.so”,也就是網上所說的,調用靜态庫的時候,于是基礎的庫,越是放到最後。 

6、庫内部函數的檢視 

nm用來列出目标檔案的符号清單。 

擴充閱讀: http://www.linuxidc.com/Linux/2011-05/35777.htm  

nm 指令使用以下符号(用同樣的字元表示弱符号作為全局符号)之一來表示檔案符号類型: 

A    Global absolute 符号。 

a    Local absolute 符号。 

B    Global bss 符号。 

b    Local bss 符号。 

D    Global data 符号。 

d    Local data 符号。 

f    源檔案名稱符号。 

T    Global text 符号。 

t    Local text 符号。 

U    未定義符号。 

擴充閱讀: http://blog.chinaunix.net/uid-28458801-id-3475711.html  

上述包含“showcoor”函數和“showpoint”函數時生成的動态庫,檢視内部函數: 

如果采用隻封裝“showpoint”函數的方式生成的動态庫,檢視内部函數: 

我們可以看到,這個時候,雖然在“showpoint”函數中調用“showcoor”函數,但是該函數在庫中并未定義,是以類型是“U”。 

7、庫依賴關系的檢視 

ldd指令用于判斷某個可執行的 binary 檔案含有什麼動态函式庫。 

但是ldd本身不是一個程式,而僅是一個shell腳本: 

$ which ldd 

/usr/bin/ldd 

$ file /usr/bin/ldd  

/usr/bin/ldd: Bourne-Again shell script text executable 

擴充閱讀: http://blog.chinaunix.net/uid-23622436-id-3235778.html  

使用上述包含“showcoor”函數和“showpoint”函數時生成的動态庫,生成可執行檔案,檢視依賴庫: 

如果兩個函數分别生成一個動态庫的話,檢視依賴庫:     

可以看到,通過這種方式,就引用了兩個庫。 

通過這個簡單的例子,雖然不能說讓自己精通編譯、連結、靜态庫和動态庫,但是,我想,對于我的學習還是很有幫助的。最起碼,當我下次在遇到類似的問題的時候,我可以更容易的想到是因為什麼原因。而這,就夠了!