Say hello to x86_64 Assembly 系列的第五篇,这篇会看到宏(macros)。但这与 x86_64 并没有关系,仅仅是 nasm 这种汇编工具与其预处理的。若有兴趣的话可以接着阅读。
宏(Macros)
NASM 支持以下两种形式的宏:
- 单行(single-line)
- 多行(multiline)
所有单行定义的宏必须以%define
开始,像下面的形式一样:
1 | %define macro_name(parameter) value |
Nasm 宏的行为与 C 非常相似。比如,我们可以这样创建单行宏:
1 | %define argc rsp + 8 |
之后可以在代码中这么使用他们:
1 | ;; |
多行宏以%macro
起始,以%endmacro
结束。通常的形式是这样的:
1 | %macro number_of_parameters |
例如(译者注:1 代表参数的个数为 1):
1 | %macro bootstrap 1 |
然后这样使用它:
1 | _start: |
再举几个 PRINT 宏的例子:
1 | %macro PRINT 1 |
让我们浏览一遍、懂得他们是怎样工作的:第一行定义了 PRINT 宏,参数为 1。之后利用 pusha 与 pushf 指令把所有数据与标志(flag, pushf 指令)入栈。然后跳向 %%astr 标签继续执行。注意到宏定义的所有的标签都以 %% 开始。现在转向 _syscall_write 宏,它的参数为 2。**_syscall_write** 是怎么执行的呢?是否还记得我们使用的 write 系统调用来打印字符至标准输出。它长成这样:
1 | ;; write syscall number |
在我们的 _syscall_write 宏中,我们先使用两条指令把 1 放入 rax(write system call number)与 rdi(stdout file descriptor)。之后把 %%str 放入 rsi 寄存器(指向字符串),%%str 是局部标签,它获取 PRINT 宏的参数(要注意宏的参数可以使用 $parameter_number),%%str 以 0 结束(每个字符串必须以 0 结束)。%%strln 为计算出的字符串的长度。最后进行了系统调用。
现在便可以这样使用它:
1 | label: PRINT "Hello World!" |
有用的标准宏(Useful standard macros)
NASM 支持下列形式的标准宏:
STRUC
可以为数据指令(data structure)使用STRUC
与ENDSTRUC
。例如:
1 | struc person |
现在可以这样为上面的指令创建一个实例:
1 | section .data |
%include
可以 include 其他的汇编文件,然后跳转到这些文件中的某一行继续执行,或者调用其中的函数。
结语
…
译者罗嗦几句:感觉这结语没啥好翻译的,与前面都差不多;也并没学会宏,使用的话看还是再找些资料读吧。