龙空技术网

Linux 下高版本glibc库应用程序在低版本环境下运行方法

软核改造祝融叔 465

前言:

现时同学们对“丢失qt5network”大体比较关怀,兄弟们都想要学习一些“丢失qt5network”的相关知识。那么小编在网摘上网罗了一些关于“丢失qt5network””的相关内容,希望小伙伴们能喜欢,朋友们快快来了解一下吧!

整个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动态库,可以这个开源项目下 取得自己的想要的版本编译一下.或者直接搜索相应的 deb安装包

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

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

显式链接指定动态库加载器 -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低版 本下环境 运行.

标签: #丢失qt5network