【gcc】高版本gcc编译出的程序在低版本glibc机器上运行

目录

1.静态编译(多数场景不行)

2.容器发布(部分场景可以使用)

3.安装部署devtoolset

4.打包依赖的so发布(通用方案)

3.1 方式1 在编译时设置rpath和dynamic linker

3.2 方式2 直接修改二进制程序的rpath和interpreter


比如我们用gcc 9.3.0编译程序,但需要发布的机器gcc版本是4.8.5,怎么办?

你可能想到如下方法

  1. 静态编译
  2. 容器发布
  3. 打包依赖的so,使用本地so运行程序

1.静态编译(多数场景不行)

其中静态编译是行不通的,libstdc++是可以静态编译,但是libc没有提供这方面的功能,即使你是cpp程序,依然会大概率依赖libc.so
可以通过nm <bin> | grep GLIBC_确定你的程序是否依赖了glibc,没有的话,你可以考虑直接静态编译libstdc++。

2.容器发布(部分场景可以使用)

使用携带gcc9.3.0环境的容器发布程序,是可以的。但是在一些没有容器且没有sudo权限的场合,依然不太友好。

3.安装部署devtoolset

从源码安装的难度不低,向了devtoolset可能是不错的选择

RedHat 推出Software Collections的目的就是为了解决想在RedHat系统下能使用新版本的工具,让同一个工具(如gcc)的不同版本能在系统中共存,在需要的时候切换到对应的版本中

如何安装和使用devtoolset,可以参考官方指导文档。在看装前,我们可以先到Information for build搜索自己需要的package对应的源码版本,看devtoolset中的安装包对应的源码版本是多少,是否满足自身的需求。

devtoolset-8-gcc-8.3.1-3.2.el7 | Build Info | CentOS Community Build Service

  • sclo7-devtoolset-8-rh-candidate下载全部RPM包(这里偷懒下载全部包,这样就不用处理他们之间复杂的依赖关系)
  • 下载完成后,在RPM包的目录中执行yum install *.rpm,安装全部
  • 然后执行scl enable devtoolset-8 bash切换环境。
> stap -V # 我们可以执行这个命令进行验证,我们得到了Systemtap-3.3,支持linux kernel 2.6.28-4.18
Systemtap translator/driver (version 3.3/0.173, rpm 3.3-1.el7)
Copyright (C) 2005-2018 Red Hat, Inc. and others
This is free software; see the source for copying conditions.
tested kernel versions: 2.6.18 ... 4.18-rc0
enabled features: AVAHI BOOST_STRING_REF DYNINST LIBRPM LIBSQLITE3 NLS NSS READLINE

 安装最新版 devtoolset-8 — 源代码

4.打包依赖的so发布(通用方案)

这个方法虽然听起来不是很优雅,但其实如果你对elf文件有一些了解,是不错的方式。下面说下具体的方法。

3.1 方式1 在编译时设置rpath和dynamic linker

当你有条件获得程序源码,并能够重新编译时,可以直接在编译时指定相关参数来解决。
先说编译时要增加的参数:

# 绝对路径
gcc -Wl,-rpath='/my/lib',-dynamic-linker='/my/lib/ld-linux.so.2'

gcc参数

-Wl,option
Pass option as an option to the linker.

ld参数

-rpath=dir
Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects.
--dynamic-linker=file
Set the name of the dynamic linker.

这两个参数分别设置的elf文件中的rpath和interpreter字段。

rpath
全名run-time search path,是elf文件中一个字段,它指定了可执行文件执行时搜索so文件的第一优先位置,一般编译器默认将该字段设为空。elf文件中还有一个类似的字段runpath,其作用与rpath类似,但搜索优先级稍低。搜索优先级:

rpath > LD_LIBRARY_PATH > runpath > ldconfig缓存 > 默认的/lib,/usr/lib等

如果你需要使用相对路径指定lib文件夹,可以使用ORIGIN变量,ld会将ORIGIN理解成可执行文件所在的路径。

gcc -Wl,-rpath='$ORIGIN/../lib'

interpreter
全名elf interpreter,用于加载elf文件。这个字段在链接时会帮你自动设置,64bit程序一般为/lib64/ld-linux-x86-64.so.2。这也是打包so的坑之一,很多人(比如我)通过ldd找出程序依赖的so,进行打包后,在目标机器修改rpath或者LD_LIBRARY_PATH指向本地lib目录,但ldd程序,发现/lib64/ld-linux-x86-64.so.2这个so仍然指向系统so。原因就是这个字段是写死在elf文件中的,并不受LD_LIBRARY_PATH影响。

编译时带上这两个参数,下面只需要将你程序依赖的so打包一份,随程序进行发布即可。

3.2 方式2 直接修改二进制程序的rpath和interpreter

patchelf具体用法见:https://blog.csdn.net/bandaoyu/article/details/113181179

当你无法编译程序时,也可以通过其他方式修改rpath和interpreter。这种情况需要使用到一个工具patchelf,通过dnf install patchelf即可安装。你可以通过它修改elf文件的rpath和interpreter:

patchelf --set-rpath /my/lib your_program
patchelf --set-interpreter /my/lib/ld-linux.so.2 your_program

除了绝对路径,一种比较常见的方式是在部署前,使用pwd获取当前路径,使用相对路径指向本地lib。

patchelf --set-rpath `pwd`/../lib your_program
patchelf --set-interpreter `pwd`/../lib/ld-linux-x86-64.so.2 ./your_program

摘抄和参考:

链接:https://www.jianshu.com/p/77d7f7dc93b3

安装最新版 devtoolset-8 — 源代码

Guess you like

Origin blog.csdn.net/bandaoyu/article/details/121476940