简介GCC 9.5.0 是 GNU 编译器套件进入成熟期的稳定版本这份 .tar.gz 源码压缩包面向需要在 Linux/Unix 类系统上从源码构建编译器、进行交叉编译或希望阅读与定制编译器实现的开发者、学生和科研人员。压缩包大小约 122.2MB内含 gcc-9.5.0 顶层源码目录并提供 configure、make 等构建阶段所需脚本上游未返回文件明细具体文件数量与类型暂无法列出。已有 170 人学习下载。获取后可完整经历从配置、编译到安装的标准流程也能按需裁剪特性、修改 gcc/g 前端或后端代码借此理解 C、C、Fortran、Ada 等语言如何经过词法分析、语法分析、优化与代码生成最终生成可执行程序。该源码遵循 GPL 许可证适合作为编译器原理教学案例、工具链移植基础或二次开发起点对开源编译器技术研究具有较高参考价值。1. gcc-9.5.0.tar.gz 是什么为什么一个旧版编译器源码包会被反复下载gcc-9.5.0.tar.gz 是 GCC 9.x 分支最后一次正式发布的源码压缩包解压后得到完整的编译器源码和配套运行库而不是现成的二进制。很多人第一反应是“apt 装一个不就行了吗”但实际工作中固定工具链版本、复现旧工程的构建环境、在内网或国产化平台离线交付恰恰需要这种可重复安装的源码包。系统仓库里的 gcc 版本和发行版绑定一旦升级牵连 libstdc 和一堆依赖旧库的程序手动编一份装到独立目录反而是最省心的选择。这篇笔记完整走一遍从下载校验、configure、make 到避坑的落地路径适合服务器维护、CI 流水线和嵌入式交叉开发的同事照着重现。2. 下载与校验装 gcc-9.5.0.tar.gz 前先锁定源和哈希2.1 文件从哪来才放心目录名、包名和工具链版本要对得上Linux 下下载 GCC 编译器源码包我一般不会直接去搜索引擎找一个“gcc-9.5.0.tar.gz”链接而是打开 GNU 镜像站进入gnu/gcc/gcc-9.5.0/目录。GNU 官方维护的镜像列表里比较顺手的是清华 TUNA 和中科大 USTC 镜像两者都同步了 gcc 的全部历史版本目录结构和官方 ftp 一致。mkdir -p ~/src/gcc-9.5.0-src cd ~/src/gcc-9.5.0-src wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.5.0/gcc-9.5.0.tar.gz # 解压前先看看压缩包里的顶层目录确认不是被改名过的文件 tar -tzf gcc-9.5.0.tar.gz | head -5逻辑说明tar -tzf只列出压缩包内容不实际解压这一步能同时验证压缩包是否损坏、顶层目录名是否正确。正常情况第一行应该是gcc-9.5.0/后面跟着gcc-9.5.0/COPYING之类。如果解压后顶层目录名不一致后续脚本里的相对路径全要改容易埋雷。镜像站同时提供.tar.xz和.tar.gz两种格式.xz体积更小但解压依赖 xz-utils对老系统并不友好。我一般选.tar.gz通用性最好在最小化安装的 Ubuntu Server 上不用额外装 xz 工具。同时也是为了后续把源码包拷贝进内网机器时少一个解压工具的依赖。2.2 哈希与 GPG 签名给压缩包上双保险光看文件名不能证明包没被替换过。GNU 发布目录里会带校验文件常见的是SHA512SUMS和.sig签名文件。我先算哈希再对值这一步能挡掉绝大多数据包损坏和镜像同步不完整的问题。cd ~/src/gcc-9.5.0-src wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.5.0/SHA512SUMS # 只关心 gcc-9.5.0.tar.gz 那一行的校验结果 sha512sum -c SHA512SUMS 21 | grep gcc-9.5.0.tar.gz参数说明sha512sum -c会按校验文件里的记录逐条比对输出OK才代表匹配。个别镜像站不提供 SHA512SUMS 而只给 SHA256SUMS写法一样把命令换成sha256sum -c即可。如果镜像上两种校验文件都没有我的习惯是再从另一个镜像把同一版本拉一遍比较两个文件的哈希至少能排除“下到一半断了”和“镜像同步了一半”这两类低级问题。比哈希更严谨的是 GPG 签名验证需要先从 GNU 官网导入 FSF 的发布公钥再对gcc-9.5.0.tar.gz.sig做验签。这一步在实际工程里用得不多因为哈希校验已经能覆盖绝大多数场景只有在内网高安全要求的环境才强制要求验签。需要注意GCC 发布密钥号和 GnuPG 版本有关老密钥在主机上导入后偶尔会报 trust 问题不要在这个时候纠结公钥信任链改走哈希比对是更快的路。2.3 提前准备 gmp、mpfr、mpcconfigure 卡住的头号原因GCC 源码包里缺三样东西GMP、MPFR、MPC分别是多精度整数、多精度浮点和多精度复数运算库编译器在生成代码和做常量折叠时要调用它们。Ubuntu 上如果没装这几个开发库configure 会在早期的依赖检查阶段直接退出报错指向gmp.h找不到很多人这时候才回头补依赖白白浪费一轮编译前的等待。有两种常见做法。第一种是直接用发行版提供的开发包sudo apt-get install -y libgmp-dev libmpfr-dev libmpc-dev第二种是把依赖源码放到 GCC 源码树里由 GCC 构建系统一并用上。GCC 官方在源码包自带了contrib/download_prerequisites脚本执行一次就能把匹配当前 GCC 版本的依赖源码下载并解压到位cd ~/src/gcc-9.5.0-src/gcc-9.5.0 ./contrib/download_prerequisites逻辑说明脚本会按 GCC 9.5.0 的配置记录下载对应版本的 gmp、mpfr、mpc 源码包解压到 GCC 源码树下的同级目录之后 configure 会自动识别并使用这些依赖。好处是版本完全匹配不受系统仓库版本影响后续如果把源码包和依赖一起拷进内网机器离线也能完成整体构建。我自己的习惯是偏向第二种原因在于 Ubuntu 20.04 的 libmpc-dev 版本和 GCC 9.5 需要的版本虽然能编译通过但版本组合偶发 “GMP/MPFR/MPC version mismatch” 的诡异报错源码版依赖把这些组合锁死少一层变量。3. configure 阶段把 gcc-9.5.0 的安装路径、语言集和自举方式一次定对3.1 在独立 build 目录跑 configure给后悔药留个后门GCC 源码树可以直接在源码目录里执行 configure但我从不这么做。GCC 是出了名的大工程编译过程会生成大量目标文件一旦配置参数要调整源码目录里残留的中间产物会导致下一次 configure 行为不可预期。独立 build 目录是源码编译领域的标准做法GCC 官方文档也明确支持 out-of-tree build。mkdir -p ~/src/gcc-9.5.0-build cd ~/src/gcc-9.5.0-build ../gcc-9.5.0-src/gcc-9.5.0/configure \ --prefix/opt/gcc-9.5.0 \ --enable-languagesc,c \ --disable-multilib参数说明--prefix决定安装位置写成/opt/gcc-9.5.0而不是系统默认的/usr/local是为了让新旧两个编译器可以长期共存。--enable-languagesc,c把编译器限制在 C 和 C如果只做后端开发这两个语言够用了不要默认全开全开意味着 Fortran、Ada、Go 等语言前端也要一起编编译时间会明显变长。--disable-multilib是关掉 32 位和 64 位两套库的重复构建后文单独展开。这一步执行完build 目录里会出现 Makefile、config.status 和 config.log。只要这个目录还在改前缀或者调整参数后重新 configure成本很低这也是 out-of-tree 构建最直接的价值。3.2 三个几乎必调的参数prefix、languages、multilibGCC configure 可以接受的参数非常多实际落地时大部分参数保持默认即可真正需要认真想的是以下三个。--prefix是第一个要想清楚的。默认值是/usr/local也就是make install之后会把 gcc、g 放到/usr/local/bin。问题在于很多发行版的 PATH 里/usr/local/bin排在/usr/bin前面装完以后系统原有的 gcc 等于被“覆盖”了但又不完全覆盖库文件仍然混在系统路径里反而最难排查。显式指定/opt/gcc-9.5.0整个编译器从 bin 到 lib64 都在一个独立目录里不用了直接删目录不留残余。--disable-multilib没有看起来那么简单。默认情况下 x86_64 系统会同时构建 64 位和 32 位两套库整个构建时间和磁盘占用接近翻倍。很多服务器场景根本不需要产出 32 位可执行文件关掉它是最常见的选择。反过来如果你的项目确实要编 32 位程序就不能关还得先确认系统装了 32 位的 glibc 开发库也就是libc6-dev-i386之类的包否则 configure 会因为找不到 32 位系统库直接报错。--program-suffix-9.5这个参数在需要多版本并存的场景非常实用装出来的命令名会变成gcc-9.5、g-9.5。Ubuntu 自己维护的 gcc-9 包就是这么做的。我可以随时调用任意版本的命令而不是反复改 PATH../gcc-9.5.0-src/gcc-9.5.0/configure \ --prefix/opt/gcc-9.5.0 \ --enable-languagesc,c \ --disable-multilib \ --program-suffix-9.5参数说明加了这行之后/opt/gcc-9.5.0/bin/下出现的是gcc-9.5而不是gcc符号链接方式也可以生效。注意gcc-9.5和g-9.5两个命令之间不会互相覆盖因为它们各自带了不同的后缀。后续 Makefile 里写死/opt/gcc-9.5.0/bin/g-9.5路径唯一不会出现“今天 PATH 变了编译行为就变了”的问题。3.3 bootstrap 的取舍与 config.log 的用法不再面对黑匣子GCC configure 默认会开启 bootstrap也就是把编译器分阶段构建三遍先用手头已有的编译器把当前源码编译一遍再用刚生成的编译器编译第二遍最后用第二遍的产物编译第三遍并检查后两遍结果是否一致。这一整套流程的意义在于验证编译器自身没有被源编译器的问题传染也是 GCC 作为一个自举编译器最底层的可信机制。代价是时间。关闭 bootstrap 可以明显缩短构建时间代价是少了一次自我验证。我的建议在服务器上固定长期使用的工具链保留默认 bootstrap如果是 CI 里临时构建验证可以考虑加--disable-bootstrap。构建时间不是关键指标产出工具的长期可信才是。configure 失败的时候很多新手喜欢反复改参数重试这是效率最低的做法。正确动作是先翻 config.logcd ~/src/gcc-9.5.0-build tail -n 80 config.log逻辑说明config.log 记录了 configure 每一步探测的完整输出包括编译测试程序时的详细错误信息。比如依赖缺失时报错不会直接说“缺 gmp”而是在某个测试程序编译失败后的输出里出现/usr/include/stdc-predef.h:0 找不到之类的线索。读了日志再改参数通常一轮就能定位。4. make 与 make install拿到 /opt/gcc-9.5.0/bin/gcc 的完整动作4.1 并发度选择-j 按核数还是按内存编译阶段最容易犯的错误是直接make -j$(nproc)。nproc 返回的是机器逻辑核数在内存充足的服务器上没问题但内存不足的机器上GCC 的每个编译进程都能吃几百 MB 内存并行度过高会把系统拖入 swap轻则编译时间翻倍重则 OOM 被内核杀掉。我一般在编译前先看两个指标nproc free -h然后用多少并行度取决于内存而不是核数。4 核 4GB 内存的机器-j2已经足够了16 核 32GB 内存的机器可以放心用-j8或者-j$(nproc)。如果机器还在跑生产业务并行度再降一档避免构建把在线服务的延时拉高。GCC 的构建不像小项目那样三分钟跑完bootstrap 模式下一个多小时的编译时间很正常中间被 OOM 杀掉一次损失的是前面的全部时间。为了拿到编译进度也可以直接这样写make -j4 21 | tee make.log逻辑说明tee 把 make 的标准输出同时送到屏幕和 make.log。编译过程很长终端滚掉的信息事后想回看时 make.log 就是唯一线索。出错时最有效的排查动作是先搜索 make.log 里的Error再沿错误位置往上看对应的命令通常能直接定位是缺头文件还是缺库。4.2 bootstrap 三轮构建慢但值得的安全感默认配置下make 执行的是 bootstrap 这一套流程。第一阶段用系统自带的 gcc 去编译我们的 GCC 源码生成一个 stage1 编译器第二阶段用 stage1 编译同一份源码生成 stage2 编译器第三阶段再用 stage2 重新编译源码生成 stage3 编译器最终安装的是 stage3 的产物。GCC 会通过比较 stage2 和 stage3 的编译结果判断是否存在自举不一致。这套机制的实际意义不是玄学而是保障编译器没有继承宿主编译器里潜藏的错误。曾经出现过某些版本的系统自带 gcc 存在代码生成 bug编译出来的 GCC 会带着同样的 bug 跑完整个工具链而 bootstrap 三轮构建会在这个环节暴露出差异而不是等到线上程序出现诡异崩溃才被发现。如果 configure 时没有加--disable-bootstrap就不需要额外参数直接make就会走完整流程。如果当初加了又想补上这一步骤可以单独执行make -j4 bootstrap参数说明bootstrap是 Makefile 里的一个明确目标对应完整的三阶段自举流程。对正式环境我基本不做这个补救而是在 configure 阶段就决定好到底要不要 bootstrap中途切换会让构建系统反复生成同一批目标文件耗时更长。4.3 安装与环境变量让 PATH 先找到 9.5.0make 完成之后就是安装。安装本身没有能否成功的问题关键是装完之后环境变量配不配。sudo make install export PATH/opt/gcc-9.5.0/bin:$PATH export LD_LIBRARY_PATH/opt/gcc-9.5.0/lib64:$LD_LIBRARY_PATH逻辑说明PATH决定 shell 执行gcc时优先找哪个目录把/opt/gcc-9.5.0/bin放在最前面gcc才会解析到新版本。LD_LIBRARY_PATH则是给动态链接器用的编译出的 C 程序运行时需要.so库gcc 9.5.0 的自带 libstdc.so 在lib64子目录下。如果不想每个终端都手动 export更持久的方式是把这两行写进~/.bashrc。需要注意系统全局环境如/etc/environment我不建议改因为一旦新版本 GCC 的优先级影响到其他系统级编译任务排查范围会大很多。用用户级配置问题只存在自己的会话里。另一种发行版规范做法是 update-alternativessudo update-alternatives --install /usr/bin/gcc gcc /opt/gcc-9.5.0/bin/gcc-9.5 100 sudo update-alternatives --config gcc这个是 Debian/Ubuntu 体系管理多版本命令的标准工具适合需要系统全局切换的场景。它会维护一组指向各版本 gcc 的软链通过优先级自动选版本。个人开发环境用 PATH 足够不必引入这一层。4.4 验证安装结果用一段真正链接 libstdc 的代码gcc --version只能说明命令本身是新版本不能证明头文件和动态库都对齐了。我验证一个安装是否成功永远是用一段真实代码编译并运行再接 ldd 看链接路径cat /tmp/hello.cpp EOF #include iostream int main() { std::cout gcc-9.5 works std::endl; return 0; } EOF /opt/gcc-9.5.0/bin/g /tmp/hello.cpp -o /tmp/hello /tmp/hello ldd /tmp/hello | grep stdc逻辑说明iostream的头文件和 libstdc 动态库都是新版本编译器自带的只要头文件路径或库路径有任何不对这一串命令都会在编译或链接阶段报错。ldd的输出应该指向/opt/gcc-9.5.0/lib64/libstdc.so.6如果指向/usr/lib/x86_64-linux-gnu/libstdc.so.6说明LD_LIBRARY_PATH没设置对或者系统库优先级反而更高。编译出可执行文件只是第一步运行时动态链接器用的是另一套搜索规则这一步验证的是最终程序能不能在目标环境跑起来。踩过一次运行时链接失败的坑之后我就不再只看版本号了。5. 避坑指南手装 gcc-9.5.0.tar.gz 最容易翻车的五个场景5.1 现象装了 9.5.0gcc --version 还是旧版本这是“gcc 升级后为啥还是旧版本”这个热搜词背后最典型的问题。明明/opt/gcc-9.5.0/bin/gcc-9.5能运行但直接敲gcc --version看到的还是老版本。原因大多是三选一shell 的 PATH 里/opt/gcc-9.5.0/bin没排到最前面bash 缓存了旧命令路径或者某个别名把gcc指到了旧位置。解决步骤按顺序排查type gcc # 看是否有 alias hash -r # 清掉 bash 命令路径缓存 which gcc echo $PATH逻辑说明type gcc会先显示 alias有的话直接改.bashrc里的别名定义。没有 alias就执行hash -r清除路径缓存再which gcc看实际解析到哪个目录。PATH 里/opt/gcc-9.5.0/bin必须出现在/usr/bin之前否则系统找到的还是旧命令。5.2 现象Ubuntu 上 configure 报 C compiler cannot create executables这个报错几乎每个手动编译 GCC 的人都见过Ubuntu 服务器最小化安装最容触发。configure 在早期会先探测系统是否存在能用的 C 和 C 编译器没有的话整个流程直接终止而不是中途给你提示缺哪个包。原因很简单机器上没有安装 build-essential或者只有 gcc 没有 g。GCC 的 configure 脚本需要 g 作为引导编译器之一很多精简系统默认只装了 gcc。解决sudo apt-get update sudo apt-get install -y build-essential make bzip2 xz-utils说明build-essential 会带齐 gcc、g、make、dpkg-dev 等一整套构建工具之后再回 build 目录重新跑 configure 就不会在这个环节报错了。这也是 ubuntu 安装 gcc 失败这个方向里最基础的修复动作。5.3 现象make 过程中系统卡死鼠标动不了编译 GCC 是很吃资源的操作尤其是 bootstrap 模式同一时间会有多个 cc1plus 进程同时跑每个进程的内存占用都不小。原因是并行度设得太高内存不够用之后系统进入 swap 风暴最终整个操作系统卡住后台编译进程被 OOM Killer 随机杀掉。我踩过最重的一次是在一台 2 核 2GB 的云主机上执行make -j$(nproc)编译到 stage2 时系统直接不可用只能强制重启。解决编译前先看free -h内存小于 4GB 就老老实实-j1或-j2不要存在侥幸心理。另外可以把make放进nice里运行降低对线上服务的影响nice -n 10 make -j2 21 | tee make.log参数说明nice -n 10把编译进程的 CPU 优先级调低普通用户执行时优先级会被系统限制但至少不会跟生产进程抢资源。5.4 现象编译出的程序一运行就报 GLIBCXX_3.4.x not foundGCC 9.5 编出来的 C 程序运行时动态链接器报错找不到某个版本的 GLIBCXX这个版本的库是 GCC 自带的 libstdc 里的符号版本标识。原因是编译时用了新编译器但运行时的动态链接器搜索路径里没有/opt/gcc-9.5.0/lib64程序找到的是系统自带的旧版 libstdc.so.6里面没有新版本 GCC 导出的符号。解决就是设置环境变量或者用 rpath 把搜索路径写进可执行文件export LD_LIBRARY_PATH/opt/gcc-9.5.0/lib64:$LD_LIBRARY_PATH在 CMake 项目里还可以直接用编译选项写死-Wl,-rpath,/opt/gcc-9.5.0/lib64参数说明rpath 把库路径硬编码进可执行文件这样即使部署到没有设置LD_LIBRARY_PATH的机器上也能正常运行。对于自建的独立工具链环境rpath 比全局环境变量更可控。5.5 现象麒麟系统按 Ubuntu 步骤装完gcc 找不到 crt1.o麒麟系统基于 Ubuntu 系但又不完全一致常见的老旧版本和部分国产化硬件平台上装完新 GCC 后编译任何 C 程序都会报/usr/bin/ld: cannot find crt1.o。这个文件是程序启动代码的一部分属于 libc6-dev 包的产物跟 GCC 自身无关。原因是系统里的 libc6-dev 不完整或者/usr/lib/x86_64-linux-gnu/下根本没有 crt1.o。部分麒麟系统的软件源同步不完整升级过部分包后依赖处于半新半旧状态就容易出现这种缺失。解决先确认文件位置find /usr -name crt1.o 2/dev/null ls /usr/lib/x86_64-linux-gnu/crt1.o如果没有重装基础开发包sudo apt-get install --reinstall libc6-dev注意如果机器上同时装了新版和旧版 libc6-dev需要确认最终生效的是哪一个。麒麟升级 gcc 这个场景里系统自带仓库的 gcc 版本往往偏旧手动编译新工具链前先把 libc6-dev 和 binutils 这两个底层依赖对齐能避开一大半莫名其妙的链接错误。6. 固定安装的标准动作把 9.5.0 作为环境的一部分而不是一次性产物工具链装好只是开始能不能稳定用一年取决于一开始怎么把环境“钉住”。第一个习惯是给 CMake 项目显式指定编译器路径而不是靠 PATHcmake -S . -B build \ -DCMAKE_C_COMPILER/opt/gcc-9.5.0/bin/gcc-9.5 \ -DCMAKE_CXX_COMPILER/opt/gcc-9.5.0/bin/g-9.5逻辑说明CMake 在第一次 configure 之后会缓存编译器路径即使你在当前终端改了 PATH开始构建用的还是缓存值。显式指定路径后项目构建行为和 shell 环境完全解耦团队成员各自的环境不一致也不会导致构建结果漂移。第二个习惯是保留 build 目录和 configure 日志。编译完的 build 目录不会占太多空间但它记录了成功的配置命令和 config.log。下次换机器重新搭建时直接把旧 build 目录里的命令历史翻出来照着跑就行不用再从头回忆参数。我自己吃过一次亏换了台新机器同样版本同样参数编译出来的 GCC 行为却有差异最后对比两台机器的 config.log 才发现是系统 binutils 版本不一样。第三个习惯是把源码包和依赖包归档到内网目录。gcc-9.5.0.tar.gz、gmp、mpfr、mpc 的源码包一次下载后存好后面在新环境装的时候全部走本地文件不再从公网拉取节约时间也避免不同镜像之间的源文件差异。这些年我在服务器上反反复复做的动作就是先用./configure --help看参数再跑 configure卡住了先查 config.log最后 make install 完用一段真实代码做验证。这套流程听起来不花哨但每一步都是在减少变量。希望帮到你。本文还有配套的精品资源点击获取