CentOS上GCC多版本GCC Toolset支持探究

1、前言

CentOS是免费的、开源的、可以重新分发的开源操作系统,CentOS Linux发行版是一个稳定的,可预测的,可管理的和可复现的平台,源于Red Hat Enterprise Linux(RHEL)依照开放源代码(大部分是GPL开源协议)规定开放的源码所编译而成。
由于稳定性的要求,CentOS上的默认GCC版本通常比较低,比如CentOS 8上是GCC 8.5,而目前GCC已经发布了GCC 12.2,这对开发者是不太友好的,如果开发着想使用高版本特性就必须自己编译一个高版本GCC出来,比较麻烦,也不利于维护。
那么有没有办法可以让开发者像安装默认GCC编译器一样,使用yum install的方式安装rpm包呢?CentOS提供了GCC Toolset机制。

2 GCC Toolset使用介绍

本章参考这篇文章

2.1 什么是GCC Toolset

CentOS 8/Red Hat Enterprise Linux(RHEL)8引入了GCC Toolset,包含了最新的开发和性能调优工具,这样与RHEL 7中的Developer Toolset 类似。GCC Toolset在AppStream存储库中以软件集合的形式作为应用程序流提供,GCC Toolset提供的应用程序和库不会取代系统默认版本,不会覆盖它们,也不会自动成为默认或首选选项。

2.2 安装GCC Toolset

通过以下命令可以查询可以安装的package。在CentOS 8上,可以看到有三个版本的gcc-toolset,分别是9,10,11。

yum search gcc-toolset


尝试同时安装9、11两个版本。以gcc-toolset-11为例,我们可以看到这个toolset中包含多个工具,如gcc、make、valgrind等。

yum search gcc-toolset-9
yum search gcc-toolset-11

在这里插入图片描述
安装完成后,此时系统中共有三个版本的GCC,分别是系统默认安装的8.5.0,gcc 9和gcc 11。

2.3 使用GCC Toolset & scl

目前问题的关键是如何快速地、无相互影响地切换不同的GCC版本呢?这里就需要用到scl这个工具了。
通过以下这个命令安装scl:

yum install scl-utils
2.3.1 Running a shell session with GCC Toolset
scl enable gcc-toolset-N bash

启动一个新的bash shell会话,其中使用gcc-toolset-N内的工具版本,而不是系统默认版本。在新的bash shell会话中,无需显式使用scl命令。当您需要多次以交互方式启动工具时,例如在设置或测试开发设置时,非常有用。

exit

在新的会话上输入exit命令即可退出,此时gcc的版本切换成系统默认版本。

2.3.2 Running a tool from GCC Toolset
scl enable gcc-toolset-N gcc

直接运行 gcc-toolset-N中的一个工具,如gcc。

3 GCC Toolset实现原理

3.1 版本切换机制

以toolset中的gcc为例,分析一下版本切换的机制。首先可以看一下不同版本的gcc的安装位置。

  • gcc version 8.5.0: /usr/bin/gcc
  • gcc version 9.2.1: /opt/rh/gcc-toolset-9/root/usr/bin/gcc
  • gcc version 11.2.1: /opt/rh/gcc-toolset-11/root/usr/bin/gcc
    其实,scl工具的本质就是自动设置不同工具版本的环境变量,具体可以参考/opt/rh/gcc-toolset-11/enable文档。
# General environment variables
export PATH=/opt/rh/gcc-toolset-11/root/usr/bin${PATH:+:${PATH}}
export MANPATH=/opt/rh/gcc-toolset-11/root/usr/share/man:${MANPATH}
export INFOPATH=/opt/rh/gcc-toolset-11/root/usr/share/info${INFOPATH:+:${INFOPATH}}
export PCP_DIR=/opt/rh/gcc-toolset-11/root
# bz847911 workaround:
# we need to evaluate rpm's installed run-time % { _libdir }, not rpmbuild time
# or else /etc/ld.so.conf.d files?
rpmlibdir=$(rpm --eval "%{_libdir}")
# bz1017604: On 64-bit hosts, we should include also the 32-bit library path.
# bz1873882: On 32-bit hosts, we should include also the 64-bit library path.
if [ "$rpmlibdir" != "${rpmlibdir/lib64/}" ]; then
  rpmlibdir32=":/opt/rh/gcc-toolset-11/root${rpmlibdir/lib64/lib}"
  dynpath32="$rpmlibdir32/dyninst"
else
  rpmlibdir64=":/opt/rh/gcc-toolset-11/root${rpmlibdir/lib/lib64}"
  dynpath64="$rpmlibdir64/dyninst"
fi
# Add SCL dyninst to LD_LIBRARY_PATH, both 64- and 32-bit paths.
export LD_LIBRARY_PATH=/opt/rh/gcc-toolset-11/root$rpmlibdir/dyninst$dynpath64$dynpath32${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
# Now prepend the usual /opt/.../usr/lib{64,}.
export LD_LIBRARY_PATH=/opt/rh/gcc-toolset-11/root$rpmlibdir$rpmlibdir64$rpmlibdir32${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
export PKG_CONFIG_PATH=/opt/rh/gcc-toolset-11/root/usr/lib64/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}

3.2 Spec文件分析

3.2.1 gcc-toolset-N.spec

gcc-toolset-11.spec为例,这个spec主要是处理对tools的依赖关系、安装路径等,以及enable脚本的安装。

%package toolchain
Summary: Package shipping basic toolchain applications
Group: Applications/File
Requires: %{scl_prefix}runtime
Requires: %{scl_prefix}gcc %{scl_prefix}gcc-c++ %{scl_prefix}gcc-gfortran
Requires: %{scl_prefix}binutils %{scl_prefix}gdb %{scl_prefix}strace
Requires: %{scl_prefix}dwz %{scl_prefix}elfutils
Requires: %{scl_prefix}ltrace %{scl_prefix}make
Requires: %{scl_prefix}annobin

可以看到spec脚本列举了对相应tools的依赖关系,yum工具可以自动分析相关依赖并安装相应工具。

3.2.2 tools.spec

gcc-toolset-11-gcc.spec为例,这个spec主要处理gcc toolchain的依赖关系、编译、测试、安装等,与默认的gcc版本的spec脚本类似。

3.3 关键场景:在低版本GCC的OS环境运行高版本GCC编译出的应用

试想这样一个场景:
一个应用使用高版本的GCC完成编译构建,那么是否可以在只安装了低版本GCC的OS上运行呢?具体来讲,在CentOS 8上通过使能gcc-toolset-11编译的某应用,是否可以在另一个安装了CentOS8上运行呢?

很可行会运行失败,并出现这样一个错误。

/usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by ...)

这是什么原因呢?因为GCC与其依赖的libstdc++库有对于关系,如下:

gcc版本libstdc++版本GLIBCXX版本
gcc 8.5.06.0.25GLIBCXX_3.4.25, CXXABI_1.3.11
gcc 116.0.29GLIBCXX_3.4.29, CXXABI_1.3.13

当一个应用用高版本GCC编译时,会链接使用高版本的GLIBCXX共享库并使用某些只有在高版本中才有的符号,当这个应用在只含有低版本GCC的OS上运行时,将会出现找不到符号的错误。这时候一个解决方案是在这个OS上再把高版本的GCC安装上去,就很麻烦。

3.3.1 CenOS的解决方法

先看看gcc-toolset-11上提供的libstdc++库:
/opt/rh/gcc-toolset-11/root/lib/gcc/aarch64-redhat-linux/11/libstdc++.so,这个文档不是一个ELF文件,而是文本文件:

/* GNU ld script
   Use the shared library, but some functions are only in
   the static library, so try that secondarily.  */
OUTPUT_FORMAT(elf64-littleaarch64)
INPUT ( /usr/lib64/libstdc++.so.6 -lstdc++_nonshared )

同时在当前目录下可以看到一个文件是libstdc++_nonshared.a
原来,CentOS上把高版本的libstdc++分成了两个部分:

  • 与系统默认版本一致的shared so文件;
  • 高版本libstdc++库中增加的non-shared部分。

这样,当使用高版本GCC编译应用时,会把两部分都链接进去,其中non-shared部分是通过静态链接的方式。当应用在只含有低版本GCC的OS上运行时,可以正确地找到libstdc++库中的符号。
从而可以实现前面描述的应用场景。

3.3.2 libstdc++_nonshared.a的制作方式

怎么才能准确无误地制作出libstdc++_nonshared.a呢?难道需要对比两个libstdc++的差异吗?
其实不用那么复杂,具体可以参考这篇问答
因为libstdc++的开发者非常重视libstdc++ ABI的兼容性,这使得很容易看到两个不同版本的libstdc++之间引入了哪些新符号。
查看libstdc++的ABI compatibility page,您可以看到GCC 8.5的libstdc++是GLIBCXX_3.4.25,GCC 11.1的libstdc++是GLIBCXX_3.4.29。然后可以通过libstdc++ version scripts,查看在这两个版本之间添加了哪些符号。

4 结语

本篇文章调研了CentOS上提供多版本GCC的方法,总体来看,使用体验还是不错的,可以供在其他OS上支持多版本GCC时借鉴,甚至也可以为提供LLVM工具链多版本时借鉴。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值