前言
最近准备学习汇编,然后在B站上看到叫iOS小贤的作者发的视频挺不错,打算跟着学,文章是看视频的笔记,最后有原视频链接,想看视频的可以看看通过链接查看视频。
函数的参数和返回值
- ARM64下,函数的参数通常情况下是存放在X0到X7(W0到W7)这8个寄存器里面的。如果超过8个参数,就会入栈。
- 编译器决定的函数的返回值通常都是放在x0中,之所以是通常是因为比如返回值是一个结构体,x0只能放64位即8个字节,这个时候放不下。
我们新写一个sum方法,然后打断点
mov w0, #0xa
mov w1, #0x14
这两句是把#0xa(10),#0x14(放入)w0(x0的低32位)和w1(x1的低32位)中。接下来bl 0x10273a94c
就进入sum函数。
sub sp, sp, #0x10
拉伸栈空间#0x10(16位)。
str w0, [sp, #0xc]
str w1, [sp, #0x8]
w0和w1的值放入了栈空间,位置是sp偏移#0xc,sp偏移#0x8。
ldr w0, [sp, #0xc]
ldr w1, [sp, #0x8]
把栈空间的值读出来放入寄存器w0,w1。看起来有点滑稽,但是在打包的时候编译器会做优化。
add w0, w0, w1
w0和w1的值相加然后放入w0。
add sp, sp, #0x10
ret
栈平衡并且return。
用汇编手写求和函数
在函数内部没有调用其他函数的函数叫做叶子函数。
叶子函数没有必要开辟栈空间,不移动sp的位置,直接通过sp的偏移,放入sp开始的低地址的区域中,因为接下来不会调用别的函数,就不会有其他函数干扰,所以没问题。而且调用完毕之后这块区域也不需要了,别的函数在用的时候,sp往低地址移动,开辟新的栈空间栈空间会先写再读,所以不会有影响。
这样我们可以写出精简版的sum函数的汇编代码
.text
.global _suma
_suma:
add x0, x0 , x1
ret
我们看到输出了正确结果30。
有人可能会问为何结果放入x0中,这是由编译器的,我们试试看放入x1会怎样,结果输出了10。这是因为把结果放入x1,而x0仍然第一个参数是10。
函数参数超过8个
sub sp, sp, #0x30
sp向低地址拉伸16*3=48个字节,如下图
stp x29, x30, [sp, #0x20]
sp偏移#0x20即向高地址出偏移2*16=32个字节,然后写入x29和x30,前面讲过读写是往高地址,于是之后的内存布局如下图:
add x29, sp, #0x20
sp加#0x20其实和上面[sp, #0x20]一样,然后赋值给x29。
stur wzr, [x29, #-0x4]
stur w0, [x29, #-0x8]
str x1, [sp, #0x10]
执行这几个命令之后内存图如下:
orr w0, wzr, #0x1
orr w1, wzr, #0x2
orr w2, wzr, #0x3
orr w3, wzr, #0x4
mov w4, #0x5
orr w5, wzr, #0x6
orr w6, wzr, #0x7
orr w7, wzr, #0x8
mov w8, #0x9
orr是或得意思,前面讲过ARM64中
- 64位: X0-X30, XZR(零寄存器)
- 32位: W0-W30, WZR(零寄存器)
所以是#0x1和0进行或运算,然后赋值给w0,相当于mov w0, #0x1
。
w8, [sp]
因为函数的参数通常情况下是存放在X0到X7(W0到W7)这8个寄存器里面的,所以w8不能参数,于是直接把w8放入栈中,也就是#0x9,就是参数9。
在调用sum函数
bl 0x1050028bc ; sum1 at main.m:16
之前,对栈的操作就是这么个过程。总结:
- 就是拉伸了栈空间
- 然后往栈中保护了两个寄存器x29,x30
- 接下来保护w0和x1寄存器
- 然后把参数放入w1到w7
- 最后多出来参数放入栈空间
然后进入sum函数
sub sp, sp, #0x30
又是拉伸栈空间,拉升了#0x30。
ldr w8, [sp, #0x30]
[sp, #0x30]是sp加上#0x30,就是刚刚sp的位置,然后向高地址处读取值放入w8中,就是刚刚放的参数,这里读出放入栈中的参数。
str w0, [sp, #0x2c]
str w1, [sp, #0x28]
str w2, [sp, #0x24]
str w3, [sp, #0x20]
str w4, [sp, #0x1c]
str w5, [sp, #0x18]
str w6, [sp, #0x14]
str w7, [sp, #0x10]
把w0到w7里的参数入栈。
ldr w0, [sp, #0x2c]
ldr w1, [sp, #0x28]
add w0, w0, w1
从栈中取出来,两两相加。
ldr w1, [sp, #0x24]
add w0, w0, w1
ldr w1, [sp, #0x20]
add w0, w0, w1
ldr w1, [sp, #0x1c]
add w0, w0, w1
ldr w1, [sp, #0x18]
add w0, w0, w1
ldr w1, [sp, #0x14]
add w0, w0, w1
ldr w1, [sp, #0x10]
add w0, w0, w1
ldr w1, [sp, #0x30]
add w0, w0, w1