大约两个月前我开始使用汇编编程,到目前为止一切顺利。让我简单总结一下我到目前为止所学到的知识。
语法
x86 汇编有两种主要语法:Intel 和 AT&T。各有利弊。 Intel 语法似乎只用于基于 x86 的处理器,而 AT&T 语法用于几种不同的架构(例如 ARM)。如果您查看 OpenBLAS 的源代码,您会发现它们使用 AT&T 语法用于几种不同的架构。但是,许多人认为 Intel 语法更具可读性。到目前为止,我一直在使用 Intel 语法进行编程,但我大部分时间都知道如何阅读 AT&T 语法。
汇编器
您可以将内联汇编与 GCC 一起使用,但不能与 MSVC 64 位一起使用。到目前为止,我还没有为内联汇编而烦恼。您可以选择多种汇编程序,例如:MASM、NASM、YASM、FASM 和 GAS。 MASM 仅使用 Intel 语法,并且据我所知仅真正用于 Windows(我不认为它可以是 Linux 的 ELF 目标文件)。 NASM 也仅使用 Intel 语法,但可以创建多个不同的目标文件,例如适用于 Windows 和 Linux。据我所知,YASM 在很大程度上是 NASM,但也支持 AT&T 语法。 FASM 使用 Intel 语法,也可以创建几个不同的目标文件,但它在几个方面与 NASM 和 YASM 不同。我还没有使用过 FASM,但它看起来很诱人。 GAS 使用 AT&T 语法(尽管可以使用 Intel 语法),实际上是使用 GCC 编译时使用的语法。 GCC 生成组件,然后发送到 GAS。
了解每个汇编程序都只有方言很重要,因此您不能期望用 MASM 编写的代码必须在 NASM 中开箱即用地进行汇编。据我了解,NASM 和 YASM 在很大程度上是兼容的。
您应该选择哪个汇编程序?到目前为止,我只使用过 NASM。
调用约定和用 C 链接
到目前为止,对我来说学习汇编的最佳来源是 GCC。用 C 编写代码,然后查看程序集。例如,如果你有一个简单的函数foo 你可以这样做
gcc -O3 -S foo.c //AT&T syntax
gcc -O3 -S -masm=intel foo.c //Intel syntax
然后查看文件foo.s 或者你可以使用objdump
gcc -O3 -c foo.c
objdump -d foo.o //AT&T syntax
objdump -d -Mintel foo.o //Intel syntax
您应该知道您的操作系统的function calling conventions。 32 位代码和 64 位代码的调用约定不同。对于 Windows 和 Linux,它们对于 32 位代码是相同的,但对于 64 位代码是不同的。到目前为止,我只用 NASM 为 Linux 64 位编写了汇编代码。
关于 SO 的许多汇编问题似乎是关于在汇编中编写整个函数,包括用户输入和输出。我不认为这是必要的。让 C 处理输入和输出。您可以在this question 中看到一个示例。我给出了 NASM 代码和 C 代码,并解释了如何组装、编译和链接它们。这是我在 x86 汇编中写的第一件事。在那个问题中,我有一个功能
float triad(float *x, float *y, float *z, const int n);
Linux x86-64(或者更确切地说是 System V AMD64 ABI)调用约定传递rdi 寄存器中的第一个参数,rsi 中的第二个参数和rdx 中的第三个参数。所以在这种情况下rdi=x, rsi=y, rdx=n。
一旦您掌握了调用约定,并且可以将汇编中的目标文件与 C 接口,您会发现使用汇编更加容易。
最后,我学习汇编的第二个最佳来源是Agner Fog's Optimizing Assembly manual。手册的第一部分为初学者提供了很多很好的建议。一旦你获得了一些经验,手册的后面部分就会有很多很好的信息。