2026/10/22 13:55:08

gcc-9.1.0源码编译安装指南:从configure到避坑

gcc-9.1.0源码编译安装指南:从configure到避坑 简介GCC 9.1.0GNU 编译器集合源码包面向需要在 Linux 及类 Unix 系统上自行构建编译器的开发者、系统管理员以及想弄清编译原理与工具链实现的进阶学习者。压缩包为 gz 压缩格式体积约 118.36MB内含完整的 GCC 源码树——C/C 语言前端、各语言运行时与标准库、autoconf/automake 构建脚本及 configure 配置入口可按目标架构定制优化选项后编译安装。9.1.0 属于主版本 9 的一次稳定发布带来性能优化、新语言标准支持并修复旧版缺陷。该资源已有 269 人学习/下载适合需要掌握源码级编译流程、维护自有工具链或做编译器二次开发的读者既可搭建完整的编译环境也能深入研究内部结构与跨平台移植思路是一份不可多得的开源学习样本。1. 一个 gcc-9.1.0.tar.gz 牵出的系统级选择如果你是从某个需要新特性的开源项目一路找过来最后停在 gcc-9.1.0.tar.gz 这个文件名上那你大概率不是在找文档而是在和一套老系统里的旧编译器较劲。我见过太多这种情况项目要求 GCC 9 或更高系统默认的 gcc 还停在好几年前的版本既不能随便升级系统组件又不想让整个工程一直编译失败。gcc-9.1.0.tar.gz 是 GCC 9.1.0 的完整源码包也是 GCC 9 系列的第一个正式发布版2019 年 5 月面世。它把 C17 的支持补到了能放心投入的程度同时又不像 GCC 10、11 那样对老代码兼容性要求激进所以很多团队在做工具链换代时反复回到这个版本。适合谁看这篇文章手里只有一台旧环境、需要精准锁定工具链版本、又不想动系统底层 gcc 的开发者。下面按我实际编译这套源码包的路径来讲先判断值不值得手动编再讲依赖、configure、make 和安装最后是五个高频坑以及装完怎么让新工具链真正被项目用起来。新手可以照着命令走熟手可以直接跳到避坑章节看边界。2. 为什么非要这个版本从 GCC 9.1.0 的特性边界到值不值得手动编2.1 GCC 9.1.0 的核心变化C17 完整度与编译体验GCC 9.1.0 是整个 GCC 9 大版本的起点。对 C 项目来说最直观的收益是 C17 语言特性终于进入完整可用状态if constexpr、结构化绑定、内联变量、折叠表达式这些特性在 GCC 7/8 里多少都有支持但到了模板实例化和错误诊断环节经常翻车。GCC 8 时代编带 if constexpr 的模板代码还能碰到内部编译器错误升到 9 之后这类问题明显收敛。如果你手里是一套等着从 C14 往 C17 迁移的老代码GCC 9.1.0 是一个很合适的落点。编译诊断方面GCC 9 的改进也值得留意。错误输出会带更高亮、带行内标注模板报错时能指出具体是哪一组模板实参触发了失败这比 4.8/7.x 时代一屏屏的堆栈信息友好得多。很多人说切到 GCC 9 后第一感受不是“编译快了”而是“报错终于能看懂了”。这个体验变化不影响性能却直接影响排查效率对刚接手一个大型老工程的人来说这个成本节省很实在。libstdc 这边也有实际动作。filesystem 的边角问题、并行算法相关实现的完善让filesystem和execution可以在生产代码里放心引用。对老工程来说库层面的修复比语言标准更实在因为它们决定了一个头文件能不能编译、一个 feature test macro 到底返回 true 还是 false。2.2 为什么不直接选 GCC 10/11兼容性窗口的问题我经常被问到“都有 10、11 了为什么还要手动编 9.1.0”。答案是GCC 10 开始默认打开 -fno-common老 C 项目里依赖 common 符号跨编译单元合并的写法直接变成链接错误GCC 11 又把 C 默认标准抬到 gnu17一批按 C14 写的代码在编译期会冒出行为差异虽然能加参数压回去但对一个老旧工程来说每多一次默认行为变化就多一轮未知的回归测试。这就能解释为什么不少团队把 GCC 9.1.0 当作“新标准可用、旧代码还能编”的过渡版本。它默认语言标准仍是 gnu14能用 C17 的新写法而不被默认标准卡住又没有 GCC 10 那个 common 符号的坑。当然这不是说新项目也要用 9.1.0新项目直接上最新稳定版更好但如果是给一个存量系统做工具链升级9.1.0 往往比“最新版”更稳。2.3 手动编译还是仓库包先做三个判断再动手手动编译一个编译器时间成本基本在一小时以上还不算排错。所以拿到 gcc-9.1.0.tar.gz 之前先问三个问题。第一系统仓库里有没有 9.x 或更高的 gcc如果有优先用仓库包补丁、依赖、卸载都由系统的包管理负责这是成本最低的路径。第二项目是不是必须精确锁定 9.1.0 这个点比如对接某个二进制接口、发布脚本里写死了版本号这种才值得手动编。第三有没有特殊需求比如编译器要装到只读盘、构建机不能联网、或者只需要 c/c 语言而不想装一堆额外组件有这些需求时手动编是唯一路径。只有走到后两种情况我才会打开这个 tar.gz。不是因为折腾有意思而是因为仓库里没有或者仓库里的版本太新。这个判断很多新手容易漏掉一上来就 ./configure忙活两小时后发现自己其实只需要装一个名为 gcc-9 的软件包。2.4 解压前的三步检查大小、校验值、目录前缀拿到 gcc-9.1.0.tar.gz 别急着解压。我一般先做三条检查ls -lh gcc-9.1.0.tar.gz sha256sum gcc-9.1.0.tar.gz tar -tzf gcc-9.1.0.tar.gz | head -20ls 看文件大小是否正常确认下载没被截断sha256sum 算出校验值和发布页面给出的 sha256 对比这一步在公共场所下载源码时不要跳过编译器本身是可执行代码校验值不对再便宜也不要解包tar -tzf 只列出包内容而不解压目的是确认顶层目录是 gcc-9.1.0/。如果顶层是散落的 configure 和一堆子目录直接解压会把文件撒在当前目录和后面的构建文件混在一起。三条命令都通过以后再执行解压。我习惯让源码目录和构建目录在同一个父目录下后面 configure 的相对路径写起来最省心mkdir -p /data/build tar -xf gcc-9.1.0.tar.gz -C /data/build cd /data/build/gcc-9.1.0-C 指定解压到 /data/build 下解出来的顶层目录就是 /data/build/gcc-9.1.0。这个目录结构里configure 是构建入口gcc/ 是编译器前端本体libstdc-v3/ 是 C 标准库实现contrib/ 下面有后面要用到的依赖处理脚本。这几处目录下文都会反复提到。3. 从依赖到 configure三次关键准备慢慢来比较快3.1 在源码目录之外建 build 目录GCC 支持 out-of-tree 构建也就是在源码目录之外的另一个目录里跑 configure。常见做法是这样cd /data/build mkdir -p gcc-9.1.0-build cd gcc-9.1.0-build之后所有 configure 和 make 都在这个空目录里执行。这样做的理由有三个。第一源码目录保持原始状态以后想重新配置比如换 prefix、换语言列表不用重新解压第二可以在同一份源码旁边维护两个不同配置的 build 目录一个用于日常编译一个用于验证最小配置第三清理成本极低不要构建产物时把 build 目录删掉即可源码目录那边只剩检测脚本和源码没有一堆中间文件。我见过不少直接进源码目录执行 configure 的遇到“想换 prefix 重新编”时只能 make distclean而 GCC 的 distclean 并不总是把 config.cache、auto-host.h 这些生成物清彻底残留文件会让第二次 configure 的检测结果带上上一次的痕迹。我的习惯是源码目录只做源码build 目录只做编译绝不交叉。3.2 GMP、MPFR、MPC先补齐 GCC 的数学三件套GCC 在 configure 阶段会检查三个基础库GMP、MPFR、MPC分别负责大整数运算、高精度浮点和多精度复数常量处理。系统里没装或者版本太旧configure 走到一半就会失败。最快的方法是用源码包自带的脚本处理依赖cd /data/build/gcc-9.1.0 ./contrib/download_prerequisites脚本做的事并不复杂把依赖的几个 tarball 下载到源码目录解压然后建立 gmp、mpfr、mpc 这样的短名软链接让 GCC 的 configure 直接通过软链接找到依赖源码。我一般让它把 isl 也一起处理好isl 虽然只在开启 GRAPHITE 优化时才用到但提前装上以后想开相关优化特性就不用再回头补依赖。这个脚本要求构建机能联网。离线环境里常见做法是找一台有网的机器把对应的依赖 tarball 下载好拷到源码目录再手动执行 tar -xf 和 ln -s 建软链接效果和脚本一样。另外注意如果系统已经装了符合版本要求的 GMP/MPFR/MPC 开发包也可以不跑脚本configure 时用 --with-gmp、--with-mpfr、--with-mpc 指到对应路径。但我在老系统上更愿意跑脚本因为老系统的这些库版本往往不够新和 GCC 9 的检测逻辑对不上。依赖不到位时configure 的报错通常是 “configure: error: Building GCC requires GMP 4.2, MPFR 2.4.0 or later”。它只告诉你缺东西不告诉你哪里去拿按上面脚本跑一遍最常见的缺依赖问题就结束了。3.3 configure 最小命令与三个必须弄懂的选项依赖就绪后回到 build 目录执行 configure。这是我在绝大多数机器上用的最小命令cd /data/build/gcc-9.1.0-build ../gcc-9.1.0/configure \ --prefix/opt/gcc-9.1.0 \ --enable-languagesc,c \ --disable-multilib三个选项各有各的账。--prefix 指定安装根目录。装到 /opt/gcc-9.1.0 而不是 /usr/local是因为这个编译器未来要和系统自带 gcc 并存独立目录可以让它的 bin、lib、include 全部待在自己名下不碰系统任何文件想卸载时直接删目录即可。这里提醒一句不要在 configure 阶段用相对路径写 prefix比如 --prefix~/gcc9 这种最后 make install 解释出来的路径会相对你的工作目录极易装错地方。我用的都是绝对路径。--enable-languagesc,c 决定编哪些前端。GCC 默认支持的语言比你想的多不开这个选项configure 会为 fortran、go、ada如果宿主支持都生成编译目标make 时间会被额外拉长一个小时以上。只要不是专门写 Fortran 的程序c,c 就是性价比最高的一对。不要照抄某些教程里的 c,c,fortran 三连多一个语言就多一整套库要编。--disable-multilib 关闭 32 位库编译。在 x86_64 上不关时默认会同时构建一套 32 位 libgcc/libstdc 供 -m32 使用构建时间更长而且一旦宿主机没有 32 位系统库编译会直接失败。如果你不需要 -m32果断加上这个开关需要出 32 位二进制时再去掉它并事先装好 32 位开发包。最后可以根据目的决定要不要 bootstrap。configure 默认会执行 bootstrap 流程也就是拿新编译出的编译器再去编译一遍源码用两次输出一致性验证编译器没有自举问题代价是编译时间接近翻倍。如果只是自己快速试一下 GCC 9.1.0 能不能用可以加 --disable-bootstrap如果是团队统一工具链、要给所有人用的我建议保留默认bootstrap 验证过的编译器出了问题更容易筛掉“编译器自身”这个因素。4. make 和安装并行度算内存账日志学会看关键行4.1 -j 参数不是越大越好先数核再算内存configure 通过后就是漫长的 make。GCC 的编译是典型的 CPU 密集加内存密集编到 libstdc 时多个 cc1plus 进程同时跑每个轻松吃掉 1GB 以上内存。我的经验公式是并行路数 min(CPU 核数 - 1, 可用内存 GB / 2)。16G 内存、16 核的机器开 8 路比开 16 路稳定4G 内存的小机器老老实实 -j2。开始编译时这样写nproc free -g make -j4 21 | tee /data/logs/gcc-build.log第一行看逻辑核数第二行看可用内存然后决定第三行的 -j 值。实际调整时如果编译开始后一两分钟内出现 “cc1plus: internal compiler error: Killed” 或者 “virtual memory exhausted”说明并行开大了进程被内核 OOM 杀掉这时把 -j 降一半重新 make因为失败的目标还没有完成GCC 的 Makefile 会从断点继续不会从头再来。这里说的重新执行同一条 make 命令不是删掉 build 目录重来。GCC 构建过程有大量 stamp 文件和目标文件跟随状态没完成的目标会自己补齐。很多人以为编译失败等于白跑直接把 build 目录删了重开其实大多数情况不用。还要提醒一个容易被忽略的因素构建目录的磁盘类型。GCC 编译大量小文件读写机械盘和 SSD 的耗时差距可以到一倍以上。如果把 build 目录放在 NFS 或者网络盘上make 过程中的锁和文件同步还会引入诡异的间歇性失败我吃过这个亏所以构建目录一定用本地盘。4.2 日志里找真凶build.log 和 config.log 的分工等待编译的时间不会太短期间如果报错不要只看屏幕上最后十几行。终端默认会把错误上下文冲掉所以把输出落盘是第一步grep -n error: /data/logs/gcc-build.log | tail -30 grep -n error: /data/build/gcc-9.1.0-build/config.log | tail -30第一条 grep 针对构建日志编译阶段的 error 大多形如 “as: unrecognized option”、链接阶段的 “ld: cannot find -lxxx”第二条针对 configure 阶段生成的 config.log那才是 configure 失败时的第一手材料。config.log 里搜 error 时会看到系统头文件路径、编译器版本这类上下文关键信息往往比 build.log 更靠前。日常排错记住一个原则尾部那几条错误只是一个结果它们上方 20 行以内的 “checking for … no” 或者 “libs: …” 才是原因。我见过太多人在最后一行 error 上死磕实际上真正的问题在五步之前的依赖检测上。翻车记录里最常见的三种死法cc1plus 被 OOM 杀掉按 4.1 降 j 处理链接器报缺库多半是 LIBRARY_PATH 或 --with-xxx 配置问题进 config.log 看搜索路径configure 阶段报 “C compiler cannot create executables”基本是环境变量污染下一章专门讲。4.3 make install 与 hash -r安装完成后立刻验证编译顺利结束后执行安装make install /opt/gcc-9.1.0/bin/gcc --version hash -r which gccmake install 会把所有产物按 configure 时设定的 --prefix 结构放好不需要加 DESTDIR。DESTDIR 是打包场景用的它会把文件安装到一个 staging 目录等待二次打包运行时路径并不正确自己用的环境直接 make install 即可。装完以后/opt/gcc-9.1.0/bin/gcc --version 应该能看到 “gcc (GCC) 9.1.0” 字样hash -r 是清掉 shell 对旧命令路径的记忆不然 which gcc 可能还指向系统旧路径。看到 which gcc 指向 /opt/gcc-9.1.0/bin/gcc安装环节才算真正结束。另外如果你用一个没有 root 权限的账号编译make install 到 /opt 这步会报权限拒绝。常见处理是让管理员执行 make install或者更省事一点configure 阶段就把 --prefix 改成当前用户有写权限的目录比如 /data/opt/gcc-9.1.0。没有特殊需求时不要为了权限问题去放宽 /opt 的目录权限安全上的坑比编译坑难排。5. 编译安装 gcc-9.1.0 的 5 个高频避坑记录以下五条是我在不同机器上反复踩过、也帮别人擦过的问题。前三条在 configure 和早期 make 阶段后两条集中在“装完却跑不起来”每一条都是现象、原因、解决三步说清。5.1 configure 报 C compiler cannot create executables现象运行 configure 后很快失败关键行是 “configure: error: C compiler cannot create executables”打开 config.log 能看到尝试编译测试程序时头文件或库路径错乱。原因最常见的是环境里残留了 CPATH、C_INCLUDE_PATH、LIBRARY_PATH 等变量它们指向另一个编译器版本的头文件或库目录新编译器的测试程序被强行嫁接到老头文件上自然链接失败。这类变量往往来自之前给某个软件配环境时 export 的配置登录一次就活在当前 shell 里。解决清空所有可能干扰的构建环境变量重新 configure。我一般用 env -i 只保留必要变量env -i PATH$PATH HOME$HOME ../gcc-9.1.0/configure \ --prefix/opt/gcc-9.1.0 --enable-languagesc,c --disable-multilibenv -i 把 CPATH、LIBRARY_PATH、LD_LIBRARY_PATH 全部隔离在外。这样配置如果成功说明就是环境变量污染不是源码包或宿主编译器本身的问题。5.2 64 位机器上链接报 cannot find crt1.o现象configure 通过make 进行到一半时链接失败报 “cannot find crt1.o: No such file or directory” 或 “cannot find -lc”。原因库搜索路径被污染。crt1.o 和 libc.so 并不是不存在而是链接器没被领到正确的系统库目录。常见来源是之前为了另一个软件 export 过 LIBRARY_PATH 指向 /opt 下某个目录或者配置里开了 multilib 后64 位机器上缺 32 位库搜索路径。解决先执行 env | grep -E LIBRARY|CPATH 看看有没有可疑路径有就清掉重配。没有可疑路径的情况下再检查是不是 multilib 的问题——如果你并不需要 -m32确认 configure 时带上了 --disable-multilib。我把这条单列出来是因为它太容易让人误以为系统库坏了实际大多数时候只是链接器不知道自己在几位的世界里。5.3 在源码目录里二次 configure 导致的诡异检测结果现象第一次在源码目录里跑了 configure后来想换 prefix 重新来make distclean 之后再次 configure检测结果和第一次明显不一致甚至之前能过的检测这次不过。原因GCC 源码目录内的 distclean 并不保证把所有自动生成文件清理干净。config.cache 缓存了上一次的检测结论reset 不彻底第二次检测带着上一份答案跑就会出现“改了配置但没生效”的错觉。解决别在源码目录里 configure。如果已经发生最稳的处理是删除整个源码目录重新解压或者至少执行 find . -name config.cache -delete 把缓存清掉。从此以后所有构建都在独立 build 目录进行这个坑就不会再出现。这也是我在 3.1 里坚持源码和构建分开的原因不是洁癖是这套源码包真的会记仇。5.4 make 中途报 as: unrecognized option 或汇编器报错现象编译到汇编阶段失败错误信息里出现系统自带的 GNU as 不认识某个选项比如 “as: unrecognized option -march...”。原因GCC 9 生成的汇编代码或传给汇编器的参数比较新宿主系统上的 binutils 版本太老两边版本不匹配。老系统上自带的 binutils 通常停留在非常保守的版本和新的编译器前端搭不到一起。解决先升级 binutils再重新 make。GCC 构建依赖 as 来处理生成的汇编binutils 落后于编译器版本是老系统上编译新 GCC 最常见的一类问题。如果 binutils 不方便整体升级也可以 configure 时用 --with-as/path/to/new/as 单独指定汇编器但优先级不如直接升级 binutils 干净。5.5 装好后运行程序报找不到 libstdc.so.6现象编译安装都成功gcc --version 正常但拿新 g 编出来的可执行文件一运行就报 “error while loading shared libraries: libstdc.so.6: cannot open shared object file”。原因新编译器的 libstdc 被安装在 /opt/gcc-9.1.0/lib64 下这个路径既不在系统动态库搜索路径里也没有被写进可执行文件的 RUNPATH。g 编译时默认链接的是新版本库运行时动态加载器却找不到它。这是 GCC 装进独立 prefix 后最典型的“装好了但用不了”场景。解决按场景三选一。只在自己终端用export LD_LIBRARY_PATH/opt/gcc-9.1.0/lib64:$LD_LIBRARY_PATH给整机用在 /etc/ld.so.conf.d/ 下新建一个配置文件写入该目录然后执行 ldconfig最推荐的是编译项目时加 -Wl,-rpath,/opt/gcc-9.1.0/lib64把查找路径直接烧进可执行文件别人拿过去也能跑。最后一种也是我坚持给项目 Makefile 加的一条选项下一章展开说。6. 验证与多版本共存把新工具链真正用起来6.1 不只看 gcc --version用一个小程序验证动态库落地版本号只说明编译器本体装好了不代表编出来的程序能跑。我的验证三步是这样cat /tmp/test.cpp EOF #include iostream int main() { std::cout gcc9 ok std::endl; } EOF /opt/gcc-9.1.0/bin/g /tmp/test.cpp -o /tmp/test ldd /tmp/test | grep libstdcldd 输出里如果看到 /opt/gcc-9.1.0/lib64/libstdc.so.6说明可执行文件已经正确绑定新库如果显示系统目录下的旧库说明链接器优先选择了系统库路径回到 5.5 处理。这一步比 gcc --version 更能证明“这个 GCC 是在真正工作”也是我在新机器上验证工具链时从不少跑的一步。6.2 多版本共存别动 /usr/bin 下的软链新老 gcc 并存时我坚决不动 /usr/bin/gcc 的软链。系统里大量软件在构建时会调 /usr/bin/gcc把它指向新版本等于让这些软件集体换了 ABI风险面太大。合理做法是让 /opt/gcc-9.1.0/bin 在 PATH 中最靠前或者更稳妥地在新项目的构建脚本里写死路径CXX : /opt/gcc-9.1.0/bin/g CC : /opt/gcc-9.1.0/bin/gcc在多人共用的机器上改 PATH 会影响所有人碰到这种场景我建议用系统自带的工具来做切换比如 update-alternatives让需要新版本的账号单独使用而不是全局改 PATH 顺序。6.3 把 rpath 写进项目一个抵十个环境变量最后推荐一个我当成默认习惯的参数编译时加 -Wl,-rpath,/opt/gcc-9.1.0/lib64。临时单机验证可以图省事用 -static-libstdc但团队项目里把 rpath 写进链接选项是最省心的做法每个可执行文件都自带正确的动态库路径不依赖使用者 shell 里有没有那一条 export。我第一次在一台旧服务器上编完 GCC 9.1.0 后就因为在运行程序时才暴露 libstdc 找不到折腾了半个多小时。后来所有新工具链一律走独立目录加 rpath再没因为“装好了但用不了”翻过车。希望这个习惯也能帮到你。本文还有配套的精品资源点击获取