1、目標庫
編譯程序的一種簡單方式是簡單地將每一個源文件編譯成目標文件,然后將這些目標鏈接在一起組成可執行程序:
?
gcc -g prog.c mod1.c mod2.c mod3.c gcc -g prog_nolib prog.o mod1.o mod2.o mod3.o
?
鏈接實際上是用鏈接器 ld 來完成的,使用 gcc 命令鏈接一個程序時,編譯器會再幕后調用 ld。
為了解決共享的問題,可以將一組目標文件組織成一個對象庫,對象庫分為兩種:
靜態庫
共享庫,比靜態庫更具優勢
2、靜態庫
靜態庫也稱為歸檔文件,有點:
可以將一組經常被調用到的目標文件組織成單個庫文件,這樣避免構建多個程序時反復編譯原來代碼的問題
鏈接命令變得更加簡單,鏈接時只需要指定靜態庫的名稱即可,不需要列出一個個目標文件
2.1、創建和維護靜態庫
使用 ar 命令能夠創建和維護靜態庫,靜態庫的名稱一般為 libname.a:
?
ar options archive object-filr...
?
options 參數由一系列的字母構成,其中一個是操作碼,其他是可能影響操作的執行修飾符:
r : 替換,將一個目標文件插入到歸檔文件中并取代同名的目標文件
t :目錄表,顯示歸檔中的目錄表,默認情況下只會列出歸檔文件中目標文件的名稱,添加 v 修飾之后可以看到記錄在歸檔文件中的各個目標文件的其他特性
d : 從歸檔文件中刪除一個模塊
2.2、使用靜態庫
程序和靜態庫鏈接起來存在的兩種方式:
在連接命令中指定靜態庫的名稱
?
gcc -g -c prog.c gcc -g -o prog prog.o libdemo.a
?
或者將靜態庫放在鏈接器搜索的其中一個標準目錄中 ,例如/usr/lib,然后使用 -l 選項指定庫名,庫名需要去除 lib ?前綴和 .a 后綴:
?
gcc -g -o prog prog.o -ldemo
?
如果庫不在鏈接器的搜索目錄中,則需要使用 -L 指定額外的目錄:
?
gcc -g -o prog prog.o -Llibdir -ldemo
?
3、共享庫概述
多個程序使用同一個靜態庫時,每個程序都有自己的目標模塊的副本,這種代碼冗余存在的缺點:
存儲同一個目標模塊的多個副本會浪費磁盤空間,并且所浪費的空間是比較大的
如果使用了同一模塊的程序在同一時刻執行,每個程序都有自己獨立的虛擬地址空間,其中保存了一份目標代碼副本,這將會提供系統中虛擬內存的整體使用量
如果需要修改一個靜態庫的一個目標模塊,那么所有使用這個模塊的可執行程序都必須重新進行鏈接合并這個變更
共享庫就是為了克服靜態庫的諸多缺點,它還具有的優勢有:
由于整個程序的大小變得更小了,一些情況下,程序可以完全被加載到內存中,從而能夠更快的啟動程序,這一點只有在大型共享庫正被其他程序使用時才成立,第一個加載共享庫的程序實際上會在啟動時花費更長的時間,因為必須要先找到共享庫并將其加載到內存中
目標模塊修改是 ,不需要重新編譯程序就可以看到變更,甚至當運行著的程序正在使用共享庫的現有版本時也能夠進行這樣的變更
共享庫的額外開銷:
創建共享庫和構建使用共享庫的程序比靜態庫更復雜
共享庫在編譯時需要使用位置獨立代碼,這在大多數架構上會帶來性能開銷,因為它需要使用一個額外的寄存器
在運行時必須要執行符號重定位,符號重定位期間,需要對共享庫中的每個符號(變量或函數)的引用修改成符合在虛擬內存中的實際運行時位置,這個重定位的過程需要花費一定的時間
雖然共享庫的代碼是多個進程共享的,但是其中的變量不是,每個使用庫的進程會擁有自己的在庫中定義的全局和靜態變量的副本。
3.1、創建和使用共享庫
創建一個共享庫
?
gcc -g -c -fPIC -Wall mod1.c mod2.c mod3.c gcc -g -shared libfoo.so mod1.o mod2.o mod3.o
?
貢獻庫的命名方式一般為 libname.so
可以向之前構建的共享庫中添加單個目標模塊,也可以從中刪除單個目標模塊
也可以將編譯共享庫的命令放在一行:
?
gcc -g -fPIC -Wall mod1.c mod2.c mod3.c -shared -o libfoo.so
?
位置獨立的代碼
-fPIC ?選項指定編譯器應該生成位置無關代碼,這會改變編譯器生成執行文件特定操作的代碼方式,包括訪問全局、靜態和外部變量,訪問字符串常量,以及獲取函數的地址,這些改變使得代碼可以在運行時放在任意虛擬地址處,這一點對于共享庫來說是必須的,因為鏈接的時候是無法知道共享庫代碼位于內存何處。
在 linux/x86-32 上,可以使用不加 -fPIC 選項編譯的模塊來創建共享庫,為了確定一個既有目標文件在編譯時是否使用了 -fPIC 選項,可以使用檢查目標文件符號表中是否存在 _GLOBAL_OFFSET_TABLE:
?
nm mod1.o | grep _GLOBAL_OFFSET_TABLE readelf -s mod1.o | grep _GLOBAL_OFFSET_TABLE
?
下面的命令中如果產生了輸出,則說明指定的共享庫中至少存在一個目標模塊在編譯時沒有指定 -fPIC:
?
objdump --all-headers libfoo.so | grep TEXTREL objdump --all-headers libfoo.so | grep TEXTREL
?
TEXTREL 表示存在一個目標模塊,其文本段中包含需要運行時重定位的引用。
使用一個共享庫
為了使用共享庫需要做兩件事情,而使用靜態庫則無需完成這兩件事情:
可執行文件中不包含它所需要的目標文件的副本,因此需要通過某種機制找到運行時所需要的共享庫:鏈接階段將共享庫名稱嵌入可執行文件中,一個程序所依賴的所有共享庫列表稱為程序的動態依賴表
在程序運行時需要某種機制解析嵌入的庫名,即找出與在可執行文件中指定的名稱對應的共享庫文件,如果庫不再內存中的話就將庫加載進內存
運行時解析內嵌庫名的工作由動態鏈接器(也稱為動態鏈接加載器或運行時加載器)來完成,動態鏈接器本身也是一個共享庫,其名稱為 lib/ld-linux.so.2,所有使用共享庫的 ELF 可執行文件都會用到這個共享庫。
lib/ld-linux.so.2 通常是指向動態鏈接器可執行文件的符號鏈接,這個文件的名稱為 ld-version.so。
動態鏈接器會檢查程序所需的共享庫清單并使用一組預先定義好的規則來在文件系統上找出相關的庫文件。其中一些規則指定了一組存放共享庫的標準目錄,如 /lib,/usr/lib。
LD_LIBRARY_PATH 環境變量
通知動態鏈接器一個共享庫位于一個非標準目錄中的一種方法是將該目錄添加到 LD_LIBRARY_PATH ?環境變量中以分號分隔的目錄列表中,如果定義了 LD_LIBRARY_PATH,那么動態鏈接器在查找標準庫目錄前會先查找該環境變量列出的目錄中的共享庫。
?
LD_LIBRARY_PATH=. ./prog # 告知動態鏈接器在當前工作目錄中搜索共享庫
?
靜態鏈接和動態鏈接的比較
術語鏈接通常指的是使用鏈接器 ld 將一個或多個編譯過的目標文件合成一個可執行程序,也稱為靜態鏈接。
在運行時使用共享庫的可執行程序需要經歷額外的動態鏈接階段。
3.2、共享庫 soname
嵌入到可執行程序以及動態鏈接器在運行時搜索的名稱除了使用真實名稱,還經常使用別名來創建共享庫,這種別名稱為 soname,ELF 中的 ?DT_SONAME ?標簽。
如果共享庫擁有一個 soname,那么在靜態鏈接階段會將 soname 嵌入到可執行文件中,而不會使用真實名稱,同時后面的動態鏈接器在運行時也會使用這個 soname 來搜索庫,引入 soname 的目的是為了提供一個間接層,使得可執行程序能夠在運行時使用與鏈接時使用不同的但兼容的共享庫。
?
gcc -g -c -fPIC -Wall mod1.c mod2.c mod3.c gcc -g -shared -Wl,-soname,libbar.so -o libfoo.so mod1.o mod2.o mod3.o
?
-Wl,-soname,libbar.so 等選項都是傳給鏈接器的指令,以將共享庫 libfoo.so 的 soname 設置為 libbar.so
使用一下命令中的任意一個,可以確定既有共享庫的 soname:
?
objdump -p libz.so | grep SONAME SONAME ? ? ? ? ? ? ? libz.so.1 ? readelf -d libz.so | grep SONAME 0x000000000000000e (SONAME) ? ? ? ? ? ? Library soname: [libz.so.1]
?
使用 soname ?創建程序:
?
gcc -g -Wall -o prog prog.c libfoo.so
?
鏈接器檢查到庫 libfoo.so 包含了 soname : libbar.so,就將這個 soname 嵌入到可執行文件中。
使用共享庫時必須要創建一個符號鏈接,將 soname 指向庫的真實名稱,并且必須要將這個符號鏈接放在動態鏈接器搜索的其中一個目錄中。
程序載入內存以設備執行時發生的事情:
要找出一個進程當前使用的共享庫則可以列出相應的 Linux 特有的 /proc/PID/maps 文件中的內容。 ?
4、使用共享庫的有用工具
ldd
ldd ?: 列出動態依賴,顯示一個程序運行時所需要的共享庫。
ldd ?會解析出每個庫引用,使用的搜索方式與動態鏈接器一樣,并以下面的形式顯示結果:
?
library-name => resolves-to-path
?
對于多數的 ELF 文件,ldd 至少會列出與 ld-linux.so.2、動態鏈接器以及標準 C 庫 lib.so.6 相關的條目。
objdump 和 readelf
objdump ?:用來從可執行文件、編譯過的目標、以及共享庫中獲取各類信息,包括反匯編的二進制機器碼,還可以用來顯示這些文件各個 ELF 節的頭部信息,當這樣使用時,它就類似于 readelf,readelf 能顯示類似的信息,但是格式不同。
nm
nm 命令會列出目標庫或可執行文件中定義的一組符號,常用來尋找哪些庫定義了一個符號。
5、共享庫版本和命名規則
命名形式:
?
libname.so major-id.minor-id
?
major-id :主要版本標識符,區分兩個不兼容的版本
minor-id :次要版本可以是任意字符串,但是通常的形式是兩個由點分隔的數字,第一個數字表示次要版本,第二個數字表示該次要版本的補丁號或者修訂號
?
libdemo.so.1.0.1 libdemo.so.1.0.2 libdemo.so.2.0.0
?
共享庫的 soname 包括相應的主版本標識符,但是不包含次要版本標識符,因此 ?soname 的命名形式為:
?
libname.so.major-id
?
通常,會將 soname ?創建為包含真實名稱的目錄的一個相對符號鏈接:
?
libdemo.so.1 ? ? -> libdemo.so.1.0.2 libdemo.so.2 ? ? -> libdemo.so.2.0.0
?
共享庫的一個主要版本可能有多個不同的次要版本,通常每個庫的主要版本的 ?soname ?會指向主要版本中最新的次要版本,由于靜態鏈接階段會將 soname ?的副本(獨立于次要版本)嵌入到可執行文件中,并且 soname 符號鏈接后面可能會被修改為指向更新的次要版本,從而確保在執行期間能夠加載庫的最新的次要版本。
同一個庫的不同主要版本也能夠同時存在,被需要它們的程序分別訪問。
除了真實名稱和 soname 之外,通常還會為每個共享庫定義第三個名稱:鏈接器名稱,將可執行文件與共享庫鏈接起來時會使用這個名稱,鏈接器名稱是一個只包含庫名稱不包含主要版本和次要版本標識符的符號鏈接,其形式為 libname.so,有了鏈接器名稱之后就可以構建能夠自動使用共享庫最新版本的鏈接命令。
鏈接器名稱一般與它所引用的文件位于同一個目錄中,它既可以鏈接到真實名稱,也可以鏈接到庫的最新主要版本的 soname。通常最好使用指向 soname ?的鏈接,從而對 soname 的修改會自動反應到鏈接器名稱。
如果需要使用一個舊版本的共享庫,就不能使用鏈接器名稱,要使用真實名稱或者 soname 來指示出需要的版本。
5.1、使用標準規范創建一個共享庫
創建目標文件
?
gcc -g -c -fPIC -Wall mod1.c mod2.c mod3.c
?
創建共享庫,指定真實名稱和 soname
?
gcc -g -shared -Wl,-soname,libdemo.so.1 -o libdemo.so.1.0.1 mod1.o mod2.o mod3.o
?
為 soname 和鏈接器名稱創建恰當的符號鏈接
?
ln -s libdemo.so.1.0.1 libdemo.so.1 ln -s libdemo.so.1 libdemo.so
?
使用鏈接器名稱構建可執行文件
?
gcc -g -Wall -o prog prog.c -L. -ldemo LD_LIBRARY_PATH=. ./prog
?
5.2、安裝共享庫
共享庫及其關聯的符號鏈接一般會被安裝到一個標準目錄中,標準目錄包括:
/usr/lib :是大多數標準庫安裝的目錄
/lib :應該將系統啟動時用到的庫安裝到這個目錄,因為系統啟動時可能還沒有掛載 /usr/lib
/usr/local/lib :應該將非標準或者實驗性質的庫安裝到這個目錄
/etc/ld.so.conf 中列出的目錄
安裝完成后一般需要創建 soname ?和鏈接器名稱的符號鏈接:
?
mv libdemo.so.1.0.1 /usr/lib cd /usr/lib ln -s libdemo.so.1.0.1 libdemo.so.1 # 創建 soname ln -s libdemo.so.1 libdemo.so# 創建鏈接器名稱
?
ldconfig
ldconfig 解決了共享庫的兩個潛在問題:
共享庫可以位于各種目錄中,如果動態鏈接器需要通過搜索這些目錄來找一個庫并加載這個庫,整個過程將非常慢
當安裝了新版本的庫或者刪除了舊版本的庫,那么 ?soname 符號鏈接就不是最新的
ldconfig 可以通過執行兩個任務來解決這些問題:
它搜索一組標準的目錄并創建或更新一個緩存文件 /etc/ld.so.cache 使之包含在所有這些目錄中的主要版本(每個庫的主要版本的最新的次要版本)列表,動態鏈接器在運行時解析庫名稱時會輪流使用這個緩存文件:
為了構建這個緩存,ldconfig 會搜索在 /etc/ld.so.conf 中指定的目錄,然后搜索 /lib 和 /usr/lib
`/etc/ld.so.conf 文件由一個目錄路徑名(應該是絕對路徑名)列表構成,其中路徑名之間用換行、空格、制表符、逗號或冒號分隔
在一些發行版中, /usr/local/lib 目錄也位于這個列表中
ldconfig -p 會顯示 /etc/ls.so.cache 的當前內容
它檢查每個庫的各個主要版本的最新次要版本以找出嵌入的 soname,然后在同一目錄中為每個 soname 創建(或更新)相對符號鏈接:
為了能夠正確執行這些動作,ldconfig 要求庫的名稱要根據前面介紹的規范來命名:庫的真實名稱包含主要和次要標識符,它們隨著庫的版本的更新而恰當的增長
可以使用命令行選項來指定它執行其中一個動作: -N 選項會防止緩存的重建,-X 選項會阻止 soname 符號鏈接的創建。 此外, -v(verbose)選項會使得 ldconfig 輸出描述其所執行的動作的信息
每當安裝了一個新的庫,更新或者刪除一個既有庫,以及 /etc/ld.so.conf 中的目錄列表被修改后,都運行相應的 ldconfig。
安裝一個庫的兩個不同主要版本:
?
mv libdemo.so.1.0.1 libdemo.so.2.0.0 /usr/lib
?
為鏈接器名稱創建符號鏈接:
?
ln -s libdemo.so.2 libdemo.so
?
如果更新庫的一個次要版本,由于鏈接器名稱指向了最新的 soname,因此 ldconfig 還能取得保持鏈接器名稱的最新效果:
?
mv libdemo.so.2.0.1 /usr/lib
?
如果使用的是私有庫,即沒有安裝在上述的標準目錄中的庫,那么可以使用 -n 選項讓 ldconfig 創建 soname ,這個選項指定了只處理在命令行中列出的目錄的庫,無需更新緩存文件。
使用 ldconfig 來處理當前工作目錄中的庫:
?
gcc -g -c -fPIC -Wall mod1.c mod2.c mod3.c gcc -g -c -shared -Wl,-soname,libdemo.so.1 -o libdemo.so.1.0.1 mod1.o mod2.o mod3.o /sbin/ldconfig -nv .
?
5.3、兼容與不兼容庫比較
滿足下列條件時表示修改過的庫與既有版本兼容:
庫中所有公共方法和變量的語義保持不變
沒有刪除庫的公共 API 中的函數和變量,但向公共 API 中添加新函數和變量
每個函數分配的結構以及沒和函數的返回結構保持不變
如果這些條件都滿足,更新庫時只需要調增既有庫的次要版本號,否則就要創建新的主版本。
5.4、升級共享庫
共享庫的優點之一就是當一個運行著的程序正在使用共享庫的一個既有版本時,也能夠安裝新的主要版本或者次要版本的庫,需要做的是:
創建新的庫版本,將其安裝到恰當的目錄
根據需要更新 soname 和鏈接器名稱符號鏈接,或者使用 ldconfig 完成這部分工作
更新次要版本:
?
gcc -g -c -fPIC -Wall mod1.c mod2.c mod3.c gcc -g -c -shared -Wl,-soname,libdemo.so.1 -o libdemo.so.1.0.2 mod1.o mod2.o mod3.o mv libdemo.so.1.0.2 /usr/lib ldconfig -v | grep libdemo
?
更新主要版本:
?
gcc -g -c -fPIC -Wall mod1.c mod2.c mod3.c gcc -g -c -shared -Wl,-soname,libdemo.so.2 -o libdemo.so.2.0.0 mod1.o mod2.o mod3.o mv libdemo.so.2.0.0 /usr/lib ldconfig -v | grep libdemo cd /usr/lib ln -sf libdemo.so.2 libdemo.so
?
6、在目標文件中指定庫搜索目錄
通知動態鏈接器共享庫的位置的方式:
LD_LIBRARY_PATH 環境變量中指定
將共享庫安裝到標準庫目錄:/lib,/usr/lib,/etc/ls.so.conf 中列出的其中一個目錄
第三種方式:在靜態編輯階段可以在執行文件中插入一個在運行時搜索共享庫的目錄列表,這種方式對于庫位于一個固定的但不屬于動態鏈接器搜索標準位置時非常有用,在創建文件時需要增加 -rpath 鏈接器選項。
?
gcc -g -Wall,-rpath,/home/mtk/pdir -o prog prog.c libdemo.so
?
將字符串 /home/mtk/pdir 復制到可執行 文件 prog 的運行時庫路徑列表中,在程序運行時,動態鏈接器在解析共享庫引用時還會搜索這個路徑。
指定多個目錄:
多次指定 -rpath 選項,所有這些列出的目錄會被鏈接成一個放到可執行文件中的有序 -rpath ?列表
在一個 -rpath 選項中可以指定多個由分號分隔開的目錄列表,運行時,動態鏈接器會按照 -rpath 選項中指定的目錄順序來搜索目錄
-rpath 的一個可替代方案就是使用 LD_RUN_PARH 環境變量,可以將由一個逗號分隔開的目錄的字符串賦值給該變量,只有當構建可執行文件時沒有指定 -rpath 選項才會使用 LD_RUN_PATH
6.1、在構建貢獻庫時使用 -rpath 鏈接器選項
假設有一個庫 lib1.so 依賴于另一個共享庫 lib2.so ,另外再假設這些庫分別位于非標準目錄 d1 和 d2中:
首先在 pdir/d2 目錄中構建 libx2.so,這里省略了庫的版本號和 soanme:
?
cd /home/mtk/pdir/d2 gcc -g -c -fPIC -Wall modx2.c gcc -g -shared -o libx2.so modx2.o
?
接著在 pdir/d1 目錄中構建 libx1.so。由于 libx1.so 依賴于 lib2.so,并且 libx2.so 位于一個非標準目錄中,因此在指定 libx2.so 的運行時位置時需要使用 -rpath 鏈接器選項。這個選項的取值與庫的鏈接時位置可以不同:
?
cd /home/mtk/pdir/d1 gcc -g -c -Wall modx1.c gcc -g -shared -o libx1.so modx1.o -Wl,-rpath,/home/mtk/pdir/d2 -L/home/mtk/pdir/d2 -lx2
?
pdir 目錄中構建主程序,由于主程序使用了 ?libx1.so 并且這個庫位于一個非標準目錄中,因此還需要使用 -rpath 鏈接器選項:
?
cd /home/mtk/pdir gcc -g -Wall -o prog prog.c -Wl,-rpath,/home/mtk/pdir/d1 -L/home/mtk/pdir/d1 -lx1
?
在鏈接主程序是,無需指定 libx2.so,由于鏈接器能夠分析 libx1.so 中的 rpath 列表,因此它能夠找到 libx2.so,同時在靜態鏈接階段解析出所有的符號。
可以通過 readelf --dynamic 或者等價的 readelf -d 命令的輸出來查看 rpath 列表。
6.2、ELF DT_RPATH 和 DT_RUNPATH 條目
第一版 ELF 規范中,只有一種 rpath 列表能夠被嵌入到可執行文件或共享庫中,對應 ?ELF ?文件中的 DT_RPATH 標簽。后續的 ELF 舍棄了 DT_RPATH ,同時引入 DT_RUNPATH 來表示 rpath 列表,兩者的差別在于動態鏈接器在運行時搜索共享庫時它們相對于 LD_LIBRARY_PATH 環境變量的優先級,DT_RPATH 的優先級更高,DT_RUNPATH 的優先級更低。
默認情況下,鏈接器將 rpath 列表創建為 DT_RPATH 標簽。為了讓鏈接器將 rpath 列表創建為 DT_RUNPATH 條目必須使用 --enable-new-dtags。
6.3、在 rpath 中使用 $ORIGIN
應用程序中使用了自身的共享庫,但同時不希望強制要求將這些庫安裝在其中一個標準目錄中,可以在構建鏈接器的時候,增加 $ORIGIN,動態鏈接器將這個字符串解釋成 "包含應用程序的目錄":
?
gcc -Wl,-rpath,'ORIGIN'/lib ...
?
7、在運行時找出共享庫
在解析庫依賴時,動態鏈接器首先會檢查各個依賴字符串以確定它是否包含 /,如果找到了一個斜線,那么依賴字符串就會被解釋成一個路徑名,并且會使用該路徑名加載庫,否則動態鏈接器會使用下面的規則來搜索共享庫:
如果可執行文件的 DT_RPATH 運行時庫路徑列表中包含目錄并且不包含 DT_RUNPATH 列表,那么就搜索這些目錄,按照鏈接程序時指定的目錄順序
如果定義了 LD_LIBRARY_PATH 環境變量,那么就會輪流搜索該變量值中以冒號分隔的各個目錄,如果可執行文件是一個 set-user-ID 或者 set-group-ID 程序,那么就會忽略 LD_LIBRARY_PATH 變量。這項安全措施就是為了防止用戶欺騙動態鏈接器讓其加載一個與可執行文件所屬的庫的名稱一樣的私有庫
如果可執行文件 DT_RUNPATH 運行時庫路徑列表中包含目錄,那么就搜索這些目錄,按照鏈接程序時指定的目錄順序
檢查 /etc/ld.so.cache 文件以確認它是否包含了與庫相關的條目
搜索 /lib 和 /usr/lib 目錄
運行時符號解析
假設現在有一個主程序和一個共享庫,它們兩個都定義了一個全局函數 xyz(),并且共享庫中的另一個函數調用了 xyz():
?
gcc -g -c -fPIC -Wall -c foo.c gcc -g -shared -o libfoo.so foo.o gcc -g -o prog prog.c libfoo.so LD_LIBRARY_PATH=. ./prog
?
主程序中的 xyz() 定義將覆蓋共享庫中的定義
如果一個全局符號在多個庫中進行了定義,那么對該符號的引用會綁定在掃描庫時找到的第一個定義,掃描順序是按照這些庫在靜態鏈接命令行中列出時從左到右的順序
如果想使用共享庫中的 ?xyz() 調用,需要指定 -Bsymbolic ?鏈接器選項:
?
gcc -g -c -fPIC -Wall -c foo.c gcc -g -shared -Wl,-Bsymbolic -o libfoo.so foo.o gcc -g -o prog prog.c libfoo.so LD_LIBRARY_PATH=. ./prog
?
-Bsymbolic ?鏈接器選項指定了共享庫中對全局符號的應用應該優先綁定到庫中的相應定義(如果存在)。
使用靜態庫取代共享庫
默認情況下,當鏈接器能夠選擇名稱一樣的共享庫和靜態庫時,會優先使用共享庫:
?
-Lsomedir -ldemo
?
并且 libdemo.so 和 libdemo.a 都存在的話,會優先使用 libdemo.a。
如果要強制使用靜態庫:
在 gcc 命令中指定靜態庫的路徑名,包括 ?.a ?擴展
在 gcc 命令行中指定 -static 選項
使用 -Wl,-Bstatic 和 -Wl,Bdynamic gcc 選項來顯示指定鏈接到靜態庫還是動態庫
評論
查看更多