简介GCC 3.3.6源码压缩包gcc-3.3.6.tar.gz专为构建交叉编译工具链设计可在Linux/Unix环境下完成跨平台编译环境的搭建是嵌入式开发、系统移植及操作系统定制中常用的基础工具包。压缩包约30.03MB共2000个文件以C/C源码.c、.h、.cpp、Java源文件、汇编代码、配置脚本与文档为主解压后包含完整GCC源代码、configure配置脚本、Makefile.in模板、库、头文件及大量测试用例。源码目录中既有编译器前端、后端实现也有适用于多种目标架构的配置片段便于按需定制借助configure脚本与Makefile.in模板可完整经历配置、编译、测试、安装的交叉编译链构建流程深入理解gcc、g、cpp等组件的分工。对于需要移植GCC到新架构或定制编译器行为的场景这份源码包提供了直接的修改起点。已有221人学习/下载适合希望系统学习编译器构造、动手搭建嵌入式编译环境的开发者参考。1. 还在装 gcc-3.3.6.tar.gz老代码为什么认死这个版本拿到 gcc-3.3.6.tar.gz 这个包的人多半不是要给新项目换编译器而是手头有 2004 年前后的 C/C 代码必须在老工具链上原样编译老内核模块、跑了几年的老服务、或者构建环境要和产线对齐。gcc-3.3.6 是 3.3.x 系列的终点版本也是不少老系统默认工具链的实际刻度。发行版源里不会有现成包Ubuntu 上直接 apt 装它必然失败常见做法是把压缩包拉下来configure、bootstrap、install 三步自己走一遍。这篇文章按这个顺序写先讲怎么校验解压再讲编译参数和 Make 流程然后解决多版本共存与切换最后列五个高频坑和验证方法。照着做一个下午能装完。2. 先把 gcc-3.3.6.tar.gz 下载对校验、解压与构建目录规划2.1 从哪拿包GNU FTP、官方镜像与第三方网盘的取舍gcc-3.3.6.tar.gz 这个文件在 GNU 官方 FTP 的 gcc/gcc-3.3.6/ 目录下一直保留着官方镜像站也会同步老版本目录。在 Linux 上下载 gcc 老版本编译器时我一般不建议从第三方博客的网盘或者不知名站点拿包因为老包在网上被改过的不在少数一个被替换的编译器比没有编译器更危险它能在你不知情时改变编译结果。常见做法是选一个离自己近的官方镜像镜像目录结构和官方 FTP 一致能找到 gcc-3.3.6.tar.gz 和对应的 .sig 签名文件。wget -c https://mirror.example.com/pub/gnu/gcc/gcc-3.3.6/gcc-3.3.6.tar.gz wget https://mirror.example.com/pub/gnu/gcc/gcc-3.3.6/gcc-3.3.6.tar.gz.sig gpg --verify gcc-3.3.6.tar.gz.sig gcc-3.3.6.tar.gz上面的路径是按 GNU 官方 FTP 目录结构写的示例实际镜像站的路径可能多一层 mirror 前缀按站内结构找 gcc/gcc-3.3.6/ 目录即可。gpg --verify是最可靠的来源校验但老版本的签名公钥可能不在你的默认钥匙环里首次会遇到 trust 警告此时不要只依赖警告就放弃验签可以改用 2.2 节的哈希比对。如果镜像站不提供 .sig至少把文件大小和 md5 值跟官方页面核对不要只比对文件名因为同名改包并不难。注意确认来源后把压缩包放到一个固定目录比如 ~/src后面解压和配置都从这开始。不要在 /tmp 里放重启丢包返工很常见。2.2 下载后先校验file、md5sum、gzip -t 三连很多人在 Linux 上下载 gcc 压缩包失败不是网络断了而是文件没下全下载过程中断导致尾部截断。解压时报错再去重新下载浪费的时间比校验多得多。所以开头花 30 秒做三件事看文件类型、算校验值、测 gzip 完整性。file gcc-3.3.6.tar.gz md5sum gcc-3.3.6.tar.gz sha256sum gcc-3.3.6.tar.gz gzip -t gcc-3.3.6.tar.gzfile输出应该是 gzip compressed data如果输出显示 HTML document 或 ASCII text说明你下到的是网页错误页而不是压缩包。md5sum和sha256sum输出格式都是「哈希值 空格 文件名」把算出来的值和官方页面对应文件名的那一行比对。gzip -t只测压缩流是否完整不保证内容正确所以它和 md5 配合使用而不是互相替代。老版本下载页上的校验值大多只给 md5如果给的 sha256 就优先用 sha256两者都算一下也就多几秒钟。2.3 解压到源码目录另建构建目录老版 gcc 支持 out-of-tree 构建也就是在源码目录之外另开一个目录跑 configure 和 make。这一点很重要不要直接在解压出来的 gcc-3.3.6/ 目录里 configure虽然能跑但以后想清理重建会很痛苦而且源码目录被生成的 Makefile 和中间文件弄脏后你想换个 configure 参数得先 make distclean老版本 distclean 经常清不干净。tar xzf gcc-3.3.6.tar.gz mkdir -p /opt/build/gcc-3.3.6 cd /opt/build/gcc-3.3.6 df -h /opt/buildtar xzf会在当前目录生成 gcc-3.3.6/ 源码目录。mkdir -p创建构建目录这一步单独做是为了给 configure 准备一个空目录。df -h是确认磁盘够用老编译器编译过程中生成的中间文件在几百 MB 量级如果 /opt 空间紧张就用 /home 下的路径代替后面 configure 的参数不用改只改实际路径。构建目录和源码目录分离后configure 的写法是相对路径 ../gcc-3.3.6/configure安装目录用 --prefix 单独指定。这样源码、构建、安装三个位置互不干扰想删掉某个版本时直接删对应目录就行不用去翻 /usr 里的散落文件。3. 编译 gcc 3.3.6configure 参数、make bootstrap 与安装自检3.1 编译器引导先确认系统里有可用的 C 编译器编译老 gcc 需要一个能跑的 C 编译器来引导这是经典的鸡蛋问题。在 Linux 上搭建编译器环境时第一步永远是确认三个东西系统 C 编译器、make、binutils。常见做法是先用系统包管理器装好基础开发环境Ubuntu/Debian 上装 build-essentialCentOS/RHEL 上装 Development Tools这类包会把 gcc、make、bison、flex、binutils 一并装齐。老版 gcc 的 configure 依赖 bison 和 make缺了会在中途报错所以不要只装 gcc 就开跑。gcc --version make --version bison --version which ld这三条命令分别确认编译器、构建工具、语法分析器是否可用。第 4 条确认 binutils 的 ld 在 PATH 里如果新系统上 ld 在 /usr/bin/ld而 gcc 却找不到通常是因为 PATH 里只有一个符号链接后面 configure 也能查出来。对于只有 clang 没有 gcc 的系统理论上可以用 clang 作为 stage1 引导但我一般不建议在这上面花时间直接装好系统 gcc 再开始最稳省得 configure 阶段出现诡异链接问题。3.2 configure 的关键参数与选择理由cd /opt/build/gcc-3.3.6 ../gcc-3.3.6/configure \ --prefix/opt/gcc-3.3.6 \ --program-suffix-3.3 \ --enable-languagesc,c \ --disable-nls \ --disable-werror第一行--prefix指定最终安装目录这是编译老工具链最该重视的参数。装到 /opt/gcc-3.3.6 而不是 /usr能让 gcc-3.3.6 的二进制、头文件、库文件全部待在一个目录树里后面想删除或做快照都容易。--program-suffix-3.3让生成的可执行文件叫 gcc-3.3、g-3.3和系统自带的 gcc 区分开避免 /usr/bin/gcc 被意外覆盖。--enable-languagesc,c只启 C 和 C如果项目里有用到 Objective-C 再加 objc用不到的不要开老版本开多了会拖长 bootstrap 时间还可能引入新的依赖。--disable-nls关掉本地化消息老 gcc 的 gettext 依赖在新系统上容易出问题关闭后还能减少编译量。--disable-werror最实用老代码在新系统头文件下经常出现新的 warning如果不关configure 和 make 阶段会把 warning 当 error 直接终止。configure 执行后会在构建目录生成 Makefile并输出一堆检查结果。看到末尾出现 configured successfully 之类的字样再继续。新系统上如果 configure 中途报错先别急着换参数回到上一节检查引导编译器老 gcc 对新 glibc 的兼容问题绝大多数出现在这里。这张参数表是我每次装老版本都会对照的参数作用踩坑点--prefix指定安装根目录别选 /usr后期卸载和多版本都会很难受--program-suffix给可执行文件加后缀加版本号可以避免覆盖系统 gcc--enable-languages控制要编译的语言前端只开需要的开多会拖长时间--disable-nls关闭本地化消息老 gettext 依赖在新系统容易报错--disable-werror不把警告当错误新系统头文件下几乎必开3.3 make bootstrap老版本为什么要多花一轮时间现代 gcc 安装时经常用 make -j4 直接编但 3.3.6 这种老版本我坚持用 make bootstrap。bootstrap 是三阶段自我编译stage1 用系统 gcc 编译一份老 gccstage2 用这份刚编出来的老 gcc 再去编译老 gcc 的源码stage3 再用 stage2 的结果编译一次最后对比 stage2 和 stage3 的结果。这个过程能兜住老编译器在陌生系统头文件下的兼容性偏差比直接 make 生成的结果可靠得多。make bootstrap -j$(nproc) 21 | tee /opt/build/gcc-3.3.6/make-bootstrap.log-j$(nproc)让 make 按 CPU 核数并行老源码的并行构建在大内存机器上没问题如果编译过程中内存吃紧把 $(nproc) 改成 2。tee把输出同时写到日志和屏幕编译失败时看日志最后 50 行不要翻终端滚动条。整个过程 30 到 90 分钟取决于机器如果 bootstrap 在某个阶段报错典型原因还是 3.1 里的基础工具版本问题换个新版 binutils 往往就好了。注意老 gcc 的 make bootstrap 完成前不要急着 make install。bootstrap 结束后 Makefile 里已经有可用的编译器但 install 目标和 bootstrap 产物对应直接 install 会装一份没经过三阶段校验的版本出问题时很难定位。3.4 make install 与安装自检make install 21 | tee /opt/build/gcc-3.3.6/make-install.log /opt/gcc-3.3.6/bin/gcc-3.3 --version /opt/gcc-3.3.6/bin/g-3.3 --versionmake install 会把前面 configure 指定的目录结构建出来其中 bin 目录下有 gcc-3.3、g-3.3 以及 cpp 等工具。安装完成后立刻用绝对路径执行 --version确认两个可执行文件都在。这一步如果报错 no such file先看 make-install.log 末尾常见原因是 --prefix 路径写错导致装到了别处或者权限不足没写入 /opt。装完后不要急着 ln -sf 到 /usr/bin下一章专门解决多版本切换。4. 装完还是旧版本多版本共存、alternatives 切换与工具链连带问题4.1 为什么升级成功却还显示旧版本装完 gcc-3.3.6、运行 gcc --version 却还是旧版本这是老编译器安装里最常见的困惑热搜里天天有人问“gcc 升级后为啥还是旧版本”。原因基本就两类一是 PATH 里 /usr/bin 排在 /opt/gcc-3.3.6/bin 前面shell 找可执行文件按 PATH 顺序找到 /usr/bin/gcc 就停了二是 make 或 configure 用了绝对路径 /usr/bin/gcc或者环境变量 CC 指向旧编译器。type -a gcc echo $PATH which gcctype -a会列出所有同名的 gcc 路径能看到 /opt 里的和 /usr/bin 里的哪个在前。echo $PATH看目录顺序如果 /usr/bin 在 /opt/gcc-3.3.6/bin 之前命令行里直接敲 gcc 永远命中旧版本。which gcc只输出第一个命中的路径用来确认当前实际生效的是哪一个。解决方向不是去删 /usr/bin/gcc而是让选择变得可控。如果只在当前终端用老工具链export PATH/opt/gcc-3.3.6/bin:$PATH 就行如果要全局生效用下一节的 alternatives 才是正确做法。4.2 用 update-alternatives 管理多版本Debian/Ubuntu 以及 CentOS、openEuler、麒麟这类系统都带 update-alternatives它是为同级可执行文件提供候选切换的标准机制。用 ln -sf 手动替换 /usr/bin/gcc 的下场很惨系统里其他软件包编译时依赖特定版本你换完可能连内核头文件编译都过不去而且没有后悔药。alternatives 的作用是把 /usr/bin/gcc 做成一个软链指向 /etc/alternatives/gcc 再指向你选中的版本切换时只需要改链接不破坏系统原有安装。sudo update-alternatives --install /usr/bin/gcc gcc /opt/gcc-3.3.6/bin/gcc-3.3 100 sudo update-alternatives --install /usr/bin/g g /opt/gcc-3.3.6/bin/g-3.3 100 sudo update-alternatives --config gcc sudo update-alternatives --config g gcc --version g --versioninstall 后面的三个参数分别是管理链接路径、链接组名、候选可执行文件路径最后的 100 是优先级数字越大默认越优先。把 gcc 和 g 都注册进去避免只切了 gcc 而 g 还是旧的。--config 会列出候选版本让你交互选择选完 /usr/bin/gcc 立即生效。如果 --config 里没看到你刚注册的版本说明 install 时的路径写错了用 type -a 再确认一次。4.3 关联工具链CXX 变量、库搜索路径与 make 硬编码alternatives 只解决命令行里 gcc 指向谁解决不了所有项目。老 C 工程的 Makefile 里经常写死 CXXg如果它优先取环境变量那 export 才有效如果写死的就要改 Makefile。另一处关联是链接和运行3.3.6 编出来的程序默认去 /opt/gcc-3.3.6/lib 下找 libstdc.so而系统动态链接器不知道这个路径编译能过、运行时报找不到共享库。export CC/opt/gcc-3.3.6/bin/gcc-3.3 export CXX/opt/gcc-3.3.6/bin/g-3.3 export LD_LIBRARY_PATH/opt/gcc-3.3.6/lib64:/opt/gcc-3.3.6/lib前两行把老编译器注入到 configure 和 make 的默认编译器探查里第三行把老 libstdc 的目录加到运行期搜索路径。注意 lib64 和 lib 在不同发行版结构不一样轻量系统常见 lib64Debian 系更容易落在 lib两个路径都写上并不冲突。在 CentOS、openEuler、麒麟这类系统上升级 gcc 时很多人习惯把新版本直接装到 /usr然后发现系统工具链整个乱了。实际上用 alternatives 指向 /opt 下的版本秩序完全可控系统自带 gcc 留在原处作为兜底。这是我在生产环境里最稳的一版做法。4.4 交叉编译别指望它直接出 Windows exe经常有人问能不能用 clang/gcc 直接在 macOS 或 Linux 上编译出 Windows 下的 exe。答案是不能靠一个普通 gcc 完成。gcc-3.3.6 支持交叉编译但前提是 configure 时带 --targeti686-w64-mingw32 这类目标三元组并且系统里要有对应的 mingw binutils。如果只是把 3.3.6 装好了编译出来的还是本机平台的可执行文件。需要 Windows 产物时常见做法是直接使用 mingw-w64 项目维护好的交叉工具链而不是拿老 gcc 硬凑。5. 老编译器的 5 个高频翻车现场避坑、排查与解决路径5.1 configure 报错“C compiler cannot create executables”现象configure 执行到 checking whether the C compiler works 时直接退出日志最后一行的错误通常是 C compiler cannot create executables。新手看到这行字以为 gcc 坏了其实多半不是编译器的问题。原因configure 在测试阶段会用找到的 C 编译器生成一个临时可执行文件如果系统里 glibc 开发头文件没装全缺 libc6-dev或者 ld 不在 PATH 里临时文件就生成不了。老 gcc 在全新容器里最常踩这个坑。解决先装 build-essential 或 yum groupinstall Development Tools然后重新跑 configure。注意 make clean 或直接删掉构建目录重来否则 configure 的结果缓存还在会继续报同一个错。还要确认 configure 命令里的 CC 环境变量没有残留指向旧路径。sudo apt install build-essential rm -rf /opt/build/gcc-3.3.6 mkdir -p /opt/build/gcc-3.3.6 cd /opt/build/gcc-3.3.6 ../gcc-3.3.6/configure --prefix/opt/gcc-3.3.6 --program-suffix-3.3 \ --enable-languagesc,c --disable-nls --disable-werror5.2 新 glibc 下编译老 C 项目报“unknown type name”现象用 3.3.6 编译一份老 C 代码报错里出现 unknown type name、implicit declaration 一类的信息而且集中在系统头文件内部而不是业务代码里。原因新 glibc 对 C 语言标准做了更严格的检查老 gcc 默认的 -std 行为和新头文件冲突比如没有定义 _GNU_SOURCE 时一些声明不会暴露。这不是代码写错是编译器与系统库的代差。解决编译时加 -D_GNU_SOURCE -stdgnu89如果还有 stack protector 相关报错再加 -fno-stack-protector。老 C 代码按 C89 习惯写的很常见gnu89 能最大程度保持原语义不要用 -stdc99 硬切会引入新的类型冲突。/opt/gcc-3.3.6/bin/gcc-3.3 -D_GNU_SOURCE -stdgnu89 \ -fno-stack-protector old_app.c -o old_app编译器参数顺序其实不敏感但建议把宏定义选项写在源码文件前面读起来清楚。如果项目是 make 管理直接改 CFLAGS 里对应的变量避免每次手敲长命令。5.3 编译成功运行时报找不到 libstdc.so.6现象老 gcc 编译的 C 程序在开发机上能跑换台机器或容器里运行报 error while loading shared libraries: libstdc.so.6: cannot open shared object file。原因3.3.6 的 C 运行库装在 /opt/gcc-3.3.6/lib 下不是系统默认搜索路径。编译新程序时 rpath 没写进去运行时就只能靠 LD_LIBRARY_PATH 找到它一旦换环境这个变量丢失就翻车。解决链接时用 -Wl,-rpath 把库路径写进可执行文件这样拷贝到同架构机器上也能跑同时 export LD_LIBRARY_PATH 作为开发期兜底。注意 rpath 用绝对路径别用 $ORIGIN 相对路径老格式解释器对 $ORIGIN 支持不完整。/opt/gcc-3.3.6/bin/g-3.3 old_app.cpp -o old_app \ -Wl,-rpath,/opt/gcc-3.3.6/lib这条命令把运行库路径固化进可执行文件之后 ldd old_app 会显示 libstdc 的解析路径指向老库。5.4 Ubuntu 上 apt 安装 gcc-3.3 失败现象执行 apt install gcc-3.3 或 apt install gcc-3.3.6提示找不到软件包源里搜不到。原因3.3.6 是很老的版本现代 Debian/Ubuntu 源早已移除。网上很多教程默认包名存在实际上只有少数第三方 PPA 保留老版本直接用系统源不可能装上。解决不要在这条路上耗时间。用源码编译是唯一普遍可靠的办法下载 gcc-3.3.6.tar.gz按前面三章 configure/make bootstrap/install。想省事的话也可以用 Docker 拉一个带老工具链的发行版镜像在容器里编译产物再把二进制取出来部署。5.5 VSCode 一键编译时用的还是系统 gcc现象在 VSCode 里配置好 tasks.json 和 gcc 路径后点击运行还是调用 /usr/bin/gcc完全绕过你装好的老版本。原因VSCode 的 C/C 扩展和 tasks.json 都有各自独立的 compilerPath 配置命令行里就算 alternatives 切好了扩展仍然按默认值找 /usr/bin/gcc。解决把 c_cpp_properties.json 里的 compilerPath 显式写成 /opt/gcc-3.3.6/bin/gcc-3.3tasks.json 里的 command 同步改成绝对路径。同时把 includePath 指到 /opt/gcc-3.3.6/include 下否则智能提示会把系统头文件和新库混在一起出现跳转错误。compilerPath: /opt/gcc-3.3.6/bin/gcc-3.3, tasks: { command: /opt/gcc-3.3.6/bin/gcc-3.3 }配置里最关键的是两处路径绝对值一致不要一个写 /usr/bin/gcc 一个写 /opt 路径编译器路径和 includePath 不一致时编辑器的语法分析结果会和实际编译结果对不上。6. 装完这样验证老 ABI 跑通、库路径确认与工具链快照6.1 验证老 ABI编译、链接与符号版本装完不是用 gcc --version 看一眼就完事真正要紧的是验证老编译器产生的程序在新系统里能链接能运行顺带确认 gcc 用法没问题。写一段最老实的 C 代码用绝对路径编译然后用 ldd 和 readelf 确认它链接的是老 libstdc 而不是系统新库。/opt/gcc-3.3.6/bin/g-3.3 demo_old.cc -o demo_old ldd demo_old readelf -V demo_old | grep GLIBCXXldd 能看到动态依赖确认 libstdc.so.6 的解析路径是 /opt/gcc-3.3.6/lib 下的老库readelf -V 输出版本信息grep GLIBCXX 后看到 GLIBCXX_3.4 表示链接的是老符号版本。新系统上的高版本 libstdc 一般都能反向兼容这些老符号但如果程序里出现 GLIBCXX_3.4.7 以上的引用说明编译器并没真正用到老运行库需要回去检查 rpath。6.2 进阶把老工具链做成可迁移的快照如果这台机器不是终点还要把环境搬到别的机器我会把 /opt/gcc-3.3.6 整个目录打包和一份最小化的老构建脚本放在一起有条件的话直接做成 chroot 环境把老 gcc、老 binutils、老 glibc 依赖都装进去作为完整构建快照。这样产线升级系统时老代码还能在原样工具链里编译而不是靠玄学碰运气。tar czf gcc-3.3.6-toolchain.tar.gz /opt/gcc-3.3.6迁移到新机器解包到同样路径即可如果路径不一致需要重新 export 环境变量并修正 rpath。工具链快照比重新编译一遍省事很多也避免新机器的系统库把老编译器带崩。我最早一次装老 gcc图省事直接 ln -sf 到 /usr/bin结果系统里别的软件重编全部翻车最后老老实实把 /opt 版本配好 alternatives 才消停。从那以后凡是要跟旧工具链打交道我都坚持源码、构建、安装三目录分离版本切换走 alternatives运行验证看 ldd。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?