{arm,thumb}*-none-eabi(hf)?

通用目标详情

本文档涵盖了适用于 32 位 Arm CPU 的一系列裸机目标的详细信息。GNU 编译器工具链的 arm-none-eabi 版本通常用于辅助编译到这些目标。

仅适用于此组中特定目标的详细信息将在其自己的文档中介绍。

第二层目标列表

第三层目标列表

指令集

Arm 定义了两种 32 位指令集架构 (ISA)

  • A32 ISA,具有固定宽度的 32 位指令。以前称为 Arm ISA,它起源于 1985 年的原始 ARM1,并且自那时以来通过对架构规范的各种修订进行了更新。
  • T32 ISA,具有 16 位和 32 位混合宽度的指令。请注意,此术语包括 1994 年 Armv4T 架构引入的原始 16 位宽度 Thumb ISA,以及 2003 年 Armv6T2 架构引入的后来的 16/32 位大小的 Thumb-2 ISA。同样,这些 ISA 已通过对相关 Arm 架构规范的后续修订进行了修订。

还有一个具有固定宽度 32 位指令的 64 位 ISA,称为 A64 ISA,但实现该指令集的目标通常以 aarch64* 开头,并在其他地方讨论。

默认情况下,以 arm* 开头的 Rust 目标会生成 Arm (A32) 代码,而名为 thumb* 的目标默认生成 Thumb (T32) 代码。大多数 Arm 芯片都支持 Thumb 模式和 Arm 模式,但值得注意的是,M-profile 处理器(thumbv*m*-none-eabi* 目标)支持 Thumb 模式。

eabi 结尾的 Rust 目标使用所谓的 软浮点 ABI:将 f32f64 作为参数的函数会将这些值打包到整数寄存器中。这意味着从 ABI 的角度来看不需要 FPU,但如果代码使用启用 FPU 支持的 target-cputarget-feature 选项编译,则在函数内部仍然可以使用浮点指令。

eabihf 结尾的 Rust 目标使用所谓的 硬浮点 ABI:将 f32f64 作为参数的函数将通过 FPU 寄存器传递它们。因此,这些目标需要 FPU 的可用性,并且会假设可以使用某种基线的浮点支持(这会因目标而异)。如果代码使用启用此类额外 FPU 支持的 target-cputarget-feature 选项进行编译,则可能会生成更高级的浮点指令。例如,如果给定的硬浮点目标在硬件中具有基线 单精度 (f32) 支持,则可能存在 target-cputarget-feature 选项,告诉 LLVM 假定您的处理器实际上也具有 双精度 (f64) 支持。

当然,您可以在代码中使用 f32f64 类型,无论使用的是哪种 ABI,或者您的处理器对在硬件中执行此类操作的支持级别如何。LLVM 假设您的处理器不支持的任何浮点运算都将降低为执行浮点运算的库调用(如 __aeabi_dadd),该浮点运算在软件中使用整数指令执行。

目标 CPU 和目标特性选项

可以使用 -C target-cpu 选项告诉 Rust(或 LLVM)您具有特定型号的 Arm 处理器。您还可以使用 -C target-feature 控制 Rust(或 LLVM)是否会包含针对可选硬件功能的指令,例如硬件浮点或高级 SIMD 操作。

重要的是要注意,选择 target-cpu 通常会启用该型号 CPU 上 Arm 提供的所有可选功能,并且您的 CPU 的特定实现可能不具有这些功能。在这种情况下,您可以使用 -C target-feature=-option 关闭您不具备的特定 CPU 功能,从而为您保留优化的指令调度和对您具备的功能的支持。更多详细信息可在详细的特定于目标的文档中找到。

许多目标特性目前不稳定,可能会发生更改,如果您使用它们,则应反汇编编译器输出并手动检查,以确保仅生成适合您 CPU 的指令。

如果您希望使用 target-cputarget-feature 选项,您可以将它们添加到您的 .cargo/config.toml 文件中以及您的项目使用的任何其他标志(可能是链接器相关的标志)

rustflags = [
  # Usual Arm bare-metal linker setup
  "-Clink-arg=-Tlink.x",
  "-Clink-arg=--nmagic",
  # tell Rust we have a Cortex-M55
  "-Ctarget-cpu=cortex-m55",
  # tell Rust our Cortex-M55 doesn't have Floating-Point M-Profile Vector
  # Extensions (but it does have everything else a Cortex-M55 could have).
  "-Ctarget-feature=-mve.fp"
]

[build]
target = "thumbv8m.main-none-eabihf"

要求

这些目标是交叉编译的,并使用静态链接。

默认情况下,将使用 Rust 附带的 lld 链接器;但是,您可能希望使用 GNU 链接器。可以从 Arm 开发人员网站 或您的操作系统的软件包管理器中获取适用于 Windows/Mac/Linux 的 GNU 链接器。要使用它,请将以下内容添加到您的 .cargo/config.toml

[target.<your-target>]
linker = "arm-none-eabi-ld"

通过指定 arm-none-eabi-gcc 作为链接器也可以使用 GNU 链接器。使用 GCC 的链接时间优化时需要这样做。

这些目标不提供链接器脚本,因此您需要根据您正在使用的特定设备自带链接器脚本。将 -Clink-arg=-Tyour_script.ld 作为 rustc 参数传递,使链接器在链接期间使用 your_script.ld

对于 arm* 目标,可以使用 -C target-feature=+thumb-mode 启用 Thumb 模式代码生成。使用不稳定的 #![feature(arm_target_feature)],可以将属性 #[target_feature(enable = "thumb-mode")] 应用于各个 unsafe 函数,以使这些函数编译为 Thumb 模式代码。

构建 Rust 程序

对于此系列中的第三层目标,rust 不会随附预编译的工件。

只需使用 build-std nightly cargo 功能来构建 core 库。您可以将其作为命令行参数传递给 cargo,或者您的 .cargo/config.toml 文件可能包含以下行

[unstable]
build-std = ["core"]

大多数 core 应该按预期工作,但请注意以下事项

  • 除非告知 LLVM 启用 FPU 支持(通过使用 eabihf 目标、指定具有 FPU 支持的 target-cpu,或使用 target-feature 来支持特定类型的 FPU),否则浮点运算将在软件中模拟。
  • 在某些目标上,整数除法也在软件中模拟,具体取决于目标、target-cputarget-feature
  • 较旧的 Arm 架构(例如 Armv4、Armv5TE 和 Armv6-M)仅限于基本的 loadstore 操作,而不限于更高级的操作,如 fetch_addcompare_exchange

只要您提供自己的全局分配器,也支持 alloc

Rust 程序以 ELF 文件形式输出。

测试

这是一个交叉编译的目标,您需要在测试期间进行模拟。

您需要的确切模拟器取决于您希望在其中运行代码的特定设备。

交叉编译工具链和 C 代码

该目标支持使用 arm-none-eabi 目标三元组和适当的 -march-mcpu 标志编译的 C 代码。

可以使用 gccclang,但请注意,gcc 默认对 arm-none* 目标使用 -fshort-enums,而 clang 则不使用。 rustcgcc 的行为匹配,即 Rust 中 #[repr(C)] enum 的大小可以小到 1 个字节,而不是像在 arm-linux 目标上那样为 4 个字节。