如果你手头有一台跑着 Ubuntu 20.04 的电脑,又正好想把代码编译成 ARM64 架构的可执行文件,那你大概率绕不开一个叫 aarch64-linux-gnu-gcc 的交叉编译工具链。我第一次接触它,是在给一块 ARM64 开发板写用户态程序的时候。本机明明是 x86_64,用普通 gcc 编出来的程序拷到板子上,终端直接报 Exec format error,后来才意识到必须用交叉编译工具链。这篇文章会把 Ubuntu 20.04 上安装和验证 aarch64-linux-gnu-gcc 的完整流程讲透,包括命令、原理、常见错误和解法。不管你是做嵌入式开发、智能硬件,还是刚接触 ARM 服务器,只要想在 x86 主机上产出 ARM64 程序,这篇文章都适合你。
很多人以为交叉编译是件特别复杂的事,其实只要理解一个核心:x86 主机上运行编译器,生成的目标文件是 ARM64 指令集。编译器本身是 x86 程序,却知道怎么生成 ARM64 代码。整套流程我已经反复跑过好几遍,下面按实际操作的顺序写,能少踩一个坑就少踩一个坑。
1. 先搞清楚:aarch64-linux-gnu-gcc 到底是什么,帮你解决什么问题
1.1 交叉编译是怎么一回事
交叉编译这个概念,很多人一听就觉得高深,其实特别直白。你平时在 Ubuntu 20.04 上敲 gcc main.c,生成的是一个 x86_64 架构的可执行文件,因为你的电脑 CPU 是 x86_64。可如果你的目标平台是一个 ARM64 开发板,比如树莓派 4B、RK3399 或者其他采用 ARMv8 架构的单板电脑,板子上的内核和用户空间都是 ARM64 的,你用 x86 的 gcc 编出来的程序,板子压根不认。这就好比你给一个只讲粤语的人写信,却用了普通话的发音规则,对方当然看不懂。交叉编译要做的事情,就是在 x86 主机上运行一个懂得输出 ARM64 指令的编译器,让编译产物能直接拷到目标板子上执行。
aarch64-linux-gnu-gcc 就是这样一个交叉编译器。它的名字本身就是一套信息:aarch64 是目标架构,linux 是目标操作系统,gnu 表示编译器工具链来自 GNU 项目,gcc 是 C 编译器。大家平时说的交叉编译工具链,通常不只包含 gcc,还有 g++、binutils、C 运行库头文件等一整套东西。所以要真正用起来,往往要把配套包一起装齐,只装一个光杆编译器会非常难受。
1.2 为什么我建议在 Ubuntu 20.04 上用 apt 安装
可能有人会想,GCC 不是开源的嘛,直接下源码自己编不就行了?理论上当然可以,但实践中我非常不建议。GCC 的构建依赖非常多,包括 GMP、MPFR、MPC 等数学库,还要选择 target tuple,配置源码树,编一次可能要几十分钟甚至几个小时,中间报一个错又得从头排查。相比之下,Ubuntu 20.04 的软件源里本来就有 aarch64 交叉编译相关的二进制包,一条 apt 命令就能把所有组件装好,依赖关系也会自动处理。
还有一类选择是去下载 Linaro 等预编译工具链,解压之后把 bin 目录加进 PATH 也能用。问题是这类工具链版本通常比较固定,更新不如 apt 源及时,而且一旦和 SDK 要求的 GCC 版本不一致,很容易出现兼容性报错。我常规的做法是:先试 apt,除非项目 SDK 明确指定了某个版本的工具链,才去用 Linaro 或者官方的预编译包。
1.3 适合场景与必备基础
聊完原理,说说这东西有什么用。最常见的场景就是给 ARM64 开发板写系统级或用户态程序,比如修改内核、编写驱动、移植应用、跑机器学习推理框架。另一个场景是 ARM 服务器代码的本地快速构建,如果你有一台 x86 的 CI 机器,但最终部署环境是 ARM64 云服务器,交叉编译可以让构建过程不必跑到目标机上执行。此外,很多嵌入式 SDK 在编译驱动模块或动态库时,也会通过环境变量直接调用这套工具链。
你需要的基础并不高:能在 Ubuntu 终端里敲命令、会用 sudo、大概知道源码构建是什么。不需要你之前编译过 GCC 或参与过嵌入式底层开发。不过有一个概念必须清楚:交叉编译的产物不能直接在主机上运行,如果需要运行验证,要么拷到目标板,要么借助 QEMU 用户模式模拟。后面我会专门演示怎么验证。好了,概念铺垫到这里,下面开始实际安装。
2. 安装前确认环境:系统版本、架构与源状态
2.1 用几条命令拉起环境信息
在动手之前,先确认你的系统确实是 Ubuntu 20.04,并且是 x86_64 架构。这步看起来多余,但真能拦住不少人。有的机器虽然是 Ubuntu,但已经升级到了 22.04,或者本身跑在 ARM64 的树莓派上,那么安装方式就完全不同了。我用的是下面几条命令:
lsb_release -a uname -m sudo apt update第一条显示发行版版本,第二条显示当前 CPU 架构,第三条把软件源索引拉到最新。如果 uname -m 输出的是 x86_64,那这篇文章的流程完全适用。如果输出的是 aarch64,说明你已经在 ARM64 设备上了,这种情况直接本地编译,不需要交叉工具链。如果 apt update 有红字报错,先别往下装,排查一下网络或者源配置。
这里多说一句:不管你是物理机上的 Ubuntu、双系统里的 Ubuntu、WSL2 里的 Ubuntu,还是从一个 Ubuntu 20.04 镜像启动的容器,只要内部系统版本和架构正确,后面的命令都一样。WSL2 虽然运行在 Windows 上,但内部确实是一个完整的 Linux 环境,apt 逻辑与物理机完全一致,只是注意别把编译产物直接塞到 /mnt/c 下的 Windows 文件系统里,访问速度慢还容易触发权限问题。
2.2 看懂工具链相关包名
Ubuntu 20.04 的包管理对交叉编译有一整套命名规则,看懂之后能少很多猜谜时间。核心包是 gcc-aarch64-linux-gnu,装完会提供 aarch64-linux-gnu-gcc。如果你还要编 C++ 代码,就装 g++-aarch64-linux-gnu。如果发现头文件或 C 库缺失,那多半是缺了 libc6-dev-arm64-cross,这个包提供了 ARM64 版本的 C 标准库头文件和静态库。搜索相关包可以用:
apt-cache search aarch64 | grep -E 'linux-gnu|arm64'你会看到很多带 -cross 后缀的包,它们都是针对不同架构的交叉编译库。Ubuntu 20.04 的命名规律很统一,照着“架构-系统-工具”的思路找就行。比如 libstdc++-10-dev-arm64-cross 就是 ARM64 架构的 C++ 标准库开发文件。不要看到一大堆包名就慌,实际核心安装就那几个。
2.3 确认 sudo 权限与磁盘空间
apt 安装需要写系统目录,所以一定要有 sudo 权限。你可以先跑一下 sudo whoami,如果返回 root 就说明权限没问题。磁盘空间方面,交叉编译工具链加库文件大概会占用 1GB 左右,不算夸张,但如果你用的是精简安装的 Linux,建议先 df -h 看一眼 /usr 所在分区剩余空间,别装到一半提示 No space left on device。我见过有人卡在这里,到处问为什么安装报错,结果一看磁盘满了。
如果你以前装过其他交叉工具链,或者系统里已经有 aarch64-linux-gnu-* 命令,也要先确认版本。命令 aarch64-linux-gnu-gcc --version 能显示版本号,如果版本特别老,后续编译新代码可能会因为缺某些特性而失败。此时最好先清理旧版本,再安装当前源里的新版本,避免多个工具链互相干扰。
3. 快速安装 aarch64-linux-gnu-gcc 的完整步骤
3.1 一条命令安装核心编译套件
环境没问题,下面就是整个文章最核心的一段操作。我的习惯是把 gcc、g++ 和 C 库一次性装齐,避免后面编译到一半发现少了东西。在 Ubuntu 20.04 上执行:
sudo apt install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev-arm64-cross这个命令会同时安装交叉编译器、交叉汇编器、交叉链接器以及 ARM64 的 C/C++ 运行时库。安装完成后,/usr/bin 目录下会多出一批 aarch64-linux-gnu-* 前缀的命令,包括 gcc、g++、as、ld、objcopy 等。你用 ls /usr/bin/aarch64-linux-gnu-* 就能看到。这里解释一下为什么 libc6-dev-arm64-cross 要一起装:交叉编译器生成目标代码后,链接时需要去找 ARM64 版本的 C 标准库。没有它,最简单的 printf 程序都会链接失败。
如果你只需要 C 编译器,不编 C++,可以只装 gcc-aarch64-linux-gnu。但我还是建议顺手把 g++ 也装了,因为你永远不知道项目后面会不会引入 C++ 模块,到时候再补装一次也不麻烦,但一次装齐能节省重复等待下载的时间。
3.2 验证安装是否成功
装完之后不要急着写代码,先做两步验证。第一步看命令能不能找到:
which aarch64-linux-gnu-gcc aarch64-linux-gnu-gcc --version正常会返回类似 aarch64-linux-gnu-gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0 这样的信息。第二步用 file /usr/bin/aarch64-linux-gnu-gcc 查看编译器自身,你会发现它其实是 x86_64 架构的程序,但它生成本文要用的目标架构由编译参数决定。这两个验证能帮你快速确认是环境问题还是工具链版本问题。
要注意的是,如果你是在普通用户登录的状态下验证,不需要重新启动系统。apt 安装的二进制在 /usr/bin 下,默认就在 PATH 里。如果哪个终端窗口提示 command not found,可以先执行 hash -r 清理 shell 的命令缓存,或者重新打开一个终端窗口再试。
3.3 编译并验证第一个 ARM64 程序
现在写一个最基础的 hello world,把整个流程跑通。在终端建立文件:
cat > hello.c << 'EOF' #include <stdio.h> int main(void) { printf("hello aarch64\n"); return 0; } EOF然后用交叉编译器编译:
aarch64-linux-gnu-gcc -o hello_aarch64 hello.c file hello_aarch64如果看到输出里有 ELF 64-bit LSB executable, ARM aarch64,说明交叉编译成功。对比一下,用本机 gcc 编出来的文件会是 ELF 64-bit ... x86-64。这个差异一眼就能看出来,也是判断工具链是否起作用的最直观方法。很多教程到这一步就结束了,但我建议你再多做一步:在同一个目录下用 gcc 编一个本机版本,然后用 file 分别查看两个产物,这样你能直观理解“交叉”在产物层面的体现。
3.4 用 qemu-user 运行 ARM64 二进制做快速验证
在没有开发板的情况下,怎么确认这个产物逻辑正确?可以用 QEMU 用户模式模拟。Ubuntu 20.04 下安装 qemu-user-static:
sudo apt install -y qemu-user-static ./hello_aarch64只要 binfmt_misc 内核模块没有被禁用,安装后就能直接运行 ARM64 用户态程序,终端会打印出 hello aarch64。这个技巧在 CI 或本地测试里特别实用,不用反复拷贝文件到板子上。需要注意的是,qemu-user 只是模拟指令集,不模拟硬件外设。如果程序直接操作寄存器或者依赖特定设备文件,那还得放到真实目标板上测试。
如果你在执行 ./hello_aarch64 时遇到 Permission denied,先 chmod +x hello_aarch64。如果遇到 Exec format error,最大的可能是你的内核没有加载 binfmt_misc 模块,可以临时执行 sudo modprobe binfmt_misc,让它重新挂载 /proc/sys/fs/binfmt_misc,然后再试。
3.5 设置编译选项和 sysroot 的补充说明
交叉编译时,默认的头文件和库文件路径与本地编译不同。编译器会用 --sysroot、-I、-L 等参数来定位。apt 安装的交叉工具链默认的 sysroot 是 /usr/aarch64-linux-gnu,你可以通过 aarch64-linux-gnu-gcc -print-sysroot 查看。如果只是编译简单 C 程序,默认值就够用。但如果是要编译带第三方库的项目,比如想链接 openssl 或 libcurl,就得单独指定头文件和库。常见做法是加:
aarch64-linux-gnu-gcc -I/path/to/arm64/include -L/path/to/arm64/lib -o app app.c如果项目自带一个完整的 ARM64 rootfs,那么用 --sysroot=/path/to/rootfs 更省心,编译器会优先在 sysroot 里搜索所有依赖。新手最容易犯的错误,是把 -I/usr/include 这种本机路径手动加进交叉编译命令,结果编译器同时混用不同架构的头文件和库,报出一堆莫名其妙的错误。最好的习惯是让编译器自己处理 sysroot,只在确实需要额外第三方头文件时,再用 -I 指过去。
4. 常见错误与解决方案实录
4.1 apt 找不到包 gcc-aarch64-linux-gnu
很多新手在 Ubuntu 20.04 上执行 sudo apt install gcc-aarch64-linux-gnu,结果得到 E: Unable to locate package。一看就是没执行 sudo apt update,或者软件源里没有开启 universe 组件。gcc 交叉工具链在 Ubuntu 官方源中主要是 universe 组件的软件包,如果你的 source.list 只开了 main,自然找不到。解决方式:先执行 sudo apt update,再尝试安装。如果还是找不到,可以用 sudo apt install -y software-properties-common 安装完整工具,然后执行 sudo add-apt-repository universe,再次 sudo apt update,最后装包。
如果执行 add-apt-repository 之后源还是没有变化,可以手动检查 /etc/apt/sources.list 和 /etc/apt/sources.list.d/ 下的文件,确认有没有被注释掉的条目。不过我要提醒一句:不要为了省事去网络上随便找第三方源,官方源里的工具链已经够用了。
4.2 command not found 或工具前缀带有版本号
安装后执行 aarch64-linux-gnu-gcc --version,却显示 command not found。这种情况多半是 shell 缓存或者 PATH 出了问题。其实 apt 安装的二进制在 /usr/bin 下,/usr/bin 默认就在 PATH 中。但如果你用了非交互 shell,或者之前手动把 PATH 改坏了,可以执行 export PATH=/usr/bin:$PATH 再看。另一种情况是系统里同时存在多个 GCC 版本,命令被放在 aarch64-linux-gnu-gcc-9 这样的名字下,需要检查:
ls /usr/bin/aarch64-linux-gnu-*如果确实只有带版本号的命令,建一个软链接就能解决问题:sudo ln -s /usr/bin/aarch64-linux-gnu-gcc-9 /usr/bin/aarch64-linux-gnu-gcc。这一步在 Ubuntu 20.04 上一般用不到,但如果你从旧系统迁移过,或者手动配置过 alternatives,可能会遇到。
4.3 编译时提示找不到 stdio.h
编译 hello.c 时如果报 fatal error: stdio.h: No such file or directory,那基本可以断定是交叉 C 库的头文件没装。Ubuntu 把 ARM64 的 C 标准库放到 libc6-dev-arm64-cross 包里,它会把头文件安装在 /usr/aarch64-linux-gnu/include,把库放在 /usr/aarch64-linux-gnu/lib。所以解法就是:
sudo apt install -y libc6-dev-arm64-cross同理,如果 C++ 头文件缺失,就要检查 libstdc++-dev-arm64-cross 是否安装。记住:交叉编译环境里,本机的 /usr/include 不应该出现在搜索路径里,否则就算头文件找得到,里面的机器相关定义也会导致链接错误。这个问题的排查顺序应当是:先确认交叉编译器存在,再确认交叉库开发包存在。
4.4 undefined reference toprintf' 或__libc_start_main'
有时候头文件能通过,但链接时报 undefined reference to 'printf',或者更奇怪地指向 __libc_start_main。这通常是 C 运行库缺失,或者链接器没有到 ARM64 的 libc 路径里找库。交叉编译器默认的 sysroot 是 /usr/aarch64-linux-gnu,如果这个目录下没有 libc.so.6,链接必然失败。解决办法还是安装 libc6-dev-arm64-cross。如果已经安装,可以用 aarch64-linux-gnu-gcc -print-search-dirs 查看编译器实际搜索的库路径,确认是否指向了本机 x86 库目录。
如果指向错了,多半是编译器选取出现问题,这时候检查是否调用的是真正的交叉 gcc,而不是本机 gcc。你可以在 Makefile 里强制指定 CC=aarch64-linux-gnu-gcc,并在编译命令前加 echo "Compiling with $(CC)" 做输出确认。别小看这个操作,很多项目默认使用 CC=gcc,即使你装了交叉工具链,Makefile 没改的话照样用错编译器。
4.5 Exec format error:拷到开发板后无法运行
这个错误我在文章开头就提到了,是最典型的“没用交叉编译”的信号。Exec format error 的意思是目标文件格式不对,内核不认。执行 file 你的程序,如果显示 x86-64,说明你用了本机 gcc;如果显示 ARM aarch64 但还是这个错误,那就要检查目标板系统是不是 32 位内核、程序是否缺少动态链接器。比如目标板是 ARMv7,但你编译成了 ARMv8,也会出错。另外记得给程序加可执行权限:chmod +x 程序,不要笑,真的有不少人忘掉这一步。
如果你的目标板是 32 位内核但实际处理器是 64 位,你需要的是 arm-linux-gnueabihf 这套工具链,而不是 aarch64-linux-gnu-gcc。刻意区分这一点,能省下大量排查时间。可以在目标板上执行 uname -m 看输出,如果是 armv7l 而不是 aarch64,说明系统运行在 32 位模式。
4.6 目标板上运行提示 libc.so.6 not found
即使交叉编译成功,拷到开发板跑起来还是可能报 error while loading shared libraries: libc.so.6: cannot open shared object file。这是因为你动态链接了目标板没有的 glibc 版本。开发板系统比较老,而你的工具链带了太新的 GLIBC_XX,就会出现这种问题。几个思路:一是改用静态编译,aarch64-linux-gnu-gcc -static -o app app.c,所有 C 库都打进可执行文件,不用再依赖目标板 glibc;二是为目标板准备匹配的 sysroot;三是如果实在要动态链接,就要确保目标板的 libc 版本不低于编译环境的版本。在嵌入式开发里,这是经典痛点。
静态编译虽然能解决动态库依赖,但它不是万能的。比如你用了 NSS、dlopen 动态加载等机制,静态编译反而会引入新的问题。遇到这类情况,我建议优先想办法拿到目标板对应的 rootfs,然后用 --sysroot 做配套编译。这样编译出来的程序在目标板上跑,动态库版本才是真正对齐的。
4.7 常见错误速查表
| 现象 | 原因 | 解决办法 |
|---|---|---|
| 找不到包 | apt 源未更新或缺少 universe 组件 | apt update,启用 universe |
| command not found | 未安装或 PATH 异常 | 安装;检查 /usr/bin;查看带版本号命令 |
| 缺少 stdio.h | 缺 libc6-dev-arm64-cross | 安装 libc6-dev-arm64-cross |
| undefined reference | C 库未安装或链接路径不对 | 安装交叉库;检查 sysroot |
| Exec format error | 用了本机编译器/架构不对 | file 检查;用 aarch64-linux-gnu-gcc;chmod +x |
| libc.so.6 not found | 目标板 glibc 不兼容 | 静态编译或匹配 rootfs |
| 链接第三方库报错 | -L 路径指向 x86 库 | 改为 arm64 库路径 |
这张表是我实际排错时最常用到的。每次出问题先对着现象看一遍,能少走很多弯路。
5. 深入一点:CMake 交叉编译配置与替代工具链
5.1 用 CMake 管理交叉编译项目
如果你的项目不是简单单文件,而是用了 CMake,那就需要写一个 toolchain 文件,否则项目还是会默认调用本机 gcc。下面这个文件是我常用的最小配置,保存为 toolchain-aarch64.cmake:
set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)第一行告诉 CMake 目标系统是 Linux,第二行声明目标架构是 aarch64。编译器一行明确指定交叉编译器。CMAKE_FIND_ROOT_PATH 非常关键,它会告诉 CMake 在找第三方库和头文件时,只在 /usr/aarch64-linux-gnu 目录下找,不要去找本机 x86 路径。三个 MODE 字段分别控制程序、库、头文件的查找范围:程序仍然可以在本机 PATH 里找,这样 find_package 能找到本机安装的开发工具;库和头文件则限定在目标 root 目录。使用方法是:
cmake -B build -DCMAKE_TOOLCHAIN_FILE=toolchain-aarch64.cmake然后正常 make。这样做的好处是,项目里不会到处硬编码路径,所有交叉编译配置集中在一个文件里,维护起来非常方便。如果你的项目还定义了自定义的第三方库路径,可以在 toolchain 文件里继续加 set(CMAKE_PREFIX_PATH /path/to/arm64/deps) 之类的字段。
5.2 留意 SDK 自带的工具链版本
很多硬件厂商会给自家 ARM 平台提供一套完整的 SDK,里面通常自带交叉工具链。这种情况下,我不建议再另装一套 apt 工具链,因为 SDK 可能针对某个 GCC 版本做过大量兼容性测试,自己换一套容易在编译内核模块时出现 ABI 不匹配。如果必须用 SDK 自带工具链,记得把工具链的 bin 目录绝对路径写到编译参数里,比如:
make CROSS_COMPILE=/path/to/sdk/toolchain/bin/aarch64-linux-gnu-这里 CROSS_COMPILE 的值是带横杠的前缀,make 会根据它自动拼接 aarch64-linux-gnu-gcc、aarch64-linux-gnu-ld 等命令。此时可以用 file 看一下 SDK 里的 aarch64-linux-gnu-gcc,如果显示是 x86_64 的可执行文件,说明这个工具链可以在 Ubuntu 20.04 上运行;如果显示其他架构,就要检查是不是拿错了版本。
5.3 如果 apt 还是不够,没找到的库怎么办
交叉开发经常遇到依赖库缺失的问题,比如 openssl、libpthread、libbluetooth 等,apt 不一定都提供 arm64 交叉版。对这种情况,我的建议是先检查 apt-cache search 库名 | grep arm64,如果官方有 xxx-dev:arm64,那可以试试用 dpkg --add-architecture arm64 加上 apt install 库:arm64 的方式把目标库装进系统,然后通过 --sysroot 或 CMake 根路径去引用。不过这个方法要谨慎:它会把 arm64 库和本机 x86 库混在同一套 dpkg 管理里,偶尔会发生文件冲突。
更稳妥的办法是单独建一个 rootfs,或者利用 SDK 内置的 sysroot,不要轻易动系统级 dpkg 状态。我自己的习惯是,优先用官方源和 SDK,如果两者都没有,再考虑从源码交叉编译那个库。源码交叉编译也没多恐怖,无非是执行 ./configure --host=aarch64-linux-gnu --prefix=/usr/aarch64-linux-gnu,然后 make install。真正需要小心的是那些还依赖一堆子库的项目,最好先看依赖树。
6. 我在实际操作中踩过的几个坑
6.1 检查编译链接用的到底是哪个 gcc
我最开始做交叉编译时,有个项目 Makefile 里写死了 CC=gcc,我加了一堆环境变量也没用。后来才想到,先执行 make clean,再 make V=1,把每条编译命令完整打印出来,看看实际执行的编译器是不是 aarch64-linux-gnu-gcc。这个方法以后排查任何交叉编译问题都通用。打印出来如果还是 gcc,就去改 Makefile 或者传入 make CC=aarch64-linux-gnu-gcc。很多所谓“工具链没生效”的问题,最后查下来都是这个原因。
6.2 头文件路径不要混用
另一个坑是把 -I/usr/include 手动加进了交叉编译命令里,结果 stdbool.h、stdint.h 这类基础头文件出现了“无法兼容的架构定义”。后来我把本机包含路径全部去掉,只保留 sysroot 和项目自己的 include 目录,问题立刻消失。交叉编译的铁律是:头文件、库文件、可执行文件必须来自同一目标架构,混用任何一层都会产生奇怪的错误。
如果你发现程序在链接阶段报“找不到某个符号”,但头文件明明已经引入了,先不要怀疑编译器,先用 aarch64-linux-gnu-nm 查看库文件里的符号,确认库本身是不是 arm64 版本。本机 x86 的 .so 文件虽然也能被链接器读,但架构不匹配,最终产物多半加载不了。
6.3 不要盲目加 -static
静态编译确实能解决很多运行库缺失的问题,但如果你准备链接 glibc 的 NSS 模块、进行 dlopen 动态加载,或者使用某些需要与目标板内核交互的库,静态编译会带来新的坑。比如 -static 之后的程序虽然不依赖目标板 glibc,但 DNS 解析、用户信息查询可能因为 NSS 特性失效。所以 -static 只适合简单的命令行工具,如果项目会用到系统能力,还是老老实实准备正确版本的 sysroot。这一点我在一个网络工具项目里栽过跟头,最后改用 SDK 提供的老版本工具链才解决问题。
工具链这件事,装起来不难,难的是让项目持续用对工具链。我最后再分享一个小习惯:在项目根目录建一个 setenv.sh,把 CC、CXX、CFLAGS、LDFLAGS 这些变量统一放进去,source 之后再 make。这样换项目、换工具链时,只需要改一个文件,不会因为忘记设置环境变量而重新踩一遍坑。