1、函数的类型

叶子函数:函数内部没有调用其他函数:

  1. void test1() {
  2. int a = 2;
  3. int b = 3;
  4. }

非叶子函数:函数内部嵌套了其他函数:

  1. void test2() {
  2. int a = 4;
  3. int b = 5;
  4. test1();
  5. }

2、函数的堆栈

2.1、叶子函数

  1. sub sp, sp, #16
  2. mov w8, #2
  3. str w8, [sp, #12]
  4. mov w8, #3
  5. str w8, [sp, #8]
  6. add sp, sp, #16
  7. ret

image.png

代码分析:(假设执行test1函数前sp指针的值是0x10010) sub sp, sp, #16 ; sp = sp - 16,sp的地址值更新为0x10001,相当于开辟了16个字节的内存 mov w8, #2 ; 将2赋值给w8寄存器 str w8, [sp, #12] ; 将2存储到sp + 12地址中,也就是0x1000c开始的4个字节 mov w8, #3 ; 将3赋值给w8寄存器 str w8, [sp, #8] ; 将3存储到sp + 8地址中,也就是0x10008开始的4个字节 add sp, sp, #16 ; sp = sp + 16,sp的的值更新为0x10010,相当于释放16个字节的内存 ret ; 返回,将lr赋值给pc

2.2、非叶子函数

  1. sub sp, sp, #32
  2. stp x29, x30, [sp, #16]
  3. add x29, sp, #16
  4. mov w8, #4
  5. stur w8, [x29, #-4]
  6. mov w8, #5
  7. str w8, [sp, #8]
  8. bl _test1
  9. ldp x29, x30, [sp, #16]
  10. add sp, sp, #32
  11. ret

image.png

代码分析:(假设执行test2函数前sp指针的值是0x10021) sub sp, sp, #32 ; sp = sp - 32,sp的地址值更新为0x10001,相当于开辟了32个字节的内存 stp x29, x30, [sp, #16] ; 将fp和lr存放到sp + 16开始的地址中,(保存lr是因为bl指令会更新lr) add x29, sp, #16 ; fp = sp + 16,sp和fp中间的16个字节空间就是函数留给局部变量使用的空间 mov w8, #4 ; 将4赋值给w8寄存器 stur w8, [x29, #-4] ; 将4保存到fp - 4, mov w8, #5 ; 将5赋值给w8寄存器 str w8, [sp, #8] ; 将5赋值给sp + 8 bl _test1 ; 跳转到test1函数,更新sp再复位sp ldp x29, x30, [sp, #16] ; 读取sp+16,将之前保存的fp和lr数据重新负值给fp和lr add sp, sp, #32 ; sp = sp + 32,恢复sp的位置,恢复堆栈平衡 ret ; 返回,将lr赋值给pc