ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

gcc-3.3.6 源码构建与旧代码复现指南

gcc-3.3.6 源码构建与旧代码复现指南

简介:gcc-3.3.6.tar.gz 是 GNU Compiler Collection 3.3.6 的完整源码归档,面向需要构建交叉编译工具链的嵌入式开发者、操作系统与编译原理学习者。包体标注文件总数2000,类型明细中C源码七千余个、Java两千余个、头文件一千二百余个,另有Ada、C++、Fortran源码以及configure配置脚本、Makefile模板、文档和测试用例,压缩包约30.03MB。源码树覆盖前端解析、表达式与声明处理、i386与rs6000等目标后端、优化合并及DWARF2调试信息输出等模块,并附带预处理器、汇编器、链接器等工具相关代码,便于读者研究GCC 3.3.6的内部组织与跨平台编译流程。已有221人学习下载,适合作为旧版工具链复现、嵌入式交叉编译环境搭建和编译器源码阅读的参考材料。

1. 为什么今天还要下载 gcc-3.3.6.tar.gz:三个真实触发场景

gcc-3.3.6.tar.gz 是 3.3 分支的收尾版本,源码里 C、C++、Fortran、Java 前端齐全,构建脚本还停留在 2005 年前后的习惯上。有人重新翻出它,是因为手上的旧代码只认它:某实验室维护的数值计算库大量使用 K&R 风格函数定义和隐式 int,现代编译器直接拒绝;某公司产线板子的交叉工具链与 3.3.6 的 libstdc++ ABI 绑死,换掉任何一端都会破坏二进制兼容;还有一批操作系统实验代码,指导书明确要求用 3.3.x 构建。这份资源解决的是「让旧代码在可控环境里重新编出来」,适合需要复现历史构建的嵌入式工程师、做编译原理课程设计的学生,以及给老项目做归档构建的人。只写现代 C 的话,系统自带编译器更省事。

2. 构建前的环境盘点:build、host、target 三个变量怎么定

老 GCC 的构建系统把「在哪编译」「在哪运行」「生成谁用的代码」拆成三个三元组,分别是 build、host、target。本机构建时三者相同,configure 会自动探测;一旦你要做交叉工具链,host 和 target 就必须手动指定,否则编出来的 gcc 只能在宿主机上跑。3.3.6 的自动探测脚本年代久远,面对新机器经常给出错误结论,所以这一步不能全信它,得自己核对。

2.1 解包后先认清目录结构

拿到 tar 包不要急着 configure,先看一遍里面有什么。3.3.6 的顶层目录结构清晰,但和 4.x 之后的布局差异不小,比如没有顶层的libgcc/独立目录,libgcc 的代码还在gcc/里面;boehm-gc、zlib、libffi这些运行时库以兄弟目录的形式躺在源码树里,由顶层 Makefile 统一调度。

# 解包到独立源码目录,避免和构建目录混在一起 mkdir -p /opt/src tar -xzf gcc-3.3.6.tar.gz -C /opt/src # 只看前两层,快速建立目录印象 tar -tzf gcc-3.3.6.tar.gz | awk -F/ 'NF<=2' | sort -u | head -n 40 # 确认解出来的体积,预留磁盘空间 du -sh /opt/src/gcc-3.3.6

上面第三条命令里的awk -F/ 'NF<=2'是按斜杠切分后只保留两层以内的路径,用来过滤掉海量源文件,只留目录骨架。du -sh看的是解包后的实际占用,3.3.6 源码加构建产物通常要预留数 GB,具体取决于你开了哪些语言前端;只编 C 的话压力小很多。解包时如果 tar 报出时间戳异常,先别忽略,这类问题会在make阶段变成莫名其妙的循环重建,第 6 章会专门处理。

目录里最值得先看的是gcc/和libstdc++-v3/。gcc/下是驱动、前端、后端和 libgcc 的全部逻辑,config/子目录里是按目标平台分的移植文件;libstdc++-v3/是 C++ 标准库实现,它和系统 glibc 头文件的兼容性是最容易翻车的地方。libiberty/提供一组通用工具函数,编译过程中会先被编出来给其他组件用。

2.2 引导编译器:为什么我一般选 gcc 4.8 而不是系统最新的 gcc

3.3.6 的 C 代码按 C89 写,大量函数没有原型声明,有些地方还依赖隐式类型转换。现代 gcc 默认按较新的标准做诊断,遇到这类写法会直接报 error,而不是 warning。官方文档给的底线是 2.95 以上的 gcc 就能编,但实践里最省事的宿主编译器区间是 gcc 4.x 早期到中期,比如 4.8 或 4.9——它们默认标准还没那么严,同时又能在新内核上正常跑。

宿主编译器编译 3.3.6 的典型表现建议
gcc 2.95 / 3.x最贴近原始环境,几乎不用改难获取,适合容器镜像里找
gcc 4.8 / 4.9少量 warning,libstdc++ 需注意 math.h首选,兼容性和可获得性平衡
gcc 6 ~ 9隐式声明报 error,需加兼容 flag可救,但要补-std=gnu89
gcc 10 及以上默认 PIE、严格诊断,失败面大不建议,优先换老编译器

先确认手上有什么:

# 看当前系统编译器版本 gcc --version | head -n 1 # 看系统里还装了哪些版本(路径因发行版而异) ls /usr/bin/gcc-* 2>/dev/null

gcc --version只输出第一行版本信息,避免刷屏。第二条列出系统里所有带版本后缀的 gcc 可执行文件,如果能看到gcc-4.8之类,就省去自己搭环境。找不到也别硬上最新版,常见做法是用容器跑一个带老编译器的镜像,把源码挂进去编,构建完再把产物拷出来。

2.3 目录规划与 out-of-tree 构建

3.3.6 支持 VPATH 构建,也就是源码目录和构建目录分离。这件事不是洁癖,而是为了失败后能一键重来:rm -rf构建目录即可,不用重新解包。源码目录保持只读状态还有个好处,多个目标平台的构建可以共用同一份源码。

export SRC=/opt/src/gcc-3.3.6 export BUILD=/opt/build/gcc-3.3.6 export PREFIX=/opt/gcc-3.3.6 mkdir -p "$BUILD" "$PREFIX" cd "$BUILD" # 先看 configure 支持哪些选项,不同小版本的选项名有出入 "$SRC/configure" --help | grep -E 'enable-languages|program-suffix|multilib|nls'

SRC指向解包后的源码,BUILD是本次构建的工作目录,PREFIX是最终安装位置。把安装目录和系统/usr分开,是为了不覆盖系统编译器——3.3.6 装进/usr会给你带来一堆链接器找不到libstdc++.so.6的麻烦。最后那条grep是过滤帮助信息,只留关心的几项,确认当前这棵源码树到底支持哪些开关,别照抄网上别的版本的参数表。

--prefix选一个带版本号的路径,比如/opt/gcc-3.3.6,后面用--program-suffix=-3.3.6让可执行文件也带后缀,这样系统里同时存在gcc和gcc-3.3.6两套编译器,切换靠 PATH 或者绝对路径,互不干扰。

3. configure 与 make bootstrap:一份能直接抄的参数表和构建记录

参数配错是 3.3.6 构建失败的主要来源之一,但真正致命的选项其实就那几个,剩下的都是锦上添花。核心思路是:只开你需要的语言前端、关掉多库支持、关掉国际化、安装路径带版本号。开得越多,踩到旧代码与现代头文件冲突的概率越大。

3.1 关键参数逐项说明

参数作用3.3.6 上的注意事项
--prefix=/opt/gcc-3.3.6安装根目录不要用/usr,避免污染系统工具链
--enable-languages=c,c++只编需要的语言前端只编 C 可写成c,能避开 libstdc++ 的头文件冲突
--disable-multilib不生成 32/64 位双份运行时不加这条,缺 32 位 glibc 时会在链接阶段失败
--disable-nls关闭国际化消息减少依赖,构建更快,报错仍是英文
--enable-threads=posix指定线程模型与系统 glibc 保持一致,避免 libgcc 线程支持缺失
--program-suffix=-3.3.6给所有产物加后缀方便与系统编译器共存,注意它也会作用于cpp等工具
--with-gnu-as/--with-gnu-ld明确使用 GNU 汇编器与链接器现代发行版默认就是 GNU 工具链,写上更稳

几个参数值得展开。--enable-languages的取值在 3.3.6 上支持c、c++、f77、java、objc,早期版本还有objc++,具体以configure --help的输出为准。很多人一上来就写c,c++,f77,java,结果卡在 libstdc++ 或 libjava 编译上,排查半天发现 C 前端早就编好了——先只开c跑通全流程,再回头加语言,是更稳的推进方式。

--disable-multilib在老机器上几乎是必选项。3.3.6 默认会根据目标平台探测是否支持多库,如果探测到宿主有 64 位能力,就会尝试同时生成 32 位和 64 位两套运行时;而现代发行版往往没装 32 位的开发包,于是链接时找不到crt1.o,报出cannot compute suffix of object files这类看起来和编译器本身无关的错误。

--program-suffix是个小技巧:安装后你会得到gcc-3.3.6、g++-3.3.6、cpp-3.3.6,而系统原有的gcc、g++纹丝不动。代价是构建系统内部有些地方会引用不带后缀的名字,需要靠 PATH 兜底,第 5 章会讲怎么配。

3.2 完整执行记录与日志检查点

把参数拼成一条命令,重定向到日志文件。不要图省事让输出直接刷屏,configure 的几千行输出里真正有用的就几十行,落到文件里才好搜。

cd /opt/build/gcc-3.3.6 # 显式关掉 PIE,并把标准降到 gnu89,缓解宿主新编译器带来的诊断压力 CFLAGS="-O2 -fno-pie -std=gnu89" \ CXXFLAGS="-O2 -fno-pie -std=gnu++98" \ LDFLAGS="-no-pie" \ /opt/src/gcc-3.3.6/configure \ --prefix=/opt/gcc-3.3.6 \ --enable-languages=c,c++ \ --disable-multilib \ --disable-nls \ --enable-threads=posix \ --program-suffix=-3.3.6 \ --with-gnu-as \ --with-gnu-ld \ > configure.log 2>&1 echo "exit=$?" tail -n 20 configure.log

CFLAGS和CXXFLAGS里的-fno-pie与LDFLAGS里的-no-pie是给宿主编译器用的,不是给 3.3.6 用的。现代发行版默认生成位置无关可执行文件,而 3.3.6 的构建脚本不认识这套约定,链接阶段容易报重定位错误。-std=gnu89让宿主编译器用老标准去编译这份老代码,能挡掉一批「隐式函数声明」级别的错误;C++ 侧的gnu++98同理。

configure 跑完之后,重点看三处。第一处是三元组:

grep -E 'checking (build|host|target) system type' configure.log

三个值正常情况下应该一致;如果在 ARM 机器上看到 build 是x86_64之类,说明config.guess猜错了,需要走第 6 章的替换流程。第二处是编译器可用性:

grep -E 'whether the C compiler.*works|checking for gcc' configure.log

必须看到yes。如果是no,别往下走,直接看config.log里对应段落,通常是缺头文件、缺库或者编译器本身崩溃。第三处是结尾:

grep -E 'creating Makefile|config.status' configure.log | tail -n 5

出现creating Makefile说明配置阶段走完了。日志里大量checking for ... no不必紧张,老脚本探测的很多特性现代系统本来就没有,只要不影响核心功能就让它去。

3.3 make bootstrap 的三个阶段在干什么

3.3.6 的推荐构建目标是bootstrap,它要编三遍。第一遍用宿主编译器编出xgcc和cc1,这叫 stage1;第二遍用 stage1 刚编出来的编译器再编同一份源码,得到 stage2;第三遍用 stage2 再编一次,得到 stage3。最后构建系统会逐字节比较 stage2 和 stage3 的目标文件,一致才算自举成功,不一致就报错退出。

cd /opt/build/gcc-3.3.6 # bootstrap-lean 在每个 stage 结束后清理中间文件,节省磁盘 # -j 的数值按物理核心数来,内存小的机器降到 2 甚至 1 make bootstrap-lean -j4 > build.log 2>&1 echo "exit=$?" tail -n 30 build.log

bootstrap-lean和bootstrap的区别只在磁盘占用:前者每阶段后删掉.o文件,后者保留以便调试。老机器磁盘紧张时选 lean 很实用。-j4是并行度,3.3.6 的并行构建在单个 stage 内部是安全的,但并行度太高会让内存吃紧,尤其是在编译insn-attrtab.c这种超大文件时,常见做法是把并行度压到物理核心数的一半。

构建日志要盯的关键行有这几类:Configuring in表示进入某个子目录,gcc -c开头的命令行是实际编译动作,Error和internal compiler error是失败信号。如果卡在 stage1 就挂了,问题多半出在宿主编译器和源码的兼容性上;如果 stage1 过、stage2 挂,通常意味着刚编出来的编译器有代码生成缺陷,和宿主环境关系不大,得回头看 CFLAGS 是不是开了过于激进的优化。

4. 常见问题排查:六条编译 3.3.6 时的翻车记录

下面六条是我在不同机器上实际撞过的,按出现频率排序。每条都按「现象、原因、解决」写,命令可以直接抄。

4.1 现代 gcc 一上来就报一堆隐式声明错误

现象:configure 阶段报error: implicit declaration of function 'xxx',或者error: 'NULL' undeclared,位置在libiberty/或gcc/的老源文件里。

原因:宿主 gcc 按较新标准编译,把 C89 里合法的隐式声明当成错误处理,加上部分源文件依赖的旧头文件在新 glibc 里已经改了声明。

解决:先给宿主编译器降标准,再考虑换编译器。

# 方案一:降标准重跑 configure CFLAGS="-O2 -fno-pie -std=gnu89 -Wno-implicit-function-declaration" \ /opt/src/gcc-3.3.6/configure ... > configure.log 2>&1 # 方案二:换老编译器,指定 CC 和 CXX CC=/usr/bin/gcc-4.8 CXX=/usr/bin/g++-4.8 \ /opt/src/gcc-3.3.6/configure ... > configure.log 2>&1

-Wno-implicit-function-declaration只是把诊断降级,不改变语义;真正稳妥的是方案二。如果两个方案都不行,说明这棵源码树需要打补丁,去搜对应发行版的旧包补丁集是常见做法。

4.2 配置阶段报 cannot compute suffix of object files

现象:checking for suffix of object files... configure: error: cannot compute suffix of object files,日志里看不到具体原因。

原因:configure 会用宿主编译器编一个极小的测试程序再链接,这一步失败就会报这个笼统错误。真实原因藏在config.log里,常见的是缺 32 位运行时库、缺crt1.o,或者宿主编译器因为 PIE 设置链接失败。

解决:先翻日志定位。

# 在 config.log 里找失败的那次编译 grep -n -A 20 'cannot compute suffix' config.log # 或者是直接搜 error grep -n 'error' config.log | head -n 20

如果日志里出现relocation R_X86_64_32 ... can not be used when making a PIE object,就是 PIE 问题,按 3.2 节加-fno-pie -no-pie;如果出现cannot find crt1.o,说明缺 32 位开发包,加上--disable-multilib重新配置即可。

4.3 config.guess 不认识你的机器

现象:checking build system type...后面跟一个奇怪的或者空的值,随后 configure 报error: cannot guess build type。

原因:3.3.6 自带的config.guess是 2003 年前后的版本,不认识后来出现的架构命名,在新型号 CPU 或新版内核上会失效。

解决:用系统里较新的config.guess和config.sub替换,或者手动指定三元组。

# 先找系统里最新的那份(路径因发行版而异) ls /usr/share/automake-*/config.guess 2>/dev/null ls /usr/share/misc/config.guess 2>/dev/null # 替换源码树里所有副本 NEW_GUESS=$(ls /usr/share/automake-*/config.guess 2>/dev/null | tail -n 1) find /opt/src/gcc-3.3.6 -name config.guess -exec cp "$NEW_GUESS" {} \; find /opt/src/gcc-3.3.6 -name config.sub -exec cp "${NEW_GUESS%guess}sub" {} \;

替换完记得把构建目录清空重来,configure 会缓存探测结果,不清空可能继续用旧结论。实在找不到新脚本,就在 configure 命令行上显式给--build=x86_64-pc-linux-gnu这类值,绕过探测。

4.4 make 在 stage1 阶段报 internal compiler error 或段错误

现象:编译某个大文件时宿主 gcc 报internal compiler error,或者cc1: Segmentation fault。

原因:宿主 gcc 在编译这份老代码时触发了自身的优化缺陷,常见于开启了较高优化级别、或者内存不足导致编译器进程被杀。

解决:降低优化级别,减少并行度,必要时关掉宿主的部分特性。

# 用 -O1 代替 -O2,并行度降到 2 make clean make bootstrap-lean -j2 CFLAGS="-O1 -fno-pie -std=gnu89" > build.log 2>&1 # 查看是不是内存问题 free -h dmesg | tail -n 20

free -h看可用内存,dmesg里如果有Out of memory就是被系统杀掉了,这时候降并行度比调优化更有效。

4.5 C++ 前端编译到 libstdc++ 时和系统 math.h 冲突

现象:libstdc++-v3目录下报error: '::isnan' has not been declared、ambiguating new declaration of 'double nan(...)'一类错误。

原因:3.3.6 的 libstdc++ 假设系统math.h里的数学函数是普通函数声明,而现代 glibc 把它们改成了宏或者带__THROW属性的内联形式,两边对不上。

解决:如果只需要 C 编译器,直接不开 C++ 前端,这是最省事的。

# 只编 C 前端,彻底绕开 libstdc++ /opt/src/gcc-3.3.6/configure \ --prefix=/opt/gcc-3.3.6 \ --enable-languages=c \ --disable-multilib \ --disable-nls \ --program-suffix=-3.3.6 \ > configure.log 2>&1

如果 C++ 是硬需求,就得给libstdc++-v3打补丁,把冲突的函数名改成显式限定调用,或者用与编译器年代匹配的老 glibc 头文件做构建环境。这类补丁在不同发行版上不一样,没有通用解。

4.6 make 反复重建同一批文件,或者 makeinfo 报错

现象:make看起来在跑,但总在同一批文件上打转,进度条不动;或者报makeinfo: command not found、error: unknown command。

原因:一是源码文件的时间戳在未来,make的依赖判断失效,认为目标总是比依赖旧;二是系统里的 texinfo 版本太新,不认 3.3.6 文档里的老语法。

解决:统一时间戳,跳过文档构建。

# 把所有源码文件的时间戳压到一个过去的时间点 find /opt/src/gcc-3.3.6 -type f -exec touch -d "2005-01-01 00:00:00" {} + # 构建时跳过 info 文档,用 true 冒充 makeinfo make bootstrap-lean MAKEINFO=true -j4 > build.log 2>&1

-exec ... {} +的写法比-exec ... \;效率高,因为它会批量调用touch。MAKEINFO=true是把变量覆盖成真值命令,构建系统调用它时不报错也不生成文档,日志里会看到一堆被跳过的提示,属于正常现象。

5. 装完先别急着用:版本、宏、ABI 三层验证

make install跑完不等于能用。老编译器装到新系统上,最常见的失败不是它自己坏了,而是它默认去搜系统的新头文件和新库,编出来的东西行为不符合预期。验证要分三层做:先确认装的是不是你以为的版本,再确认真实编译能过,最后看产物的 ABI 标记对不对。

5.1 版本号与内建宏

# 版本与配置信息 /opt/gcc-3.3.6/bin/gcc-3.3.6 -v /opt/gcc-3.3.6/bin/gcc-3.3.6 -dumpversion # 看内建宏,确认 __GNUC__ 和 __GNUC_MINOR__ echo | /opt/gcc-3.3.6/bin/gcc-3.3.6 -E -dM - | grep -E '^#define __GNUC'

-v会打印完整配置串,包括 configure 时的参数、目标三元组、线程模型,这一行信息比版本号本身有用得多,出问题时先把它贴出来能省很多沟通。-dumpversion只输出3.3.6,适合脚本判断。最后一条把空输入喂给预处理器,再导出所有内建宏,正常应该看到__GNUC__ 3和__GNUC_MINOR__ 3;如果看到别的值,说明你调用的是系统编译器,不是刚装的那个,检查路径。

# 确认搜索路径都指向新 prefix /opt/gcc-3.3.6/bin/gcc-3.3.6 -print-search-dirs

这条命令的输出能看出编译器去哪找cc1、去哪找库。如果programs那一行里还混着/usr/lib/gcc,说明安装不完整或者 PATH 里有干扰,编译时会用错后端。

5.2 用 K&R 风格代码做一次真实编译

写一段现代编译器会警告、老编译器能接受的代码,这是最直接的功能验证。

/* legacy_demo.c:K&R 风格函数定义,用来验证 3.3.6 的 C 前端 */ #include <stdio.h> int add(a, b) int a; int b; { return a + b; } int main() { printf("%d\n", add(2, 3)); return 0; }
# 用 3.3.6 编,不指定标准也能过 /opt/gcc-3.3.6/bin/gcc-3.3.6 -Wall -o legacy_demo legacy_demo.c ./legacy_demo # 用系统新编译器对照,观察差异 gcc -Wall -o legacy_demo_new legacy_demo.c 2>&1 | head -n 10

-Wall会打开常用警告但不改变语义。用 3.3.6 编的时候最多出现未使用变量之类的提示;换成新编译器,K&R 风格定义会触发old-style function definition警告,某些更严的标准下直接报错。这个对照组的意义在于确认:同一份代码在两套工具链下的行为差异是预期内的,不是你的构建出了问题。

5.3 产物检查与和系统 gcc 共存

# 看目标文件里的编译器标记 readelf -p .comment legacy_demo # 看动态依赖,确认没有链到系统的 libstdc++ ldd legacy_demo

readelf -p .comment会打印出生成该目标文件的编译器版本字符串,这是判断产物来源最硬的证据,比看文件时间靠谱。ldd用来确认运行时依赖:纯 C 程序一般只依赖libc.so.6,如果发现链到了libstdc++.so.6,说明编译时混入了 C++ 的东西,得查命令行。

共存配置有两种常见做法。一是纯 PATH 方案,在需要旧编译器的 shell 里临时切换:

# 只在当前 shell 生效,不影响系统 export PATH=/opt/gcc-3.3.6/bin:$PATH export LD_LIBRARY_PATH=/opt/gcc-3.3.6/lib:$LD_LIBRARY_PATH which gcc-3.3.6

二是用发行版的 alternatives 机制登记,适合需要频繁切换的场景。注意--program-suffix装出来的名字带后缀,alternatives 里登记时要写全名,否则系统gcc会被顶掉。

6. 进阶用法:三处补丁让 3.3.6 在现代机器上少折腾

真正让 3.3.6 在现代机器上难编的,往往不是编译器代码本身,而是三件外围的事:文件时间戳、平台探测脚本、以及不必要的语言前端。把这三处提前处理掉,后面的构建会顺很多。

6.1 时间戳与 config.guess 的一次性处理

时间戳问题的根源是 tar 包在打包、传输过程中可能带上未来的修改时间,make的依赖比较会因此失效,表现是无限重建。统一压到过去某个时间点是一劳永逸的做法。config.guess的问题前面提过,这里给一个完整的预处理脚本,放在解包之后、configure 之前跑:

#!/bin/bash # prep.sh:构建前的一次性预处理 set -e SRC=/opt/src/gcc-3.3.6 # 1) 统一时间戳,避免 make 循环重建 find "$SRC" -type f -exec touch -d "2005-01-01 00:00:00" {} + # 2) 替换平台探测脚本,取系统里最新的一份 NEW_GUESS=$(ls /usr/share/automake-*/config.guess 2>/dev/null | tail -n 1) if [ -n "$NEW_GUESS" ]; then find "$SRC" -name config.guess -exec cp "$NEW_GUESS" {} \; find "$SRC" -name config.sub -exec cp "${NEW_GUESS%guess}sub" {} \; echo "config.guess updated from $NEW_GUESS" else echo "no newer config.guess found, rely on --build/--host" fi # 3) 清掉可能残留的缓存 rm -rf /opt/build/gcc-3.3.6/*

set -e让脚本任何一步失败就退出,避免半途而废还继续往下跑。${NEW_GUESS%guess}sub是 shell 的参数展开,把路径结尾的guess去掉再拼sub,得到同目录下的config.sub。第三段清空构建目录是必须的,configure 的探测结果会缓存,不清理可能沿用旧的错误结论。

6.2 关掉不需要的前端换构建速度

语言前端的取舍对构建时间影响很大。只编 C 和同时编 C、C++、Fortran、Java,工作量差好几倍,而 C++ 前端恰恰是最容易和现代 glibc 冲突的部分。按用途选组合:

用途建议语言组合说明
只编旧 C 代码、内核模块c最快,绕开 libstdc++ 冲突
需要旧 C++ ABI 的二进制c,c++需处理 math.h 冲突,必要时打补丁
复现完整历史工具链c,c++,f77Fortran 老代码能编,失败面更大
教学演示c够用,构建日志干净,便于讲解

配置命令相应简化:

# 最小组合,适合绝大多数「只编旧 C 代码」的场景 /opt/src/gcc-3.3.6/configure \ --prefix=/opt/gcc-3.3.6-c \ --enable-languages=c \ --disable-multilib \ --disable-nls \ --enable-threads=posix \ --program-suffix=-3.3.6 \ > configure.log 2>&1

安装目录特意换成-c后缀,是为了和完整版共存,两套产物互不覆盖。这种精简构建的另一个好处是 stage2 和 stage3 的比较更快,自举验证能在更短时间内完成,失败时也更容易定位到具体是哪个前端出的问题。

最后提醒一句关于环境隔离的习惯:3.3.6 编出来的程序跑在它自己的运行时上,别把它装进系统路径,也别在全局LD_LIBRARY_PATH里加它的 lib 目录,否则系统里其他程序可能链到老版本的libstdc++,出现一些莫名其妙的崩溃。从那以后我每次重建 3.3.6,都先把时间戳和 config.guess 处理掉,再谈编译参数,顺序反了就是白折腾。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表