3. ARM64 交叉编译工具链:安装与验证

在嵌入式 ARM64 开发中,目标硬件(如树莓派、QEMU 模拟的 virt 平台)通常资源有限,无法直接运行本地编译器。因此,我们需要在性能强大的宿主机(x86_64)上安装交叉编译工具链,生成可在 ARM64 目标上执行的代码。本节将带领你完成 aarch64-linux-gnu-gcc 的安装、环境配置、验证,并编写第一个 ARM64 汇编程序。

核心概念:交叉编译(Cross Compilation)是指在一种架构(如 x86_64)上编译出另一种架构(如 ARM64)可执行代码的过程。工具链前缀 aarch64-linux-gnu- 明确标识了目标架构、操作系统和二进制接口。

3.1 安装 aarch64-linux-gnu-gcc

在 Ubuntu/Debian 系统上,推荐使用包管理器直接安装完整的交叉编译工具链。该工具链包含编译器、汇编器、链接器以及针对 ARM64 的 C 标准库。

# 更新软件包列表
sudo apt update

# 安装 ARM64 交叉编译工具链
sudo apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu

# 验证安装是否成功
which aarch64-linux-gnu-gcc

安装完成后,系统会提供以下核心工具(位于 /usr/bin/ 目录下):

工具名称 功能描述 对应原生工具
aarch64-linux-gnu-gcc ARM64 C 语言编译器 gcc
aarch64-linux-gnu-as ARM64 汇编器(GAS) as
aarch64-linux-gnu-ld ARM64 链接器 ld
aarch64-linux-gnu-objdump ARM64 目标文件反汇编工具 objdump
aarch64-linux-gnu-readelf ARM64 ELF 文件信息查看工具 readelf
注意:如果使用 macOS 或 Windows,建议通过 Docker 或虚拟机运行 Ubuntu 环境。Windows 用户也可以使用 WSL2(Windows Subsystem for Linux)获得原生 Linux 体验。

3.2 配置环境变量

为了使用方便,建议将工具链路径添加到 PATH 环境变量中。虽然 Ubuntu 默认安装路径已在 /usr/bin 中,但为了项目管理的清晰性,我们可以在用户配置文件中设置别名或专用变量。

# 编辑 ~/.bashrc 或 ~/.zshrc 文件
echo 'export CROSS_COMPILE=aarch64-linux-gnu-' >> ~/.bashrc
echo 'export CC=${CROSS_COMPILE}gcc' >> ~/.bashrc
echo 'export AS=${CROSS_COMPILE}as' >> ~/.bashrc
echo 'export LD=${CROSS_COMPILE}ld' >> ~/.bashrc

# 使配置生效
source ~/.bashrc

# 验证环境变量
echo $CC
# 输出应为:aarch64-linux-gnu-gcc

设置 CROSS_COMPILE 前缀变量是嵌入式开发中的常见做法,后续在 Makefile 或编译脚本中可以直接引用,方便切换不同架构的工具链。

3.3 验证工具链

编写一个简单的 C 程序并交叉编译,验证工具链是否正常工作。

// hello.c
#include <stdio.h>

int main() {
    printf("Hello, ARM64 World!\n");
    return 0;
}
# 使用交叉编译器编译
aarch64-linux-gnu-gcc -o hello.arm64 hello.c

# 查看生成文件的架构信息
file hello.arm64
# 输出应包含:ELF 64-bit LSB executable, ARM aarch64

# 使用 readelf 进一步确认
aarch64-linux-gnu-readelf -h hello.arm64 | grep Machine
# 输出应为:Machine: AArch64
验证要点:
  • file 命令显示 ARM aarch64,说明目标架构正确。
  • 在 x86_64 宿主机上直接运行 ./hello.arm64 会失败(提示 Exec format error),这是正常现象,因为架构不匹配。
  • 后续章节中,我们将通过 QEMU 用户态模拟来运行此程序。

3.4 编写第一个 ARM64 汇编程序

为了深入理解 ARM64 指令集,我们直接编写一个汇编程序,实现“退出程序并返回状态码”的功能。这是嵌入式系统中最基础的裸机操作。

// exit.s - ARM64 汇编程序
// 功能:调用 Linux 系统调用 exit(42)

.section .text
.global _start

_start:
    // 系统调用号:93 (exit) 放入 x8
    mov x8, #93
    // 退出状态码:42 放入 x0
    mov x0, #42
    // 执行系统调用
    svc #0

// 数据段(可选)
.section .data
// 此处可定义数据,本程序暂不需要

汇编与链接步骤:

# 汇编:将 .s 文件转换为目标文件 .o
aarch64-linux-gnu-as -o exit.o exit.s

# 链接:生成可执行文件
aarch64-linux-gnu-ld -o exit.arm64 exit.o

# 验证文件类型
file exit.arm64
# 输出:ELF 64-bit LSB executable, ARM aarch64

# 查看反汇编,确认指令正确
aarch64-linux-gnu-objdump -d exit.arm64

反汇编输出应包含以下关键指令:

exit.arm64:     file format elf64-littleaarch64

Disassembly of section .text:

0000000000400078 <_start>:
    400078:   d2800ba8    mov x8, #0x5d    // #93
    40007c:   d2800540    mov x0, #0x2a    // #42
    400080:   d4000001    svc #0x0
汇编要点:
  • .section .text:定义代码段。
  • .global _start:声明 _start 为全局符号,作为程序入口点。
  • mov x8, #93:ARM64 Linux 系统调用号通过 x8 寄存器传递,93 对应 exit 系统调用。
  • mov x0, #42:第一个参数(退出码)放入 x0。
  • svc #0:超级用户调用(Supervisor Call),触发系统调用。

3.5 工具链工作流程 SVG 图

下图展示了从汇编源代码到 ARM64 可执行文件的完整工具链工作流程:

ARM64 交叉编译工具链工作流程 exit.s ARM64 汇编源码 aarch64-linux-gnu-as ARM64 汇编器 exit.o ELF 目标文件 aarch64-linux-gnu-ld ARM64 链接器 exit.arm64 ARM64 可执行文件 说明 汇编 + 链接两步完成

3.6 常见问题与排查

问题现象 可能原因 解决方案
command not found: aarch64-linux-gnu-gcc 工具链未安装或 PATH 未配置 执行 sudo apt install gcc-aarch64-linux-gnu 重新安装
file 命令显示 x86-64 架构 误用了本地 gcc 而非交叉编译器 确认使用 aarch64-linux-gnu-gcc 全名编译
汇编时提示 unknown mnemonic 指令语法错误或使用了不支持的指令 检查 ARM64 指令手册,确保指令格式正确
链接时提示 cannot find entry symbol _start 未定义 _start 或拼写错误 在汇编代码中添加 .global _start 和 _start: 标签
本节总结:
  • 成功安装并验证了 aarch64-linux-gnu-gcc 交叉编译工具链。
  • 掌握了通过 CROSS_COMPILE 环境变量管理工具链的方法。
  • 编写了第一个 ARM64 汇编程序,理解系统调用 svc 的基本用法。
  • 熟悉了从 .s 源码到 .o 目标文件再到可执行文件的完整编译流程。