ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

CentOS 7源码编译升级GCC 9.3.0完整指南

CentOS 7源码编译升级GCC 9.3.0完整指南 1. 为什么 CentOS 7 非要自己编译 GCC先说个让人头疼的现实。CentOS 7 自带的 GCC 是 4.8.5这个版本是 2013 年发布的。放在当年它确实是主力但放到现在很多项目根本编译不过去。尤其是 C17 的完整支持、OpenMP 的新特性、还有一堆现代 C 库要求的最低编译器版本4.8.5 基本都满足不了。更麻烦的是你在 CentOS 上用 pip 装某些 Python 包或者用源码编译 Node.js 插件经常会看到类似的报错gcc: error: unrecognized command line option ‘-stdc17’看到这个报错基本就明白该换编译器了。但 CentOS 7 的官方源里GCC 最高只到 4.8.5不管你怎么yum update都不可能升级到 9.x。第三方源比如 Software Collections虽然能提供更新的 GCC但配置源、装依赖、处理兼容性一套流程走下来未必比源码编译简单而且 SCL 的 GCC 版本通常也不算最新。所以自己手动编译 GCC 9.3.0就成了最稳妥、最可控的方案。自己编译的好处有三个第一安装路径完全自己定想装哪装哪第二可以针对自己的 CPU 架构做优化第三后续想换成其他版本直接删掉旧目录就行不影响系统自带的 GCC。当然自己编译也有不小的代价最直观的就是编译时间。GCC 的源码包解压后就接近 1GB完整编译需要大概 30 到 60 分钟取决于机器配置。这是正常的GCC 本身就是一个巨型项目它的编译过程要比普通软件慢得多。我在自己的机器上实测过CentOS 7.94 核 8GB 内存完整编译 GCC 9.3.0 大概花了 45 分钟左右。如果是在 2 核 4GB 的低配机器上可能需要一个半小时以上。建议编译期间别干别的重活不然时间会更长。2. 编译前的基础环境准备2.1 确认系统版本和当前 GCC开始动手前先确认一下你的系统环境免得后面出问题找不到原因。cat /etc/redhat-release gcc --version uname -m正常输出大概是这样CentOS Linux release 7.9.2009 (Core) gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44) x86_64需要确认的核心信息有两点。第一系统是 CentOS 7 的哪个小版本基本不影响7.x 都行第二当前的 GCC 是不是 4.8.5如果之前有人动过系统 GCC版本不同的话需要稍微调整思路。2.2 安装编译所需的依赖包GCC 9.3.0 编译过程中依赖几个关键的库gmp、mpfr、mpc。这三个库分别负责大整数运算、浮点数精度和复数运算GCC 内部很多优化和代码生成步骤都依赖它们。编译之前系统自带的开发工具链也得准备好。yum install -y gcc gcc-c make bzip2 flex texinfo yum install -y gmp-devel mpfr-devel libmpc-devel注意gmp-devel、mpfr-devel、libmpc-devel这几个包必须装。虽然 GCC 源码包里自带这三个库的子模块我待会儿也会讲到可以用--with-gmp这类参数指定源码包内部的库但用系统自带的依赖库更方便也少踩一些坑。另外texinfo是用来生成文档的如果没装编译过程会在生成 info 文档那一步卡住。flex和bison是语法分析器生成工具GCC 源码中某些解析器代码需要它们。这些都是我在实际编译中踩过坑之后确认需要的包。提示如果你的机器是最小化安装yum groupinstall Development Tools也可以直接解决大部分依赖但我更推荐按上面列表一个个装避免把一堆用不到的开发包全拉进来。2.3 下载 GCC 9.3.0 源码包下载地址选国内镜像会快很多GCC 官网的 FTP 服务器在国外速度不太理想。cd /usr/local/src wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.3.0/gcc-9.3.0.tar.gz清华的镜像源速度比较稳定没有的话也能用中科大的镜像wget https://mirrors.ustc.edu.cn/gnu/gcc/gcc-9.3.0/gcc-9.3.0.tar.gz下载完先确认一下文件大小和校验值ls -lh gcc-9.3.0.tar.gz md5sum gcc-9.3.0.tar.gz正常文件大小在 160MB 上下。GCC 9.3.0 的官方 MD5 校验值是66b5a4f5d0c4e3f0f0b9a1e2d4c398e0之类的实际以镜像站点给出的为准。如果下载的文件只有几十 KB那大概率下载不完整解压会报错。接着解压tar -xzf gcc-9.3.0.tar.gz cd gcc-9.3.03. 编译选项配置与参数解读3.1 关键配置项逐项分析进入解压后的目录后先别急着执行./configure把参数搞清楚再动手。GCC 的 configure 参数非常多但对我们来说关键的就那么几个。mkdir -p /usr/local/gcc-9.3.0 ../gcc-9.3.0/configure \ --prefix/usr/local/gcc-9.3.0 \ --enable-languagesc,c \ --disable-multilib \ --enable-bootstrap \ --disable-libstdcxx-pch我习惯在源码目录外面建一个build目录来编译也就是 out-of-source 编译。这样源码目录保持干净出问题可以直接删掉 build 目录重来不用重新解压源码包。逐个解释这些参数因为后面出问题基本都是参数配置不当引起的--prefix/usr/local/gcc-9.3.0指定安装路径。不写的话默认会装在/usr/local下bin、lib、include 全散在系统目录里后面想卸载或者换版本就很麻烦。单独指定一个路径所有文件都在/usr/local/gcc-9.3.0下面想删直接rm -rf就完了。--enable-languagesc,c只需要 C 和 C 编译器没必要编译 Fortran、Ada 这些用不到的前端。少一个语言前端编译时间能缩短不少。--disable-multilib这个参数非常关键。CentOS 7 的系统库默认只支持 64 位如果不加这个参数configure 会去检测 32 位库的支持情况然后大概率报错。就算不报错编译过程中也会多编译一套 32 位版本时间至少多花二十分钟。纯 64 位环境的话必须加上。--enable-bootstrap用当前系统的 GCC 4.8.5 先编译一版 GCC 9.3然后再用这版 9.3 自己编译自己确保编译器是自举的。这个参数默认是开启的但建议显式写出来让日志里能看到。代价是编译时间几乎翻倍。想省时间的话可以加--disable-bootstrap但那样得到的编译器稳定性没保证我不建议。--disable-libstdcxx-pch关闭预编译头文件。GCC 自带的 libstdc 预编译头有时候会引发一些诡异的问题关闭之后问题少很多代价是编译 C 代码时速度稍微慢一点影响不大。3.2 支持库缺失时的处理策略如果执行 configure 后出现类似这样的错误checking for gmp.h... no checking for GMP... no configure: error: Building GCC requires GMP 4.2, MPFR 2.4.0 and MPC 0.8.0说明前面说的gmp-devel那几个依赖包没装好。这时候有两种处理方案。方案一推荐回去用 yum 把缺的依赖包装上。yum install -y gmp-devel mpfr-devel libmpc-devel方案二用 GCC 源码包自带的子模块。在 GCC 9.3.0 源码目录里执行./contrib/download_prerequisites这个脚本会自动下载 gmp、mpfr、mpc 三个库的源码并在本地编译。但问题在于这些库需要从 GCC 的官方 FTP 下载速度很慢而且有时候会失败。我在国内网络环境下试过成功率大概五五开所以更推荐直接用系统自带的包管理器装依赖。提示确认依赖库版本是否够用时可以使用rpm -qa | grep gmp查看已安装的版本。CentOS 7 仓库里的 gmp 版本是 6.0.0mpfr 是 3.1.1libmpc 是 1.0.1这些版本完全满足 GCC 9.3.0 的最低要求。4. 完整编译安装过程实录4.1 配置与编译阶段配置命令确认没问题之后就可以正式开始编译了。我会把每一步的预期输出和耗时写清楚方便你对照。cd /usr/local/src mkdir build-gcc-9.3.0 cd build-gcc-9.3.0 ../gcc-9.3.0/configure \ --prefix/usr/local/gcc-9.3.0 \ --enable-languagesc,c \ --disable-multilib \ --enable-bootstrap \ --disable-libstdcxx-pchconfigure 的过程大约需要 5 到 10 分钟会检测大量系统特性。看到结尾出现checking for default PIE... no checking for thread model... posix configure: creating ./config.status说明系统环境检测通过config.status 已生成配置成功。然后开始编译make -j$(nproc)$(nproc)会自动获取 CPU 核数。比如 4 核机器上等价于make -j4。我这台机器是 4 核 8GB RAM为期大约 45 分钟的编译过程消耗如下编译过程内存占用峰值大约 3GB 到 3.5GB8GB 内存完全够用-j4时 CPU 满载负载约 3.8 到 4.0磁盘空间消耗大约 6GB 到 8GBGCC 的中间文件非常多。编译过程中最耗时的是三个大阶段构建stage1编译器用系统自带 GCC 4.8.5 编译大约 15 分钟用 stage1 编译器重新编译 GCC 自身也就是stage2大约 20 分钟用 stage2 编译器再编译一次做一致性校验约 10 分钟。如果执行过程中某个环节报错先确认错误信息大多数情况下都能靠make clean后重试解决。4.2 安装阶段与时间估算编译完成后继续执行安装命令make install安装过程比编译快得多大约 5 分钟。装好后验证一下文件结构ls /usr/local/gcc-9.3.0/bin/正常情况下你会看到这些文件c cpp g gcc gcc-ar gcc-nm gcc-ranlib gcov gcov-dump其中gcc和g就是我们要的编译器。还要确认一下动态库有没有到位ls /usr/local/gcc-9.3.0/lib64/应该会看到libstdc.so.6和libgcc_s.so.1这样的动态库文件。这个libstdc.so.6特别重要后面配置运行环境的时候全靠它。注意如果你的系统是按 x86_64 架构装的库文件在lib64目录下如果是 32 位系统则会在lib目录下。绝大多数 CentOS 7 是 64 位所以以下配置都以 lib64 为准。5. 环境变量配置与系统级切换5.1 环境变量配置与路径方案选择编译安装完成后直接输入gcc --version还是旧版本因为系统 PATH 里优先找到的是/usr/bin/gcc。要使用新版本需要把新 GCC 的目录放在 PATH 前面。我习惯在/etc/profile.d/下新建一个脚本这样所有用户登录时都会自动加载不用每个人单独配。vi /etc/profile.d/gcc93.sh写入以下内容export PATH/usr/local/gcc-9.3.0/bin:$PATH export LD_LIBRARY_PATH/usr/local/gcc-9.3.0/lib64:$LD_LIBRARY_PATH export MANPATH/usr/local/gcc-9.3.0/share/man:$MANPATH保存后让配置立即生效source /etc/profile.d/gcc93.sh验证一下which gcc gcc --version如果输出/usr/local/gcc-9.3.0/bin/gcc gcc (GCC) 9.3.0说明 PATH 配置已经生效。但要注意一个问题PATH 变量在 shell 中的优先级顺序是硬编码在当前 shell 设置里的export PATH/usr/local/gcc-9.3.0/bin:$PATH是把新路径追加到最前面。如果你在/etc/profile.d/里用了别的顺序可以能不会生效务必检查一下。LD_LIBRARY_PATH的配置同样重要。GCC 编译出来的 C 程序默认动态链接 libstdc.so.6如果这个变量没配置好编译时没问题但运行编译出来的程序会报错./a.out: error while loading shared libraries: libstdc.so.6: cannot open shared object file: No such file or directory5.2 libstdc.so.6 版本确认为什么这里专门把 libstdc.so.6 拿出来讲因为系统自带的和新版 GCC 自带的版本差别很大。检查一下strings /usr/lib64/libstdc.so.6 | grep GLIBCXXCentOS 7 自带的 libstdc 最高支持到GLIBCXX_3.4.19。而 GCC 9.3 自带的是strings /usr/local/gcc-9.3.0/lib64/libstdc.so.6 | grep GLIBCXX最高到GLIBCXX_3.4.28。差了好几个版本。这个版本号非常关键。如果你编译了一个 C 程序链接的是新 libstdc然后拿到另一台没有安装新 GCC 的 CentOS 7 机器上运行就会报上面那个找不到libstdc.so.6的错误或者报./a.out: /usr/lib64/libstdc.so.6: version GLIBCXX_3.4.28 not found这是我在实际项目中踩过的大坑。解决思路有两个第一个思路是部署时带着动态库一起走把/usr/local/gcc-9.3.0/lib64/libstdc.so.6拷贝到程序同级的 lib 目录再用LD_LIBRARY_PATH指定。第二个思路是直接替换系统级的 libstdc但替换有风险如果系统的某些管理工具还没准备好兼容新版库可能会导致一堆命令间接依赖它的工具崩掉。个人建议用方案一只在环境变量层面做切换不要动系统库。5.3 编译后的库路径检查配置完环境变量之后一个简单的验证方式是直接编译并运行一个 C 程序看它正在链接哪些动态库echo #include iostream int main() { std::cout Hello, GCC 9.3 std::endl; return 0; } test.cpp g test.cpp -o test ldd test正常情况下ldd test会显示linux-vdso.so.1 (0x00007ffc...) libstdc.so.6 /usr/local/gcc-9.3.0/lib64/libstdc.so.6 (0x00007f...) libc.so.6 /lib64/libc.so.6 (0x00007f...)看到 libstdc 指向了/usr/local/gcc-9.3.0/lib64/说明动态库加载是正常的。6. 常见问题与排查技巧实录6.1 configure 报错问题 1configure: error: I suspect your system does not have 32-bit libraries.原因运行 configure 时没有加--disable-multilibGCC 检测 32 位库支持CentOS 7 默认没装 32 位库。解决清掉 build 目录重新 configure这次加上--disable-multilib。rm -rf /usr/local/src/build-gcc-9.3.0/* ../gcc-9.3.0/configure --prefix/usr/local/gcc-9.3.0 --enable-languagesc,c --disable-multilib问题 2configure: error: Building GCC requires GMP 4.2, MPFR 2.4.0 and MPC 0.8.0原因前面没装gmp-devel、mpfr-devel、libmpc-devel或者装了但是 pkg-config 找不到。解决yum install -y gmp-devel mpfr-devel libmpc-devel装完重新 configure 一般就能过了。6.2 make 阶段报错与内存问题问题 3c: internal compiler error: Killed (program cc1plus)原因编译过程中内存不足系统 OOM killer 把 g 进程杀了。我在这台 8GB 内存机器上编译时没遇到但在另一台 2GB 内存的云服务器上编译 libstdc 时报过这个错。解决限制并行编译数不要用-j4改成make -j2如果 2GB 都不够就make -j1虽然慢但能保证内存够用。问题 4make[3]: *** [all-stage2-gcc] Error 1这个报错比较笼统具体原因要看前面的具体信息。最常见的两种情况一是磁盘写满二是代码里有些文件权限不对。先看磁盘df -hGCC 编译过程中间文件很多占用磁盘空间 6GB 到 8GB。如果你/usr/local/src所在分区只有 5GB一开始就要检查容量。加一个比较大的新分区比如挂到/data把 build 目录挪过去重新编译。6.3 环境变量配置不生效问题 5配置了/etc/profile.d/gcc93.sh但gcc --version还是旧版本。原因当前 shell 会话的配置文件加载顺序问题。/etc/profile.d/的脚本是在登录 shell 时才加载如果你当前是在图形界面打开终端或者用 su 切到 root之前的 PATH 可能仍然保留了旧值。排查步骤echo $PATH如果输出没有/usr/local/gcc-9.3.0/bin说明脚本没加载成功。先手动 source 一下source /etc/profile.d/gcc93.sh然后重新检查which gcc。如果还是不生效看看脚本文件有没有执行权限chmod x /etc/profile.d/gcc93.sh还有不同的 shellsource方式有区别如果你的 shell 是 zsh 而不是 bash加载逻辑可能不同需要单独在~/.zshrc里加参数。6.4 运行编译程序时报版本错误问题 6./test: /usr/lib64/libstdc.so.6: version GLIBCXX_3.4.28 not found原因程序编译时链接的是新版 libstdc运行时却加载了系统自带的旧版库。解决确认当前环境变量echo $LD_LIBRARY_PATH确认/usr/local/gcc-9.3.0/lib64有没有在里面。如果里面没有就重新 export 一下。如果LD_LIBRARY_PATH已经配置了还是有这个报错用ldd test看它真正加载的 libstdc 路径就知道环境变量有没有真实生效。也有可能是你在编译时用了绝对路径链接了/usr/lib64/libstdc.so.6这种情况得重新编译。一个问题速查表问题现象可能原因排查命令解决方案configure 报 32 位库错误缺少 --disable-multilibls /usr/lib加 --disable-multilib 重新 configureconfigure 提示 GMP/MPFR/MPC 缺失依赖包未安装rpm -qa | grep gmpyum install -y gmp-devel mpfr-devel libmpc-develmake 阶段提示 Killed内存不足free -h降低 -j 参数如 make -j2 或 make -j1make 阶段报错信息零散依赖工具链缺失which flex / which bisonyum install -y flex bison texinfogcc 版本没变PATH 没生效echo $PATHsource /etc/profile.d/gcc93.sh运行程序找不到 libstdcLD_LIBRARY_PATH 未配置echo $LD_LIBRARY_PATHexport LD_LIBRARY_PATH/usr/local/gcc-9.3.0/lib64:$LD_LIBRARY_PATHGLIBCXX_3.4.28 not found默认加载系统旧版库ldd test设定 LD_LIBRARY_PATH 或直接替换链接路径7. 三段个人实战总结与避坑思路编译 GCC 这件事本身不复杂但在实际项目里反复踩坑之后我总结出几个心法这里直接分享给准备动手的朋友。第一永远不要动系统自带的 GCC。不管你编译出来的 9.3.0 多好用都不要尝试去替换/usr/bin/gcc或者把新版的 libstdc 直接覆盖到/usr/lib64里。CentOS 7 很多系统组件是基于 4.8.5 编译的比如 udev、systemd 里的部分工具它们对 libstdc 的依赖很保守。把系统库替换掉轻则系统日志刷满告警重则以后装内核模块、开发工具全报错。新编译的 GCC 放到独立目录靠环境变量切换用不顺手就删掉完全无副作用。第二LD_LIBRARY_PATH很强大但它不是万能的。我遇到过一个场景用新 GCC 编译出来的动态库部署到生产环境明明已经设置了LD_LIBRARY_PATH程序还是加载了系统自带的 libstdc。最后排查下来是因为程序依赖链里有一个动态库系统自带的 libcurl它的 DT_RPATH 硬编码了/usr/lib64导致运行时会优先去那边找库。这种情况问题的根源是依赖链里的 RPATH 优先级高于环境变量。绕过思路也比较奔放要么把依赖都换成自己编译的版本要么直接用patchelf修改目标程序的 RPATH。这个知识短期内不一定用得上但遇到莫名加载错误的时候能救命。第三别光顾着装 GCC配套工具的版本也要留意。新 GCC 编译出来的 C 代码可能会用到比较新的链接器特性如果你系统的 binutils 还是旧版 2.27链接时偶尔会报一些奇怪的问题比如unrecognized option -plugin。碰到这类情况顺手把 binutils 也编译一份新版就完事了。新版 GCC 还会默认使用新的 DWARF5 调试信息格式如果你用旧版的 gdb 去调试可能读不了调试信息。要么用-gdwarf-4编译参数降级要么给 gdb 升级。总之工具的版本要配套这种坑遇到一次就会长记性。根据我个人的经验在 CentOS 7 上编译安装高版本 GCC最核心的思路就是独立安装路径、环境变量隔离、不碰系统库。这套思路不只是 GCC 适用其他需要编译安装的开发工具比如 CMake、LLVM、OpenSSL 新版都可以照搬。装好之后你再用 C17 的特性写代码那种顺利编译的体验是旧版 4.8.5 完全给不了的。
返回列表