4、宿主机与目标机:宿主机环境要求、目标机架构(ARM、MIPS、RISC-V)、ABI与EABI

好,咱们进入正题。交叉编译这事儿,说白了就是「我在我的电脑上,编译出能在你电脑上跑的程序」。那我的电脑叫宿主机,你的电脑叫目标机。这两者之间,隔着的不只是网线,还有架构、指令集、ABI 这些硬骨头。

我个人习惯,在开始任何 SDK 发布工作之前,先把宿主机环境理清楚。不然编译到一半报错,你都不知道是工具链的问题,还是系统库的问题。

4.1 宿主机环境要求

宿主机,就是你面前这台开发机。我建议用 Linux,最好是 Ubuntu 20.04 LTS 或 22.04 LTS。为什么?因为绝大多数嵌入式工具链和 SDK 都是基于 Linux 生态构建的。你用 Windows 当然也能搞,但坑会多不少——我当年在 Windows 上折腾 ARM GCC,光是环境变量就配了一下午。

宿主机的基本要求,我列个清单:

  • 操作系统:Ubuntu 20.04/22.04 LTS,或者 CentOS 7/8。Debian 也行,但别太新,库版本可能不兼容。
  • 磁盘空间:至少 50GB 空闲。一个完整的交叉编译工具链加上 SDK,轻松吃掉 10-20GB。我见过有人只留了 20GB,编译到一半磁盘满了,那叫一个尴尬。
  • 内存:8GB 起步,16GB 更稳。编译大型项目(比如 Linux 内核)时,内存不够会直接 OOM。
  • CPU:多核优先。交叉编译可以并行,make -j$(nproc) 能省不少时间。
  • 必备软件:build-essential、git、wget、curl、python3、texinfo、bison、flex、libncurses-dev。这些是基础,缺一个都可能让编译卡住。
我的小技巧:每次搭建新环境,我都会写一个 setup.sh 脚本,把依赖一键装好。这样换机器或者重装系统时,跑一遍脚本就行,省心。

4.2 目标机架构:ARM、MIPS、RISC-V

目标机就是你的嵌入式设备。它的 CPU 架构决定了你用什么样的交叉编译工具链。目前主流的三驾马车:ARM、MIPS、RISC-V。

4.2.1 ARM 架构

ARM 是嵌入式领域的霸主。从手机到路由器,从单片机到服务器,到处都有它。ARM 是个精简指令集(RISC)架构,特点是功耗低、性能不错。

ARM 有两个主要系列:

  • Cortex-A:应用处理器,跑 Linux、Android。比如树莓派的 BCM2835 就是 Cortex-A 系列。
  • Cortex-M:微控制器,跑裸机或 RTOS。比如 STM32 系列。
  • Cortex-R:实时处理器,用于汽车、工业控制。

我在项目中遇到过最头疼的事:ARM 的版本太多了。ARMv7、ARMv8、ARMv9,指令集不完全兼容。你编译时选错了 -march,程序跑起来就崩。

4.2.2 MIPS 架构

MIPS 曾经很火,尤其是在路由器、机顶盒领域。龙芯早期也用过 MIPS 指令集。MIPS 也是 RISC 架构,设计哲学比 ARM 更简洁。

MIPS 的特点:

  • 指令长度固定(32位),不像 ARM 有 Thumb 模式。
  • 延迟槽(Delay Slot)——这个坑了我好几次。MIPS 在分支指令后面会执行一条指令,哪怕分支已经跳转了。写汇编时得小心。
  • 现在 MIPS 生态在萎缩,但存量市场依然很大。
注意:MIPS 有大小端之分。mips 是大端,mipsel 是小端。选错的话,程序根本跑不起来。我曾经因为没注意这个,浪费了一整天。

4.2.3 RISC-V 架构

RISC-V 是后起之秀,开源、简洁、可扩展。我最近几年越来越看好它。为什么?因为它没有历史包袱,指令集设计干净,而且从 MCU 到超级计算机都能用。

RISC-V 的模块化设计:

  • RV32I/RV64I:基础整数指令集,必选。
  • M:乘除法扩展。
  • F/D:单精度/双精度浮点。
  • C:压缩指令,减小代码体积。

嗯,这里要注意:RISC-V 的工具链还在快速演进中。我建议用官方的 riscv-gnu-toolchain,或者 SiFive 提供的预编译包。自己从源码编译?那得准备好咖啡,编译一次要几个小时。

4.3 ABI 与 EABI

ABI(Application Binary Interface),说白了就是「二进制层面的约定」。它规定了函数怎么传参、寄存器怎么用、栈怎么布局、数据怎么对齐。两个模块如果 ABI 不一致,链接在一起就会出问题。

EABI(Embedded ABI)是嵌入式领域的 ABI 标准。它和桌面版的 ABI 有什么区别?

特性 桌面 ABI EABI
浮点传参 用栈传递 用寄存器传递(更高效)
结构体对齐 自然对齐 可配置,节省空间
位置无关代码 支持 可选,常用于裸机
系统调用接口 依赖操作系统 可无操作系统

举个例子。ARM 的 EABI 有两种常见变体:

  • arm-eabi:老版本,浮点用软浮点(soft float),效率低。
  • arm-eabihf:新版本,硬浮点(hard float),直接用 FPU 寄存器传参,快很多。

我踩过的一个坑:编译了一个库用的是 arm-eabihf,但主程序用的是 arm-eabi。链接时一堆「undefined reference」,查了半天才发现是 ABI 不匹配。所以,整个项目的所有模块必须用同一个 ABI。

核心原则:交叉编译时,工具链的 ABI 必须和目标机的 ABI 完全一致。否则,程序要么链接失败,要么运行时崩溃。

4.4 知识体系结构图

下面这张图,帮你理清宿主机、目标机、ABI 之间的关系:

宿主机 (Host) Ubuntu 20.04 LTS x86_64 架构 交叉编译工具链 交叉编译 目标机 (Target) CPU 架构 ARM | MIPS | RISC-V ABI / EABI 函数调用约定 | 寄存器使用 | 数据对齐 运行环境 Linux | RTOS | 裸机 关键:宿主机编译出的二进制,必须与目标机的架构 + ABI 完全匹配

这张图想表达的核心:宿主机上跑的是 x86_64 的 Linux,工具链把源码编译成目标机架构的机器码。而目标机那边,架构和 ABI 共同决定了这个二进制能不能跑起来。

避坑指南:我曾经在发布 SDK 时,忘记在文档里注明目标机的 ABI 版本。结果客户拿回去,链接时报了一堆错。后来我养成了习惯——在 SDK 的 README 里,第一行就写清楚「目标架构:armv7a-eabihf」。

好了,这一节的内容就到这儿。记住:宿主机环境要稳,目标机架构要准,ABI 要一致。这三样搞定了,交叉编译就成功了一半。


可可精品资料库