今天初步认识了 ARM 汇编。
CPU 内寄存器
aarch64 有 31 个 64 位通用寄存器,它们的名字是为 xn(n 为 0-30 的整数),不像 80X86 一样暗示了它们的用途。
类似 rax 和 eax 的关系,它们对应的 32 位寄存器叫做 wn(n 为 0-30 的整数)。
还有几个特殊寄存器和 80X86 类似,不过多出来了链接寄存器。
段的定义方法
段的定义方法与 80X86 有很大区别,但是和 nasm 语法基本一致。
指令语句及其格式
目前学到的一些区别:
- 格式上更类似于 Intel 的语法,第一个操作数是目的寄存器(目的存储器)。
- ARM 的很多指令都是三操作数的,这给编码带来了更多的灵活性。
- ARM 指令是定长的,每一条指令都是固定的 4 字节长度。
- 立即数表示不一样,使用了
#
作为前缀。 - 寄存器基址变址寻址不需要加号,使用
[R1, #8]
这样的格式。 LDR.W R0, [R1,#20]!
相当于 x86 汇编的mov eax [ebx, 20]
mov eax [eax]
两句。- 寄存器列表
{}
语法。
4005f4: a9be7bfd stp x29, x30, [sp, #-32]!
4005f8: 910003fd mov x29, sp
可以看到机器指令a9be7bfd
和910003fd
是定长的
一些特殊的指令
- LDR 字数据加载指令
- LDRB 字节数据加载指令
- LDRH 半字数据加载指令
- STR 字数据存储指令
- TRB 字节数据存储指令
- STRH 半字数据存储指令
子程序调用
ARM 使用 bl 跳转进入子程序,并不像 80X86 一样使用 call 。
参数传递
提供了 8 个通用寄存器(r0-r7)用于传递函数参数,一次对应arg1
到arg8
,超过 8 个的参数会存放在堆栈中。
GCC 生成的汇编依然是 Caller Save 。
使用 GCC 编译时,发现虽然传递到寄存器的顺序不会对函数调用有影响,但是 GCC 还是逆序传入寄存器,可能是为了结构化生成代码。
返回值
如果返回值是整型,则是 x0 。
与 C 语言混合编程
将.s
和.c
文件分开编程与 80X86 没有区别,内联汇编也没有区别,区别主要在于 ARM 汇编的语法。