前言:
動态連結庫是程式運作時加載的庫,當動态連結庫正确部署之後,運作的多個程式可以使用同一個加載到記憶體中的動态庫,是以在 Linux 中動态連結庫也可稱之為共享庫。
動态連結庫是目标檔案的集合,目标檔案在動态連結庫中的組織方式是按照特殊方式形成的。庫中函數和變量的位址使用的是相對位址(靜态庫中使用的是絕對位址),其真實位址是在應用程式加載動态庫時形成的。
在 Linux 中動态庫以 lib 作為字首,以.so 作為字尾,中間是庫的名字自己指定即可,即: libxxx.so
在 Windows 中動态庫一般以 lib 作為字首,以 dll 作為字尾,中間是庫的名字需要自己指定,即: libxxx.dll
生成動态連結庫:
生成動态連結庫是直接使用 gcc 指令并且需要添加 -fPIC(-fpic) 以及 -shared 參數。
-fPIC 或 -fpic 參數的作用是使得 gcc 生成的代碼是與位置無關的,也就是使用相對位置。
-shared參數的作用是告訴編譯器生成一個動态連結庫。
生成動态連結庫的具體步驟如下:
- 将源檔案進行彙編操作,需要使用參數 -c, 還需要添加額外參數 -fpic /-fPIC
# 得到若幹個 .o檔案
$ gcc 源檔案(*.c) -c -fpic
- 将得到的.o 檔案打包成動态庫,還是使用 gcc, 使用參數 -shared 指定生成動态庫。
$ gcc -shared 與位置無關的目标檔案(*.o) -o 動态庫(libxxx.so)
- 釋出動态庫和頭檔案
# 釋出
1. 提供頭檔案: xxx.h
2. 提供動态庫: libxxx.so
動态庫制作舉例
第一步:使用 gcc 将源檔案進行彙編 (參數-c), 生成與位置無關的目标檔案,需要使用參數 -fpic或者-fPIC
# 1. 将.c彙編得到.o, 需要額外的參數 -fpic/-fPIC
$ gcc add.c div.c mult.c sub.c -c -fpic -I ./include/
# 檢視目錄檔案資訊, 檢查是否生成了目标檔案
$ tree
.
├── add.c
├── add.o # 生成的目标檔案
├── div.c
├── div.o # 生成的目标檔案
├── include
│ └── head.h
├── main.c
├── mult.c
├── mult.o # 生成的目标檔案
├── sub.c
└── sub.o # 生成的目标檔案
第二步:使用 gcc 将得到的目标檔案打包生成動态庫,需要使用參數 -shared
# 2. 将得到 .o 打包成動态庫, 使用gcc , 參數 -shared
$ gcc -shared add.o div.o mult.o sub.o -o libcalc.so
# 檢查目錄中是否生成了動态庫
$ tree
.
├── add.c
├── add.o
├── div.c
├── div.o
├── include
│ └── `head.h ===> 和動态庫一起釋出
├── `libcalc.so ===> 生成的動态庫
├── main.c
├── mult.c
├── mult.o
├── sub.c
└── sub.o
第三步:釋出生成的動态庫和相關的頭檔案
# 3. 釋出庫檔案和頭檔案
1. head.h
2. libcalc.so
動态庫的使用
當我們得到了一個可用的動态庫之後,需要将其放到一個目錄中,然後根據得到的頭檔案編寫測試代碼,對動态庫中的函數進行調用。
# 1. 拿到釋出的動态庫
`head.h libcalc.so
# 2. 基于頭檔案編寫測試程式, 測試動态庫中提供的接口是否可用
`main.c`
# 示例目錄:
.
├── head.h ==> 函數聲明
├── libcalc.so ==> 函數定義
└── main.c ==> 函數測試
代碼編譯:
# 在編譯的時候指定動态庫相關的資訊: 庫的路徑 -L, 庫的名字 -l
$ gcc main.c -o app -L./ -lcalc
# 檢視是否生成了可執行程式
$ tree
.
├── app # 生成的可執行程式
├── head.h
├── libcalc.so
└── main.c
# 執行生成的可執行程式, 錯誤提示 ==> 可執行程式執行的時候找不到動态庫
$ ./app
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory
解決動态庫無法加載問題:
動态連結器是一個獨立于應用程式的程序,屬于作業系統,當使用者的程式需要加載動态庫的時候動态連接配接器就開始工作了,很顯然動态連接配接器根本就不知道使用者通過 gcc 編譯程式的時候通過參數 -L 指定的路徑。
那麼動态連結器是如何搜尋某一個動态庫的呢,在它内部有一個預設的搜尋順序,按照優先級從高到低的順序分别是:
- 可執行檔案内部的 DT_RPATH 段
- 系統的環境變量 LD_LIBRARY_PATH
- 系統動态庫的緩存檔案 /etc/ld.so.cache
- 存儲動态庫 / 靜态庫的系統目錄 /lib/, /usr/lib 等
按照以上四個順序,依次搜尋,找到之後結束周遊,最終還是沒找到,動态連接配接器就會提示動态庫找不到的錯誤資訊。
上述運作錯誤就是找不到動态庫的路徑導緻的
解決方案如下:
方案 1: 将庫路徑添加到環境變量 LD_LIBRARY_PATH 中
- 找到相關的配置檔案
使用者級别: ~/.bashrc —> 設定對目前使用者有效
系統級别: /etc/profile —> 設定對所有使用者有效
- 使用 vim 打開配置檔案,在檔案最後添加這樣一句話
# 自己把路徑寫進去就行了
export LIBRARY_PATH=$LIBRARY_PATH:動态庫的絕對路徑
-
讓修改的配置檔案生效
修改了使用者級别的配置檔案,關閉目前終端,打開一個新的終端配置就生效了
修改了系統級别的配置檔案,登出或關閉系統,再開機配置就生效了
不想執行上邊的操作,可以執行一個指令讓配置重新被加載
# 修改的是哪一個就執行對應的那個指令
# source 可以簡寫為一個 . , 作用是讓檔案内容被重新加載
$ source ~/.bashrc (. ~/.bashrc)
$ source /etc/profile (. /etc/profile)
方案 2: 更新 /etc/ld.so.cache 檔案
- 找到動态庫所在的絕對路徑(不包括庫的名字)比如:/home/hou/Library/
- 使用 vim 修改 /etc/ld.so.conf 這個檔案,将上邊的路徑添加到檔案中 (獨自占一行)
# 1. 打開檔案
$ sudo vim /etc/ld.so.conf
# 2. 添加動态庫路徑, 并儲存退出
- 更新 /etc/ld.so.conf 中的資料到 /etc/ld.so.cache 中
# 必須使用管理者權限執行這個指令
$ sudo ldconfig
# 庫拷貝
sudo cp /xxx/xxx/libxxx.so /usr/lib
# 建立軟連接配接
sudo ln -s /xxx/xxx/libxxx.so /usr/lib/libxxx.so
驗證
# 文法:
$ ldd 可執行程式名
# 舉例:
$ ldd app
linux-vdso.so.1 => (0x00007ffe8fbd6000)
libcalc.so => /home/hou/Linux/3Day/calc/test/libcalc.so (0x00007f5d85dd4000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5d85a0a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5d85fd6000) ==> 動态連結器, 作業系統提供