非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