汇编基础-探索函数本质
初探
我们先看一下一个无参数无返回值的空函数,系统都干了啥。
void emptFun(){
}
函数汇编
0x100be9f20<+64>: bl 0x100be9cec ; emptFun at ViewController.m:18:1
001--Demo`emptFun:
-> 0x100be9cec<+0>: ret
可以发现,emptyFun函数在viewController的位置,通过bl跳转指令跳到0x100be9cec函数入口地址执行,从汇编可以看出,emptyFun函数就只有一个ret返回指令。那它是怎么返回viewController并继续执行后面的代码的呢:
可以看到在执行bl跳转指令的时候,系统会把下一条指令的地址放入lr(X30寄存器),当emptyFun函数执行完执行ret指令的时候,系统从lr寄存器中读取地址,并继续执行相关的指令。
带参数、带返回值函数
我们把函数参数个数放得多一点
int test(int a, int b, int c, int d, int e, int f, int g, int h, int I, int j, int k){
returna + b + c + d + e + f + g + h + i + j + k;
}
运行调试我们看一下汇编:
0x1005d9f24<+64>: orr w0, wzr,#0x1
0x1005d9f28<+68>: orr w1, wzr,#0x2
0x1005d9f2c<+72>: orr w2, wzr,#0x3
0x1005d9f30<+76>: orr w3, wzr,#0x4
0x1005d9f34<+80>: mov w4,#0x5
0x1005d9f38<+84>: orr w5, wzr,#0x6
0x1005d9f3c<+88>: orr w6, wzr,#0x7
0x1005d9f40<+92>: orr w7, wzr,#0x8
0x1005d9f44<+96>: mov w10,#0x9
0x1005d9f48<+100>: str w10, [sp]
0x1005d9f4c<+104>: mov w10,#0xa
0x1005d9f50<+108>: str w10, [sp,#0x4]
0x1005d9f54<+112>: mov w10,#0xb
0x1005d9f58<+116>: str w10, [sp,#0x8]
0x1005d9f5c<+120>: bl 0x1005d9d20 ; test at ViewController.m:24
0x1005d9f60<+124>: str w0, [sp,#0xc]
0x1005d9f64<+128>: ldp x29, x30, [sp,#0x30]
常数#0x1,#0x2,#0x3,#0x4,#0x5,#0x6,#0x7,#0x8,都是放入通用寄存器的低32位W0~W7,但是#0x9、#0xa、#0xb,会从栈顶sp开始每隔4个字节依次入栈。然后bl跳转到test函数
0x1005d9f5c<+120>: bl 0x1005d9d20 ; test at ViewController.m:24
进入test函数的汇编代码:
如图,调用函数开辟(拉伸)了24个字节的栈空间,但是我们发现,框框起来的汇编代码,好像操作的是这个函数开辟的栈外面的空间,很明显,[sp, #0x38]大于0x30大小,是上一个函数的栈,原来,在上一个函数,系统将超过8个的参数,放入了栈顶提供给test函数,也就是说:函数参数超过8个的时候,超过的部分就会通过函数栈来传递值。 没有超过8个的参数1、2、 3、4、5、6、7、8是从寄存器中读取。
后面的就简单了,通过栈顶sp指针以及偏移取一个相加放到X0寄存器。
最后add sp, sp,#0x30 栈平衡,ret
返回控制器中,我们可以打印w0的值就是函数返回结果;
这里特别说明一下,基本上一个函数的调用汇编里面,在ret指令之前基本都会有:
0x1005d9f64<+128>: ldp x29, x30, [sp,#0x30]
0x1005d9f68 <+132>: add sp, sp,#0x40 ; =0x40 栈平衡,开辟多少就释放所少
0x1005d9f6c <+136>: ret
ldp x29, x30, [sp,#0x30],这句就是现场还原/恢复,因为前面进行了stp x29, x30, [sp,#0x30]这一句,空了8个字节保护X29,X30寄存器,为的是在函数嵌套调用里面能够找到回去的路,不至于死循环。当然也不是所有的函数调用都需要现场保护,如叶子函数。
所以如果从效率的方向考虑,函数的参数,最好不要超过8个,除去两个隐式参数,剩下6个。因为从上面的阐述可以知道,如果超过8个,需要处理到2个栈的操作。