汇编基础(四)函数&返回值

2018-04-23  本文已影响78人  struggle3g

bl和ret指令

bl标号

注意:当我们遇到bl指令的时候,是跳转指令,跳转之后我们是怎么回来的?怎么回到bl下面的指令?遇到bl后,CPU会将下一个指令的内存地址存到lr寄存器当中。
验证:

ret
ARM64平台的特色指令,它面向硬件做了优化处理apple文档写的

练习题:

.text
.global _Test1,_Test2
_Test1:
    mov x0,#0xaaaa
    mov x0,#0xcccc
    bl _Test2
    mov x0,#0xdddd
    ret

_Test2:
    mov x0,#0xbbbb
    mov x0,#0xffff
    ret

.text
.global _Test1,_Test2

_Test1:
    mov x0,#0xaaaa
    str x30,[sp,#-0x10]!
    bl _Test2
    mov x0,#0xcccc
    ldr x30,[sp],#0x10
    ret

_Test2:
    mov x0,#0xbbbb
    ret
答案

我们平时写的函数转换成汇编会变成什么?

int sum(int a, int b){
    return a + b;
}
int main (){
    sum(10,20);
    return 0;
}

汇编中

->  0x1049ba8ac <+0>:  sub    sp, sp, #0x10             ; =0x10 
    0x1049ba8b0 <+4>:  str    w0, [sp, #0xc]
    0x1049ba8b4 <+8>:  str    w1, [sp, #0x8]
    0x1049ba8b8 <+12>: ldr    w0, [sp, #0xc]
    0x1049ba8bc <+16>: ldr    w1, [sp, #0x8]
    0x1049ba8c0 <+20>: add    w0, w0, w1
    0x1049ba8c4 <+24>: add    sp, sp, #0x10             ; =0x10 
    0x1049ba8c8 <+28>: ret  

解读

练习题,自己写一个sum函数

int sumA(int a, int b);

int main(int argc, char * argv[]) {

    int b = sumA(10, 20);
    printf("%d",b);
    return 0;
  
}
.text
.global _sumA

_sumA:
    add x0,x0,x1
    ret
.text
.global _sumA

_sumA:
    add x1,x0,x1
    ret

验证
返回x0寄存器是正确的值,返回x1寄存器的值是10
那么:

通常情况下函数的返回值通常都放在x0 里面!!ARM64下,函数的参数是存放在X0到X7(W0到W7)这8个寄存器里面的.如果超过8个参数,就会入栈.

代码验证:

int map(int a, int b,int c, int d, int e,int f,int g, int h, int i,int j){
    
    return a+b+c+d+e+f+g+h+j+i+j;
}
int main(int argc, char * argv[]) {
    map(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    return 0;
}

汇编代码

0x1005c289c <+0>: sub sp, sp, #0x30 ; =0x30
0x1005c28a0 <+4>: stp x29, x30, [sp, #0x20]
0x1005c28a4 <+8>: add x29, sp, #0x20 ; =0x20
0x1005c28a8 <+12>: orr w8, wzr, #0x1
0x1005c28ac <+16>: orr w9, wzr, #0x2
0x1005c28b0 <+20>: orr w2, wzr, #0x3
0x1005c28b4 <+24>: orr w3, wzr, #0x4
0x1005c28b8 <+28>: mov w4, #0x5
0x1005c28bc <+32>: orr w5, wzr, #0x6
0x1005c28c0 <+36>: orr w6, wzr, #0x7
0x1005c28c4 <+40>: orr w7, wzr, #0x8
0x1005c28c8 <+44>: mov w10, #0x9
0x1005c28cc <+48>: mov w11, #0xa
0x1005c28d0 <+52>: stur wzr, [x29, #-0x4]
0x1005c28d4 <+56>: stur w0, [x29, #-0x8]
0x1005c28d8 <+60>: str x1, [sp, #0x10]
0x1005c28dc <+64>: mov x0, x8
0x1005c28e0 <+68>: mov x1, x9
0x1005c28e4 <+72>: str w10, [sp]
0x1005c28e8 <+76>: str w11, [sp, #0x4]
0x1005c28ec <+80>: bl 0x1005c280c ; map at main.m:21 bl之前是当前的准备工作,开辟栈空间,设置局部变量
0x1005c28f0 <+84>: mov w8, #0x0
0x1005c28f4 <+88>: str w0, [sp, #0xc]
0x1005c28f8 <+92>: mov x0, x8
0x1005c28fc <+96>: ldp x29, x30, [sp, #0x20]
0x1005c2900 <+100>: add sp, sp, #0x30 ; =0x30
0x1005c2904 <+104>: ret

代码分析:

0x1005c280c <+0>:   sub    sp, sp, #0x30             ; =0x30 
    0x1005c2810 <+4>:   ldr    w8, [sp, #0x34]
    0x1005c2814 <+8>:   ldr    w9, [sp, #0x30]
    0x1005c2818 <+12>:  str    w0, [sp, #0x2c]
    0x1005c281c <+16>:  str    w1, [sp, #0x28]
    0x1005c2820 <+20>:  str    w2, [sp, #0x24]
    0x1005c2824 <+24>:  str    w3, [sp, #0x20]
    0x1005c2828 <+28>:  str    w4, [sp, #0x1c]
    0x1005c282c <+32>:  str    w5, [sp, #0x18]
    0x1005c2830 <+36>:  str    w6, [sp, #0x14]
    0x1005c2834 <+40>:  str    w7, [sp, #0x10]
    0x1005c2838 <+44>:  str    w9, [sp, #0xc]
    0x1005c283c <+48>:  str    w8, [sp, #0x8]
    0x1005c2840 <+52>:  ldr    w8, [sp, #0x2c]
    0x1005c2844 <+56>:  ldr    w9, [sp, #0x28]
    0x1005c2848 <+60>:  add    w8, w8, w9
    0x1005c284c <+64>:  ldr    w9, [sp, #0x24]
    0x1005c2850 <+68>:  add    w8, w8, w9
    0x1005c2854 <+72>:  ldr    w9, [sp, #0x20]
    0x1005c2858 <+76>:  add    w8, w8, w9
    0x1005c285c <+80>:  ldr    w9, [sp, #0x1c]
    0x1005c2860 <+84>:  add    w8, w8, w9
    0x1005c2864 <+88>:  ldr    w9, [sp, #0x18]
    0x1005c2868 <+92>:  add    w8, w8, w9
    0x1005c286c <+96>:  ldr    w9, [sp, #0x14]
    0x1005c2870 <+100>: add    w8, w8, w9
    0x1005c2874 <+104>: ldr    w9, [sp, #0x10]
    0x1005c2878 <+108>: add    w8, w8, w9
    0x1005c287c <+112>: ldr    w9, [sp, #0x8]
    0x1005c2880 <+116>: add    w8, w8, w9
    0x1005c2884 <+120>: ldr    w9, [sp, #0xc]
    0x1005c2888 <+124>: add    w8, w8, w9
    0x1005c288c <+128>: ldr    w9, [sp, #0x8]
    0x1005c2890 <+132>: add    w0, w8, w9
    0x1005c2894 <+136>: add    sp, sp, #0x30             ; =0x30 
    0x1005c2898 <+140>: ret    

这代码我就不分析,可以根据上面的分析进行类推,

练习:

int sum(int a, int b){
    int c = 6;
    int d = 0; 
    return  a + b + c;
}

int main(int argc, char * argv[]) {
    sum(10,20);
    return 0;
}

汇编分配的栈空间

  sub  sp,sp,#0x10
int sum(int a, int b){
    int c = 6;
    int d = 0; 
    int f = 1;  
    return  a + b + c;
}

int main(int argc, char * argv[]) {
    sum(10,20);
    return 0;
}

汇编分配的栈空间

  sub  sp,sp,#0x20

结论
这个应该可以理解哦 ,因为ARM64是16位每次开辟空间都是16为基准,只能是16的倍数, int = 4字节,4* 4正好 4*5 = 20 需要再开辟一个16位的空间 也就是 sp + 0x20

练习

int sum(int a, int b){
    int c = 6;
    printf("%d",c);
    return  a + b + c;
}

int main(int argc, char * argv[]) {
    sum(10,20);
    return 0;
}

汇编分配的栈空间

  sub  sp,sp,#0x30

为什么会多呐? 因为嵌套函数必须保护寄存器x30,lr 回家的路

练习

int sum(int a, int b){
    
    return  a + b ;
}


int funcA(int a, int b){
    
    int d = sum(a, b);
    int e = sum(a, b);
    return e;
    
}
  0x102e62880 <+0>:  sub    sp, sp, #0x20             ; =0x20 
    0x102e62884 <+4>:  stp    x29, x30, [sp, #0x10]
    0x102e62888 <+8>:  add    x29, sp, #0x10            ; =0x10 
    0x102e6288c <+12>: stur   w0, [x29, #-0x4]
    0x102e62890 <+16>: str    w1, [sp, #0x8]
    0x102e62894 <+20>: ldur   w0, [x29, #-0x4]
    0x102e62898 <+24>: ldr    w1, [sp, #0x8]
    0x102e6289c <+28>: bl     0x102e62860               ; sum at main.m:14
    0x102e628a0 <+32>: str    w0, [sp, #0x4]
    0x102e628a4 <+36>: ldur   w0, [x29, #-0x4]
    0x102e628a8 <+40>: ldr    w1, [sp, #0x8]
    0x102e628ac <+44>: bl     0x102e62860               ; sum at main.m:14
    0x102e628b0 <+48>: str    w0, [sp]
    0x102e628b4 <+52>: ldr    w0, [sp]
    0x102e628b8 <+56>: ldp    x29, x30, [sp, #0x10]
    0x102e628bc <+60>: add    sp, sp, #0x20             ; =0x20 
    0x102e628c0 <+64>: ret    

分析

 - 开辟栈空间
- 保护回家的路
- 保存栈底
- 保存w0
- 保存w1
- 读取 w0
- 读取w1
- sum跳转
- 保存w0的值
- 读取a值到x0
- 读取b值到x1
- sum跳转
- 保存w0的的值搭配 栈
- 读取栈内存的值到w0
- 恢复x30的值 准备回家
- 平衡栈空间
- 回家

总结

bl指令: 跳转 将下一条执行的指令放入lr(x30)寄存器
ret指令:返回lr寄存器所保存的地址,执行代码
pc 寄存器指向马上要执行的代码地址
sp 指向了栈
函数调用会开辟一个栈空间,

函数中的参数放在哪里:
ARM64每一个寄存器都是64位的

代码只有一份,内存不只一份
函数嵌套调用:
| - A(开辟)-->B(开辟)--->A(开辟)
| - A<-->A 死的溢出 递归

扩展 小知识

[self viewDidLoad];
OC方法的调用 本质上是不是调用msgSend()
msgSend(self,@selector(viewDidLoad));
x0 x1 寄存器!!  
上一篇 下一篇

猜你喜欢

热点阅读