
从 2024 年 2 月 5 日那天在机房里折腾 GCC 源码编译算起这套流程我前后在不同发行版上跑过七八遍。Linux 纯命令行下用源码编译安装 gcc说白了就是绕开发行版的包管理器自己把编译器从 C 源码一路生出来。这事听起来硬核做起来也确实耗时间、吃磁盘、容易在半路报错但它的价值在于你能拿到任意指定版本、能精确控制语言前端、能在没有外网仓库的离线机器上把工具链补齐。这篇内容适合三类人看——系统自带 GCC 版本太老又必须升级的运维、需要在离线环境搭工具链的嵌入式开发、以及想搞明白编译器是怎么编译自己的 Linux 学习者。下面我把选型理由、依赖准备、configure 参数拆解、完整命令序列、多版本共存和踩坑排查一次性讲透你照着抄作业基本能跑通。1. 为什么值得花几个小时源码编译 GCC1.1 包管理器装出来的 GCC 到底差在哪大多数发行版仓库里的 GCC 是被阉割过的。最典型的是只装了 C 和 C 前端Fortran、Ada、Go 这些语言前端要么拆成单独的包要么根本没编译进去再就是版本锁定Ubuntu 20.04 长期停在 GCC 9RHEL 7 停在 GCC 4.8.5你想上 GCC 13 只能用第三方源而第三方源在离线机房、内网隔离环境里根本连不上。另外仓库版本通常带发行版自己的补丁集行为跟上游 release 有细微差异做编译复现时容易出同一份代码在 A 机器能过 B 机器报错的鬼故事。还有一个容易被忽略的点发行版的 GCC 会绑定同版本的libstdc.so.6运行时。当你在 A 机器上用新标准编译了一个 C20 程序拷到只有老libstdc的 B 机器上GLIBCXX_3.4.29 not found的报错就来了。源码编译时可以自己决定运行时的部署位置问题反而可控。apt install gcc -y这条命令本身没毛病它快、稳、依赖自动解决日常开发完全够用。它的问题只在你要的东西它给不了的时候才暴露出来。1.2 自举编译三阶段耗时到底耗在哪里GCC 的源码编译不是编译一次就完事它默认走的是bootstrap自举流程分三个阶段阶段用什么编译器产物目的stage1宿主系统自带的旧 GCC一个功能受限的新版编译器先把新编译器生出来stage2stage1 产出的新编译器完整的新编译器用自己的产物再编一遍自己stage3stage2 产出的编译器第二次完整产物与 stage2 逐文件比对一致性三个阶段的比对compare才是 bootstrap 的真正意义如果 stage2 和 stage3 编出来的目标文件完全一致说明这个编译器已经能稳定地编译自身逻辑上自洽。任何不一致都会在 make 阶段报错并停下来等于免费帮你做了一轮编译器正确性验证。代价就是时间。同样一份源码--disable-bootstrap大概只要三分之一到四成的构建时间。我给你一个实测参考16 核 32G 内存的机器GCC 13.2.0 只开c,c两个前端、开启 bootstrap大约 55 分钟到 1 小时 10 分如果开了 Fortran 和 LTO能拖到 1 小时 40 分左右。单核虚拟机的话请直接按 5 到 8 小时做心理准备。1.3 磁盘、内存、CPU 三个硬指标怎么估磁盘是最容易翻车的。建议预留 25GB 以上来源如下源码包解压后约 1GBdownload_prerequisites拉下来的 GMP/MPFR/MPC/ISL 加起来约 200MB构建目录在 bootstrap 全开时能涨到 15 到 20GB三个阶段的中间目标文件都要留着做比对make install之后安装目录再占 2 到 3GB。如果你想省空间装完以后构建目录可以直接删掉但删掉就没法make uninstall也没法补装语言前端。内存按每并行任务 1.5 到 2GB估算。make -j16就需要 24 到 32GB 可用内存不然很容易在编译insn-emit.cc、i386.md生成的巨型文件时触发 OOM Killer。我见过最惨的一次是全程跑了 50 分钟最后在 stage3 被内核杀掉进程dmesg里一行Out of memory: Killed process直接白干。内存不够就别硬上高并发-j4慢慢跑或者临时挂一块 8GB swap。CPU 只决定速度不决定成败但有一点要注意GCC 的构建过程里有不少单线程串行的环节比如configure的各子目录探测、genattrtab之类的生成器所以核心数从 8 加到 32总时间并不会线性减少大概只能再快 20% 到 30%。2. 动手前的环境盘点与依赖准备2.1 先确认宿主工具链能编译出编译器GCC 的源代码本身是用 C 和 C 写的所以你的机器上必须已经存在一个能用的 C/C 编译器这叫宿主编译器。检查方式很简单gcc --version g --version make --version ld --version只要gcc和g都能正常输出版本号一般就能起步。但还有个隐藏条件宿主 GCC 的版本不能太老。GCC 官方要求构建 GCC 13 需要宿主支持 C11 以上的编译器实践中用 4.8.5 去构建 GCC 13 会在 stage1 阶段就报一堆语法错误。如果系统自带的是 4.8 这种级别先用仓库把 gcc/g 升到能升的最高版本或者干脆装一个开发工具集Software Collections / Devtoolset再拿它当宿主。另外确认make版本不低于 3.80binutilsas、ld版本不要太旧。用新一代 GCC 配一个 2010 年的ld会在链接阶段遇到无法识别 .note.gnu.property 段之类的报错这时候升级binutils是唯一出路。提示动手之前先把系统时间校准一遍。源码包里文件时间戳如果比系统时间还新make 会抛出Clock skew detected警告严重时会导致依赖判断错乱、构建结果不可信。2.2 一次性补齐构建依赖离线环境怎么绕这几个依赖分两拨一拨是必须由系统提供的一拨是可以让 GCC 自己下载的。系统侧依赖清单Debian/Ubuntu 系sudo apt update sudo apt install -y build-essential libgmp-dev libmpfr-dev libmpc-dev \ libisl-dev zlib1g-dev libc6-dev texinfo bison flex gawk m4 \ python3 gettext libncurses-dev bzip2 xz-utilsRHEL/CentOS 系对应的是sudo yum install -y gcc gcc-c make glibc-devel zlib-devel \ gmp-devel mpfr-devel libmpc-devel isl-devel bison flex \ texinfo gawk diffutils patch perl python3 gettext这里每个包都不是随便加的。gmp-devel、mpfr-devel、libmpc-devel、isl-devel是 GCC 做常量折叠、循环优化、多项式算术必须的数学库缺任何一个configure阶段就会报Building GCC requires GMP 4.2, MPFR 3.1.0 and MPC 0.8.0。bison和flex用来生成语法分析器有些发行版的最小化安装里是没有的。texinfo是因为 GCC 会构建 info 格式的文档缺了会报makeinfo: command not found。gawk在构建脚本里被大量调用某些系统把awk指向了mawk可能导致解析结果异常。离线机器的做法是把这些 rpm/deb 包提前下载好用yum localinstall/dpkg -i装或者干脆挂载发行版 ISO 做成本地源。另一个偷懒但可行的办法是把所有需要 build 的语言限定为 C这样对 texinfo、gawk 的依赖会宽松一些。GMP/MPFR/MPC/ISL 也可以让 GCC 自己带。源码根目录下有个contrib/download_prerequisites脚本cd /usr/local/src/gcc-13.2.0 ./contrib/download_prerequisites它会下载指定版本的这几个库并在源码目录里创建软链接构建时直接用自带的版本跟系统装没装 devel 包无关。这个脚本对网络有要求内网环境需要提前把对应的 tar 包放到源码根目录脚本会自动跳过下载直接用。2.3 源码获取与校验顺手说说中文乱码解压源码从 GCC 官方镜像站拿。2024 年 2 月的时候13.2.0 是最新的稳定 release14 系列还没正式发布所以这个时间点直接选gcc-13.2.0.tar.xz是合理的。cd /usr/local/src wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-13.2.0/gcc-13.2.0.tar.xz wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-13.2.0/gcc-13.2.0.tar.xz.sig sha512sum gcc-13.2.0.tar.xz下载完一定要比对官方.sig或镜像站给出的哈希值。GCC 源码包两个多 G不.tar.xz大约 130MB解压后约 900MB 到 1GB。校验这一步不要省我在一次传输中遇到过镜像站同步不完全、包体被截断的情况解开以后configure阶段报configure: error: source directory already configured这种莫名其妙的错误查了半天才发现是包不完整。解压用tar基本不会出乱码因为它不处理文件名编码转换。真正会遇到乱码的是unzip解压 Windows 发过来的 zip 包Linux 默认按 UTF-8 解析 zip 里的文件名而 Windows 中文版压出来的 zip 用的是 GBK 编码于是文件名变成一堆问号和方块。解决办法是unzip -O CP936 xxx.zip注意-O这个参数需要 unzip 6.0 以上才支持老版本要么升级要么用python3 -c import zipfile;...脚本手动指定编码重命名。至于tar.gz/tar.xz里如果出现中文名乱码那是打包时的 locale 环境导致用tar --to-command配合iconv转码可以救回来但更省事的做法是让打包方统一用 UTF-8。解压命令tar -xf gcc-13.2.0.tar.xz ls -l /usr/local/src/gcc-13.2.03. configure 参数逐项拆解别照抄网上那套3.1 必须显式指定、漏了就后悔的几个参数GCC 的configure参数有上百个绝大多数可以不管但下面这几个强烈建议显式写出来因为它们直接决定你装完之后能不能舒心地用。第一是--prefix。不指定的话默认装到/usr/local然后bin、lib、include全糊在已有的目录里——你到时候想卸载、想换版本、想同时留两个版本就全是灾难。我的习惯是每个版本一个独立目录--prefix/usr/local/gcc-13.2.0第二是--enable-languages。不写这个参数GCC 会尝试构建全部语言前端包括 Ada、Go、D、Rust新版本有 gccrs这会让构建时间和磁盘占用直接翻倍而且 Ada 前端还需要一个 GNAT 编译器做宿主大概率中途报错停下。只写你真正要用的--enable-languagesc,c,fortran只要你写的是 C 和嵌入式开发c,c就够了。做科学计算的把fortran加上。写 Go 的话现在基本都是官方 toolchain不用 GCC 的go前端了。第三是--disable-multilib。在 x86_64 上GCC 默认会尝试同时构建 32 位和 64 位的运行时库这时候它需要系统里有 32 位的 glibc 开发包glibc-devel.i686/libc6-dev-i386。没装的话会在链接 32 位libgcc时报cannot find crti.o: No such file or directory。绝大多数现代场景根本不需要 32 位支持直接关掉能省下将近一半的构建时间和磁盘。第四是--enable-checkingrelease。这是把编译器内部的断言检查降到发布级别。默认在某些快照版本上可能是yes会显著拖慢构建设成release既保证基本正确性又不会被调试断言拖死。3.2 --disable-bootstrap 到底要不要加这是最值得纠结的一个参数。结论是第一次编译建议保留 bootstrap跑通一遍确认环境和源码都没问题后续做版本迭代或者只是想要个能用的编译器可以加上--disable-bootstrap省时间。加上之后 GCC 只做一次编译用宿主编译器直接把完整功能的 GCC 编译出来跳过 stage2/stage3 的自举和比对。快是真快但有两个代价一是失去了编译器的自洽性验证一个隐藏的代码生成 bug 可能就这么留在你的编译器里二是某些发行版在 make 时会检查stage_final文件用--disable-bootstrap后如果你还想make install路径判断逻辑有细微差别偶尔会遇到 install 阶段提示找不到文件加个make all就能绕过去。如果你追求编译器自身的执行性能还有一个--enable-bootstrapprofile或make profiledbootstrap它会用带性能分析的编译器再编译一遍产出的 GCC 编译代码能快几个百分点。代价是构建时间再涨 30% 左右除非是做长期使用的生产工具链否则不值。3.3 多版本共存program-suffix 比你想的更好用如果你机器上已经有系统 GCC而且你不想动它最优雅的方案是给新装的编译器统一加后缀--program-suffix-13这样装出来的可执行文件是gcc-13、g-13、gcov-13、gfortran-13跟系统里的gcc完全井水不犯河水不需要改 PATH也不需要update-alternatives。要用的地方直接CCgcc-13 CXXg-13 make指向明确排错时一眼就能看出用的是哪个版本。相比之下update-alternatives那套更适合我想让全局默认就是新版本的场景但它的隐患是某些系统脚本、dkms内核模块编译、还有内核源码树构建它们内部写死了gcc这个名字你把默认切到 13编不了老内核模块切回去又能编了——这种问题排查起来非常费劲。所以我的实际做法是两者结合--program-suffix保证隔离需要切默认的时候用update-alternatives显式切并且给每个版本打上不同的优先级数字。顺带提一句--with-pkgversion这个参数可以往gcc -v的输出里塞一段自定义标识比如--with-pkgversioninternal-build-2024Q1。团队多人共用一台构建机时这个标识能帮你快速确认这个 gcc 到底是谁编的、什么时候编的。4. 完整编译安装实操记录4.1 从解压到 make 的完整命令序列第一步创建独立的构建目录。GCC 支持源码外构建out-of-tree而且强烈推荐这么做因为构建过程中会生成大量文件污染源码目录后再想重新配置就很麻烦。mkdir -p /usr/local/src/build-gcc-13.2.0 cd /usr/local/src/build-gcc-13.2.0第二步跑 configure。注意配置文件在源码目录里所以路径是../gcc-13.2.0/configure。../gcc-13.2.0/configure \ --prefix/usr/local/gcc-13.2.0 \ --enable-languagesc,c,fortran \ --disable-multilib \ --enable-checkingrelease \ --with-system-zlib \ --enable-lto \ --program-suffix-13 \ --with-pkgversionbuild-20240205 \ 21 | tee configure.log--with-system-zlib表示用系统的 zlib 而不是 GCC 自带的能少编译一份代码。前提是装了zlib1g-dev/zlib-devel没装就去掉这个参数。--enable-lto开启链接时优化支持做大型 C 项目这个很有用。configure跑完大概 2 到 5 分钟最后会打印一段 summary把Languages、Bootstrap、Thread model都列出来。这一步务必确认Bootstrap显示的是enabled如果你想要自举Languages里只有你要的那几个。我曾经因为手误写成--disable-multilibyesconfigure 静默接受了这个奇怪的赋值结果构建出来的东西缺了 64 位运行时装完才发现白跑两小时。把输出用tee存成configure.log是个好习惯出问题的时候翻日志比重新跑一遍快得多。第三步开始 make。并发数不要无脑用nprocfree -g nproc如果free -g显示的 available 内存是 32GBnproc是 16那-j16大概会吃到 24 到 30GB勉强够用但没余量。稳妥起见用内存GB / 2和nproc这两个数里较小的那个make -j12 21 | tee build.log如果想后台跑、断线不中断用nohup或者screen/tmuxtmux new -s gccbuild make -j12 21 | tee build.log # CtrlB 然后 D 分离会话第四步安装。sudo make install 21 | tee install.logmake install会做一次make all的检查然后复制文件。想顺便剥掉调试符号、省点磁盘可以用make install-strip安装体积能小 30% 左右——但代价是你再也不能用它来调试编译器自身的崩溃了。第一次装我还是建议保留完整符号。4.2 编译过程中的资源监控与中断恢复策略构建跑起来之后另开一个终端盯着资源watch -n 5 free -g; echo ---; df -h /usr/local; echo ---; uptime重点看三件事可用内存有没有掉到 1GB 以下快 OOM 了、构建目录所在分区剩余空间还有多少低于 5GB 就危险、平均负载是不是远高于核数说明 I/O 成瓶颈了并发再高也没用。我在一次 8 核 16G 的机器上跑-j8构建到两个小时的时候内存掉到 300MB赶紧CtrlC停掉改成-j4重跑。好在 make 是幂等的已经编译好的目标文件不会重编接着往下走只花了二十分钟补完剩下的部分。这就是不要一开始就用最高并发的理由中断一次重启的代价远小于被 OOM 杀掉后重跑 stage3。如果确实被 OOM Killer 干掉了先看日志确认dmesg | tail -30 grep -i killed process /var/log/messages确认是 OOM 之后再降并发重跑别怀疑源码有问题。还有个小技巧构建过程会产生大量临时文件如果/tmp是 tmpfs 挂在内存里的很可能在链接某个巨型目标文件时把/tmp撑爆报No space left on device。这时候指定TMPDIR到磁盘上export TMPDIR/usr/local/src/tmp mkdir -p $TMPDIR make -j124.3 安装后的环境变量、动态库与验证装完不是就完事了还有三件事必须做。第一件把 bin 加进 PATH。如果用了--program-suffix这一步是可选的不用后缀的话必须加。写一个/etc/profile.d/gcc-13.shexport GCC13_HOME/usr/local/gcc-13.2.0 export PATH$GCC13_HOME/bin:$PATH export LD_LIBRARY_PATH$GCC13_HOME/lib64:$LD_LIBRARY_PATH第二件注册动态库搜索路径。GCC 装出来的libstdc.so.6、libgcc_s.so.1在$prefix/lib64下不注册的话用新编译器编出来的程序运行时会报libstdc.so.6: version GLIBCXX_3.4.32 not found。正确姿势是写 ldconfig 配置而不是依赖LD_LIBRARY_PATHecho /usr/local/gcc-13.2.0/lib64 | sudo tee /etc/ld.so.conf.d/gcc-13.2.0.conf sudo ldconfig sudo ldconfig -p | grep libstdc用ldconfig的好处是全局生效、对 setuid 程序也有效而LD_LIBRARY_PATH在 sudo 环境下经常莫名其妙丢失。注意如果你系统里同时存在多个版本的libstdc.so.6ldconfig 的配置顺序会影响实际加载哪个。安全的做法是不覆盖系统路径只在编译期用-Wl,-rpath,/usr/local/gcc-13.2.0/lib64把运行时路径编进可执行文件里。这样每个程序各带各的路径互不干扰也不会影响系统其他软件。这个技巧在给客户交付二进制程序时特别有用。第三件做一次真实编译验证。别只看gcc -v的输出那只是元数据真正要验证的是它能不能编出能跑的东西cat /tmp/hello.cpp EOF #include iostream #include vector #include algorithm int main() { std::vectorint v{3, 1, 4, 1, 5, 9, 2, 6}; std::sort(v.begin(), v.end()); for (int x : v) std::cout x ; std::cout \n__cplusplus __cplusplus std::endl; return 0; } EOF g-13 -stdc20 -O2 /tmp/hello.cpp -o /tmp/hello /tmp/hello g-13 -print-search-dirs gcc-13 -dumpversion__cplusplus打印出202002说明 C20 标准支持到位-print-search-dirs输出里确认install:指向你的新路径、programs:里包含新路径下的libexec/gcc/x86_64-pc-linux-gnu/13.2.0就说明整套工具链自洽了。5. 多版本切换与工程侧适配5.1 update-alternatives 与 profile.d 两种方案怎么选如果你想要敲gcc就是新版本的体验用update-alternatives注册一组从属链接sudo update-alternatives --install /usr/bin/gcc gcc /usr/local/gcc-13.2.0/bin/gcc-13 100 \ --slave /usr/bin/g g /usr/local/gcc-13.2.0/bin/g-13 \ --slave /usr/bin/gcc-ar gcc-ar /usr/local/gcc-13.2.0/bin/gcc-ar-13 \ --slave /usr/bin/gcc-nm gcc-nm /usr/local/gcc-13.2.0/bin/gcc-nm-13 \ --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/local/gcc-13.2.0/bin/gcc-ranlib-13那三个gcc-ar、gcc-nm、gcc-ranlib是配套的不带它们的话开了 LTO 的项目在链接阶段会因为ar不认识 LTO 中间文件而报错这个坑很多人踩过。注册完用sudo update-alternatives --config gcc交互式切换优先级数字越大越优先。而profile.d方案更轻改一行文件、重新登录 shell 就生效走 PATH 优先级覆盖。缺点是 root 用户用sudo执行命令时会走secure_path默认不包含/usr/local/gcc-13.2.0/bin于是sudo gcc ...用的还是老的。要解决这个得改/etc/sudoers里的secure_path——但不建议动它因为你一旦改了其他所有 sudo 命令的环境都跟着变风险扩散。更推荐的做法是明确写全路径或者用-H让 sudo 保留 HOME 环境。5.2 编译内核、Redis、LLVM 项目时的版本适配新 GCC 编译老代码最容易踩的是这几类内核源码。Linux 5.10 及更早的内核对 GCC 版本有比较严格的要求用 GCC 13 编译常报unknown register name、-Werror相关错误或者某个Kconfig选项直接报 requires GCC version 12。解决办法不是去改内核源码而是在make时显式指定编译器make CC/usr/bin/gcc-11 HOSTCC/usr/bin/gcc-11 -j8CC指定目标代码的编译器HOSTCC指定给主机跑的工具scripts/下面那些用的编译器。两个都指定成功率最高。老 C 项目。GCC 14 开始-Wimplicit-function-declaration、-Wint-conversion这类警告在很多发行版里被提升成了错误老代码一编就挂。临时解决办法是加-Wno-errorimplicit-function-declaration或者在CFLAGS里补-stdgnu89——C89 默认允许隐式声明。这属于掩盖问题的做法能用它先跑通但长期还是得把代码修干净。C 项目。一个非常高频的报错是error: uint32_t does not name a type。新版本的libstdc头文件对自己的依赖做了清理以前间接包含进来的cstdint现在不自动带了。修法很简单在报错的文件头部加#include cstdint。类似的情况还有memory、algorithm、cstdioGCC 版本越高越倾向于不帮你顺手带上这些头文件。编译 Redis 这类纯 C 项目基本上不会因为 GCC 版本升级而翻车它的代码质量比较高-Wall -Werror也一直保持能过。真正容易出问题的是那些年代久远的 C 项目、CUDA 扩展、以及带自定义汇编的内核模块。6. 踩坑记录常见报错逐条排查6.1 configure 阶段报错速查表报错信息真实原因解法Building GCC requires GMP 4.2, MPFR 3.1.0 and MPC 0.8.0缺数学库开发包装libgmp-dev等或跑download_prerequisiteschecking for the correct version of mpfr.h... no装了运行时库但没装头文件装-dev/-devel包别只装libmpfr4C preprocessor /lib/cpp fails sanity check缺 C 编译器或 glibc 头文件装g和libc6-dev/glibc-develcannot compute suffix of object files链接器不可用或缺crti.o装libc6-dev-i386或加--disable-multiliberror: no acceptable C compiler found in $PATH宿主没有编译器先装一个系统 gcc 当宿主The directory that should contain system headers does not exist缺 glibc 开发包补齐glibc-devel有一类报错特别迷惑人configure报 MPFR 版本不对但你明明已经装了正确版本的libmpfr-dev。这种情况通常是 GCC 内部对 MPFR 的库文件名做了硬编码检查比如它找libmpfr.so.4而你系统里是libmpfr.so.6。网上流传的建个软链接骗过检查的做法能用但会让构建结果的可靠性打折。正路是把系统的 MPFR 升到 GCC 要求的版本或者让download_prerequisites下载指定版本的 MPFR 一起捆进源码目录后者更干净。6.2 make 阶段报错速查表报错信息真实原因解法Killed且 dmesg 有 OOM 记录并发数过高吃爆内存降-j或加 swapNo space left on device构建/临时分区空间不足TMPDIR指到大盘、清 /tmpmakeinfo: command not found缺 texinfo装texinfo或MAKEINFOtrueerror: expected ) before x出现在 stage1宿主编译器太老升级宿主 gcc 到 C11 以上Clock skew detected. Your build may be incomplete源码时间戳超前于系统时间date -s校准时间后重跑error while loading shared libraries出现在构建过程中生成的工具上新生成的工具找不到自带的 libstdc确认没手动设置过LD_LIBRARY_PATH干扰关于Clock skew这个坑我印象很深。有一台虚拟机因为宿主机休眠导致时间漂移GCC 源码解压后的时间戳比系统时间还新make 认为目标文件比源码旧于是反复重编同一批文件跑了一晚上都没进展。touch -d把源码时间戳改旧、或者直接ntpdate校准问题立刻消失。构建前顺手date看一眼能省掉一整晚的排查。还有一类报错出现在 stage3 的比对环节error: comparison failed。这说明 stage2 和 stage3 编出来的目标文件不一致。可能的原因包括宿主编译器本身有 bug、构建过程被时间和路径差异干扰这是为什么官方建议构建路径不要带随机部分、磁盘出现静默数据错误。这种时候最省事的办法是加--disable-bootstrap先拿到一个能用的编译器别在这个环节死磕除非你的目的就是验证编译器正确性。6.3 装完不生效的排查思路装完 gcc 了为什么gcc -v还是旧版本——这是被问得最多的问题。按下面顺序查基本三分钟定位which -a gcc # 列出 PATH 里所有名为 gcc 的命令 type -a gcc # 看看是不是被 alias 或 shell function 覆盖 echo $PATH # 确认新路径排在最前面 hash -r # 清掉 shell 的命令路径缓存 sudo -i which gcc # 换 root 身份看看是不是 secure_path 的锅最常见的三个原因PATH 顺序写反了把新路径 append 到末尾而不是 prepend 到开头shell 缓存了旧命令路径hash -r解决这个坑最阴改了 profile 也还是一直用旧路径sudo 的 secure_path 不认你的新路径。第三个前面说过不要改 sudoers写全路径最稳。另一个高频问题新编译器编出来的程序在本机跑得欢拷到别的机器就报GLIBCXX_3.4.32 not found。原因是新版本 GCC 带的libstdc.so.6在目标机器上不存在。解决办法是静态链接 libstdc-static-libstdc -static-libgcc或者用-Wl,-rpath把库路径编进去并随程序一起打包。前者简单但静态链接出来的二进制约大 1 到 2MB而且失去了运行时库更新的好处。7. 我个人在几次构建中沉淀下来的经验源码编译 GCC 这事技术门槛其实不高难的是耐心和细节。我头两年在云服务器上编译make跑到 stage3 报错去搜错误信息发现是 gawk 的一个版本差异导致某个生成脚本输出格式变了换个 gawk 重跑就行。后来我养成了一个习惯开工前把宿主环境的完整版本信息抓一遍存档包括cat /etc/os-release、gcc -v、ld --version、awk --version、make --version、nproc、free -g、df -h。这样后面出问题的时候能一眼看出哦这台机器的 awk 是 mawk之类的差异不用靠猜。关于--enable-languages的选择我踩过一次挺亏的坑为了省事直接不写这个参数让 GCC 构建全部语言前端结果构建到 Ada 部分时因为宿主没有 GNAT 而中止前面一个多小时全白费。永远显式声明你要的语言永远显式声明--disable-multilib这两条能避开至少一半的构建失败。再就是关于磁盘的事故。构建目录占 20GB 这件事说了很多遍还是有人栽。我有次把构建目录放在了一个 40GB 的系统盘上跑到 70% 的时候df显示只剩 200MBmake 报No space left on device全盘大扫除清理后重跑。现在我的做法是构建目录固定在单独挂载的数据盘上/etc/fstab里给足空间构建前先df -h确认。最后一个我觉得很值的经验把构建过程完全脚本化。把下载、校验、解压、configure、make、install、ldconfig、验证这一串写成一个 shell 脚本每次要新版本时只需要改脚本最上面那几个变量。这么做的好处不只是省事更重要的是可复现——半年后你要在另一台机器上再装一次同样的版本跑一遍脚本就行不会因为当时敲的是哪条命令来着而重头摸索。脚本里记得加上每步的时间戳输出构建失败以后一眼就能看出卡在哪一步、卡了多久。还有一点构建日志千万别随手删。configure.log、build.log、install.log这三个文件加起来也就几十兆但当你三个月后需要确认当时到底编译了哪些语言前端时它就是唯一可靠的证据。我一般的做法是在安装目录下建一个BUILD-INFO.txt把日期、宿主机信息、完整 configure 命令、构建耗时都记在里面跟工具链一起归档。