iOS彙編研究-2-寄存器 一、寄存器 二、通用寄存器 三、浮點數寄存器 四、向量寄存器 五、寄存器相關lldb命令

一、寄存器

        對於日常使用高級語言的程序員來說,其看到的是連續的主存(內存)空間,使用的也是主存儲器(簡稱內存)的空間。但是對於使用匯編的程序原來說,其面對的主要是寄存器。事實上,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)級別的優化,在參數太多的情況下會通過內存來傳遞。
        總的來說,前幾個寄存器一般用於傳遞函數的參數



2.2 x0寄存器

        對於x0(w0)寄存器來說,其還有一個特殊用途,就是用來存儲函數的返回值,如下圖所示。



        圖中左邊是一個返回10的函數,右邊是對應的彙編代碼,可見直接把16進制的10複製到w0寄存器來實現傳遞返回值。

2.3 fp/x29

        fp(frame pointer)寄存器是x29寄存器的簡稱,用於存儲函數調用堆棧的棧底信息。


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    

        在上面的截圖中,函數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寄存器所指的內容就會被當做指令。


2.7 cpsr/x33

        目前,上面介紹的寄存器都是作爲一個整體來使用的,但是cpsr寄存器是每個位都單獨來使用的。其標識當前程序所處的狀態,目前暫未涉及,先不做深入介紹,如果想要深入理解的話,可以自行百度。


三、浮點數寄存器

        爲了加速浮點數的處理,現在arm提供專用的浮點數寄存器。64位的浮點數寄存器是V0-V31、spsr、spcr。


四、向量寄存器

        爲了支持遊戲等用到向量計算比較多的情況,CPU針對向量計算,提供了向量寄存器。


五、寄存器相關lldb命令

  • register read 讀取所有通用寄存器
  • register read x0 讀取某個寄存器
  • register write x0 0x0001 向寄存器寫入值

注意

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章