ARM64汇编

/ 4

生成汇编文件

将c语言源文件编译成arm64汇编代码:

xcrun --sdk iphones clang -S -arch arm64 main.c -o main.s

寄存器

arm64有34个寄存器,包括通用寄存器fplrsppccpsr

寄存器 描述
x0~x7 传递子程序的参数和返回值,多余的参数用堆栈传递,使用时不需要保存。64bit将x0用来存放子程序的返回值
x8 用于保存子程序的返回地址,使用时不需要保存
x9~x15 临时寄存器,也叫可变寄存器,子程序使用时不需要保存
x16~x17 子程序内部调用寄存器IPx,使用时不需要保存,尽量不要使用
x18 平台寄存器,它的使用与平台相关,尽量不要使用
x19~x28 临时寄存器,子程序使用时必须保存
fp(x29) 帧指针寄存器,(Frame Pointer),用于保存栈帧地址(栈底指针),使用时必须保存
lr(x30) 链接寄存器,(Link Register),用于保存子程序的返回地址,也就是子程序结束后需要执行的下一条指令
sp 堆栈指针寄存器,(Stack Pointer),用于指向每个函数的栈顶(栈顶指针
pc 程序计数器,(Program Counter),俗称pc指针,总是指向即将要执行的下一条指令。在arm64中,软件是不能改写pc寄存器的
cpsr 程序状态寄存器,(Current Program Status Register)
spsr 程序状态寄存器,(Saved Program Status Register),异常状态下使用

常用指令

retreturn 的缩写。子程序返回,将 lr(x30)寄存器的值赋值给 pc 寄存器

ret

movmove 的缩写。将后面一个寄存器的值或立即数加载到前面一个寄存器

mov x0, #0x5
mov x1, x0

(lldb) register read x0
      x0 = 0x0000000000000005
(lldb) register read x1
      x1 = 0x0000000000000005

add:将后面两个寄存器的值相加的结果,加载到第一个寄存器

mov x0, #0x1
mov x1, #0x2
add x2, x0, x1

(lldb) register read x0
      x0 = 0x0000000000000001
(lldb) register read x1
      x1 = 0x0000000000000002
(lldb) register read x2
      x2 = 0x0000000000000003

subsubtract 的缩写。将后面两个寄存器相减的结果,加载到第一个寄存器

mov x0, #0x5
mov x1, #0x2
sub x2, x0, x1

(lldb) register read x0
      x0 = 0x0000000000000005
(lldb) register read x1
      x1 = 0x0000000000000002
(lldb) register read x2
      x2 = 0x0000000000000003

cmpcompare 的缩写。将两个寄存器相减,相减的结果会影响 cpsr 中条件标志位的值

mov x0, #0x3
mov x1, #0x1
cmp x0, x1

cpsr 结构:

cpsr 标志位含义:

b:不带返回的跳转指令。可以带条件跳转,带条件后一般跟 cmp 指令配合使用

;b指令带条件
mov x0, #0x1
mov x1, #0x2
cmp x0, x1
beq mycode1
mov x0, #0x3
mycode1:
mov x0, #0x4

;b指令不带条件
b mycode2
mov x0, #0x5
mycode2:
mov x0, #0x6

指令的条件码:

bl:带返回的跳转指令。有点类似函数调用,执行完子函数后会回到上一个函数。先将下一条指令的地址存储到 lr(x30)寄存器中,然后跳转到标记处开始执行代码。

mycode:
mov x0, #0x1
mov x1, #0x2
add x2, x0, x1
ret

_test:
bl mycode
mov x3, #0x3
mov x4, #0x4
ret

load:从内存中读取数据
ldr(立即数是正数用这个)、ldur(立即数是负数用这个)

;64bit是读取8字节,32bit是读取4字节
ldr x0, [x1] ;从x1寄存器读取数据到x0寄存器
ldr x0, [x1, #0x4] ;x1寄存器地址偏移4,再从偏移后的地址读取数据存到x0寄存器
ldr x0, [x1, #0x4]! ;x1寄存器地址偏移4,读取数据存储到x1寄存器,再存到x0寄存器
ldr x0, [x1], #0x4 ;x1寄存器读取数据到x0寄存器,再将x1寄存器地址偏移4,然后读取数据存储到x1寄存器
ldr x0, [x1, x2] ;x1寄存器地址偏移x2寄存器存储的值,然后读取数据存储到x0寄存器

ldp,p是pair的简称,翻译成一对。

ldp x0, x1, [x2, #0x10] ;x2寄存器地址偏移10,再从偏移后的地址读取数据存储到x0寄存器和x1寄存器

store:将数据写入到内存中
strstur

str x0, [x1] ;从x0寄存器读取数据,写入x1寄存器的地址中

stp

stp x0, x1, [x2] ;从x0寄存器和x1寄存器读取数据,写入x2寄存器的地址中

零寄存器,里面存储的值是0:wzr(32bit,Word Zero Register)、xzr(64bit)

函数的类型

叶子函数:函数里面没有再调用其他函数。

非叶子函数:函数里面还调用了其他函数。

简单调用汇编代码

arm.h

#ifndef arm_h
#define arm_h

int add(int a, int b);
int sub(int a, int b);

#endif /* arm_h */

arm.s

.text ;存放到代码段
.global _add, _sub ;公开函数访问权限

_add:
add x0, x0, x1 ;x0 x1是函数的参数,最终计算结果存入x0,并作为返回值返回
ret

_sub:
sub x0, x0, x1 ;x0 x1是函数的参数,最终计算结果存入x0,并作为返回值返回
ret

汇编代码里的单行注释用;,立即数的格式为#十六进制,比如#0x0