今天初步认识了 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

可以看到机器指令a9be7bfd910003fd是定长的

一些特殊的指令

  • LDR 字数据加载指令
  • LDRB 字节数据加载指令
  • LDRH 半字数据加载指令
  • STR 字数据存储指令
  • TRB 字节数据存储指令
  • STRH 半字数据存储指令

子程序调用

ARM 使用 bl 跳转进入子程序,并不像 80X86 一样使用 call 。

参数传递

提供了 8 个通用寄存器(r0-r7)用于传递函数参数,一次对应arg1arg8,超过 8 个的参数会存放在堆栈中。

GCC 生成的汇编依然是 Caller Save 。

使用 GCC 编译时,发现虽然传递到寄存器的顺序不会对函数调用有影响,但是 GCC 还是逆序传入寄存器,可能是为了结构化生成代码。

返回值

如果返回值是整型,则是 x0 。

与 C 语言混合编程

.s.c文件分开编程与 80X86 没有区别,内联汇编也没有区别,区别主要在于 ARM 汇编的语法。