比如我們用gcc 9.3.0編譯程序,但需要發(fā)布的機(jī)器gcc版本是4.8.5,怎么辦?
你可能想到如下方法
- 靜態(tài)編譯
- 容器發(fā)布
- 打包依賴(lài)的so,使用本地so運(yùn)行程序
1.靜態(tài)編譯
將libc和libstdc++靜態(tài)編譯,編譯時(shí)帶上如下參數(shù)。
g++ -static-libgcc -static-libstdc++
glibc并不推薦靜態(tài)鏈接,你依賴(lài)的其他庫(kù)可能依賴(lài)的了glibc,并且往往是動(dòng)態(tài)鏈接的,可以通過(guò)nm <bin> | grep GLIBC_確定你的程序是否依賴(lài)了glibc。
2.容器發(fā)布
使用攜帶gcc9.3.0環(huán)境的容器發(fā)布程序,是可以的。但是在一些沒(méi)有容器且沒(méi)有sudo權(quán)限的場(chǎng)合,依然不太友好。
3.打包依賴(lài)的so發(fā)布
這個(gè)方法雖然聽(tīng)起來(lái)不是很優(yōu)雅,但其實(shí)如果你對(duì)elf文件有一些了解,是不錯(cuò)的方式。下面說(shuō)下具體的方法。
3.1 方式1 在編譯時(shí)設(shè)置rpath和dynamic linker
當(dāng)你有條件獲得程序源碼,并能夠重新編譯時(shí),可以直接在編譯時(shí)指定相關(guān)參數(shù)來(lái)解決。
先說(shuō)編譯時(shí)要增加的參數(shù):
# 絕對(duì)路徑
gcc -Wl,-rpath='/my/lib',-dynamic-linker='/my/lib/ld-linux.so.2'
gcc參數(shù)
-Wl,option
Pass option as an option to the linker.
ld參數(shù)
-rpath=dir
Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects.
--dynamic-linker=file
Set the name of the dynamic linker.
這兩個(gè)參數(shù)分別設(shè)置的elf文件中的rpath和interpreter字段。
rpath
全名run-time search path,是elf文件中一個(gè)字段,它指定了可執(zhí)行文件執(zhí)行時(shí)搜索so文件的第一優(yōu)先位置,一般編譯器默認(rèn)將該字段設(shè)為空。elf文件中還有一個(gè)類(lèi)似的字段runpath,其作用與rpath類(lèi)似,但搜索優(yōu)先級(jí)稍低。搜索優(yōu)先級(jí):
rpath > LD_LIBRARY_PATH > runpath > ldconfig緩存 > 默認(rèn)的/lib,/usr/lib等
如果你需要使用相對(duì)路徑指定lib文件夾,可以使用ORIGIN變量,ld會(huì)將ORIGIN理解成可執(zhí)行文件所在的路徑。
gcc -Wl,-rpath='$ORIGIN/../lib'
interpreter
動(dòng)態(tài)庫(kù)加載器,程序啟動(dòng)時(shí),操作系統(tǒng)會(huì)先把控制權(quán)轉(zhuǎn)交給ld-linux-x86-64.so.2,該so負(fù)責(zé)加載所有程序依賴(lài)的so。。這個(gè)字段在鏈接時(shí)會(huì)幫你自動(dòng)設(shè)置,64bit程序一般為/lib64/ld-linux-x86-64.so.2。修改rpath或者LD_LIBRARY_PATH指向本地lib目錄,但通過(guò)ldd程序,發(fā)現(xiàn)/lib64/ld-linux-x86-64.so.2這個(gè)so仍然指向系統(tǒng)so。原因就是這個(gè)字段是寫(xiě)死在elf文件中的,并不受LD_LIBRARY_PATH影響。
編譯時(shí)帶上這兩個(gè)參數(shù),下面只需要將你程序依賴(lài)的so打包一份,隨程序進(jìn)行發(fā)布即可。
3.2 方式2 直接修改二進(jìn)制程序的rpath和interpreter
當(dāng)你無(wú)法編譯程序時(shí),也可以通過(guò)其他方式修改rpath和interpreter。這種情況需要使用到一個(gè)工具patchelf,通過(guò)dnf install patchelf即可安裝。你可以通過(guò)它修改elf文件的rpath和interpreter:
patchelf --set-rpath /my/lib your_program
patchelf --set-interpreter /my/lib/ld-linux.so.2 your_program
除了絕對(duì)路徑,一種比較常見(jiàn)的方式是在部署前,使用pwd獲取當(dāng)前路徑,使用相對(duì)路徑指向本地lib。
patchelf --set-rpath `pwd`/../lib your_program
patchelf --set-interpreter `pwd`/../lib/ld-linux-x86-64.so.2 ./your_program