Linux 下高版本glibc庫(kù)應(yīng)用程序在低版本環(huán)境下運(yùn)行方法(linux各版本對(duì)glibc版本兼容)
整個(gè)Linux底層基于gcc 編譯器編譯C和C 程序構(gòu)造的,因此 gcc 通用c類庫(kù)是最基本,最重要的類庫(kù),稱為glibc庫(kù).不僅一般的應(yīng)用程序,各種操作系統(tǒng)的命令行工具也是基glibc的構(gòu)建的.
在部署C/C 應(yīng)用時(shí),經(jīng)常碰到這樣問題,就是開發(fā)環(huán)境 的glibc版本較高,但是實(shí)際部署的運(yùn)行環(huán)境 有的glibc,版本高,有的glibc版 本低,強(qiáng)行運(yùn)行會(huì)顯示如下提示,
./ResolverService: /lib/aarch64-linux-gnu/libc.so.6: version GLIBC_2.28' not found (required by /usr/lib/libQT5Network.so.5)
這就是因?yàn)檫\(yùn)行環(huán)境 最高支持GLIBC 2.26,而程序是在2.31的環(huán)境 編譯的,這是一個(gè)非常常見的問題.如何解決?
高成本解決方案
升級(jí)系統(tǒng)glibc庫(kù)
最容易想到是升級(jí)系統(tǒng)glibc庫(kù),這危險(xiǎn)性相當(dāng)于自己給自己做心臟手術(shù),基本做到一半,所有系統(tǒng)工具都無(wú)法工作,導(dǎo)致系統(tǒng)崩掉,只能重裝系統(tǒng),所以嚴(yán)重不推薦.
使用虛擬機(jī)環(huán)境
在服務(wù)端部署經(jīng)常采用的辦法,就是在操作系統(tǒng)之上再運(yùn)行一層虛擬機(jī),比如 Docker,KVM,在虛擬機(jī)運(yùn)行是統(tǒng)一glibc庫(kù)版 本,這個(gè)方法缺點(diǎn)就需要系統(tǒng)資源較多,安裝成本比較高.在嵌入式Linux下并不適用.
低成本解決辦法 — 修改應(yīng)用程序
解決的思路就是讓應(yīng)用程序 不去裝載默認(rèn)glibc的入口程序 ,一般固定是在/lib目錄下,通常是名字是 ld-linux打頭動(dòng)態(tài)庫(kù)文件.
轉(zhuǎn)而讓應(yīng)用程序 去尋找用戶自己安裝的glibc目錄下的裝載程序.
這個(gè)方法只需在用gcc編譯應(yīng)用程序,鏈接選項(xiàng)就可以一次性解決所有動(dòng)態(tài)庫(kù)版本不符問題,而且哪怕你沒有源碼只有運(yùn)行程序,也可以同樣用patchelf這個(gè)工具 給應(yīng)用程序 打補(bǔ)丁即可.代價(jià)非常低.
比如我一個(gè)Qt應(yīng)用程序 ,使用了幾十個(gè)Qt 的so動(dòng)態(tài)庫(kù),很多都 引用高版本的glibc函數(shù),只需要在編譯最終應(yīng)用程序加一句就行.不需要重新編譯Qt類庫(kù),是不是很方便?
步驟1:準(zhǔn)備高版本的glibc
可以在安裝高版本的環(huán)境直接把文件拷出來(lái)即可,比如我ARM64板上glibc 2.31需要如下庫(kù),其中 ld-linux-aarch64.so.1 就是入口可執(zhí)行,假設(shè)我把這一些文件放在 /home/hxy/glibc-2.31 目錄下.大體有如下文件
ld-2.31.so libc.so.6 libpthread.so.0ld-linux-aarch64.so.1 libdl-2.31.so libresolv-2.31.solibc-2.31.so libdl.a libresolv.alibc.a libdl.so libresolv.solibc_nonshared.a libdl.so.2 libresolv.so.2libcrypt.a libgcc_s.so.1 librt-2.31.solibcrypto.so.1.1 libm-2.31.so librt.alibcryptsetup.so.12 libm.a librt.solibcryptsetup.so.12.5.0 libm.so librt.so.1libcrypt.so libm.so.6 libstdc .so.6libcrypt.so.1 libpthread-2.31.so libstdc .so.6.0.28libcrypt.so.1.1.0 libpthread.alibc.so libpthread.so
找不到對(duì)應(yīng)glibc動(dòng)態(tài)庫(kù),可以這個(gè)開源項(xiàng)目下 https://github.com/matrix1001/glibc-all-in-one 取得自己的想要的版本編譯一下.或者直接搜索相應(yīng)的 deb安裝包
步驟2: 編譯時(shí)加入對(duì)另一版本glibc 的引用
需要增加兩個(gè)gcc 的鏈接參數(shù):
- 顯式鏈接指定動(dòng)態(tài)庫(kù)加載器 -Wl,–dynamic-linker
顯式鏈接是相對(duì)用 -L -l c 隱式鏈接而言, 隱式鏈接就是編譯后的應(yīng)用程序ELF可執(zhí)行文件只保存鏈接庫(kù)名字,不帶路徑,加徑順序按缺省規(guī)則來(lái)進(jìn)行. 一般是 /lib –> ld.so.conf–>LD_LIBRARY_PATH 這樣順序進(jìn)行.而使用–dynamic-linker表示這是帶路徑動(dòng)態(tài)庫(kù)加載器直接裝入即可.
在我的例子是,這樣就跳開了/lib下同名動(dòng)態(tài)glibc入口的引用,直接引用我的目錄名字
-Wl,--dynamic-linker=/home/hxy/glibc-2.31/ld-linux-aarch64.so.1
2.指定動(dòng)態(tài)庫(kù)的裝載目錄 -Wl,–rpath
相當(dāng)其它未帶路徑so,優(yōu)先從這個(gè)目錄裝入,這一句也很重要因?yàn)閘d-linux-xxx.so動(dòng)態(tài)庫(kù)需要裝入同目錄下其它glibc的動(dòng)態(tài),因此必須設(shè)置這個(gè)目錄,否則又去裝入/lib的動(dòng)態(tài)庫(kù)導(dǎo)致運(yùn)行失敗
-Wl,--rpath=/home/hxy/glibc-2.31/
以上兩句是要同時(shí)放在一個(gè)鏈接語(yǔ)句當(dāng)中,順便提一下,如果你是Linux 下Qt 項(xiàng)目,只需在pro文件增加一句QMAKE_LFLAGS的定義即可在最終的編譯語(yǔ)句達(dá)到這樣效果
QMAKE_LFLAGS = -Wl,--dynamic-linker=/home/hxy/glibc-2.31/ld-linux-aarch64.so.1 -Wl,--rpath=/home/hxy/glibc-2.31/
編譯好在目標(biāo)主機(jī)上也建立一個(gè)/home/hxy/glibc-2.31 并放入動(dòng)態(tài)庫(kù).即可運(yùn)行
補(bǔ)充步驟2: 只有可執(zhí)行文件 ,用patchelf 打補(bǔ)丁
當(dāng)沒有源碼編譯或者編譯很麻煩的情況,可以直接對(duì)可執(zhí)行elf文件打補(bǔ)丁加上上述兩個(gè)參數(shù),效果一樣的 ,首先安裝patchelf
sudo apt-get install patchelf
elf 的interpreter字段是指明動(dòng)態(tài)加載器路徑,修改這個(gè)字段的作用等同于 -Wl,–dynamic-linker
rpath字段就是對(duì)應(yīng)-Wl,–rpath的內(nèi)容,假設(shè)應(yīng)用程序名叫ResolverService,補(bǔ)丁指令如下
patchelf –set-interpreter /home/firefly/glibc-2.31/ld-linux-aarch64.so.1
–set-rpath /home/firefly/glibc-2.31 ResolverService?
以上兩種方法bluedrum即在RK3399的高版本環(huán)境運(yùn)行成功,同時(shí)又能切換到RK3308低版 本下環(huán)境 運(yùn)行.