最近有幸閱讀了《高級C/C++編譯技術》深受啓發,該書深刻淺出地講解了構建過程(編譯、連接)中的各類細節,從多個角度展現了程序與庫文件或代碼的集成方法,提出了面向代碼複用和系統集成的軟件架構設計方法,以及系統開發過程當中疑難問題的解決方案。
如下將回頭記錄下其中的關鍵要點,以便後面查閱。
(1)靜態庫:lib+<library name>. + alinux
(2)動態庫:lib+<<library name> + .so + <library version information>ios
(3)動態庫的版本信息算法
dynamic libaray version information = <M>.<m>.<p>緩存
M:主版本號架構
m:次版本號函數
p:補丁(很小的代碼改動)版本號工具
(4)動態庫的soname測試
soname = lib+<libaray name>+.so+Mspa
動態庫的soname一般由鏈接器嵌入二進制文件的專有ELF字段中,一般用特定的鏈接器選項,將表示庫soname的字符串傳遞給連接器操作系統
g++ -shared <list of object files> -Wl, -soname, libfoo.so.1 -o libfoo.so.1.0.0
注意:(1)-Wl選項告訴編譯器將後面的參數傳遞給連接器
(2)凡是間接調用連接器時,咱們須要在連接器參數前追加「Wl,」
在Linux中用-L和-l選項來指定構建過程當中庫文件路徑
(1)將完整的庫文件路徑分紅兩個部分:目錄路徑和庫文件名
(2)將目錄路徑添加到-L鏈接器選項後面,並傳遞給連接器
(3)將庫文件名添加到-l參數後面,並傳遞給鏈接器
g++ main.o -L../sharedlib -lwokingdemo -o demo
在使用g++命令行一次性完整編譯連接兩個過程時,應在在連接器前添加-Wl,
g++ -Wall -fPIC main.cpp -Wl, -L../sharedlib -Wl, -lworkingdeno -o demo
對於經驗不足的linux軟件工程師常常會遇到找到不動態庫的狀況而不知所措,主要是對如下內容不夠了解。
動態庫運行時搜索算法由一組規則約束,按照優先級從高到低列出
(1)預加載庫
毫無疑問,預加載庫應該擁有最高的搜索優先級,裝載器會首先加載這些庫,而後纔開始搜索其它庫,有兩種方法能夠指定預加載庫
a:經過設置LD_PRELOAD環境變量
export LD_PRELOAD=/home/fate/shareddir/libtest.so:$LD_PRELOAD
b:經過修改、etc/ld.so.preload文件
指定預加載庫並不符合標準的設計規範。相反,該方案僅用於特殊狀況,好比設計壓力測試、診斷已經對原始代碼的緊急補丁等
(2)rpath
rpath和runpath均可供咱們使用,可是runpath在運行時搜索優先級列表中賦予了更高的優先級,只有在runpath缺失的狀況下,rpath纔是linux裝載器剩餘的搜索路徑信息中具備最高優先級的。
但若是ELF二進制文件的runpath(DT_RUNPATH)字段是非空的,那麼rpath會被忽略
g++ -Wl, -R/home/fate/shared/ -ltestlibrary
或者,也能夠用LD_RUN_PATH環境變量來設置rpath
export LD_RUN_PATH=/home/fate/shared:$LD_RUN_PATH
(3)LD_LIBARAY_PATH
從庫搜索概念發展初期開始,開發人員就但願可使用一種臨時應急的有效機制來驗證它們的設計,經過特定的環境變量(LD_LIBRARY_PATH)就能解決咱們遇到的問題,當沒有rpath時,該路徑就是路徑搜索信息中優先級最高的
export LD_LIBRARY_PATH=/home/fate/shared/:$LD_LIBRARY_PATH
注意:該機制只應用於實驗目的,軟件產品的產品版本不該該依賴於這種機制
(4)runpath
設置runpath的方法和設置rpath的方法很是類似,爲了傳遞-R或-rpath連接器選項,須要使用額外的--enable-new-dtags連接選項
g++ -Wl,-R/home/fate/shared/ -Wl, --enable-new-dtags -ltestlibrary
(5)ldconfig緩存
一種標準的代碼部署過程是基於運行linux的ldconfig工具,ldconfig會將指定的目錄路徑插入動態庫搜索列表中,該列表維護在文件/etc/ld.so.conf中。一樣地,系統會掃描新加入的目錄路徑,其結果是將發現的庫文件名添加到庫文件名列表中,該表維護在/etc/ld.so.cache文件中
/lib和/usr/lib是linux操做系統保存動態庫的兩個默認路徑
總的來講,優先級方案能夠概括爲如下兩種版本:
(1)若是指定了RUNPATH(即LD_RUNPATH字段非空)
a. LD_LIBRARY_PATH
b. runpath(LD_RUNPATH)
c. ld.so.cache
d. 默認路徑
(2)若是沒有指定runpath
a. 被加載庫的RPATh,而後是二進制文件的RPATH,直到可執行文件或者動態庫將這些庫所有加載完畢爲止
b. LD_LIBRAYR_PATH
c. ld.so.cache
d. 默認路徑
linux_so.h
#pragma once #ifdef __cplusplus extern "C" { #endif void fun(); #ifdef __cplusplus } #endif
linux_so.c
#include "linux_so.h" #include <iostream> using namespace std; void fun() { cout << "print fun" << endl; }
main.c
#include "stdio.h" #include "linux_so.h" int main() { fun(); return 0; }
編譯、連接
g++ -Wall -g -o0 -fPIC -shared linux_so.c -o liblinux_so.so
g++ -Wall -g -o0 -fPIC main.c -Wl,-L./ -Wl,-llinux_so -o out
export LD_LIBRARY_PATH=/home/fate/sharedir/dlltest/:$LD_LIBRAYR_PATH
輸出