高版本gcc編譯出的程序在低版本glibc機器上運行

比如我們用gcc 9.3.0編譯程序,但需要發佈的機器gcc版本是4.8.5,怎麼辦?

你可能想到如下方法

  1. 靜態編譯
  2. 容器發佈
  3. 打包依賴的so,使用本地so運行程序

1.靜態編譯

將libc和libstdc++靜態編譯,編譯時帶上如下參數。

g++ -static-libgcc -static-libstdc++

glibc並不推薦靜態鏈接,你依賴的其他庫可能依賴的了glibc,並且往往是動態鏈接的,可以通過nm <bin> | grep GLIBC_確定你的程序是否依賴了glibc。

2.容器發佈

使用攜帶gcc9.3.0環境的容器發佈程序,是可以的。但是在一些沒有容器且沒有sudo權限的場合,依然不太友好。

3.打包依賴的so發佈

這個方法雖然聽起來不是很優雅,但其實如果你對elf文件有一些瞭解,是不錯的方式。下面說下具體的方法。

3.1 方式1 在編譯時設置rpath和dynamic linker

當你有條件獲得程序源碼,並能夠重新編譯時,可以直接在編譯時指定相關參數來解決。
先說編譯時要增加的參數:

# 絕對路徑
gcc -Wl,-rpath='/my/lib',-dynamic-linker='/my/lib/ld-linux.so.2'

gcc參數

-Wl,option
Pass option as an option to the linker.

ld參數

-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.

這兩個參數分別設置的elf文件中的rpath和interpreter字段。

rpath
全名run-time search path,是elf文件中一個字段,它指定了可執行文件執行時搜索so文件的第一優先位置,一般編譯器默認將該字段設爲空。elf文件中還有一個類似的字段runpath,其作用與rpath類似,但搜索優先級稍低。搜索優先級:

rpath > LD_LIBRARY_PATH > runpath > ldconfig緩存 > 默認的/lib,/usr/lib等

如果你需要使用相對路徑指定lib文件夾,可以使用ORIGIN變量,ld會將ORIGIN理解成可執行文件所在的路徑。

gcc -Wl,-rpath='$ORIGIN/../lib'

interpreter
動態庫加載器,程序啓動時,操作系統會先把控制權轉交給ld-linux-x86-64.so.2,該so負責加載所有程序依賴的so。。這個字段在鏈接時會幫你自動設置,64bit程序一般爲/lib64/ld-linux-x86-64.so.2。修改rpath或者LD_LIBRARY_PATH指向本地lib目錄,但通過ldd程序,發現/lib64/ld-linux-x86-64.so.2這個so仍然指向系統so。原因就是這個字段是寫死在elf文件中的,並不受LD_LIBRARY_PATH影響。

編譯時帶上這兩個參數,下面只需要將你程序依賴的so打包一份,隨程序進行發佈即可。

3.2 方式2 直接修改二進制程序的rpath和interpreter

當你無法編譯程序時,也可以通過其他方式修改rpath和interpreter。這種情況需要使用到一個工具patchelf,通過dnf install patchelf即可安裝。你可以通過它修改elf文件的rpath和interpreter:

patchelf --set-rpath /my/lib your_program
patchelf --set-interpreter /my/lib/ld-linux.so.2 your_program

除了絕對路徑,一種比較常見的方式是在部署前,使用pwd獲取當前路徑,使用相對路徑指向本地lib。

patchelf --set-rpath `pwd`/../lib your_program
patchelf --set-interpreter `pwd`/../lib/ld-linux-x86-64.so.2 ./your_program

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章