汇编基础-探索函数本质

2021-03-26  本文已影响0人  spyn_n

初探

我们先看一下一个无参数无返回值的空函数,系统都干了啥。

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个栈的操作。

上一篇 下一篇

猜你喜欢

热点阅读