標籤:style blog http io ar color 使用 sp on
Linux下用gcc/g++產生靜態庫和動態庫(Z)
2012-07-24 16:45:10| 分類: linux | 標籤:連結庫 linux g++ gcc |舉報|字型大小 訂閱
在 linux 下,庫檔案一般放在 /usr/lib 和 /lib 下,
靜態庫的名字一般為 libxxxx.a ,其中 xxxx 是該 lib 的名稱
動態庫的名字一般為 libxxxx.so.major.minor , xxxx 是該 lib 的名稱, major 是主要版本號, minor 是副版本號碼
ldd 命令可以查看一個可執行程式依賴的共用庫
本文主要通過舉例來說明在 Linux 中如何建立靜態庫和動態庫,以及使用它們:
hello.h 為該函數庫的標頭檔。
hello.cpp是函數庫的來源程式,其中包含公用函數 hello ,該函數將在螢幕上輸出 "Hello World!" 。
main.cpp為測試庫檔案的主程式,在主程式中調用了公用函數 hello
hello.h
#ifndef _HELLO_H_
#define _HELLO_H_
void FuncOutput();
#endif
hello.cpp
#include <stdio.h>
#include "hello.h"
void FuncOutput()
{
printf("%s", "Hello World!\n");
}
main.cpp
#include"hello.h"
int main()
{
FuncOutput();
return 0;
}
這個時候我們有 三種思路:
1) 通過編譯多個源文 件,直接將目標代碼合成一個 .o 檔案。
2) 通過建立靜態連結庫 libmyhello.a ,使得 main 函數調用 hello 函數時可調用靜態連結庫。
3) 通過建立動態連結程式庫 libmyhello.so ,使得 main 函數調用 hello 函數時可調用靜態連結庫 。
2.3 思路一:編譯多個源檔案
在系統提示符下鍵入以下命令得到 hello.o 檔案。
# g++ -c hello.cpp
為什麼不適用 g++–o hello hello.cpp 這個道理 我們之前已經說了,使用 -c 是什麼意 思呢?這涉及到 g++ 編譯選項 的常識。
我們通常使用的 g++ –o 是將 .cpp 源檔案編 譯成為一個可執行檔二進位代碼,這包括調用作為 GCC 內的一部 分真正的 C 編譯器( ccl ),以及 調用 GNU C 編譯器的 輸出中實際可執行代碼的外部 GNU 彙編器和 連接器工具。
而 g++ –c 是使用 GNU 彙編器將 源檔案轉化為目標代碼之後就結束,在這種情況下連接器並沒有被執行,所以輸出的目標檔案不會包含作為 Linux 程式在被 裝載和執行時所必須的包含資訊,但它可以在以後被串連到一個程式。
我們運行 ls 命令看看 是否生存了 hello.o 檔案。
# ls
hello.cpp hello.h hello.o main.cpp
在 ls 命令結果 中,我們看到了 hello.o 檔案,本 步操作完成。
同理編譯 main
#g++ –c main.cpp
將兩個檔案連結成一個 .o 檔案。
#g++ –o hello hello.o main.o
運行
# ./hello
Hello everyone!
完成 ^ ^!
2.4 思路二:靜態連結庫
下面我們先來看看如何建立靜態庫,以及使用它。
靜態庫檔案名稱的命名規範是以 lib 為首碼, 緊接著跟靜態庫名 ,副檔名為 .a 。例如: 我們將建立的靜態庫名為 myhello ,則靜態 庫檔案名稱就是 libmyhello.a 。在建立 和使用靜態庫時,需要注意這點。 建立靜態庫用 ar 命令 。
在系統提示符下鍵入以下命令將建立靜態庫檔案 libmyhello.a 。
# ar cr libmyhello.a hello.o
我們同樣運行 ls 命令查看 結果:
# ls
hello.cpp hello.h hello.o libmyhello.a main.cpp
ls 命令結果 中有 libmyhello.a 。
靜態庫製作完了,如何使用它內部的函數呢?只需要在使用到這些公用函數的來源程式 中包含這些公用函數的原型聲明,然後在用 gcc 命令產生 目標檔案時指明靜態庫名, gcc 將會從靜 態庫中將公用函數串連到目標檔案中。注意, gcc 會在靜態 庫名前加上首碼 lib ,然後追 加副檔名 .a 得到的靜 態庫檔案名稱來尋找靜態庫檔案。
在程式 3:main.cpp 中,我們 包含了靜態庫的標頭檔 hello.h ,然後在 主程式 main 中直接調 用公用函數 hello 。下面先 產生目標程式 hello ,然後運 行 hello 程式看看 結果如何。
# g++ -o hello main.cpp -L. -lmyhello
# ./hello
Hello everyone!
我們刪除靜態庫檔案試試公用函數 hello 是否真的 串連到目標檔案 hello 中了。
# rm libmyhello.a
rm: remove regular file `libmyhello.a‘? y
# ./hello
Hello everyone!
程式照常運行,靜態庫中的公用函數已經串連到目標檔案中了。
靜態連結庫的一個缺點是,如果我們同時運行了許多程式,並且它們使用了同一個庫 函數,這樣,在記憶體中會大量拷貝同一庫函數。這樣,就會浪費很多珍貴的記憶體和儲存空間。使用了共用連結庫的Linux就可以避免這個問題。
共用函數庫和靜態函數在同一個地方,只是尾碼有所不同。比如,在一個典型的 Linux系統,標準的共用數序函數庫是/usr/lib/libm.so。
當一個程 序使用共用函數庫時,在串連階段並不把函數代碼串連進來,而只是連結函數的一個引用。當最終的函數匯入記憶體開始真正執行時,函數引用被解析,共用函數庫的 代碼才真正匯入到記憶體中。這樣,共用連結庫的函數就可以被許多程式同時共用,並且只需儲存一次就可以了。共用函數庫的另一個優點是,它可以獨立更新,與調 用它的函數毫不影響。
2.5 思路三、動態連結程式庫( 共用函數庫 )
我們繼續看看如何在 Linux 中建立動 態庫。我們還是從 .o 檔案開 始。
動態庫檔案名稱命名規範和靜態庫檔案名稱命名規範類似,也是在動態庫名增加首碼 lib ,但其文 件副檔名為 .so 。例如: 我們將建立的動態庫名為 myhello ,則動態 庫檔案名稱就是 libmyhello.so 。用 g++ 來建立動 態庫。
在系統提示符下鍵入以下命令得到動態庫檔案 libmyhello.so 。
# g++ -shared -fPCI -o libmyhello.so hello.o
“PCI” 命令列標 記告訴 GCC 產生的代 碼不要包含對函數和變數具體記憶體位置的引用,這是因為現在還無法知道使用該訊息代碼的應用程式會將它串連到哪一段記憶體位址空間。這樣編譯出的 hello.o 可以被用 於建立共用連結庫。建立共用連結庫只需要用 GCC 的 ”-shared” 標記即 可。
我們照樣使用 ls 命令看看 動態庫檔案是否產生。
# ls
hello.cpp hello.h hello.o libmyhello.so main.cpp
調用動態連結程式庫編譯目標檔案。
在程式中使用動態庫和使用靜態庫完全一樣,也是在使用到這些公用函數的來源程式中 包含這些公用函數的原型聲明,然後在用 gcc 命令產生 目標檔案時指明動態庫名進行編譯。我們先運行 gcc 命令產生 目標檔案,再運行它看看結果。
# g++ -o hello main.cpp -L. -lmyhello
{也可以這樣:g++ -o hello main.cpp / home/zhdr/test/libmyhello.so 就不用 mv libmyhello.so /usr/lib。
但也會碰到下面的問題,也需要:chcon -t texrel_shlib_t /home/zhdr/test/libmyhello.so }
使用 ”-lmyhello” 標記來告 訴 GCC 驅動程式 在串連階段引用共用函數庫 libmyhello.so 。 ”-L.” 標記告訴 GCC 函數庫可能位於目前的目錄。否則 GNU 連接器會尋找標準系統函數目錄。
# ./hello
./hello: 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 中,再試 試。 (一般不建議採用此方式,最好用修改LD的方式)
# mv libmyhello.so /usr/lib
# ./hello
Hello everyone!
{ 這步後我沒有成功,報錯內容如下: /hello: error while loading shared libraries: /usr/lib/libmyhello.so: cannot restore segment prot after reloc: Permission denied
解決辦法(首先確定你是否為root使用者,假如不是,那以下兩種解決方案也沒用):
1.
chcon -t texrel_shlib_t /usr/lib/libmyhello.so
(chcon -t texrel_shlib_t "你不能share的庫的絕對路徑")
2.
#vi /etc/sysconfig/selinux file
或者用
#gedit /etc/sysconfig/selinux file
修改SELINUX=disabled
重啟
}
編譯參數解析:
最主要的 是GCC命令列的一個選項:
-shared 該選項指定產生動態串連庫(讓連接器產生T類型的匯出符號表,有時候也產生弱串連W類型的匯出符號),不用該標誌外部程式無法串連。相當於一個可執行檔
l -fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯後的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真 正程式碼片段共用的目的。
l -L.:表示要串連的庫在目前的目錄中
l -ltest:編譯器尋找動態串連庫時有隱含的命名規則,即在給出的名字前面加上lib,後面加上.so來確定庫的名稱
l LD_LIBRARY_PATH:這個環境變數指示動態連接器可以裝載動態庫的路徑。
l 當然如果有root許可權的話,可以修改/etc/ld.so.conf檔案,然後調用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root許可權,那麼只能採用輸出LD_LIBRARY_PATH的方法了。
調用動態 庫的時候有幾個問題會經常碰到,有時,明明已經將庫的標頭檔所在目錄 通過 “-I” include進來了,庫所在檔案通過 “-L”參數引導,並指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定連結的so檔案,這時你要作的就是通過修改LD_LIBRARY_PATH或者/etc/ld.so.conf檔案來指定動態庫的目錄。通常這樣做就可以解決庫無法連結的問題了。
靜態庫連結時搜尋路徑順序:
1. ld會去找GCC命令中的參數-L
2. 再找gcc的環境變數LIBRARY_PATH
3. 再找內定目錄 /lib /usr/lib /usr/local/lib 這是當初compile gcc時寫在程式內的
動態連結時、執行時搜尋路徑順序:
1. 編譯目標代碼時指定的動態庫搜尋路徑;
2. 環境變數LD_LIBRARY_PATH指定的動態庫搜尋路徑;
3. 設定檔/etc/ld.so.conf中指定的動態庫搜尋路徑;
4. 預設的動態庫搜尋路徑/lib;
5. 預設的動態庫搜尋路徑/usr/lib。
有關環境變數:
LIBRARY_PATH環境變數:指定程式靜態連結庫檔案搜尋路徑
LD_LIBRARY_PATH環境變數:指定程式動態連結程式庫檔案搜尋路徑
參考:http://blog.163.com/[email protected]/blog/static/130245975201151552938133/
[轉]Linux下用gcc/g++產生靜態庫和動態庫(Z)