Linux 下高版本glibc库应用程序在低版本环境下运行方法(linux各版本对glibc版本兼容)

整个Linux底层基于gcc 编译器编译C和C 程序构造的,因此 gcc 通用c类库是最基本,最重要的类库,称为glibc库.不仅一般的应用程序,各种操作系统的命令行工具也是基glibc的构建的.

在部署C/C 应用时,经常碰到这样问题,就是开发环境 的glibc版本较高,但是实际部署的运行环境 有的glibc,版本高,有的glibc版 本低,强行运行会显示如下提示,

./ResolverService: /lib/aarch64-linux-gnu/libc.so.6: version GLIBC_2.28' not found (required by /usr/lib/libQT5Network.so.5)

这就是因为运行环境 最高支持GLIBC 2.26,而程序是在2.31的环境 编译的,这是一个非常常见的问题.如何解决?

高成本解决方案


升级系统glibc库

最容易想到是升级系统glibc库,这危险性相当于自己给自己做心脏手术,基本做到一半,所有系统工具都无法工作,导致系统崩掉,只能重装系统,所以严重不推荐.

使用虚拟机环境

在服务端部署经常采用的办法,就是在操作系统之上再运行一层虚拟机,比如 Docker,KVM,在虚拟机运行是统一glibc库版 本,这个方法缺点就需要系统资源较多,安装成本比较高.在嵌入式Linux下并不适用.

低成本解决办法 — 修改应用程序


解决的思路就是让应用程序 不去装载默认glibc的入口程序 ,一般固定是在/lib目录下,通常是名字是 ld-linux打头动态库文件.

转而让应用程序 去寻找用户自己安装的glibc目录下的装载程序.

这个方法只需在用gcc编译应用程序,链接选项就可以一次性解决所有动态库版本不符问题,而且哪怕你没有源码只有运行程序,也可以同样用patchelf这个工具 给应用程序 打补丁即可.代价非常低.

比如我一个Qt应用程序 ,使用了几十个Qt 的so动态库,很多都 引用高版本的glibc函数,只需要在编译最终应用程序加一句就行.不需要重新编译Qt类库,是不是很方便?

步骤1:准备高版本的glibc

可以在安装高版本的环境直接把文件拷出来即可,比如我ARM64板上glibc 2.31需要如下库,其中 ld-linux-aarch64.so.1 就是入口可执行,假设我把这一些文件放在 /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

找不到对应glibc动态库,可以这个开源项目下 https://github.com/matrix1001/glibc-all-in-one 取得自己的想要的版本编译一下.或者直接搜索相应的 deb安装包

步骤2: 编译时加入对另一版本glibc 的引用

需要增加两个gcc 的链接参数:

  1. 显式链接指定动态库加载器 -Wl,–dynamic-linker

显式链接是相对用 -L -l c 隐式链接而言, 隐式链接就是编译后的应用程序ELF可执行文件只保存链接库名字,不带路径,加径顺序按缺省规则来进行. 一般是 /lib –> ld.so.conf–>LD_LIBRARY_PATH 这样顺序进行.而使用–dynamic-linker表示这是带路径动态库加载器直接装入即可.

在我的例子是,这样就跳开了/lib下同名动态glibc入口的引用,直接引用我的目录名字

-Wl,--dynamic-linker=/home/hxy/glibc-2.31/ld-linux-aarch64.so.1

2.指定动态库的装载目录 -Wl,–rpath

相当其它未带路径so,优先从这个目录装入,这一句也很重要因为ld-linux-xxx.so动态库需要装入同目录下其它glibc的动态,因此必须设置这个目录,否则又去装入/lib的动态库导致运行失败

-Wl,--rpath=/home/hxy/glibc-2.31/

以上两句是要同时放在一个链接语句当中,顺便提一下,如果你是Linux 下Qt 项目,只需在pro文件增加一句QMAKE_LFLAGS的定义即可在最终的编译语句达到这样效果

QMAKE_LFLAGS = -Wl,--dynamic-linker=/home/hxy/glibc-2.31/ld-linux-aarch64.so.1 -Wl,--rpath=/home/hxy/glibc-2.31/

编译好在目标主机上也建立一个/home/hxy/glibc-2.31 并放入动态库.即可运行

补充步骤2: 只有可执行文件 ,用patchelf 打补丁

当没有源码编译或者编译很麻烦的情况,可以直接对可执行elf文件打补丁加上上述两个参数,效果一样的 ,首先安装patchelf

sudo apt-get install patchelf

elf 的interpreter字段是指明动态加载器路径,修改这个字段的作用等同于 -Wl,–dynamic-linker

rpath字段就是对应-Wl,–rpath的内容,假设应用程序名叫ResolverService,补丁指令如下

patchelf –set-interpreter /home/firefly/glibc-2.31/ld-linux-aarch64.so.1

–set-rpath /home/firefly/glibc-2.31 ResolverService​

以上两种方法bluedrum即在RK3399的高版本环境运行成功,同时又能切换到RK3308低版 本下环境 运行.

相关新闻

联系我们
联系我们
公众号
公众号
在线咨询
分享本页
返回顶部