
简介这是一份面向 Ubuntu 18.04 用户的 GCC 离线安装资源解决内网或无外网环境下无法通过 apt 在线安装编译器工具链的难题。资源包内含 25 个文件由 24 个 deb 依赖包和 1 个一键安装脚本 do.sh 组成覆盖 libubsan0、binutils、libasan4、gcc-7、libgcc-7-dev 等关键依赖形成从底层运行库到编译器的完整链条。压缩包整体约 20.05MB体积精简便于拷贝至目标机器快速部署。目前已有 3203 人学习下载适合需要在离线或受限网络环境中搭建 C/C 编译环境的开发者、运维人员及实验课用户。相比自行逐个收集依赖并手动安装这份打包好的资源省去了逐个 dpkg 安装时版本匹配与依赖顺序排查的繁琐过程直接执行 do.sh 即可按序安装降低上手门槛。对刚接触 Ubuntu 离线软件包管理、不熟悉 dpkg 依赖关系的初学者尤为友好也可作为离线环境批量部署 GCC 工具链的参考模板。1. ubuntu18.04gcc.zip它不是安装包是编译器的“搬家方案”接手内网构建机时手头是 Ubuntu 18.04系统带了 GCC 7.5但我需要 C17 的 filesystem 和更新的 OpenMP 特性。apt 源连不上网上大多是“sudo apt install gcc-9”的教程对离线环境完全没有帮助。我的做法是找一台能联网的同版本机器把 GCC 9.3 整个安装目录打成一个 ubuntu18.04gcc.zip解压到目标机后临时加进 PATH 就用。这个 zip 不是 deb 也不是源码包而是一套可重定位的编译工具链适合离线内网、合规受限、以及需要给多台同版本机器批量交付构建环境的场景。下面从包内结构、环境变量、常见翻车点到制作一个自己的离线包逐个说清楚。2. 读懂 ubuntu18.04gcc.zip目录分工、版本选型与解压前的校验2.1 解压后的目录里是谁在干活bin、lib、libexec 的分工很多人以为 gcc 是一个单独的可执行文件实际上 bin 下的 gcc 只是驱动。真正的词法、语法、代码生成逻辑在 libexec 目录里GCC 9.3 的目录结构大致是这样的gcc-9.3.0/ ├── bin/ │ ├── gcc │ ├── g │ └── gfortran ├── include/ # GCC 内部头文件不是系统头文件 ├── lib/ │ ├── libstdc.so.6 - libstdc.so.6.0.26 │ ├── libgcc_s.so.1 │ └── gcc/x86_64-linux-gnu/9.3.0/ # crtbegin.o、crtend.o、libgcc.a ├── libexec/ │ └── gcc/x86_64-linux-gnu/9.3.0/ │ ├── cc1 │ ├── cc1plus # 真正的 C/C 编译器本体 │ └── lto-wrapper └── share/ # man 文档、locale 等你在命令行敲 gcc它只是按参数找到 cc1 或 cc1plus然后把编译、汇编、链接这一串动作串起来。链接时用到的 crt1.o、crtbegin.o、libgcc.a、libstdc.so 分散在 lib 目录里这就是为什么单独拷贝 bin/gcc 到另一台机器一定会失败。这份目录里最关键的是三个路径bin 给 shell 提供 gcc 命令lib 提供运行时和链接库libexec 提供实际干活的编译本体。搬机器时这三个目录必须保持相对位置最好把整个 gcc-9.3.0 目录原样移动不要只挑几个文件走。2.2 为什么很多 18.04 离线环境选 GCC 9.3 而不是 7.5 或 12Ubuntu 18.04 默认 GCC 是 7.5.0。对 C11 来说够用但对 C17GCC 7 对 filesystem 的支持是实验状态std::filesystem 正式落地要到 GCC 8完整可用要到 GCC 9。很多项目从 7.5 切到 9.3 之后编译选项基本不用改行为差异最小。GCC 12 呢它支持 C20 更完整但新版本带着更新版本的 libstdc对系统库依赖、ABI 兼容和一些老项目的代码写法都更挑剔。比如老代码里依赖 std::unary_function在 GCC 12 里直接编译失败。而 GCC 9.3 在 18.04 上是退可守进可攻的均衡点。版本选型上我见过三类机器各有各的固定搭配目标机器上的项目推荐 GCC理由ROS Melodic、老设备驱动、PyTorch 1.x C 扩展系统自带 7.5第三方二进制按 GCC 7 的 ABI 编译换编译器极易踩坑C17 CMake 新项目、内部基础库9.3filesystem 完整编译时间合理老代码大多能编译全新代码、C20 特性依赖强12 或 13需要自行承担 libstdc 较新的兼容成本这里提到一个常见场景Ubuntu 18.04 上部署 LabelImg、ROS 相关工具时pt 这类二进制包装包本身是用默认 GCC 7 编译的不要因为嫌弃版本旧就全局换新先区分你自己编译的代码和第三方二进制。2.3 拿到 zip 后的第一步sha256 校验与解压在把所有文件拷进内网之前先在能联网的机器上做一次完整校验。zip 包里我一般会放一个 SHA256SUMS 文件内容大致是# 在 zip 所在目录执行 sha256sum -c SHA256SUMS # 正常输出类似 # gcc-9.3.0/bin/gcc: OK # gcc-9.3.0/lib/libstdc.so.6.0.26: OK这一步不能省。GCC 离线包最常见的损坏不是文件内容少几个字节而是通过网盘、内网盘中转后符号链接被破坏。zip 格式本身能记录符号链接但如果你把 zip 拿到 Windows 上重新解压再打包libstdc.so 这几个链接文件会变成普通小文件大小只有几十字节后续编译时会出现“file format not recognized”这类报错排查成本远高于校验成本。解压命令unzip -q ubuntu18.04gcc.zip -d /opt ls -l /opt/gcc-9.3.0/lib/libstdc.so*ls 输出里如果 libstdc.so 和 libstdc.so.6 显示为普通文件而不是libstdc.so - libstdc.so.6.0.26说明符号链接已经没了把整个包删除回到原始 zip 再走一遍内网传输不要尝试手工修复大量链接。3. 切换默认 gcc 到 9.3三个环境变量和两种版本接管方式3.1 三个环境变量和它们的设置顺序解压到 /opt/gcc-9.3.0 后直接运行 gcc 仍然是系统自带的 7.5。需要让 shell 优先找到新目录。我一般会新建一个环境脚本而不是直接改 .bashrcsudo tee /etc/profile.d/gcc93.sh EOF export GCC_HOME/opt/gcc-9.3.0 export PATH$GCC_HOME/bin:$PATH export LD_LIBRARY_PATH$GCC_HOME/lib:$GCC_HOME/lib64:$LD_LIBRARY_PATH export MANPATH$GCC_HOME/share/man:$MANPATH EOF source /etc/profile.d/gcc93.sh这里三个变量各有作用PATH 让 shell 找到 gcc、g 命令必须把 $GCC_HOME/bin 放在最前面LD_LIBRARY_PATH 让动态链接器找到 libstdc.so.6、libgcc_s.so.1否则运行时报找不到共享库MANPATH 让 man gcc 显示的是 9.3 的文档。另外还有一个容易漏的变量供链接阶段寻找库文件和头文件export LIBRARY_PATH$GCC_HOME/lib/gcc/x86_64-linux-gnu/9.3.0:$GCC_HOME/lib export CPATH$GCC_HOME/includeLIBRARY_PATH 是给链接器找 -lstdc、-lgcc 用的CPATH 给预处理器找头文件。如果你的 GCC 解压在 /opt/gcc-9.3.0使用自定义前缀时这两个变量几乎必须设置否则会出现“找不到 crt1.o”或“找不到 vector 头文件”的报错。3.2 用 update-alternatives 或符号链接接管默认版本切换 gcc-12 的思路环境变量只对当前登录 shell 有效。如果希望整个系统的构建工具默认走 9.3最常见的是两种做法。第一种简单直接用 /usr/local/bin 下的优先级链接覆盖 /usr/binsudo ln -sf /opt/gcc-9.3.0/bin/gcc /usr/local/bin/gcc sudo ln -sf /opt/gcc-9.3.0/bin/g /usr/local/bin/g/usr/local/bin 在默认 PATH 顺序上先于 /usr/bin所以敲 gcc 会命中这里的软链。缺点是没有版本管理能力之后想切回 7.5 要手动改链接。第二种用 update-alternatives 管理多个 GCC 版本适合需要在 7.5、9.3、12 之间来回切换的机器sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 sudo update-alternatives --install /usr/bin/gcc gcc /opt/gcc-9.3.0/bin/gcc 90 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 110 sudo update-alternatives --config gcc数字 70、90、110 是优先级数字大的优先。执行 --config 后会列出当前所有候选输入编号即可切换。g 同理需要单独建立一组 alternatives。注意 18.04 上如果 apt 装过 gcc-12它会在 /usr/bin/gcc-12不会自动顶掉系统默认 gcc这就是很多人问“gcc 升级后为啥还是旧版本”的直接原因——系统默认 gcc 仍然指向 7.5新版本只是多了一个命令名。3.3 验证不是玄学gcc -v 和带 std::filesystem 的 C17 最小用例切换完成后先看版本再看实际编译用的可执行文件路径gcc -v 21 | grep COLLECT_GCC # COLLECT_GCC/opt/gcc-9.3.0/bin/gccCOLLECT_GCC 这一行直接指向真正被调用的 gcc 路径。如果还是 /usr/bin/gcc说明 PATH 或 alternatives 没生效。再跑一个带 filesystem 的最小用例#include filesystem #include iostream namespace fs std::filesystem; int main() { fs::create_directories(/tmp/gcc-test); std::cout fs::absolute(/tmp/gcc-test) std::endl; return 0; }编译命令注意 GCC 9 与 GCC 10 的差异g -stdc17 -Wall -lstdcfs test_fs.cpp -o test_fs ./test_fsGCC 9 的 std::filesystem 符号独立放在 libstdcfs 里必须显式加 -lstdcfsGCC 10 以后这个链接参数可以不加。如果看到“undefined reference to std::filesystem”先检查是不是漏了这个库不要急着怀疑 GCC 安装有问题。4. GCC 离线包常见排查五个翻车点的现象、原因、解决4.1 运行 gcc 时报找不到 libstdc.so.6现象解压后运行 gcc甚至是运行 gcc -v都报错error while loading shared libraries: libstdc.so.6: cannot open shared object file: No such file or directory原因GCC 9 的二进制自身也依赖新版 libstdc而系统默认库路径里只有旧版LD_LIBRARY_PATH 没有指向 /opt/gcc-9.3.0/lib动态链接器找不到它。解决确认解压目录后设置环境变量或者写入系统级配置echo /opt/gcc-9.3.0/lib | sudo tee /etc/ld.so.conf.d/gcc-9.3.conf sudo ldconfig两种方案选一种即可。建议优先用 /etc/profile.d 下的环境变量方案因为你只是给这台机器上的构建用户用ldconfig 是全系统生效可能影响其他依赖旧 libstdc 的软件。4.2 gcc --version 显示升级了但还是旧版本现象用 apt 或离线包安装了 GCC 9gcc --version还是 GCC 7.5版本号丝毫没变。原因最常见的两种情况。第一种是 PATH 里新版本目录没有排在前面shell 优先找到了 /usr/bin/gcc第二种是 update-alternatives 安装过但没有执行 --config系统默认链接没改。解决先执行which gcc如果是 /usr/bin/gcc说明 PATH 问题如果是 /etc/alternatives/gcc说明 alternatives 问题。按 3.2 的方法调整然后重新登录一遍 shell不要只 source .bashrc因为 .bashrc 里的顺序可能被系统文件覆盖。4.3 编译报找不到 crt1.o现象自定义前缀安装 GCC 后编译任何程序都报/usr/bin/ld: cannot find crt1.o: No such file or directory原因crt1.o 是系统 glibc 的启动文件通常位于 /usr/lib/x86_64-linux-gnu。GCC 在 -print-search-dirs 里没有把系统库目录和自身 lib 目录串起来尤其是设置了 --sysroot 或改了 CPATH/LIBRARY_PATH 之后老手也容易翻车。解决确认当前搜索路径gcc -print-search-dirs输出里 libraries 字段应同时包含 /usr/lib/gcc/x86_64-linux-gnu/7.5.0/、/usr/lib/x86_64-linux-gnu/ 和 /opt/gcc-9.3.0/lib 相关路径。如果缺失用 LIBRARY_PATH 手工补充系统库路径export LIBRARY_PATH$GCC_HOME/lib/gcc/x86_64-linux-gnu/9.3.0:/usr/lib/x86_64-linux-gnu:$LIBRARY_PATH4.4 std::filesystem 报 undefined reference现象代码写的是标准 C17 filesystem编译时报一堆 undefined reference链接失败。原因GCC 9 之前版本把 filesystem 作为独立实验库GCC 9 虽然正式支持但符号仍然剥离在 libstdcfs.a 中需要手动链接。这不是安装问题是参数问题。解决编译时加 -lstdcfsg -stdc17 -lstdcfs your_program.cpp -o your_program如果项目用 CMake需要在 target_link_libraries 里显式加 stdcfs不能用 CMAKE_CXX_STANDARD 代替。4.5 用新 GCC 编译老项目时报 ABI 相关警告或编译失败现象把 ROS Melodic 或老 C 库从系统 GCC 7 换成 GCC 9 后编译出现大量 _GLIBCXX_USE_CXX11_ABI 相关的重定义错误或者链接期崩溃。原因GCC 5 之后引入了新的 std::string ABIGCC 9 默认使用新 ABI而用旧 GCC 编译的第三方二进制库里是旧 ABI。ROS Melodic 官方版本基于 GCC 7与 GCC 9 混用时同一个 std::string 在两个 ABI 间传递轻则警告重则运行崩溃。解决对需要保持旧 ABI 的项目编译时加-D_GLIBCXX_USE_CXX11_ABI0强制使用旧 ABI。如果项目里有大量第三方预编译库最稳妥的决策是保留系统 GCC 7.5 编译这些库新 GCC 9 只用于你自己维护的、全量源码构建的模块。全局升级编译器前最好先做一次 ABI 审计这是我从一次整机替换失败里得来的血泪经验。5. 自己制作 ubuntu18.04gcc.zip可重定位编译、打包与移交5.1 用源码 configure 编一个 9.3.0关键 configure 参数如果没有现成离线包最可靠的做法是找一台联网的 Ubuntu 18.04 机器从源码编译一个可重定位 GCC。下载 GCC 9.3.0 源码后先装编译依赖sudo apt-get install libgmp-dev libmpfr-dev libmpc-dev flex bison然后 configure 参数是关键我常用这一组mkdir -p /build/gcc-9.3.0 cd /build/gcc-9.3.0 ../gcc-9.3.0/configure \ --prefix/opt/gcc-9.3.0 \ --disable-multilib \ --enable-languagesc,c,fortran \ --disable-bootstrap \ --disable-werror make -j$(nproc) sudo make install参数说明--prefix 决定了安装目录也决定了解压后的相对路径基础不要写成 /usr否则后续无法整体迁移--disable-multilib 只保留 64 位库体积小很多老项目需要 32 位支持时不要加这个参数--enable-languages 按需选择不需要 fortran 就去掉--disable-bootstrap 跳过用新 GCC 编译自己的三遍循环节省一半编译时间追求极限性能的发布版 GCC 建议保留 bootstrap--disable-werror 避免因警告被当成错误中断编译。整个编译在 8 核机器上大约需要 40 到 60 分钟磁盘占用 5GB 左右make install 之后的目录约 1.5GB压缩成 zip 后还能再小一些。5.2 让目录脱离 /opt 也能动rpath 和符号链接自编译的 GCC 二进制默认带绝对路径依赖直接打包到其他机器解压到不同目录可能因为 RPATH 指向 /opt/gcc-9.3.0 而无法运行。我一般用 patchelf 把关键二进制的路径改成相对查找sudo apt-get install patchelf for f in /opt/gcc-9.3.0/bin/gcc /opt/gcc-9.3.0/bin/g \ /opt/gcc-9.3.0/bin/gfortran \ /opt/gcc-9.3.0/libexec/gcc/x86_64-linux-gnu/9.3.0/cc1 \ /opt/gcc-9.3.0/libexec/gcc/x86_64-linux-gnu/9.3.0/cc1plus; do sudo patchelf --set-rpath $ORIGIN/../../lib:$ORIGIN/../lib $f done$ORIGIN 是动态链接器支持的变量表示当前可执行文件所在目录。bin/gcc 的 $ORIGIN/../lib 指向 /opt/gcc-9.3.0/liblibexec 下的 cc1 离 lib 目录更远需要 $ORIGIN/../../lib。处理完后用readelf -d /opt/gcc-9.3.0/bin/gcc | grep RPATH检查一下确保每条路径都包含 $ORIGIN。符号链接方面需要确认 zip 里保留完整的链接链。推荐直接在 Linux 上用 zip 命令打包不要经过 Windows 中转cd /opt zip -r -y ubuntu18.04gcc.zip gcc-9.3.0/-y 参数是 zip 命令里保留符号链接的关键漏掉的话整包解压后 libstdc.so 全部变成普通文件GCC 直接不可用。5.3 移交前的自检清单制作完成后先模拟目标环境把 zip 解压到一个临时目录比如 /tmp/gcc-reloc-test不要用 /opt 原路径然后执行以下检查sha256sum -c SHA256SUMS /tmp/gcc-reloc-test/gcc-9.3.0/bin/gcc -v /tmp/gcc-reloc-test/gcc-9.3.0/bin/g -stdc17 -lstdcfs \ /tmp/test_fs.cpp -o /tmp/test_fs /tmp/test_fs如果临时目录下能完成编译和运行说明 RPATH 生效包可以分发。再检查一遍动态库链接ldd /tmp/gcc-reloc-test/gcc-9.3.0/bin/gcc ldd /tmp/gcc-reloc-test/gcc-9.3.0/libexec/gcc/x86_64-linux-gnu/9.3.0/cc1plusldd 输出里不应出现指向 /opt/gcc-9.3.0 的绝对路径否则目标机器解压后仍会依赖原目录存在。自检通过后把 SHA256SUMS、解压说明和版本信息一起放进 zip当作交付基线之后每台机器核对一次校验值省去大量远程排查时间。6. 换完 GCC 后的一个习惯整理编译日志而不是靠记忆6.1 gcc 日志输出到文件-v -### 和 tee 的组合换编译器后第一次完整构建别只盯屏幕。GCC 的报错信息可能几百行终端滚动过后就看不清上下文。我的习惯是每次都把日志落到文件gcc -v -### your_program.c 2 gcc-verbose.log make clean make 21 | tee build.log-v -### 会打印 GCC 内部每一步的完整命令包括预处理、编译、汇编、链接的完整参数包括哪条路径被优先采用日志输出到文件后排查“为什么用了旧库”这种问题特别有效。make 的时候用 tee 同时输出到终端和文件不会丢掉上下文也不用 8 倍速翻终端记录。6.2 记录编译器版本与路径指纹同一台机器上隔三差五切换 GCC 版本时我会在项目 build 目录里留一个一行命令生成的指纹文件gcc -v 21 | grep -E COLLECT_GCC|gcc version compiler.info下次项目出现诡异编译错误先看 compiler.info再对照当前 gcc -v 输出立刻知道是不是版本被人动过。这个动作成本几乎为零但能省掉很多和“玄学”搏斗的时间。内网机器和在线环境的一大差别是装错编译器版本后没有快速重装的后路。我现在每接手一台 18.04 构建机第一件事就是把 GCC 版本、路径、环境变量脚本、sha256 校验记录写进团队共享的部署文档以前吃过“gcc 升级后为啥还是旧版本”的亏后来发现往往只是 PATH 顺序被环境初始化脚本覆盖。希望这篇笔记能帮你少走一遍这些弯路让 ubuntu18.04gcc.zip 真正成为随手能用的后悔药。本文还有配套的精品资源点击获取