非root用戶更新glibc版本的悲慘故事
前言
作爲一個大集羣裏的一個沒有root權限的小用戶,最近在使用一個生信軟件時,要求GLIBC庫的版本在2.27以上。衆所周知,對於沒有root權限的用戶,每次安裝更新C庫,都是一個艱難的考驗。
GLIBC(GNU Libc或GNU C庫的縮寫)是一個庫,提供應用程序和Linux內核之間的接口。儘管它的名稱是C庫(用“ C”語言編寫的程序的庫),但實際上所有動態鏈接的程序二進制文件都依賴它——它是幾乎所有Linux操作系統中的實際系統庫。Glibc 是系統的兩大核心之一(還有一個是內核)。Glibc具有允許向後兼容的版本控制系統(即在較舊版本的glibc上運行構建的舊程序可以繼續在新的glibc上運行);但是,依賴較新版本的glibc的程序通常將無法在舊版本glibc的系統上運行。
總體來說,不僅僅只是linux上的一些應用,linux上的一些基礎命令,比如ls、mv、rm什麼的,都依賴於它。一個不小心,你的服務器即將失控,出現可怕的 segmentation error 報錯。此外,還要小心網上一堆粗製濫造的教程,分分鐘帶你走上悲劇的不歸路。甚至有一些人在升級失敗系統退出後,無法重新進入系統了。
查看現有版本和動態庫依賴
查看glibc版本
strings /lib64/libc.so.6 | grep GLIBC
結果:
可以看到我的glibc版本最高只有2.17,遠遠達不到軟件的要求。
查看動態庫的依賴
這一步對於安裝不是必須的,但是由於我是安裝別的軟件時,需要這個庫,所以還是寫了。同時,也是因爲我這個軟件對C庫的依賴不止一個,導致問題變得複雜了很多。
這裏,主要使用ldd命令。 在linux中, ldd是list, dynamic, dependencies的縮寫, 意思是, 列出動態庫依賴關係。
使用示例:
ldd myapp
結果:
常規安裝過程
下載&解壓
wget https://ftp.gnu.org/gnu/libc/glibc-2.27.tar.gz #我這裏選擇下載的是2.27版本
tar -xzf glibc-2.27.tar.gz
cd glibc-2.27/ #移動到glibc安裝包文件夾裏面
mkdir build #創建一個build文件夾
cd build
這裏需要指出,安裝glibc需要gcc,因此gcc的版本不能太低。我這裏的gcc版本是6.4.0。
接下來進行編譯和安裝
../configure --prefix=/your/path/
make -j4 & make install
最後將 LD_LIBRARY_PATH 添加到配置文件
export LD_LIBRARY_PATH=/your/path/lib:$LD_LIBRARY_PATH
報錯&改錯無限循環
01. 編譯出錯
出錯的命令是:
../configure --prefix=/your/path/
本來很常規的配置目錄操作,居然就出錯了……
(由此開啓了我苦逼的安裝過程)
出錯信息是:
checking LD_LIBRARY_PATH variable... contains current directory
configure: error:
*** LD_LIBRARY_PATH shouldn't contain the current directory when
*** building glibc. Please change the environment variable
*** and run configure again.
即LD_LIBRARY_PATH的路徑裏包含了當前目錄。
我使用echo查看了我的LD_LIBRARY_PATH,其內容大概是這樣的:
echo $LD_LIBRARY_PATH
/my/path1/lib:/my/path2/lib2:
LD_LIBRARY_PATH 不能以終結符(”:” and “;”)作爲開始和最後一個字符,且不能有2個終結符連在一起。因爲在環境變量的最後面有一個“:”,程序將此分隔符解釋爲當前目錄了。解決方案是修改LD_LIBRARY_PATH,刪除掉最後的 ”:” 分隔符。
02 . 添加LD_LIBRARY_PATH之後系統崩潰
在漫長的make等待之後,終於結束了安裝環節。整個過程沒什麼報錯(好像有一個warning),然後需要將新的LD_LIBRARY_PATH添加到環境變量裏,即:
export LD_LIBRARY_PATH=/your/path/lib:$LD_LIBRARY_PATH
在使用上述命令之後,我的系統就崩潰了。具體表現爲,使用一些常用的linux命令(如ls、mv),直接輸出Segmentation fault。
在這裏,需要提到的是,很多中文教程沒有設置LD_LIBRARY_PATH,直接開始替換軟連接(這些人應該都是有root權限的,羨慕ing)。
作爲一個非root用戶的自覺,很多地方我們只能看看,並不能修改,所以那種刪除舊鏈接之後整個服務器很多命令直接不能用的重量級殺傷力我們並不能做到(最多也只能危害下自己的服務器)。另外,奉勸那些有root權限的用戶,使用下述命令一定要三思再三思,不然你一定會悔恨更久……可以使用LD_PRELOAD臨時指定一個glibc庫,具體可以參照 參考資料第一條。
rm -rf /lib64/libc.so.6
在百度搜了下,相關問題很少,回答更是……完全沒用,只能祭出了google大法。
google大法還是要好用一些,stackoverflow上,這樣的問題很多,果然世界上還是有很多人和我一樣,崩潰地在升級glibc。其中,最爲有用的回答參見第二條參考資料。
具體來說,出現這樣的問題的主要原因是glibc是一個包含很多部分的庫(超過200個共享庫),每個動態鏈接程序最重要的依賴關係是鏈接程序。所有這些庫必須與 linker 的版本匹配 。
其中有一個 ld-版本號.2,它必須與libc.so.6匹配,否則就會出現我所看到的錯誤。鏈接時,ld-linux.so.2的絕對路徑被硬編碼到可執行文件中,並且在鏈接完成後不能輕易更改。
試了一下大家給出的方案,一個有用的解決方案是使用patchelf在代碼編譯後直接修改elf格式的可執行文件,通過設置-set-rpath修改library搜索路徑,使用–set-interpreter修改dynamic linker。
首先需要下載patchelf,然後使用示例如下(myapp是我的二進制可執行文件):
patchelf --set-rpath /glibc/path/lib --set-interpreter /glibc/path/lib/ld-2.27.so myapp
需要提醒的是,這樣的操作會直接修改myapp,建議做個備份之後再進行操作(小心至上😨)。
結果
在我更新glibc之前,我執行我的文件,結果是:
在經過一段時間的摧殘安裝並使用patchelf修改文件之後,新的結果是:
可以看到,glibc_2.27的依賴已經解決,但因爲我使用了新的library,libstdc++.so.6不僅僅是版本的問題,現在完全找不到了。這個問題我本想通過將它所需的庫copy到新的library裏來解決,但是移入之後又出現了新的Segmentation fault的錯誤。一切又回到了原點(Ok,fine🙂,最後我妥協地向服務器管理員提交了更新glibc的issue,希望他能夠儘快臨幸下我,拯救下我卡來卡去的科研……)
summary
- glibc安裝涉及到系統核心,很容易出錯;
- Segmentation fault的報錯一般與動態庫鏈接不匹配有關;
- patchelf可以一定程度解決glibc更新過程中動態庫鏈接不匹配的問題,前提是你缺的只剩這一個庫了。
參考資料
Linux/Centos下/lib64/libc.so.6: version `GLIBC_2.14’ not found問題
Multiple glibc libraries on a single host