Target Triple 到底是什么?

Cross-compiling (交叉编译)是指编译一个计算机程序,使其在与编译主机不同的机器上运行。 尽管历史上编译器只为宿主机编译,但这被认为是一种过时做法:现在所有重要的原生编译器都是交叉编译器。

毕竟,你不会想在真正的 iPhone 硬件上构建你的 iPhone 应用程序。

许多不同的编译器有不同的机制来分类和识别 targets (目标)。 target 是编译器可以为其生成可执行代码的平台。 然而,由于 LLVM 的迅猛普及,实际上所有编译器现在都使用 target triples (目标三元组)。 你可能已经遇到过,比如久负盛名的 x86_64-unknown-linux,或者邪恶的 x86_64-pc-windows。 这个系统错综复杂,并且 几乎 自洽。

但是,target triple 到底是什么,它们又是从哪里来的?

愚蠢的 GCC 约定

所以,如果你去浏览 OSDev 上的 Target Triplet 页面,你会学到关于 target triples 的真假信息,因为这个页面是关于 GCC 的,而不是关于一般的原生编译器的。

一般来说,target triple 是什么并没有“绝对真理”。 没有哪个标准机构会分配这些名称。 但正如我们将看到的,LLVM 是潮流引领者。

如果你运行以下命令,你可以了解你机器的 target triple:

$gcc -dumpmachine
x86_64-linux-gnu

Console

现在,如果你对任何广泛使用 target triples 的系统稍有了解,你就会知道这不是 一个 target triple,因为这个 target 的名称是 x86_64-unknown-linux-gnu,这是 clangrustc 都称呼它的方式——

$clang -dumpmachine
x86_64-pc-linux-gnu
$rustc -vV | grep host
host: x86_64-unknown-linux-gnu

Console

哦,不。

好吧,GCC 缺少 pcunknown 组件,这专门是 GCC 的事情;它允许以明确的方式省略 triple 的某些部分。 而且它们是 GCC 的发明,所以最好从评估 GCC 的观点开始。

根据 GCC 的说法,target triple 是 <machine>-<vendor>-<os> 形式的字符串。“machine”部分明确地标识了系统的架构。 实际上,这是编译器最终输出的汇编语言。“vendor”部分本质上是不相关的,主要用于将相关的操作系统排序在一起。 最后,“os”部分标识了为此代码编译的操作系统。 对于编译器来说,这主要确定了可执行文件格式:Windows 的 COFF/PE,Apple 操作系统的 Mach-O,Linux 及其朋友的 ELF,等等(但这是一种过度简化)。

但你可能会注意到 x86_64-unknown-linux-gnu 有一个额外的第四个条目1,它扮演着许多角色,但最常被称为 target 的“ABI”。 对于 linux,它标识了 target 的 libc,这对某些语言特性(例如线程局部变量和解旋)的代码生成有影响。 它是可选的,因为许多 target 只有一个 ABI。

使用 GCC 进行交叉编译

这里的关键历史是理解 GCC 进行交叉编译的非常愚蠢的方式。 传统上,每个 GCC 二进制文件都是为 一个 target triple 构建的。 GCC 二进制文件的完整名称将包含该 triple,因此在交叉编译时,你将使用 x86_64-unknown-linux-gcc 进行编译,使用 x86_64-unknown-linux-ld 进行链接,等等(在这里,gcc 不是 triple 的第四个 ABI 组件;它只是 x86_64-unknown-linux 工具链中的一个工具)。

没有脑子的人会这样做2。 LLVM 和所有遵循它的交叉编译器都将所有的后端放在一个二进制文件中,并使用像 --target 这样的编译器标志来选择后端。

但无论如何,这就是 target triples 的由来,以及它们看起来像现在这样的原因:它们最初是 autoconf 脚本中二进制文件名称的前缀。

但 GCC 是一种古老的技术。 在 21 世纪,LLVM 统治着所有的原生编译器。

古老语言中的名称

LLVM 的 target triple 列表应该被认为是“最官方的”,原因如下:

  1. 惯性。 每个人及其母亲都使用 LLVM 作为中间端和后端,因此它的命名约定会渗透到诸如 clangrustcswiftciccnvcc 之类的语言前端中。
  2. 芯片和操作系统供应商的上游工作。 LLVM 是人们主要受雇从事的工作,而不是 GCC,因此它的平台特定约定通常反映了供应商的偏好。

这些在很大程度上是因为 Apple、Google 和 Nvidia 拥有大量的编译器工程师为 LLVM 做出贡献。

“官方” target triples 的来源有很多。 通常,我会将 target triple 描述为“官方”当:

  1. 主要的编译器(比如 clangrustc)使用它。 在记录它们的 target 方面,Rust 比 LLVM 做得更好,所以我更喜欢尊重它。 你可以在这里找到 Rust 的官方 triples。
  2. 平台开发者(例如,硬件制造商、操作系统供应商)分发一个工具链,该工具链的 target triple 格式为 arch-vendor-os

那么,(1) 类中的名称是什么? LLVM 并没有真正尽力提供这样的列表。 但我们必须从某个地方开始,所以让我们深入研究源代码。

我们可以深入研究 LLVM 的 target triple 解析器中的 Triple.cpp。 它列出了 LLVM 识别的 triple 的每个部分的所有名称。 查看 Triple::parseArch(),我们有以下名称,包括许多别名。 右列的第一个项目是 LLVM 的架构首选名称,由 Triple::getArchTypeName() 指示。

架构 | 可能的名称 ---|--- Intel x86 (32-bit) | i386, i486, i586, i686, i786, i886, i986 Intel x86 (64-bit) | x86_64, amd64, x86_86h3 ARM (32-bit) | arm, xscale, … ARM (32-bit, big-endian) | armeb, xscaleeb, … ARM (64-bit) | aarch64, aarch64e, aarch64ec, arm64, … ARM (64-bit, big-endian) | aarch64_be, … ARM (64-bit, ILP324) | aarch64_32, arm64_32, … ARM Thumb | thumb, … ARM Thumb (big-endian) | thumbeb, … IBM PowerPC5 (32-bit) | powerpc, powerpcspe, ppc, ppc32 IBM PowerPC (little-endian) | powerpcle, ppcle, ppc32le IBM PowerPC (64-bit) | powerpc64, ppu, ppc64 IBM PowerPC (64-bit, little-endian) | powerpc64le, ppc64le MIPS (32-bit) | mips, mipseb, mipsallegrex, mipsisa32r6, mipsr6 MIPS (32-bit, little-endian) | mipsel, mipsallegrexel, mipsisa32r6el, mipsr6el MIPS (64-bit) | mips64, mips64eb, mipsn32, mipsisa64r6, mips64r6, mipsn32r6 MIPS (64-bit, little-endian) | mips64el, mipsn32el, mipsisa64r6el, mips64r6el, mipsn32r6el RISC-V (32-bit) | riscv32 RISC-V (64-bit) | riscv64 IBM z/Architecture | s390x6, systemz SPARC | sparc SPARC (little-endian) | sparcel SPARC (64-bit) | sparcv6, sparc64 WebAssembly (32-bit) | wasm32 WebAssembly (64-bit) | wasm64 Loongson (32-bit) | loongarch32 Loongson (64-bit) | loongarch64 Radeon R600 | r600 AMD GCN | amdgcn Qualcomm Hexagon | hexagon Nvidia PTX7 (32-bit) | nvptx Nvidia PTX (64-bit) | nvptx64 AMD IL8 (32-bit) | amdil AMD IL (64-bit) | amdil64 Direct-X IL | dxil, … HSAIL (32-bit) | hsail HSAIL (64-bit) | hsail64 Khronos SPIR (32-bit) | spir Khronos SPIR (64-bit) | spir64 Khronos SPIR-V | spirv, … Khronos SPIR-V (32-bit) | spirv32, … Khronos SPIR-V (64-bit) | spirv64, … Android RenderScript (32-bit) | renderscript32 Android RenderScript (64-bit) | renderscript64 Movidius SHAVE | shave Atmel AVR | avr Motorola 68k | m68k Argonaut ARC | arc Texas Instruments MSP430 | msp430 Tensilica Xtensa | xtensa C-SKY | csky OpenASIP | tce OpenASIP (little-endian) | tcele Myracom Lanai | lanai XMOS xCore | xcore Kalimba9 | kalimba VE9 | ve

在这里,我们开始看到 target triples 不是一个整洁的系统。 它们是 地狱。 如果架构名称列表中包含“…”,则表示 LLVM 接受更多名称。

问题在于架构通常具有 版本特性,这些版本和特性会微妙地改变编译器生成代码的方式。 例如,在为 x86_64 编译时,我们可能需要指定我们想要使用 AVX512 指令。 在 LLVM 上,你可以使用 -mattr=+avx512 来做到这一点。 每个架构都有微妙不同的方式来做到这一点,因为每个架构都有 不同的 GCC! 每个 GCC 变体都会将不同的东西放在 -mXXX 标志(-m 代表 “machine”)后面,这意味着该接口实际上并不统一。 因此,-march-mcpu-mtune-mattr 的含义差异很大。

因为 LLVM 应该(在很大程度上)取代 GCC,所以它复制了很多这种古怪的行为。

所以,我们需要谈谈 32 位 ARM 架构名称。

ARMTargetParser.cpp

LLVM 中有一个专门用于解析 ARM 架构名称的地狱般的文件。 尽管 ARM 系列的成员具有许多可配置的特性(你可以使用 llc -march aarch64 -mattr help10发现这些特性),但架构的名称在某种程度上是有意义的,并且可以有许多选项,主要与存在的许多 ARM 版本有关。

它有多糟糕? 好吧,我们可以查看 rustc 使用 rustc --print target-list 支持的各种 ARM target:

$rustc --print target-list | grep -P 'arm|aarch|thumb' \
 | cut -d- -f1 | sort | uniq
aarch64
aarch64_be
arm
arm64_32
arm64e
arm64ec
armeb
armebv7r
armv4t
armv5te
armv6
armv6k
armv7
armv7a
armv7k
armv7r
armv7s
armv8r
thumbv4t
thumbv5te
thumbv6m
thumbv7a
thumbv7em
thumbv7m
thumbv7neon
thumbv8m.base
thumbv8m.main

Console

这些大多是 32 位 ARM 版本,附带配置文件信息。 这些对应于此处给出的名称。 为什么 ARM 将版本号放入架构名称中,而不是像你在 x86 上那样使用 -mcpu(例如 -mcpu alderlake)? 我不知道,因为 ARM 不是我的强项。 这可能是因为 ARM 支持很早就被添加到 GCC 中。

在内部,LLVM 将这些称为“subarchitectures (子架构)”,尽管 ARM 获得了特殊处理,因为存在如此多的变体。 SPIR-V、Direct X 和 MIPS 都有子架构,因此如果你倒霉,可能会看到类似 dxilv1.7 的东西。

当然,LLVM 的 ARM 支持也支持一些不属于该系统的顽皮的子架构,并带有顽皮的虚构名称。

为什么 Windows 人员发明了另一个 ABI,而不是像 Apple 使用 ARM MacBooks 上的 Rosetta 那样使事情变得干净和简单? 我不知道,但 http://www.emulators.com/docs/abc_arm64ec_explained.htm 包含各种借口,但我对其中任何一个都没有留下深刻的印象。 我的理解是,他们的编译器组织只是在生活中比 Apple 的差,这并不奇怪,因为 Apple 在编译器方面的表现优于该行业中的任何其他人。

实际上,既然我们正在讨论架构的名称,我有一些事情需要澄清。

虚构的架构名称

x86 和 ARM 似乎都吸引了很多人为它们编造昵称,这导致了以下方面的很多困惑:

  1. 什么是“真正的”名字。
  2. 特定工具链想要什么名字。
  3. 你应该在你自己的世界性工具中使用什么名称。

让我们谈谈人们喜欢为它们编造的不正确的名称。 请根据我自己在许多工具中的经验,将以下内容视为关于人们如何称呼这些架构的相对规范的参考。

当我们说“x86”不加限定词时,在 2025 年,我们几乎总是指 x86_64,因为 32 位 x86 已经死了。 如果你需要谈论 32 位 x86,你应该说“32 位 x86”、“protected mode”11 或 “i386”(第一个实现 protected mode 的 Intel 微架构)12。 你不应该称其为 x86_32 或仅仅 x86

你也可以将其称为 IA-32(Intel Architecture 32,或 ia32),但没有人会这样称呼它,并且你可能会将人们与 ia64(或 IA-64)混淆,这是 Intel 失败的通用 VLIW 架构 Itanium 的官方名称,它与 x86 完全不兼容。 ia64 是 GCC 和 LLVM 用于命名 Itanium triples 的名称。 Itanium 支持在奥巴马政府期间被淹没在浴缸中,所以它不再重要了。 Rust 从未有过官方的 Itanium 支持。

32 位 x86 绝对不能 称为 “x32”; 这是 Linux 在删除之前用来称呼其 x86 ILP324 变体的名称(按照 ARM 名称,它本应称为 x86_6432)。

还有许多虚构的 64 位 x86 名称,除非你想让年轻一代嘲笑你,否则你应该避免使用它们。 amd64 指的是 AMD 在他们的 K8 微架构中对 long mode 的原始实现,该实现首先在他们的 Athlon 64 产品中发布。 当然,AMD 仍然制造最好的 x86 芯片(我正在一台装有 Zen2 Threadripper 的机器上编写这篇文章),但是称其为 amd64 是很愚蠢的,而且看起来很像 arm64,而且我真的很讨厌我看到这么多 Go 代码的文件名为 fast_arm64.sfast_amd64.s。 Debian 也使用 amd64/arm64,这使得浏览软件包有点烦人。

关于这个话题,你_绝对不应该_ 在 AMD K8 之后将 64 位模式称为 k8。 除了像我这样奇怪的计算机分类学家之外,没有人知道那是什么。 但是 Bazel 就是这么称呼它的,这真的很令人恼火13

你也不应该称其为 x64。 尽管 LLVM 出于历史原因确实接受 amd64,但除了 Microsoft 之外,没有人称其为 x64。 即使它在 Windows 上相当普遍,当我游戏开发的朋友写 x64 时,我绝对会让他们难堪。

在 ARM 方面,好吧。 Arm14 有一个坏习惯,就是对 64 位 ARM 使用不一致的命名,因为他们同时使用了 AArch64 和 ARM64。 但是,在编译器领域,aarch64 似乎更受欢迎。

你也应该坚持使用各种架构的 LLVM 名称,而不是选择你最喜欢的 Arm Cortex 名称(如 cortex_m0)。

供应商和操作系统

最糟糕的已经过去。 现在让我们继续检查 triple 的其余部分:平台供应商和操作系统。

供应商旨在识别谁负责该 target 的 ABI 定义。 尽管对编译器本身几乎没有价值,但它确实有助于将相关的 target 排序在一起。 有点。

回到 llvm::Triple,我们可以检查 Triple::VendorType。 供应商几乎总是对应于开发操作系统或其他代码运行平台的公司,但也有一些例外。

我们还可以使用一个方便的命令来获取 rustc 知道的供应商:

rustc --print target-list | grep -P '\w+-\w+-' | cut -d- -f2 | sort | uniq

Console

结果是这样的。 这只是一个具有代表性的列表;我省略了一些不太容易识别的列表。

供应商 | 名称 | 示例 Triple ---|---|--- Vendor Unknown15 | unknown | x86_64-unknown-linux “PC” | pc | x86_64-pc-windows-msvc Advanced Micro Devices Inc. | amd | amdgcn-amd-gfx906 Apple Inc. | apple | aarch64-apple-ios-sim Intel Corporation | intel | i386-intel-elfiamcu IBM Corporation | ibm | powerpc64-ibm-aix Mesa3D Project | mesa | amdgcn-mesa-mesa3d MIPS Technologies LLC | mti | mips-mti-none-elf Nintendo | nintendo | armv6k-nintendo-3ds Nvidia Corporation | nvidia | nvptx64-nvidia-cuda Sony Interactive Entertainment | scei, sie,