美文网首页
汇编(二)

汇编(二)

作者: 昵称是乱起的 | 来源:发表于2019-04-04 01:25 被阅读0次

    1. bl指令

    • CPU从何处执行指令是由pc中的内容决定的,我们可以通过改变pc的内容来控制CPU执行目标指令
    • ARM64提供了一个mov指令(传送指令),可以用来修改大部分寄存器的值,比如mov x0,#10、mov x1,#20,但是,mov指令不能用于设置pc的值,ARM64没有提供这样的功能
    • ARM64提供了另外的指令来修改PC的值,这些指令统称为转移指令,最简单的是bl指令

    2. bl指令练习

    2.1. 在xocode中新建一个汇编文件
    image.png

    汇编是.s结尾


    image.png
    2.2. 汇编代码

    这是arm64的汇编,一定要在5s以上的真机上运行

    .text                //存储在代码段
    .global _A,_B        //暴露A,B给外部调用
    
    _A:
        mov x0,#0xa      //x0=0xa
        mov x1,#0x00     //x1=0x00
        add x1,x0,#0xff  //x1=x0+0xff
        mov x0,x1        //x0=x1
        bl _B            //跳转_B
        mov x0,#0x00
        ret
    _B:
        add x0,x0,#0x10  //x0=x0+ 0x10
        ret
    

    汇编在Xcode中是会参与编译的


    image.png

    main.m中调用


    image.png

    这样打断点是断不住的

    image.png
    main.m中打断点。Debug->Debug Workflow->Always Show Disaseembly显示汇编
    image.png
    再运行,会断在这里
    image.png
    在lldb中s进入函数中
    image.png
    image.png
    选中这个可以看到寄存器相关的
    image.png
    n单步往下执行可以看到寄存器的值已经改变
    image.png
    image.png

    3. 函数调用栈

    3.1. 栈:是一种具有特殊的访问方式的存储空间(后进先出,LIFO)
    image.png
    3.1. SP和FP寄存器
    • sp寄存器在任意时刻会保存我们栈顶的地址.
    • fp寄存器也称为x29寄存器属于通用寄存器,但是在某些时刻我们利用它保存栈底的地址!
    3.2. 函数调用栈
    .text
    .global _A,_B
    
    _A:
        mov x0,#0xa
        mov x1,#0x00
        add x1,x0,#0xff
        mov x0,x1
        bl _B
        mov x0,#0x00
        ret
    _B:
        sub sp,sp,#0x20       //拉伸0x20(32字节)空间
        stp x0,x1,[sp,#0x10]  //将x0,x1写入sp+#0x10内存中
        ldp x1,x0,[sp,#0x10]  //将sp+#0x10这段内存的数据加载到x1,x0中
        add sp,sp,#0x20       //栈平衡
        ret
    

    断点查看,memory read sp,读取的32字节的数据

    image.png
    n走一步,拉伸栈空间,读取sp的值,sp的值已经改变,里面的数据可能是以前用过的,等会就会被覆盖
    image.png
    发现数据已经写入了,前面x0,后面x1
    image.png
    n下一步,x0,x1的值已经交换
    image.png
    栈空间平衡
    image.png

    4. bl和ret指令

    • bl标号
      将下一条指令的地址放入lr(x30)寄存器
      转到标号处执行指令
    • ret
      默认使用lr(x30)寄存器的值,通过底层指令提示CPU此处作为下条指令地址!
    • lr(x30)寄存器存放的是函数的返回地址.当ret指令执行时刻,会寻找x30寄存器保存的地址值!pc存储的是cpu将要执行的指令

    上面写的bl指令是有问题的,main函数中这样执行一下,发现没有打印B,


    image.png

    打断点进去汇编里面看看,注意这个lr和pc


    image
    bl进B函数后,发现lr的值是上面A中bl的下一条指令,pc是将要执行的下一条指令
    image.png

    继续ni,直到ret后回到A函数,发现lr和sp的值一样,由此可以发现bl指令之所以能够跳转回去,是lr记录了跳转之前的bl的下一句指令


    A
    继续执行ni,发现lr的值始终始终不变,,执行完ret后,pc的值又跟lr一样了,程序在这一直死循环了,原因是main函数bl进A的时候lr存的值被覆盖掉了,
    image.png
    至此得出结论,bl进新函数后,要保存下lr的值,bl其他函数回来后要把lr的值重新取一下,把代码改成这样
    .text
    .global _A,_B
    
    _A:
        mov x0,#0xa
        mov x1,#0x00
        add x1,x0,#0xff
        mov x0,x1
    //存储lr(x30)
        sub sp,sp,#0x10
        str x30,[sp]
    ////////////
        bl _B
        mov x0,#0x00
    //取出lr
        ldr x30,[sp]
        add sp,sp,#0x10
    ////////////
        ret
    _B:
        sub sp,sp,#0x20
        stp x0,x1,[sp,#0x10]  //将x0,x1写入sp+#0x10内存中
        ldp x1,x0,[sp,#0x10]  //将sp+#0x10这段内存的数据加载到x1,x0中
        add sp,sp,#0x20
        ret
    

    执行,输出AB了,问题解决


    image.png

    上面的存储和取出的代码还可以优化成1句,表达的意思都是一样的

    //存储lr(x30)
        sub sp,sp,#0x10
        str x30,[sp]
    可以改成
    str x30,[sp,#-0x10]!
    
    //取出lr
        ldr x30,[sp]
        add sp,sp,#0x10
    可以改成
    ldr x30,[sp],#0x10
    

    5. 函数的参数和返回值

    • ARM64下,函数的参数是存放在X0到X7(W0到W7)这8个寄存器里面的.如果超过8个参数,就会入栈,如果是浮点数就会用浮点寄存器。
    • 函数的返回值是放在X0 寄存器里面的.
    5.1. main函数中定义一个9个参数的函数,然后断点查看汇编
    image.png

    来分析一下栈内存


    image.png image.png

    将上面的栈结构画成这张图更直观


    image.png

    函数的返回值进入到test函数里面


    image.png
    5.1. main函数中定义一个返回结构体的函数来看返回值是操作的
    image.png

    main函数栈空间


    image.png

    getStr函数栈


    image.png

    6. 总结

    上面主要学习了bl指令和函数的堆栈,通过一些堆栈练习了解寄存器的存储,学习了函数的参数和返回值是如何进行操作。

    相关文章

      网友评论

          本文标题:汇编(二)

          本文链接:https://www.haomeiwen.com/subject/oqeabqtx.html