本文將演示4種各自獨(dú)立的得到最終二進(jìn)制文件的方式。代碼采用C語(yǔ)言。
首先列出所有代碼文件內(nèi)容,一共3個(gè)文件:drive.h,drive.c,main.c,分別為 動(dòng)態(tài)庫(kù)libdrive.so 的頭文件、函數(shù)實(shí)現(xiàn)文件、主入口main()文件。內(nèi)容分別如下。為了簡(jiǎn)明易懂,只以最簡(jiǎn)單的功能實(shí)現(xiàn)。
(1) drive.h 文件內(nèi)容:
//聲明 加法函數(shù)的函數(shù)入?yún)⒑头祷刂?。int dr(int a, int b);
(2) drive.c 文件內(nèi)容:
#include <stdio.h>//定義一個(gè)加法函數(shù)int dr(int a, int b) { return a + b;}
(3) main.c 文件內(nèi)容
//調(diào)用動(dòng)態(tài)庫(kù)內(nèi)的add()函數(shù),3+5,所以打印結(jié)果應(yīng)當(dāng)為8#include <stdio.h>#include "drive.h"int main() { // 調(diào)用加法函數(shù) int result = add(3, 5); printf("Sum: %d/n", result); return 0;}
(1) 編譯 drive.c 文件為.o 文件:
# gcc -c drive.c -o drive.o
gcc 的幾個(gè)重要編譯參數(shù),上面用到了-c 、-o 等參數(shù),下面還會(huì)用到所以再次貼上參數(shù)說(shuō)明:
-c 編譯、匯編到目標(biāo)代碼,不進(jìn)行鏈接。 -o <文件> 輸出到 <文件>。 -pie 生成動(dòng)態(tài)鏈接的位置無(wú)關(guān)可執(zhí)行文件。 -shared 生成一個(gè)共享庫(kù)。 -static 告訴ld在鏈接的時(shí)候生成純靜態(tài)可執(zhí)行文件。
(2) 使用編譯drive.c得到的drive.o 文件作為材料,生成 libdrive.a 靜態(tài)庫(kù)文件。文件名前面加lib是為了gcc鏈接文件時(shí) 默認(rèn)的約定找以lib開(kāi)始的庫(kù)文件:
# ar rcs libdrive.a drive.o
(3) 鏈接得到最終可執(zhí)行文件:
# gcc main.c -L$PWD -ldrive -o main-static# file main-static main-static: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=84fa8514ed5a9ef043b88d1957de83248208dac2, for GNU/Linux 3.2.0, not stripped[lph@localhost 2023-12-12]$ ldd main-static linux-vdso.so.1 (0x00007ffd087f3000) libc.so.6 => /lib64/libc.so.6 (0x00007fb2cbb82000) /lib64/ld-linux-x86-64.so.2 (0x00007fb2cbd79000)
當(dāng)前目錄下只有l(wèi)ibdrive.a 庫(kù),沒(méi)有動(dòng)態(tài)庫(kù),所以不會(huì)優(yōu)先鏈接.so庫(kù),只能鏈接libdrive.a庫(kù)。所以我使用file 命令查看main-static的成分,我以為它就是靜態(tài)文件,結(jié)果發(fā)現(xiàn)怎么還是動(dòng)態(tài)鏈接的?(鏈接了3個(gè)動(dòng)態(tài)庫(kù)文件)
雖然通過(guò)-ldrive 我們確實(shí)鏈接了libdrive.a靜態(tài)文件進(jìn)入 main-static文件內(nèi)部(所以以上出現(xiàn)的鏈接信息里不會(huì)顯示libdrive.so 或 libdrive.a,但file的結(jié)果說(shuō)得到的main-static文件仍然是dynamically linked得的,為什么會(huì)這樣呢?
sudo yum install glibc-static
新安裝的libc的靜態(tài)庫(kù)文件的路徑:
# rpm -q --list glibc-static/usr/lib64/libBrokenLocale.a/usr/lib64/libc.a/usr/lib64/libm-2.37.a/usr/lib64/libm.a/usr/lib64/libmvec.a/usr/lib64/libresolv.a
重新嘗試實(shí)現(xiàn)徹底的靜態(tài)鏈接:
[lph@localhost 2023-12-12]$ gcc -static main.c -L. -ldrive -o main-static-true[lph@localhost 2023-12-12]$ file main-static-true main-static-true: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=be4d6a0e422b3a1ae1ccf0a9b162f5e628eb47b3, for GNU/Linux 3.2.0, not stripped, too many notes (256)[lph@localhost 2023-12-12]$ ldd main-static-true 不是動(dòng)態(tài)可執(zhí)行文件
以上 ldd 的結(jié)果說(shuō)明,main-static-true 文件才是真正靜態(tài)鏈接的。
來(lái)對(duì)比一下 假的 main-static 跟真的可執(zhí)行文件大小差距多大:
$ ls -lht main-static*776K 12月12日 22:01 main-static-true17K 12月12日 21:42 main-static
文件大小相差45倍。由于功能少所以這個(gè)倍數(shù)并不嚴(yán)謹(jǐn)。但大體上我們可以了解純靜態(tài)的可執(zhí)行文件,確實(shí)會(huì)比 dynamic linked的可執(zhí)行文件大很多。不過(guò)由于現(xiàn)在硬盤(pán)和內(nèi)存的擴(kuò)大,成本降低,越來(lái)越多新編程語(yǔ)言默認(rèn)采用純靜態(tài)編譯可執(zhí)行文件。以增強(qiáng)跨平臺(tái)的運(yùn)行能力。
將當(dāng)前目錄下的所有.o文件(目前只有 drive.o一個(gè)文件)作為材料,生成最終的 libdrive.so動(dòng)態(tài)庫(kù)文件(實(shí)際相當(dāng)于將 .o文件打了個(gè)壓縮包到 .a文件里,并調(diào)整了鏈接符號(hào))。
gcc -shared *.o -o libdrive.so
所以我們目錄下截至現(xiàn)在已有這些文件:
# ls -lht總計(jì) 52K-rwxrwxr-x 1 lph lph 16K 12月12日 16:43 libdrive.so-rwxrwxr-x 1 lph lph 16K 12月12日 16:30 main-static-true-rwxrwxr-x 1 lph lph 16K 12月12日 16:20 main-static-rw-rw-r-- 1 lph lph 136 12月12日 16:18 main.c-rw-rw-r-- 1 lph lph 1.4K 12月12日 16:17 libdrive.a-rw-rw-r-- 1 lph lph 1.3K 12月12日 16:17 drive.o-rw-rw-r-- 1 lph lph 91 12月12日 16:12 drive.c-rw-rw-r-- 1 lph lph 91 12月12日 16:12 drive.h
既然是動(dòng)態(tài)庫(kù),總要能被調(diào)用者直到你的暴露的借口。我們驗(yàn)證下這個(gè)libdrive.so動(dòng)態(tài)庫(kù)是否也暴露了add函數(shù)呢?
$ nm -D libdrive.so 00000000000010f9 T add w __cxa_finalize@GLIBC_2.2.5 w __gmon_start__ w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable
從結(jié)果看,確實(shí)暴露了add 函數(shù)。至于其他4個(gè)W是什么作用,我們目前暫時(shí)不管。
gcc main.c -L./ -ldrive -o main_share
上面說(shuō)過(guò),當(dāng)libdrive.so 不存在時(shí),libdrive.a 存在,則gcc 根據(jù)鏈接 -L./ -ldrive 參數(shù),在對(duì)應(yīng)目錄下只找libdrive.a,所以以前只能鏈接.a文件?,F(xiàn)在有了.so文件,那么-L./ -ldrive會(huì)優(yōu)先鏈接libdrive.so。讓我們用ldd 命令檢驗(yàn)一下:
$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD ldd ./main_share linux-vdso.so.1 (0x00007ffccd74a000) libdrive.so (0x00007ff2cbb28000) libc.so.6 => /lib64/libc.so.6 (0x00007ff2cb933000) /lib64/ld-linux-x86-64.so.2 (0x00007ff2cbb2f000)
這里我們?cè)诿钪信R時(shí)設(shè)置 庫(kù)文件搜索路徑包含當(dāng)前路徑$PWD。
從結(jié)果看,我們的main_share 確實(shí)鏈接了動(dòng)態(tài)庫(kù) libdrive.so。
「完」
先清理一下現(xiàn)場(chǎng):刪掉 drive.o libdrive.so 這兩個(gè)文件。
$ rm -f drive.o libdrive.so
這里使用現(xiàn)有的 libdrive.a 動(dòng)態(tài)庫(kù),看能否轉(zhuǎn)化成 libdrive.so 文件。
(1) 之前說(shuō)過(guò).a相當(dāng)于.o 文件的壓縮包,那么我們可以分離出 drive.o 文件
ar -x libdrive.a
(2) 編譯生成 .so 文件
gcc -shared *.o -o libdrive.so
(3) 查看導(dǎo)出函數(shù)
$ nm -D libdrive.so00000000000010f9 T add w __cxa_finalize@GLIBC_2.2.5 w __gmon_start__ w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable$
結(jié)果與上上面的 nm -D libdrive.so 輸出一致。作為驗(yàn)證我們將使用這個(gè)轉(zhuǎn)化出的so動(dòng)態(tài)庫(kù)測(cè)試對(duì)main()的鏈接和運(yùn)行效果:
$ gcc main.c -L./ -ldrive -o main_share_from_libdrive_amain.c: 在函數(shù)‘main’中:main.c:6:18: 警告:隱式聲明函數(shù)‘a(chǎn)dd’ [-Wimplicit-function-declaration] 6 | int result = add(3, 5); | ^~~ $ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD ./main_share_from_libdrive_aSum: 8
警告信息不管。運(yùn)行效果符合預(yù)期,打印了3+5的結(jié)果。說(shuō)明靜態(tài)庫(kù)轉(zhuǎn)成動(dòng)態(tài)庫(kù)也是OK可以用的(實(shí)際場(chǎng)景gcc還要加-fPIC 參數(shù)來(lái)生成動(dòng)態(tài)庫(kù),以增強(qiáng)動(dòng)態(tài)庫(kù)的通用性不依賴于固定地址,本文不深入解析 -fPIC,請(qǐng)讀者自己查信息 )。
總結(jié)一下,經(jīng)過(guò)以上4個(gè)場(chǎng)景的演示驗(yàn)證,我們對(duì)Linux的動(dòng)態(tài)庫(kù)、靜態(tài)庫(kù)之間的關(guān)系,有了更深入的認(rèn)知。
本文鏈接:http://www.tebozhan.com/showinfo-26-50765-0.htmlLinux從外到內(nèi)剝開(kāi)動(dòng)態(tài)庫(kù),一個(gè)簡(jiǎn)單例子看懂Linux下的動(dòng)態(tài)庫(kù)開(kāi)發(fā)原理
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com