美文网首页
iOS汇编研究-2-寄存器

iOS汇编研究-2-寄存器

作者: 行知路 | 来源:发表于2021-05-17 21:10 被阅读0次

    一、寄存器

            对于日常使用高级语言的程序员来说,其看到的是连续的主存(内存)空间,使用的也是主存储器(简称内存)的空间。但是对于使用汇编的程序原来说,其面对的主要是寄存器。事实上,CPU内部也是分为运算器、控制器、寄存器、多级缓存。对于基于汇编语言的程序开发来说,寄存器是必须了解的一个概念。


    CPU的多级缓存

    二、通用寄存器

            通用原来寄存器命名是r[0]、r[1]、r[2]、r[3]......,但是随着armv8,也就是64位的处理器推出,寄存器命名修改为x[n],但是为了保持兼容,x[n]的低32位被命名为w[n]


    寄存器

            通用寄存器从名字上看就是做一些通用得工作的寄存器,通用工作有哪些呢?一般就是存储内存的地址、各种计算所需的临时数据、堆栈地址等。

    2.0 栈概述

            由于寄存器与栈这种数据结构有密切的关系,并且在后面也会详细讲到函数调用堆栈,为使后面顺利讲述,在这里先概要介绍一下栈。
            栈是一种先进后出的数据结构。在日常生活中能够见到的典型栈类型的东西就是——箱子,先装进箱子的东西只能后拿出来,而后装进去的东西则可以先拿出来。


    栈示意

    注意
    在ARM64汇编中,栈是从高地址向低地址生长的、先开劈栈空间然后再使用。这句话可能暂时不理解,但是一定请先记住。

    _addwwj:                                ; @addwwj
    Lfunc_begin0:
        sub sp, sp, #16                     ; 开辟栈空间
        str w0, [sp, #12]
        str w1, [sp, #8]
    Ltmp1:
        ldr w8, [sp, #12]
        ldr w9, [sp, #8]
        add w0, w8, w9
        add sp, sp, #16                     ; 回收栈空间
        ret
    Ltmp2:
    Lfunc_end0:
    

            在上面汇编代码中(sub sp, sp, #16),sub是减的意思,由于栈空间是向低地址增长的,那么所以在当前栈底的基础之上再扩展16个字节的空间。
            在上面汇编代码中(add sp, sp, #16),把栈底又加16个字节,所以栈又恢复到初始调用此函数的状态。

    2.1 传参寄存器

            有人说xo-x7,这8个寄存器一般用来存储函数的入参,多于8个的参数会存放在内存的之中。但是经过我的测试,发现其与优化等级相关,如果不优化(o0)的情况下,多达18个参数的情况下依然会通过寄存器来传参,用于测试的电脑是:MacBook Pro (13-inch, M1, 2020)。如果选择(o3)级别的优化,在参数太多的情况下会通过内存来传递。
            总的来说,前几个寄存器一般用于传递函数的参数

    一个参数的函数寄存器状态
    一个需要18个参数的函数
    此时的寄存器状态

    2.2 x0寄存器

            对于x0(w0)寄存器来说,其还有一个特殊用途,就是用来存储函数的返回值,如下图所示。


    w0用于存储函数返回值

            图中左边是一个返回10的函数,右边是对应的汇编代码,可见直接把16进制的10复制到w0寄存器来实现传递返回值。

    2.3 fp/x29

            fp(frame pointer)寄存器是x29寄存器的简称,用于存储函数调用堆栈的栈底信息。


    fp寄存器

    2.4 lr/x30

            lr寄存器用来保存函数返回的路。

    int aTestFun(int para1) {
        return para1 + 3;
    }
    
    int theOtherTestFun(int para1) {
        return para1 + aTestFun(para1);
    }
    
    ;theOtherTestFun 对应的汇编代码如下
    0x100003f20 <+0>:  sub    sp, sp, #0x20             ; =0x20 
        0x100003f24 <+4>:  stp    x29, x30, [sp, #0x10]
        0x100003f28 <+8>:  add    x29, sp, #0x10            ; =0x10 
        0x100003f2c <+12>: stur   w0, [x29, #-0x4]
    ->  0x100003f30 <+16>: ldur   w8, [x29, #-0x4]
        0x100003f34 <+20>: ldur   w0, [x29, #-0x4]
        0x100003f38 <+24>: str    w8, [sp, #0x8]
        0x100003f3c <+28>: bl     0x100003f08               ; aTestFun at CImp.c:10 这里是调用 aTestFun函数,进入此函数之后,lr的地址是0x100003f40
        0x100003f40 <+32>: ldr    w8, [sp, #0x8]             ; 就是这个地址
        0x100003f44 <+36>: add    w0, w8, w0
        0x100003f48 <+40>: ldp    x29, x30, [sp, #0x10]
        0x100003f4c <+44>: add    sp, sp, #0x20             ; =0x20 
        0x100003f50 <+48>: ret    
    
    lr寄存器的作用

            在上面的截图中,函数theOtherTestFun调用aTestFun函数,当从aTestFun函数返回是需要执行theOtherTestFun中调用aTestFun函数的下一行。

    0x100003f3c <+28>: bl     0x100003f08               ; aTestFun at CImp.c:10 实现调用aTestFun函数
    0x100003f40 <+32>: ldr    w8, [sp, #0x8]             ; aTestFun返回后,需要执行的下语句汇编代码
    ;从上面的截图来看,当前lr地址就是上面汇编指令的地址
    

    2.5 sp/x31

            sp(stack pointer)寄存器是寄存器x31的简称,用于存储函数调用堆栈的栈顶信息。

    int need1Parameter(int para1) {
        return 10;
    }
    
        ; 这里就是上述 need1Parameter 函数的汇编代码
    
        0x100003dac <+0>:  sub    sp, sp, #0x10             ; =0x10   此时移动栈顶空间,为need1Parameter函数开辟堆栈空间
        0x100003db0 <+4>:  str    w0, [sp, #0xc]
        0x100003db4 <+8>:  mov    w0, #0xa
    ->  0x100003db8 <+12>: add    sp, sp, #0x10             ; =0x10  need1Parameter函数执行完毕,回收刚才开辟的堆栈空间
        0x100003dbc <+16>: ret  
    

    2.6 pc/x32

            所有的数据、指令、常量等,在内存中,都是01001。那么,cpu怎么知道哪段01序列是命令,需要CPU来执行呢?其实很简单,pc寄存器所指的内容就会被当做指令。


    pc

    2.7 cpsr/x33

            目前,上面介绍的寄存器都是作为一个整体来使用的,但是cpsr寄存器是每个位都单独来使用的。其标识当前程序所处的状态,目前暂未涉及,先不做深入介绍,如果想要深入理解的话,可以自行百度。


    cpsr.jpeg

    三、浮点数寄存器

            为了加速浮点数的处理,现在arm提供专用的浮点数寄存器。64位的浮点数寄存器是V0-V31、spsr、spcr。


    浮点数寄存器

    四、向量寄存器

            为了支持游戏等用到向量计算比较多的情况,CPU针对向量计算,提供了向量寄存器。


    向量寄存器

    五、寄存器相关lldb命令

    • register read 读取所有通用寄存器
    • register read x0 读取某个寄存器
    • register write x0 0x0001 向寄存器写入值

    注意

    例如,pc寄存器是不能直接写入值得。

    相关文章

      网友评论

          本文标题:iOS汇编研究-2-寄存器

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