基础指令
通过一个简单的函数来解释指令:
sub sp, sp, #0x10 ; =0x10 //减 sp=sp-0x10,开辟0x10空间的函数调用栈
mov w8, #0xa //mov移动,相当于赋值w8=0xa
str w8, [sp, #0xc] //str将数据从寄存器中读出来,存到内存中.把w8中的值0xa读出来,放到[sp #0xc]这块内存中
mov w8, #0x5 //又给w8赋值w8=0x5。
str w8, [sp, #0x8] //把w8中的值0x5读出来,放到[sp #0x8]这块内存中
ldr w8, [sp, #0xc] //将数据从内存中读出来,存到寄存器中。w8=[sp #0xc]这块内存中的数据,也就是我们刚才存的0xa,w8=0xa
ldr w9, [sp, #0x8] //w9=0x5。此ldr 和 str 的变种ldp 和 stp 还可以操作2个寄存器.
add w8, w8, w9 //加 w8=w8+w9=0xa+0x5=0xf
str w8, [sp, #0x4] //把w8的值存到[sp, #0x4]内存中
add sp, sp, #0x10 ; =0x10 //函数调用结束,恢复栈平衡 sp=sp+0x10
ret //返回
函数调用栈
每一个函数的执行,都需要开辟一块栈空间,当函数执行完毕时,释放掉栈空间。注意:ARM64里面 对栈的操作是16字节对齐的! 函数嵌套使用时,会将x29(sp)、x30(lr)寄存器入栈保护。
下面是常见的函数调用开辟和恢复的栈空间
sub sp, sp, #0x40 ; 拉伸0x40(64字节)空间
stp x29, x30, [sp, #0x30] ;x29\x30 寄存器入栈保护
add x29, sp, #0x30 ; x29指向栈帧的底部
...
ldp x29, x30, [sp, #0x30] ;恢复x29/x30 寄存器的值
add sp, sp, #0x40 ; 栈平衡
ret
bl和ret指令
bl:跳转指令,把下面的指令地址放到lr(x30)寄存器中,跳转到目标位置执行
ret:默认使用lr(x30)寄存器的值作为下一条指令的地址,所以一般函数都会把lr保存到当前的函数栈中,返回时需要用到。
函数的参数和返回值
ZZZ`main:
0x100959924 <+0>: sub sp, sp, #0x40 ;
0x100959928 <+4>: stp x29, x30, [sp, #0x30]
0x10095992c <+8>: add x29, sp, #0x30 ; =0x30
0x100959930 <+12>: stur wzr, [x29, #-0x4]
0x100959934 <+16>: stur w0, [x29, #-0x8]
0x100959938 <+20>: stur x1, [x29, #-0x10]
0x10095993c <+24>: mov w0, #0xa //w0=0xa
0x100959940 <+28>: mov w1, #0x19 //w1=0x19
-> 0x100959944 <+32>: bl 0x1009598f4 ; sum at main.m:11,跳转到0x1009598f4处执行,lr=0x100959948
0x100959948 <+36>: stur w0, [x29, #-0x14]
0x10095994c <+40>: ldur w0, [x29, #-0x8]
ZZZ`sum:
-> 0x1009598f4 <+0>: sub sp, sp, #0x10 ; =0x10
0x1009598f8 <+4>: str w0, [sp, #0xc] //w0的值0xa保存到内存[sp, #0xc]
0x1009598fc <+8>: str w1, [sp, #0x8] //w1的值0x19保存到内存[sp, #0x8]
0x100959900 <+12>: mov w8, #0x5 //w8=0x5
0x100959904 <+16>: str w8, [sp, #0x4] //w8的值0x5保存到内存[sp, #0x4]
0x100959908 <+20>: ldr w8, [sp, #0xc] //w8取出[sp, #0xc]上的值w8=0xa
0x10095990c <+24>: ldr w9, [sp, #0x8] //w9=0x19
0x100959910 <+28>: add w8, w8, w9 //w8=w8+w9=0xa+0x19=0x1d
0x100959914 <+32>: ldr w9, [sp, #0x4] //w9取出[sp, #0x4]上的值w9=0x5
0x100959918 <+36>: add w0, w8, w9 //w0=w8+w9=0x1d+0x5
0x10095991c <+40>: add sp, sp, #0x10 ; =0x10
0x100959920 <+44>: ret
从上面的汇编代码中可以看出:ARM64下,函数的参数是存放在X0到X7(W0到W7)这8个寄存器里面的.如果超过8个参数,就会入栈,存放在上一个函数的栈顶。我们平时的OC方法中,也要尽量少与6个参数,如果超过6个,最好使用集合。
函数的返回值是放在X0寄存器里面的。函数的局部变量放在栈里面!
状态寄存器 CPSR
CPU内部的寄存器中,有一种特殊的寄存器CPSR,其他寄存器是用来存放数据的,而CPSR寄存器是按位起作用的,也就是说,它的每一位都有专门的含义,记录特定的信息.注:CPSR寄存器是32位的。最高的4位中,记录着逻辑运算的结果。
31位 N 符号标志位:结果为负N=1,非负N=0
30位 Z 零标志位:结果为零Z=1,非零Z=0
29位 C 进位标志位:无符号数运算,假设一个更高位
加法运算,更高位=1,溢出
减法运算,更高位=0,溢出
28位 V 溢出标志位:有符号数运算的时候:
正数 + 正数 为负数 溢出
负数 + 负数 为正数 溢出
正数 + 负数 不可能溢出