天天看點

gcc編譯動态和靜态連結庫

我們通常把一些公用函數制作成函數庫,供其它程式使用。函數庫分為靜态庫和動态庫兩種。靜态庫在程式編譯時會被連接配接到目标代碼中,程式運作時将不再需要該靜态庫。動态庫在程式編譯時并不會被連接配接到目标代碼中,而是在程式運作是才被載入,是以在程式運作時還需要動态庫存在。本文主要通過舉例來說明在Linux中如何建立靜态庫和動态庫,以及使用它們。

第1步:編輯得到舉例的程式--hello.c和test.c;

測試程式test.c調用了公用函數my_lib_function。

hello.c:

#include <stdio.h>

void my_lib_function()

{

    printf("library routine called/n");

}

test.c:

int main()

    my_lib_function();

    return 0;

第2步:将hello.c編譯成.o檔案;

無論靜态庫,還是動态庫,都是由.o檔案建立的(動态庫可以直接通過.c)。是以,我們必須将源程式hello.c通過gcc先編譯成.o檔案。

在系統提示符下鍵入以下指令得到hello.o檔案。

# gcc -c hello.c

第3步:由.o檔案建立靜态庫;

靜态庫檔案名的命名規範是以lib為字首,緊接着跟靜态庫名,擴充名為.a。

例如:我們将建立的靜态庫名為myhello,則靜态庫檔案名就是libmyhello.a。在建立和使用靜态庫時,需要注意這點。建立靜态庫用ar指令。

在系統提示符下鍵入以下指令将建立靜态庫檔案libmyhello.a。

# ar crv libmyhello.a hello.o

第4步:在程式中使用靜态庫;

靜态庫制作完了,如何使用它内部的函數呢?隻需要在使用到這些公用函數的源程式中包含這些公用函數的原型聲明,然後在用gcc指令生成目标檔案時指明靜态庫名,gcc将會從靜态庫中将公用函數連接配接到目标檔案中。注意,gcc會在靜态庫名前加上字首lib,然後追加擴充名.a得到的靜态庫檔案名來查找靜态庫檔案。

# gcc -o test test.c -L. -lmyhello

# ./test

library routine called

我們删除靜态庫檔案試試公用函數hello是否真的連接配接到目标檔案 hello中了。

# rm libmyhello.a

程式照常運作,靜态庫中的公用函數已經連接配接到目标檔案中了。

我們繼續看看如何在Linux中建立動态庫。我們還是從.o檔案開始。

第5步:由.o檔案建立動态庫檔案;

動态庫檔案名命名規範和靜态庫檔案名命名規範類似,也是在動态庫名增加字首lib,但其檔案擴充名為.so。

例如:我們将建立的動态庫名為myhello,則動态庫檔案名就是libmyhello.so。用gcc來建立動态庫。

在系統提示符下鍵入以下指令得到動态庫檔案libmyhello.so。

# gcc -shared -fPCI -o libmyhello.so hello.o

也可以由.c檔案直接建立動态庫:gcc -shared -fPCI -o libmyhello.so hello.c

我們照樣使用ls指令看看動态庫檔案是否生成。

第6步:在程式中使用動态庫;

在程式中使用動态庫和使用靜态庫完全一樣,也是在使用到這些公用函數的源程式中包含這些公用函數的原型聲明,然後在用gcc指令生成目标檔案時指明動态庫名進行編譯。我們先運作gcc指令生成目标檔案,再運作它看看結果。

./test: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

哦!出錯了。快看看錯誤提示,原來是找不到動态庫檔案libmyhello.so。程式在運作時,會在/usr/lib和/lib等目錄中查找需要的動态庫檔案。若找到,則載入動态庫,否則将提示類似上述錯誤而終止程式運作。我們将檔案libmyhello.so複制到目錄/usr/lib中,再試試。

# mv libmyhello.so /usr/lib

成功了。這也進一步說明了動态庫在程式運作時是需要的。

如果出現:error while loading shared libraries: libmyhello.so: cannot restore segment prot after reloc: Permission denied 錯誤原因在于selinux禁用了通路此共享庫

解決辦法:關閉selinux

1、編輯/etc/selinux/config檔案,找到SELINUX=enforcing,改為SELINUX=disabled

2、編輯/etc/sysconfig/selinux檔案,找到SELINUX=enforcing,改為SELINUX=disabled

3、重新開機電腦

第7步:繼續;

我們回過頭看看,發現使用靜态庫和使用動态庫編譯成目标程式使用的gcc指令完全一樣,那當靜态庫和動态庫同名時,gcc指令會使用哪個庫檔案呢?抱着對問題必究到底的心情,來試試看。

先删除其他檔案,隻留hello.c test.c hello.o

# rm -f test /usr/lib/libmyhello.so

在來建立靜态庫檔案libmyhello.a和動态庫檔案libmyhello.so。

# ar cr libmyhello.a hello.o

# gcc –shared –fPCI -o libmyhello.so hello.o

# ls

hello.c hello.o libmyhello.a libmyhello.so test.c

通過上述最後一條ls指令,可以發現靜态庫檔案libmyhello.a和動态庫檔案libmyhello.so都已經生成,并都在目前目錄中。

從程式hello運作的結果中很容易知道,當靜态庫和動态庫同名時, gcc指令将優先使用動态庫。

Note:

編譯參數解析

最主要的是GCC指令行的一個選項:

-shared 該選項指定生成動态連接配接庫(讓連接配接器生成T類型的導出符号表,有時候也生成弱連接配接W類型的導出符号),不用該标志外部程式無法連接配接。相當于一個可執行檔案

-fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯後的代碼是位置相關的是以動态載入時是通過代碼拷貝的方式來滿足不同程序的需要,而不能達到真正代碼段共享的目的。

-L.:表示要連接配接的庫在目前目錄中

-ltest:編譯器查找動态連接配接庫時有隐含的命名規則,即在給出的名字前面加上lib,後面加上.so來确定庫的名稱

LD_LIBRARY_PATH:這個環境變量訓示動态連接配接器可以裝載動态庫的路徑。

當然如果有root權限的話,可以修改/etc/ld.so.conf檔案,然後調用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root權限,那麼隻能采用輸出LD_LIBRARY_PATH的方法了。

調用動态庫的時候有幾個問題會經常碰到,有時,明明已經将庫的頭檔案所在目錄通過 “-I” include進來了,庫所在檔案通過 “-L”參數引導,并指定了“-l”的庫名,但通過ldd指令察看時,就是死活找不到你指定連結的so檔案,這時你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf檔案來指定動态庫的目錄。通常這樣做就可以解決庫無法連結的問題了。