编写可移植的ARM64汇编代码

Hacker News Top 工具

摘要

一份关于编写可在Apple Darwin和Linux/BSD系统间移植的ARM64汇编代码的指南,涵盖ABI、符号命名和向量助记符的差异。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/06/03 09:44

# 编写可移植的 ARM64 汇编代码 来源:https://ariadne.space/2023/04/12/writing-portable-arm-assembly.html 苹果基于 ARM 的计算机日益流行,其中一个不幸的副作用是出现了大量针对 64 位 ARM ISA 的不可移植汇编代码。这是因为开发者编写这些汇编代码是为了在苹果的 ARM 计算机上加速程序运行,却没有考虑到其他 64 位 ARM 设备,例如 SBC 和运行 Linux 或 BSD 的服务器。 好消息是,编写同时适用于苹果计算机和其他运行非 Darwin 操作系统的 64 位 ARM 设备的汇编代码非常容易。只需注意 Mach-O 与 ELF ABI 之间的几个差异,并知道避免使用哪些苹果特有的语法扩展。遵循本博客的指导,你将能够编写出在苹果工具链、官方 ARM 汇编工具链和 GNU 工具链之间可移植的汇编代码。 ## ELF 与 Mach-O ABI 的区别 现代 UNIX 系统(包括基于 Linux 的系统)主要使用 ELF 二进制格式(https://en.wikipedia.org/wiki/Executable_and_Linkable_Format)。出于历史原因,苹果在 Darwin 中使用的是 Mach-O(https://en.wikipedia.org/wiki/Mach-O)。这并不是苹果使用 Mach 内核所强制的,实际上,Darwin、MkLinux 和 OSF/1 所基于的 OSFMK 内核完全可以很好地支持 ELF 二进制文件。只是苹果决定使用 Mach-O 格式。 在编写针对 Darwin 的汇编代码(实际上,任何链接代码)时,需要注意的主要区别是所有符号都带有一个下划线前缀。例如,如果一个函数在 C 语言中声明如下: ``` extern void unmask(const char *payload, const char *mask, size_t len); ``` 那么在 Darwin 上,你的汇编代码中该函数必须定义为 `_unmask`。 另一个主要区别是 ELF 定义了不同的数据类别,例如 `STT_FUNC` 和 `STT_OBJECT`。Mach-O 中没有对应物,因此在为 ELF 目标编写汇编时使用的 `.type` 指令在 Mach-O 中不受支持。 ### 关于平台 ABI 的简要说明 你还需要注意 Darwin ABI 与其他平台 ABI 之间的细微差异。一个显著的例子是 `x18` 寄存器在 Darwin ABI 中被保留,并且在某些上下文切换中会被显式清零。这个寄存器在 Android 上也被保留,但在 GNU/Linux 或 Alpine 上则没有。 ## 苹果特有的向量助记符 另一个需要注意的主要问题是苹果为 NEON 自定义的助记符。为了使编写 NEON 代码更加便捷,苹果引入了一套助记符,允许简化 NEON 指令的指定。例如,如果你只针对苹果设备,你可能会这样编写一个异或 NEON 指令: ``` eor v2.16b, v2.16b, v0.16b ``` 这是苹果对 ARM 汇编语法的扩展。官方 ARM 汇编手册(https://developer.arm.com/documentation/dui0802/b/A64-SIMD-Vector-Instructions/EOR--vector-)规定必须为每个寄存器指定内存布局: ``` eor v2.16b, v2.16b, v0.16b ``` ## 使用宏抽象 ABI 细节 好消息是,ABI 细节可以很容易地通过几个宏来抽象。至于使用 NEON 函数,答案很简单:坚持按照 ARM 手册的规定编写,而不是使用苹果的助记符。 你需要两个宏。如果需要,可以将它们放在头文件中。 第一个宏允许你处理 Darwin ABI 的下划线要求: ``` #ifdef __APPLE__ # define PROC_NAME(__proc) _ ## __proc #else # define PROC_NAME(__proc) __proc #endif ``` 第二个宏是可选的,但它允许你在苹果工具链之外定义正确的 ELF 符号类型: ``` #ifdef __clang__ # define TYPE(__proc, __typ) #else # define TYPE(__proc, __typ) .type __proc, __typ #endif ``` 然后像往常一样编写汇编代码,但使用这些宏: ``` .global PROC_NAME(unmask) .align 2 TYPE(unmask, @function) PROC_NAME(unmask): ... ``` 就是这样。只要遵循这些指导原则,你的汇编代码就能在 64 位 ARM 上的任何类 UNIX 环境中移植。

相似文章

用 x86_64 汇编写成的 Linux 桌面

Lobsters Hottest

一位开发者借助 Claude Code,用纯 x86_64 汇编重建了完整的 Linux 桌面栈——从 shell、终端、窗口管理器到各种工具,实现微秒级启动,并延长数小时续航。

让编写跨平台 SIMD 代码变得愉快

Lobsters Hottest

作者详细介绍了 bx 库跨平台 SIMD 抽象的第三次迭代,倡导无类型方法和 SSA 风格编码,以简化不同 CPU 架构上的底层性能优化。

SBCL: 终极汇编代码面包板 (2014)

Hacker News Top

一篇技术博客文章,探讨如何使用SBCL作为汇编代码的面包板,重点介绍基于堆栈的虚拟机技术,如旋转堆栈和高效的原语操作分发,并引用了F18处理器和x87堆栈。