2023年:如何编写可移植的ARM64汇编代码?掌握这些要点就够了!

2023年:如何编写可移植的ARM64汇编代码?掌握这些要点就够了! 编写可移植的ARM64汇编代码2023年4月12日 阅读时长4分钟苹果基于ARM的计算机日益流行带来了一个不太好的副作用针对64位ARM ISA的不可移植汇编代码增多了。这是因为开发者编写这些汇编代码片段是为了在苹果基于ARM的计算机上运行程序时提高速度却没有考虑其他运行Linux或BSD的64位ARM设备如单板计算机SBC和服务器。好消息是编写既能在苹果计算机上运行又能在运行非Darwin操作系统的其他64位ARM设备上运行的汇编代码非常容易。你只需了解Mach - O和ELF ABI之间的一些差异以及避免使用苹果特定的语法扩展。遵循本文的指导你就能编写出可在苹果工具链、官方ARM汇编工具链和GNU工具链之间移植的汇编代码。ELF和Mach - O ABI的差异包括基于Linux的系统在内现代UNIX系统大多使用[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语言中声明如下的函数cextern 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指令asmeor.16b v2, v2, v0这是ARM汇编语法的苹果特定扩展。[官方ARM汇编手册](https://developer.arm.com/documentation/dui0802/b/A64 - SIMD - Vector - Instructions/EOR--vector - )规定必须为每个寄存器指定内存布局asmeor v2.16b, v2.16b, v0.16b使用宏来抽象ABI细节好消息是通过几个宏可以轻松抽象ABI细节。至于使用NEON函数答案很简单遵循ARM手册的说明而不是使用苹果的助记符。你需要两个宏。如果需要可将它们放在某个头文件中。第一个宏用于处理Darwin ABI的下划线要求c#ifdef __APPLE__# define PROC_NAME(__proc) _ ## __proc#else# define PROC_NAME(__proc) __proc#endif第二个宏是可选的但它能让你在苹果工具链之外定义正确的ELF符号类型c#ifdef __clang__# define TYPE(__proc, __typ)#else# define TYPE(__proc, __typ) .type __proc, __typ#endif然后你只需像平常一样编写汇编代码但使用这些宏asm.global PROC_NAME(unmask).align 2TYPE(unmask, function)PROC_NAME(unmask):...就是这么简单。只要遵循这些指导原则你编写的汇编代码就能在任何64位ARM的类UNIX环境中移植。那么你是否准备好尝试编写可移植的汇编代码了呢