第二章:目标架构与宿主机

好,咱们正式开始动手前的准备工作。

这一章,说白了就是搞清楚两件事:你要给谁编译代码?(目标架构)和你在哪台机器上编译?(宿主机)。这两个概念搞混了,后面所有配置都会乱套。我见过太多新手上来就对着错误提示发懵,其实根源往往就是没分清楚目标机和宿主机。

2.1 常见嵌入式架构:ARM、MIPS、RISC-V、x86

嵌入式世界里,CPU架构五花八门。但咱们做交叉编译,最常打交道的就这四种。我按个人经验,从最常见到相对小众的顺序给你捋一遍。

2.1.1 ARM —— 嵌入式领域的王者

ARM架构,你肯定不陌生。手机、路由器、智能家电,到处都是它的身影。为什么这么流行?功耗低、授权灵活、生态成熟。

我个人习惯把ARM分成两个大类:

  • Cortex-A系列:跑Linux、Android的,比如树莓派的BCM2837(Cortex-A53)。性能强,带MMU(内存管理单元)。
  • Cortex-M系列:跑裸机或RTOS的,比如STM32F4(Cortex-M4)。没MMU,资源少,但实时性好。

做交叉编译时,你首先要确认目标板用的是A系列还是M系列。因为编译器选项差别很大。嗯,这里要注意:ARMv7、ARMv8这些版本号,直接决定了你该用哪种编译器。

小提示: 我刚开始做ARM交叉编译时,总以为"arm-linux-gnueabihf"和"aarch64-linux-gnu"差不多。结果编译出来的程序在树莓派3B上跑不起来。后来才发现,树莓派3B是64位的ARMv8,得用aarch64的编译器。这个坑,我替你踩过了。

2.1.2 MIPS —— 曾经的RISC明星

MIPS架构,现在可能不如ARM那么火,但在路由器、机顶盒领域,它依然占有一席之地。龙芯早期也用过MIPS指令集。

MIPS有个特点:字节序。它分大端(Big-Endian)和小端(Little-Endian)。你想想看,同一个MIPS芯片,不同厂家可能用不同的字节序。比如,博通的路由器芯片常用大端,而一些嵌入式设备用的小端。

我在项目中遇到过最头疼的事,就是拿到一个MIPS的交叉编译工具链,没注意字节序,编译出来的程序在目标板上跑得乱七八糟。后来我养成了习惯:拿到工具链第一件事,先看名字里有没有"mipsel"(小端)还是"mips"(大端)。

2.1.3 RISC-V —— 开源指令集的新星

RISC-V,这几年太火了。它是完全开源的,没有授权费。说白了,你可以免费用它设计自己的CPU。这对很多初创公司和高校来说,吸引力巨大。

RISC-V的交叉编译,目前还在快速演进中。常见的工具链前缀是riscv64-unknown-linux-gnu-。但要注意,RISC-V有多个扩展指令集,比如RV64GC(通用组合)。你编译时,得确保目标板支持这些扩展。

我记得有一次,帮朋友调试一块RISC-V开发板,他编译时没加-march=rv64gc,结果程序一跑就非法指令。嗯,这就是没对齐指令集扩展的典型问题。

2.1.4 x86 —— 你桌上的电脑,也能当嵌入式目标

等等,x86不是PC架构吗?没错。但很多工业控制、边缘计算设备,用的就是x86架构的CPU,比如Intel Atom、AMD Geode。这时候,你依然需要交叉编译——只不过目标架构是x86,宿主机可能也是x86。

你可能会问:都是x86,为什么还要交叉编译?因为宿主机可能是64位的,目标板是32位的。或者,宿主机是Ubuntu,目标板是Yocto构建的定制Linux。说白了,架构相同,但系统库不同。

核心要点: 交叉编译的关键,不是架构不同,而是运行环境不同。架构相同但系统库不同,一样需要交叉编译。

2.2 宿主机环境准备

宿主机,就是你用来编译代码的电脑。我建议你至少准备一台Linux机器,或者虚拟机。Windows也能做,但坑比较多。

2.2.1 Linux宿主机(推荐)

我个人习惯用Ubuntu LTS版本,比如20.04或22.04。为什么?社区活跃,软件包全,遇到问题好搜解决方案。

你需要安装的基础工具:

sudo apt update
sudo apt install build-essential git wget flex bison texinfo \
                 libncurses-dev gawk python3 python3-pip

这些工具是编译交叉编译器本身用的。如果你只是用现成的工具链,那只需要安装对应的交叉编译器包就行。比如:

sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
警告: 别用系统自带的gcc去编译嵌入式程序!系统gcc是给宿主机自己用的。你编译ARM程序,必须用arm-linux-gnueabihf-gcc。我曾经见过有人直接用gcc编译,然后拷到ARM板上,结果报"cannot execute binary file"。嗯,这就是典型的架构不匹配。

2.2.2 Windows宿主机(备选)

如果你非要用Windows,我建议你装WSL(Windows Subsystem for Linux)。WSL2的性能已经很好了,基本可以当原生Linux用。

或者,你也可以用Cygwin或MSYS2,但说实话,体验不如WSL。我早期在Windows上做嵌入式开发,用的是Cygwin,每次配置路径都折腾半天。后来换了WSL,世界清净了。

2.3 目标板与开发板介绍

目标板,就是你最终要运行程序的硬件。开发板,是你用来学习和调试的板子。两者有时候是同一块板子,但概念上要分清。

2.3.1 常见开发板推荐

架构 推荐开发板 CPU型号 特点
ARM 树莓派4B BCM2711 (Cortex-A72) 生态最好,资料最多
ARM STM32MP157 Cortex-A7 + Cortex-M4 双核异构,适合学习
MIPS Loongson 2K1000 龙芯2K1000 国产,MIPS64兼容
RISC-V SiFive HiFive Unmatched FU740 (U74-MC) 高性能RISC-V,可跑Linux
x86 Intel NUC Intel Celeron/Pentium 小体积,工业级

我个人建议,初学者从树莓派入手。为什么?因为遇到问题,网上随便一搜就有答案。我当年学嵌入式,就是从树莓派开始的。那时候还是树莓派2B,512MB内存,跑个Qt程序都卡。但就是那块板子,让我真正理解了交叉编译是怎么回事。

2.3.2 目标板的关键参数

拿到一块目标板,你需要确认以下信息:

  • CPU架构:ARM、MIPS、RISC-V还是x86?
  • 位数:32位还是64位?
  • 字节序:大端还是小端?(MIPS尤其要注意)
  • 浮点单元:有没有硬件FPU?是软浮点还是硬浮点?
  • 系统库版本:目标板上的glibc版本是多少?

这些信息,决定了你该用哪个交叉编译器。我习惯在目标板上跑一个uname -a和cat /proc/cpuinfo,把信息记下来。然后对照着去选工具链。

我的经验: 如果你不确定目标板的浮点单元类型,可以试试编译一个简单的C程序,用-mfloat-abi=soft和-mfloat-abi=hard分别编译,看哪个能在目标板上正常运行。这个方法虽然土,但很管用。

2.4 本章小结

这一章,我们搞清楚了:

  • 四种常见嵌入式架构的特点和注意事项
  • 宿主机环境怎么准备,Linux和Windows的优劣
  • 目标板和开发板的区别,以及如何选择

下一章,我们就要真正动手搭建交叉编译工具链了。到时候,你会用到这一章的所有知识。嗯,做好准备吧。