简介:GCC 9.5.0 官方源码压缩包,面向需要从底层构建、定制或二次开发编译器的开发者、系统管理员,以及系统学习编译原理的计算机专业学生。该资源采用 tar.gz 打包方式,压缩包整体大小约 122.2MB,根目录下仅含 gcc-9.5.0 单一主目录,文件总数暂无明确统计,属于未经第三方改动的纯净源码发行版;解压后可通过 configure、make、make install 三阶段完成从源码到可执行编译器的完整构建。目前已有 170 人学习下载。深入阅读这份源码,不仅可以理解 GCC 前端对 C/C++/Fortran 等语言语法与语义的分析过程,还能观察中端各类优化遍(pass)的执行逻辑,以及后端针对不同指令集生成汇编的实现细节;同时可对比 gcc 与 g++ 在调用编译驱动时对 C/C++ 标准、链接库和头文件路径的差异化处理。对于需要构建交叉编译器、定制本地优化选项,或希望参与开源工具链贡献的读者,这份源码包提供了权威、完整的起点,也是编译原理课程实验与生产环境二次开发的有力支撑。
1. 拿到 gcc-9.5.0.tar.gz 之后,真正的麻烦才刚开始
gcc-9.5.0.tar.gz这个文件名看着简单,但它不是一个解压就能用的二进制包,而是 GNU 编译器套件 9.5.0 版本的完整源码包。你从网上下载它,多半是遇到了发行版自带的 GCC 版本太老、内核模块编不过、或者某个项目明确要求 GCC 9.x 系列的环境。尤其在很多国产化系统(比如麒麟)和离线服务器上,包管理器里的 GCC 版本往往停留在 4.8 或 7.x,这时候手动编译一个 GCC 9.5.0 就成了绕不开的路。
但源码编译 GCC 和apt install gcc完全是两个量级的操作:你需要先配齐依赖库、选对 configure 参数、忍受半小时以上的编译时间,还要在装完后处理"明明装了新版本,gcc -v却还是旧版"这个经典玄学。这篇文章会把从拿到这个 tar.gz 到让系统真正用上 GCC 9.5.0 的每一步讲清楚,包括参数怎么设、失败了看哪里、以及那些让你翻车到怀疑人生的坑。
2. 编译 GCC 之前的环境准备:依赖、磁盘和这一步为何劝退很多人
2.1 三条硬性依赖:gmp、mpfr、mpc 和它们缺了会怎样
GCC 源码编译有三个躲不掉的依赖库:GMP(大数运算)、MPFR(浮点精度)、MPC(复数运算)。这三个库服务于 GCC 自身的优化和代码生成逻辑,缺一个,configure 阶段就会直接报错退出。常见的报错是configure: error: cannot find gmp.h或者mpfr.h不存在,很多新手在这里就卡住了,以为是自己下载的 GCC 源码包有问题,其实只是依赖没装。
一个容易忽略的细节是:这三个库只依赖头文件和静态库,也就是说你需要的是gmp-devel、mpfr-devel、mpc-devel这类开发包,而不是单纯的运行库。在 Ubuntu 和麒麟系系统上,包名分别是libgmp-dev、libmpfr-dev、libmpc-dev。如果是在 CentOS 或 RHEL 系,则对应gmp-devel、mpfr-devel、libmpc-devel,注意 CentOS 上 mpc 的包名不是mpc-devel,容易在搜索时被误导。
还有一条隐藏依赖:GCC 编译还需要gcc本身。如果你当前的系统里连一个可用的 C 编译器都没有,那就是经典的"鸡生蛋"问题。最常见做法是先用包管理器装一个旧版 GCC 作为引导编译器,再用它来编译 GCC 9.5.0。某些精简容器镜像里连make都没有,需要一并补齐。
2.2 磁盘、内存与时间预算:GCC 源码编译不是跑个脚本那么简单
很多人对源码编译的耗时和资源占用没有概念,结果在只有 2GB 内存的云服务器上直接开编,编到一半就被 OOM killer 杀掉。GCC 9.5.0 完整编译的磁盘占用大约在 5GB 到 8GB(编译产物加上临时文件),内存建议至少 4GB,否则并行编译时很容易出问题。比较稳的组合是内存 4GB、磁盘剩余 10GB 以上、单核 CPU 的情况下预留 40 到 60 分钟。
时间这东西玄学成分很大。同样是 9.5.0,只编 C 和 C++ 编译器比全部语言快很多;CPU 强不强也直接决定编译时间。我见过用 8 核机器并行编译 12 分钟跑完的,也见过单核小机器编了一个半小时还卡在最后阶段的。如果你心里没底,编译前用df -h和free -h各看一眼,心里大概有个数就行。
这些年踩过的血泪经验是:不要在磁盘剩余不足 2GB 的机器上尝试编译 GCC。编译到中途磁盘写满的表现不是立刻报错,而是某个临时文件写入失败后产生一连串莫名其妙的报错,排查起来非常吃力,最后只能删掉整个 build 目录重来。
2.3 用系统包管理器补齐依赖:Ubuntu 与麒麟系的一行命令
在 Ubuntu 和基于 Ubuntu 的麒麟系统上,准备依赖可以用一条命令完成。麒麟系统(无论是银河麒麟还是中标麒麟的桌面版)通常基于 Ubuntu 或 Debian 体系,包管理器是 apt:
# 更新索引并安装编译 GCC 所需的全部依赖 sudo apt update sudo apt install -y build-essential make gcc g++ gmp-dev libmpfr-dev libmpc-dev注意这里我写的是gmp-dev,但 Ubuntu 20.04 及之后的实际包名是libgmp-dev。麒麟系统的软件源版本不同,包名可能有差异,如果遇到Unable to locate package,先用apt search gmp搜一下实际的名字。build-essential会附带安装 make、默认版本的 gcc 和 g++,这正好满足我们前面说的"需要一个引导编译器"的要求。
如果是在 CentOS/RHEL 系的离线环境,没有外网镜像,那就得用 RPM 包手动装依赖。把gmp-devel、mpfr-devel、libmpc-devel的 RPM 文件准备好,用rpm -ivh按依赖顺序安装。这个过程偶尔会遇到某个依赖又依赖别的东西的情况,属于离线编译的常态,逐个补就是。
还有个经常被忽略的点:确认make版本不要太老。GCC 9.5.0 的构建系统要求 make 3.81 以上,绝大多数现代发行版都能满足,但有些老旧的 CentOS 6 只有 make 3.81,恰好卡在最低线上,可能出现一些诡异的构建错误。
3. 从 tar.gz 到 gcc 可执行文件:configure、make、make install 全流程
3.1 解压与目录规划:不要在源码目录里直接编译,建 build 目录的理由
拿到gcc-9.5.0.tar.gz之后,第一步当然是解压。但解压之后怎么做,不同人的习惯差异很大。官方文档推荐的、我也一直用的做法是另建一个 build 目录,在 build 目录里执行 configure 和 make,源码目录保持干净。这样做的理由很实际:GCC 支持在同一份源码上做多套配置,比如一套只编 C 编译器、另一套带 Fortran、还有一套开特定优化选项。每次重新编译时,只需要清空 build 目录,源码不会被残留的中间文件污染。如果直接在源码目录里编译,出错了想重来,要么make clean不一定清理干净,要么就得重新解压一份源码,很被动。
# 解压源码包 tar -xf gcc-9.5.0.tar.gz # 进入源码目录,创建 build 子目录 cd gcc-9.5.0 mkdir build cd build # 确认当前目录 pwd执行完pwd后,你应该处于gcc-9.5.0/build目录下。后面所有 configure 和 make 操作都在这里完成。一个小建议:解压后顺手看一眼gcc-9.5.0目录里有没有README和INSTALL文件,INSTALL文件里会写明这个版本的最低依赖版本要求,比如 GMP 要求 4.3.2 以上、MPFR 要求 2.4.2 以上。多数发行版的开发包都能满足,但如果你用的是比较老的系统,还是核对一下比较稳妥。
3.2 configure 阶段:--prefix、--enable-languages、--disable-multilib 怎么配
configure 是整个过程中最需要花心思的一步。参数配得好,后面编译顺利;配不好,要么编译失败,要么装完发现功能缺这缺那。我一般用的配置是一套经过多次验证的"通用组合":
# 在 build 目录下执行 configure ../configure \ --prefix=/usr/local/gcc-9.5.0 \ --enable-languages=c,c++ \ --disable-multilib \ --disable-bootstrap逐个解释这些参数:--prefix指定安装目录,GCC 的二进制、头文件、库文件都会装到这里。我刻意把不同版本的 GCC 装到不同目录,比如/usr/local/gcc-9.5.0,这样以后想切换或者删除都方便,不会和系统自带的 GCC 混在一起。--enable-languages指定要编译的编程语言前端,只写c,c++可以让编译时间大幅缩短,不需要 Fortran、Ada 之类的话就别开。--disable-multilib在 64 位系统上经常用到,它的作用是关闭 32 位库的编译支持。很多服务器上缺 32 位依赖库,开着 multilib 会在编译后期报错,干脆直接关掉。--disable-bootstrap是省时间的关键:GCC 默认会用新编译器把自己重新编译三遍(bootstrap)来验证编译器正确性,这个过程非常耗时,关闭后只编译一遍,省下大量时间。
要知道--prefix如果完全不指定,默认是/usr/local,这意味着 GCC 的头文件会装到/usr/local/include、库文件装到/usr/local/lib,很容易和系统自带的文件产生冲突。指定独立目录是最安全的做法,代价是在后续使用时需要显式配置 PATH。对于只想要一个能用的 GCC 9.5.0 的人来说,独立目录是正确选择。
3.3 make 阶段:并行编译的 -j 参数怎么选,失败了看哪里
configure 顺利通过后,输出结尾会出现checking for gcc...这类信息,最后提示configure: error的话就说明前面哪一步出了问题。一切正常时,下一步就是编译。命令很简单:
# -j 后面的数字按 CPU 核数设置,这里是 4 核的机器 make -j4-j后面的数字决定同时运行多少个编译任务。我一般用 CPU 核数或者核数减一。核数可以通过nproc命令获取,比如 8 核机器就执行make -j8或make -j7,留一个核给系统其他进程,避免编译时整个机器卡死。内存吃紧的机器建议把-j调小,比如 2GB 内存就用-j2,不然内存耗尽后内核的 OOM killer 会挑编译进程杀掉,你在终端里看到的报错往往是Killed而不是某个具体的编译错误,极具迷惑性。
make 过程中如果出现错误,不要急着重新跑。往上翻终端输出,找到第一个error:的位置,那才是根因。GCC 的构建系统在出错时会在终端打印出具体的文件名和错误类型,比如缺少头文件、语法错误、磁盘写入失败等等。一个常见做法是make -j4 2>&1 | tee build.log,把完整日志存成文件,出错后直接搜error:关键字,比在终端里翻快得多。编译结束后,检查一下编译器是否生成成功:
# 查看编译生成的 gcc 可执行文件 ./gcc/xgcc -vxgcc是构建过程中生成的临时编译器,如果它能打印出版本信息,说明核心编译已经成功,可以进入安装阶段。
3.4 make install 与临时 PATH 验证:装完先不急着写进系统
安装这一步权限很重要。如果你 configure 时指定的--prefix是/usr/local/gcc-9.5.0,这个目录默认归 root 所有,那么 make install 就需要 sudo:
# 安装到指定目录 sudo make install安装完成后,/usr/local/gcc-9.5.0/bin目录下应该能看到gcc、g++、gcov等可执行文件。在把这个目录写进系统 PATH 之前,先手动验证一下新 GGC 能不能正常工作:
# 直接调用新安装的 gcc /usr/local/gcc-9.5.0/bin/gcc -v这一步很重要,它能确认"编译器本身是否可用",区分于后面要处理的"系统是否使用它"。如果这里能正常输出版本信息,说明编译安装全链路已经打通。下一章要解决的就是如何让这个新编译器成为系统默认,这是很多人最容易卡住的地方。
4. 装完 GCC 9.5.0 还是旧版本?让新编译器真正生效的路径配置
4.1 为什么 which gcc 指向的还是老版本
装完 GCC 9.5.0 之后,在终端里敲gcc -v,结果打印的还是系统自带的旧版本号——这是源码编译安装 GCC 后最常见的问题,也是搜索热词里"gcc升级后为啥还是旧版本"说的现象。原因其实很简单:你在终端里敲gcc时,shell 是根据PATH环境变量从左到右寻找可执行文件的。系统自带的 GCC 在/usr/bin/gcc,而/usr/bin几乎一定排在/usr/local/gcc-9.5.0/bin前面,shell 找到/usr/bin/gcc就不再往下找了。
有一个常见的遮蔽陷阱是:如果你没有设置--prefix,安装到了/usr/local,那么新 GCC 在/usr/local/bin/gcc。有些系统上/usr/local/bin正好排在/usr/bin之前,这样gcc -v会显示新版本,看起来"升级成功";有些系统上则不是,造成同一个操作在不同机器上结果不一致。这就是我坚持用独立目录的原因——避免这种"好像装了又好像没装"的模糊状态。
要确认系统到底用的是哪个 gcc,执行which gcc看路径即可。同样,which g++看 C++ 编译器。记住,PATH的查找顺序就是一切,理解了这一点,后面排查问题就有明确方向。
4.2 用 update-alternatives 管理 gcc 版本
Linux 里管理多版本命令的标准工具是update-alternatives。对 GCC 来说,需要把gcc、g++、cc等符号链接都注册进去。如果用make install直接安装,新的 GCC 已经放在了/usr/local/gcc-9.5.0/bin,但不会有任何机制自动替换/usr/bin/gcc,除非你手动改 PATH 或者用更新替代机制。
我常用的注册方式如下:
# 注册 gcc 9.5.0 sudo update-alternatives --install /usr/bin/gcc gcc /usr/local/gcc-9.5.0/bin/gcc 100 # 注册 g++ 9.5.0 sudo update-alternatives --install /usr/bin/g++ g++ /usr/local/gcc-9.5.0/bin/g++ 100这里的/usr/bin/gcc是链接要被创建的位置,gcc是链接组名,/usr/local/gcc-9.5.0/bin/gcc是实际的可执行文件,最后的100是优先级。数字越大,在自动模式下被选中的可能性越高。如果系统里原本就有/usr/bin/gcc,需要先把它也注册进去,否则会提示目标文件已存在。注册完可以用下面的命令交互式切换版本:
# 打开交互式配置界面 sudo update-alternatives --config gcc界面里会列出所有注册过的 GCC 版本,输入编号即可切换。切完之后gcc -v应该显示新版本。注意,update-alternatives的原理是修改/usr/bin/gcc这个符号链接的指向,不影响你手工指定的 PATH 设置,两者可以同时存在,但混用时容易让人困惑。我个人的习惯是:要么只用 update-alternatives,要么只用 PATH 配置,不要两个一起上。两个方案并存时,一旦忘记自己做过什么,排查起来非常费劲。
4.3 环境变量 PATH 和 LD_LIBRARY_PATH 的配置边界
除了update-alternatives,另一条路是直接在用户环境变量里加 PATH。适用于你只希望当前用户或者当前会话使用新版本,不想动系统的全局设置。配置方法如下:
# 编辑当前用户的 bash 配置 vim ~/.bashrc # 在文件末尾追加: export PATH=/usr/local/gcc-9.5.0/bin:$PATHexport PATH=/usr/local/gcc-9.5.0/bin:$PATH的意思是把新 GCC 的 bin 目录加在原有 PATH 前面,这样 shell 找 gcc 时会先命中新地址。注意写法是把新目录放在前面,放后面的话等于没加。改完配置后执行source ~/.bashrc让它立即生效。
但这里有个很容易被忽视的连带问题:GCC 编译出的程序运行时依赖libstdc++.so.6,而这个库在/usr/local/gcc-9.5.0/lib64下,不在系统动态链接器默认搜索路径里。如果你编译的程序是给别的机器用的,或者当前机器的系统中原本有旧版本libstdc++,运行新编译的程序时可能报version GLIBCXX_3.4.21 not found。解决办法是设置LD_LIBRARY_PATH:
# 追加动态库搜索路径 export LD_LIBRARY_PATH=/usr/local/gcc-9.5.0/lib64:$LD_LIBRARY_PATH不过LD_LIBRARY_PATH属于"能不用就不用"的东西,它会影响所有从当前 shell 启动的程序。更稳妥的做法是在链接时用 rpath 把库路径写进可执行文件,但这属于进阶操作。对于大多数场景,只要记住"PATH 管的是找 gcc 命令,LD_LIBRARY_PATH 管的是找编译出来的程序运行所需的 .so",就不会在配置时搞混方向。
5. 避坑:GCC 9.5.0 源码编译的 5 个典型翻车现场与排查办法
5.1 坑:configure 报错找不到 gmp.h 或 mpfr.h
现象:执行 configure 时出现类似configure: error: cannot find gmp.h或cannot find mpfr.h,有时候还会附带提示让你用--with-gmp指定路径。
原因:系统里缺少 GMP、MPFR、MPC 的开发包,或者包已安装但 configure 没有在默认路径下找到。多数情况下是第一种:只装了运行库,没装-devel包。二进制运行库提供.so文件,但编译时需要的头文件.h在开发包里。在 Ubuntu 上就是libgmp-dev而不是libgmp10。
解决:先确认缺失的是哪个库,然后安装对应的开发包。Ubuntu 系用sudo apt install libgmp-dev libmpfr-dev libmpc-dev,装完后重新执行 configure。如果你是在内网离线环境,无法用 apt 安装,那就需要手动把这些库的源码也编译一遍,然后通过--with-gmp=/usr/local/gmp --with-mpfr=/usr/local/mpfr --with-mpc=/usr/local/mpc告诉 GCC configure 它们的安装位置。
5.2 坑:make 时出现 cannot compute suffix of object files
现象:configure 通过了,但进入 make 阶段不久,输出中出现checking for suffix of object files... configure: error: cannot compute suffix of object files: cannot compile之类的信息。注意,这条报错有时候出现在 GCC 自身的 configure 子过程中。
原因:这通常意味着"引导编译器无法正常工作"。GCC 在构建过程中会调用系统现有的 C 编译器来编译一些初始工具,如果引导编译器有问题——比如gcc命令指向了一个不存在的路径、系统的 GCC 和 G++ 版本不匹配、或者缺少libc开发包——就会导致无法编译简单的测试程序,从而得到这个报错。
解决:先检查引导编译器是否正常:echo 'int main(){return 0;}' | gcc -x c - -o /dev/null,能编译通过说明基础工具链没问题。再查libc6-dev是否安装,Ubuntu 系统可以直接sudo apt install libc6-dev。还有一种情况是/tmp目录空间不足或权限不对,GCC 的 configure 会在/tmp下创建临时目录做编译测试,如果「找不到临时目录」也会间接表现为这个错误。
5.3 坑:内存不足导致 cc1plus 进程被杀死
现象:make 进行到 C++ 相关文件时,终端突然出现类似cc1plus: fatal error: Killed或者直接提示signal terminated,进程退出。
原因:GCC 编译 C++ 代码时非常耗内存,cc1plus是 C++ 的前端编译器进程,编译某个大文件时可能吃掉 1GB 以上内存。如果机器总内存只有 2GB,开了-j4就会导致内存耗尽,内核 OOM killer 强行杀掉进程。
解决:最直接的办法是降低并行度,改用make -j1或make -j2。观察一下系统内存:free -h,如果总内存确实很小,还可以通过--disable-bootstrap已经省掉了一轮编译,再配合-j1让 GCC 以最低内存消耗完成构建。如果内存实在紧张到连-j1都撑不住,那就考虑加 swap:sudo fallocate -l 4G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile。编译完记得关掉 swap 不是必须的,留着也不影响使用,但如果你用的是云服务器,磁盘 IO 会拖慢编译速度。
5.4 坑:build 目录与源码目录混用导致重复编译
现象:第一次 make 出错后,你在源码目录里重新执行../configure,然后又直接 make,结果编译速度反而越来越慢,或者出现一些"明明改好了还是报同样的错"的诡异情况。
原因:GCC 的构建系统对"是否在源码目录里跑过 configure"非常敏感。一旦在源码目录里执行过 configure,源码目录里就会残留 config 相关文件,之后再在 build 目录执行 configure 时,GCC 的构建系统会检测到目录状态不干净,可能拒绝运行或产生不确定行为。另外,在源码目录里直接编译会让源码文件与目标文件混在一起,想要彻底清理就得手动删除一堆文件,容易漏。
解决:规范做法是源码目录只保留.tar.gz解压后的原始内容和必要的补丁,编译全部在 build 目录完成。如果已经混了,最省事的方式是删掉整个解压目录,重新解压一份,再建新 build 目录。rm -rf gcc-9.5.0之后重新tar -xf,速度比手动清理快得多,也干净得多。
5.5 坑:安装后编译器版本没变,PATH 写错位置
现象:按教程配置好 PATH 后,gcc -v仍然显示旧版本,或者终端里新开一个 shell 就失效了。
原因:最常见的两类:一是把export PATH=/usr/local/gcc-9.5.0/bin:$PATH写成了$PATH:/usr/local/gcc-9.5.0/bin,新目录放在了后面,优先级反而最低;二是编辑的是~/.bashrc,但当前环境是 Bash 以外的 shell(比如 zsh),或者修改后没有执行source,只是重开了一个终端——重开终端其实会重新加载.bashrc,但某些桌面环境启动的 shell 是登录 shell,读的是~/.profile而不是~/.bashrc。
解决:先执行echo $PATH看/usr/local/gcc-9.5.0/bin是否在输出里、以及排在什么位置。如果在但which gcc还是旧的,说明顺序不对,去检查 export 语句。如果压根没有,说明配置没生效或写错文件了。检查你用的是哪个 shell:echo $SHELL。如果是 Bash,确认~/.bashrc内容;如果是其他 shell,就写到对应的 rc 文件。改完后source ~/.bashrc再验证。
6. 收尾:版本共存、切换技巧与验证新安装 GCC 是否可用
6.1 一个快速验证 GCC 9.5.0 是否完好的测试
安装完成并完成 PATH 配置后,我习惯用一段简单的 C++ 程序做完整验证,而不是只看gcc -v就完事。验证要覆盖编译、链接、运行三个阶段,因为有些问题只有到链接或运行时才会暴露。
#include <iostream> #include <vector> #include <string> int main() { std::vector<std::string> msg = {"gcc", "9.5.0", "works", "fine"}; for (const auto& word : msg) { std::cout << word << " "; } std::cout << std::endl; return 0; }# 编译并运行测试程序 g++ test.cpp -o test ./test能正常输出gcc 9.5.0 works fine,说明编译器、标准库头文件、动态链接、运行环境四层都通过了。如果编译能过但运行时报GLIBCXX_3.4.21 not found,那就是前面说的LD_LIBRARY_PATH没配好,回到 4.3 节处理。这一步跑通了,后续用 GCC 9.5.0 编其他项目才是可信的。
6.2 需要切换回去时,别慌,有后悔药
用update-alternatives --config gcc切回旧版本后,你可能会发现某些依赖新版本编译出来的库的项目运行不了了,这是正常的——编译器版本和 ABI 不是完全兼容的。我的习惯是给不同项目单独写构建脚本,文件头部显式写明用哪个 gcc。与其在系统层面反复折腾,不如让每个项目自己决定用哪个编译器。
6.3 一点真实的经验:版本号不是万能的
GCC 9.5.0 是 GCC 9 系列的最后一个版本,bug 修复最全,稳定性好。选择它往往是出于项目兼容性的硬要求,而不是因为它是最新版本。我遇到过不少次,项目方非要 GCC 9.5.0,但实际代码用系统默认编译器也能编过——这时候要不要装就值得权衡。编译一套 GCC 的时间成本很高,如果只是为了满足某个gcc --version的检查脚本,其实可以用前面提到的 PATH 技巧快速"伪装"一个。但如果真的要编 C++17 代码、链接新版 ABI 的库,实打实装一个才安心。
希望这些踩过的坑和验证方法能帮你在编译 GCC 9.5.0 时少走一些弯路,后面编其他 GNU 工具链(比如 binutils、gdb)时,configure、make、make install 这套流程是完全通用的,一次会了就都会了。
本文还有配套的精品资源,点击获取