GCC 9.1.0源码编译实战:从依赖到多版本切换
简介gcc-9.1.0.tar.gz 是 GNU 编译器集合 9.1.0 版本的完整源码包面向需要自行编译、定制或研究编译器的开发者与计算机专业学习者。它涵盖 C、C、Objective-C、Fortran、Ada、Java 等多语言前端以及运行时库、标准库和基于 autoconf、automake 的构建脚本可用于跨平台编译、调整优化选项或适配特定目标机架构。压缩包约 118.36MB上游未提供文件总数与类型明细但按 GCC 源码结构可知内容以 C/C 源文件、头文件、构建配置脚本和文档为主分别承担编译器实现、接口声明、环境探测与安装说明等职责。目前已有 269 人浏览学习。对于想深入理解编译流程、参与开源贡献或搭建自定义工具链的读者这份源码提供了从语言前端到运行库的完整实现便于在遵守 GPL 协议的前提下阅读、修改与分发是研究编译器设计与系统编程的实用素材。1. 拿到 gcc-9.1.0.tar.gz 之后为什么源码编译比换源装包更值得折腾很多人第一次拿到gcc-9.1.0.tar.gz是在一台老系统上——CentOS 7.9 自带的 GCC 4.8.5 编不了新语法Kylin V10 的软件源里 GCC 版本又卡在 7.xgcc安装搜出来的答案全是yum install gcc装完一看版本号纹丝不动。这时候源码包就是唯一的出路。但源码编译 GCC 和编译一个普通 C 程序完全不是一回事它要先用系统里已有的旧编译器把自己编出来再拿新编出来的编译器把自己重编一遍中间还牵扯 GMP、MPFR、MPC、ISL 四个数学库的版本匹配。整个过程顺利的话四十分钟不顺利的话能卡一整天。这篇笔记就是把我自己从解压到验证的完整路径拆开包括参数怎么设、日志往哪写、升级后为什么gcc -v还是旧版本这些高频翻车点。适合手上有源码包、需要在离线或受限环境里把 GCC 升到 9.1.0 的运维和嵌入式开发人员。2. 编译前的依赖账四个数学库和系统头文件一个都不能少2.1 GCC 9.1.0 到底依赖什么GCC 源码包本身不包含它需要的数学库源码。GMP 负责大整数运算MPFR 负责多精度浮点MPC 负责复数运算ISL 用于循环优化。这四个库在配置阶段会被configure脚本检查缺一个就直接报错退出。CentOS 7.9 和 Kylin V10 的默认源里通常只有旧版本比如 GMP 6.0.0 配 GCC 9.1.0 就偏旧了常见做法是手动编一套新版本装到独立前缀下再通过--with-gmp等参数指给 GCC 的 configure。除了数学库还需要系统提供gcc、g、make、tar、bzip2、flex、bison这些基础工具。flex和bison在编译某些语言前端时会用到缺了会在编译到一半时报奇怪的语法错误。头文件方面glibc-devel、kernel-headers必须装否则编译 libgcc 时会找不到stdio.h之类的系统头。注意不要试图用--disable-bootstrap跳过自举来省时间。跳过自举虽然快但编出来的编译器在优化级别较高时可能出现自身编译自身失败的问题后续编其他软件会莫名其妙报 internal compiler error。2.2 依赖库的编译顺序和参数四个库有依赖关系MPFR 依赖 GMPMPC 依赖 GMP 和 MPFRISL 依赖 GMP。所以编译顺序必须是 GMP → MPFR → MPC → ISL。每个库都装到同一个前缀下比如/opt/gcc-deps这样 GCC 配置时只需要一个--with-gmp/opt/gcc-deps就能把所有依赖找到。# 以 GMP 为例MPFR/MPC/ISL 步骤相同只改包名和 configure 参数 tar -xf gmp-6.1.2.tar.xz cd gmp-6.1.2 ./configure --prefix/opt/gcc-deps --enable-shared --enable-static make -j$(nproc) make install--prefix指定安装路径独立于系统默认路径避免污染系统库。--enable-shared和--enable-static同时生成动态和静态库GCC 链接时两种都可能用到。-j$(nproc)用满所有 CPU 核心加速编译但注意 GMP 在make check阶段对并行敏感如果要做完整测试建议make check不加-j。MPFR 的 configure 需要额外加--with-gmp/opt/gcc-depsMPC 加--with-gmp/opt/gcc-deps --with-mpfr/opt/gcc-depsISL 加--with-gmp/opt/gcc-deps。每个库编译前先make check确认自身测试通过不然后面 GCC 编译失败时你分不清是 GCC 的问题还是依赖库的问题。2.3 系统基础工具的检查清单在开始编译 GCC 之前用下面这几条命令确认基础环境就绪# 检查旧编译器是否存在且可用 gcc --version # 检查 make 版本GCC 9.1.0 要求 make 3.80 以上 make --version # 检查 flex 和 bison flex --version bison --version # 检查系统头文件 ls /usr/include/stdio.h # 检查磁盘空间完整编译需要至少 10GB 空闲 df -h /usr/local如果gcc --version显示 4.8.5这是正常的我们就是用它来编 9.1.0。如果make --version低于 3.80需要先升级 make。flex和bison缺失的话CentOS 用yum install flex bisonKylin V10 用apt install flex bison或从对应源安装。磁盘空间不够会在编译后期报No space left on device那时候已经编了几个小时非常浪费。3. 从 configure 到 makegcc-9.1.0.tar.gz 的完整编译路径3.1 解压与目录规划gcc-9.1.0.tar.gz解压后是一个约 800MB 的源码目录。强烈建议不要在源码目录里直接 configure而是新建一个独立的 build 目录。GCC 官方明确推荐 out-of-tree 构建原因是源码目录里 configure 会生成大量中间文件一旦编译失败想重新配置清理起来非常麻烦。# 解压源码包 tar -xzf gcc-9.1.0.tar.gz # 创建独立构建目录 mkdir gcc-9.1.0-build cd gcc-9.1.0-build # 确认源码目录路径 ls ../gcc-9.1.0/configure解压后的源码目录名是gcc-9.1.0build 目录和它平级。ls确认configure脚本存在如果这一步就找不到文件说明压缩包不完整需要重新下载。下载 gcc 网速过慢怎么办常见做法是用wget -c断点续传或者找国内镜像站但注意校验文件完整性tar -tzf能列出内容就说明包没坏。3.2 configure 参数逐项拆解configure 是整个过程里最关键的一步参数设错了后面全白费。下面是我在 CentOS 7.9 和 Kylin V10 上都验证过的一套配置../gcc-9.1.0/configure \ --prefix/usr/local/gcc-9.1.0 \ --enable-languagesc,c,fortran \ --disable-multilib \ --with-gmp/opt/gcc-deps \ --with-mpfr/opt/gcc-deps \ --with-mpc/opt/gcc-deps \ --with-isl/opt/gcc-deps \ --enable-checkingrelease \ --disable-bootstrap \ --with-system-zlib--prefix决定安装位置我习惯装到/usr/local/gcc-9.1.0和系统自带的/usr/bin/gcc完全隔离升级出问题可以直接删目录回退。--enable-languages按需选择只编 C 和 C 能省不少时间需要 Fortran 就加上。--disable-multilib在 64 位系统上只生成 64 位库除非你需要编译 32 位程序否则关掉能减少编译量和出错概率。--with-gmp等四个参数指向前面编译的依赖库前缀。--enable-checkingrelease关闭编译器的内部一致性检查加快编译速度生产环境用 release 就够了。--disable-bootstrap跳过三阶段自举这是用时间换稳定性的取舍——如果你追求极致稳定去掉这个参数编译时间大约翻三倍。--with-system-zlib使用系统自带的 zlib避免 GCC 自带的那份旧 zlib 和系统库冲突。提示configure 阶段如果报Building GCC requires GMP 4.2, MPFR 2.4.0 and MPC 0.8.0说明依赖库路径没指对检查/opt/gcc-deps/lib下是否有对应的.so和.a文件。3.3 编译与日志重定向configure 成功后build 目录下会生成 Makefile。编译命令本身很简单但日志管理很重要——GCC 完整编译输出几万行终端滚动根本看不过来出错时想回溯都找不到。把日志输出到文件是必备操作。# 编译并将标准输出和错误输出都重定向到日志文件 make -j$(nproc) 21 | tee build.log # 如果编译中断查看日志末尾定位错误 tail -100 build.log # 搜索日志中的错误关键词 grep -n error: build.log | head -2021把 stderr 合并到 stdouttee同时输出到终端和文件。-j$(nproc)并行编译但 GCC 编译对内存消耗较大如果机器内存小于 8GB建议-j4或更低否则可能触发 OOM Killer 把编译进程杀掉日志里会看到Killed字样。编译过程中如果某个文件报错make默认会停止修复后重新执行make会从断点继续不需要重新 configure。编译完成后执行make install把编译产物安装到--prefix指定的目录。安装过程通常几分钟完成后/usr/local/gcc-9.1.0/bin下会有gcc、g、gfortran等可执行文件。4. 升级后 gcc -v 还是旧版本环境变量与库路径的排查4.1 为什么新编译器“隐身”了这是最高频的翻车场景明明make install成功了/usr/local/gcc-9.1.0/bin/gcc也能运行但终端里敲gcc -v显示的仍然是 4.8.5 或 7.3.0。原因只有一个——PATH环境变量里/usr/bin排在/usr/local/gcc-9.1.0/bin前面shell 找到旧的就直接用了。# 查看当前 gcc 的实际路径 which gcc # 查看 PATH 顺序 echo $PATH # 临时把新 GCC 放到最前面 export PATH/usr/local/gcc-9.1.0/bin:$PATH # 再次验证 gcc -vwhich gcc如果输出/usr/bin/gcc说明 PATH 没生效。export PATH只对当前终端会话有效要永久生效需要写进/etc/profile.d/gcc.sh或用户自己的~/.bashrc。写进 profile 后记得source一下或者重新登录。4.2 动态库路径的坑即使 PATH 对了运行新 GCC 时还可能报error while loading shared libraries: libmpfr.so.6: cannot open shared object file。这是因为 GCC 运行时需要加载/opt/gcc-deps/lib下的动态库而系统默认的库搜索路径里没有这个目录。# 把依赖库路径加入 ld 配置 echo /opt/gcc-deps/lib /etc/ld.so.conf.d/gcc-deps.conf echo /usr/local/gcc-9.1.0/lib64 /etc/ld.so.conf.d/gcc-deps.conf # 刷新动态链接器缓存 ldconfig # 验证库是否能被找到 ldd /usr/local/gcc-9.1.0/bin/gcc | grep not foundldconfig刷新缓存后ldd输出里不应该再有not found。如果还有检查/opt/gcc-deps/lib下对应的.so文件是否存在以及/etc/ld.so.conf.d/gcc-deps.conf里的路径是否写对。这一步不做的话新 GCC 编出来的程序在运行时也可能找不到libstdc.so.6的新版本报GLIBCXX_3.4.26 not found。4.3 验证新编译器是否真正可用PATH 和库路径都配好之后用一个最小程序验证新编译器能正常工作# 写一个使用 C17 特性的测试文件 cat test_gcc9.cpp EOF #include iostream #include filesystem int main() { std::cout GCC __VERSION__ std::endl; std::filesystem::path p /tmp; std::cout filesystem ok: p.string() std::endl; return 0; } EOF # 用新编译器编译指定 C17 标准 g -stdc17 test_gcc9.cpp -o test_gcc9 # 运行 ./test_gcc9__VERSION__宏会打印编译器版本号确认是 9.1.0。std::filesystem是 C17 引入的GCC 4.8.5 完全不支持能编译通过就说明新编译器确实在干活。如果编译时报undefined reference to std::filesystem说明链接的还是旧版libstdc需要检查LD_LIBRARY_PATH或ldconfig配置。5. 避坑记录源码编译 GCC 时最容易翻车的五个地方5.1 编译到一半报 internal compiler error现象编译进行到某个.cc文件时突然报internal compiler error: Segmentation fault日志里有一长串Please submit a full bug report。原因通常是内存不足导致编译器自身崩溃或者旧编译器在优化级别较高时编出了错误的中间代码。CentOS 7.9 默认的 GCC 4.8.5 在编某些 GCC 9 源码时确实会触发这个问题。解决降低并行度make -j2或者在 configure 时加--disable-bootstrap减少自举阶段的复杂度。如果仍然报错检查内存是否够用free -h看可用内存小于 4GB 的话建议加 swap 或换机器。5.2 make install 后找不到 g现象gcc能用了但g提示command not found。原因configure 时--enable-languages没包含c或者只写了c。解决重新 configure确保--enable-languagesc,c至少包含这两项。已经编译过的话不需要删 build 目录改完 configure 参数重新make即可make 会增量编译缺失的部分。5.3 编译出来的程序运行时报 GLIBCXX 版本错误现象用新 GCC 编出来的程序在另一台机器上运行时报GLIBCXX_3.4.26 not found。原因新 GCC 链接的是/usr/local/gcc-9.1.0/lib64/libstdc.so.6但目标机器上只有旧版libstdc。解决要么在目标机器上也部署新版的libstdc.so.6并ldconfig要么编译时加-static-libstdc静态链接 C 标准库。后者会让二进制文件变大但部署最简单。5.4 configure 报错找不到 gmp.h现象configure 阶段报gmp.h not found或error: cannot find GMP。原因--with-gmp路径指错了或者 GMP 编译时没有生成头文件。解决确认/opt/gcc-deps/include/gmp.h存在。如果不存在回到 GMP 源码目录重新make install。注意--with-gmp指向的是前缀路径不是lib或include子目录。5.5 编译时间过长导致磁盘写满现象编译到后期报No space left on devicebuild 目录占用超过 15GB。原因GCC 完整编译产生的中间文件非常大尤其是开启 bootstrap 时build 目录可能膨胀到 20GB 以上。解决编译前确保/usr/local或 build 目录所在分区有至少 20GB 空闲。如果空间紧张用--disable-bootstrap并只编需要的语言能显著减少中间文件体积。编译完成后make clean可以清理大部分中间文件。6. 多版本共存与快速切换把 gcc-9.1.0 管起来6.1 用 update-alternatives 管理多版本如果机器上同时存在系统自带的 GCC 和手动编译的 9.1.0用update-alternatives比手动改 PATH 更干净。它能在多个版本之间一键切换不用反复编辑 profile 文件。# 注册 gcc 的两个版本 update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8.5 48 \ --slave /usr/bin/g g /usr/bin/g-4.8.5 update-alternatives --install /usr/bin/gcc gcc /usr/local/gcc-9.1.0/bin/gcc 91 \ --slave /usr/bin/g g /usr/local/gcc-9.1.0/bin/g # 交互式选择默认版本 update-alternatives --config gcc--install的最后一个数字是优先级91 比 48 大所以默认会选 9.1.0。--slave把 g 和 gcc 绑定切换 gcc 时 g 自动跟着切。--config gcc会列出所有已注册版本输入编号即可切换。这个方式的好处是切换即时生效不需要重新登录或 source 文件。6.2 验证切换是否彻底切换后需要从三个维度确认新版本真正生效检查项命令预期结果编译器版本gcc -v显示 9.1.0实际路径which gcc/usr/bin/gcc且链接到 9.1.0运行时库ldd $(which gcc) | grep stdc指向 9.1.0 的 lib64gcc -v输出里gcc version 9.1.0这行是关键。which gcc显示/usr/bin/gcc是正常的因为 update-alternatives 就是在/usr/bin下建符号链接。ldd检查确保运行时加载的是新版libstdc这一步很多人会漏掉结果编译没问题但运行时报版本错误。6.3 我自己的习惯我现在拿到一台新机器如果系统 GCC 低于 7直接源码编 9.1.0 装到/usr/local/gcc-9.1.0然后用 update-alternatives 注册。依赖库统一放/opt/gcc-depsld 配置写进/etc/ld.so.conf.d/。这套组合在 CentOS 7.9、Kylin V10、Ubuntu 18.04 上都跑通过。唯一要记住的是每次升级 GCC 大版本后ldconfig必须执行否则新库不会被动态链接器识别。编译日志我习惯保留build.log万一后面出问题翻日志比重新编译快得多。希望帮到你。本文还有配套的精品资源点击获取