匯編基本知識
高級語言可以通過編譯得到匯編語言,匯編語言再編譯成機器語言,機器語言也可以反匯編成匯編語言。每一條機器指令都有與之對應的匯編指令。在匯編中,大部分指令都是與CPU/內(nèi)存相關(guān)的。
內(nèi)存中除了指令,還有數(shù)據(jù),但是都是0和1組合,CPU是如何區(qū)分的?是通過CPU上的部件PC寄存器來區(qū)分。因為在內(nèi)存里數(shù)據(jù)和指令本質(zhì)沒有區(qū)別,那么要加以區(qū)分就是看是否被PC指向過,CPU將pc指向的內(nèi)存單元的內(nèi)容看做指令。
CPU會先將內(nèi)存中的數(shù)據(jù)存儲到通用寄存器中,然后再對寄存器中的數(shù)據(jù)進行運算
寄存器
針對arm64的CPU來說,
如果寄存器以x開頭,則表明是一個64位的寄存器
如果寄存器以w開頭,則表明是一個32位的寄存器
32位的寄存器是64位寄存器的低32位部分,并不是獨立存在的
ARM64擁有有31個64位的通用寄存器 x0 到 x30,通常用來做數(shù)據(jù)計算的臨時存儲、累加、計數(shù)、地址保存等功能,和其他三個SP、PC、CPSR寄存器。 因為64位CPU可以兼容32位.所以可以只使用64位寄存器的低32位.w0 就是 x0的低32位。
FP(x29):保存棧幀地址,指向方法棧的底部
LR(x30):通常稱X30為程序鏈接寄存器,保存子程序結(jié)束后需要執(zhí)行的下一條指令
SP :保存棧指針,指向方法棧的頂部,使用 SP/WSP來進行對SP寄存器的訪問。
-
X0 - X7:這 8 個寄存器主要用來存儲傳遞參數(shù)。如果參數(shù)超過 8 個,則會通過棧來傳遞;X0 也用來存放上文方法的返回值; -
X29:即我們通常所說的幀指針FP(Frame Pointer),指向當前方法棧的底部(高地址)。幀指針FP保存的為“上一棧幀地址+返回地址”,其中返回地址就是調(diào)用者執(zhí)行調(diào)用函數(shù)后的下一條指令的地址。 -
X30:即鏈接寄存器 LR(Link Register)。為什么叫做鏈接,是因為這個寄存器會記錄著當前方法的調(diào)用方地址,即當前方法調(diào)用完成時應該返回的位置。例如我們遇到 Crash 要獲取方法堆棧,其本質(zhì)就是不斷的向上遞歸每一個 X30 寄存器的記錄狀態(tài)(也就是棧上 X30 寄存器的內(nèi)容)來找到上層調(diào)用方。
除了這些通用寄存器,還有一個最重要的 SP 寄存器: -
SP 寄存器:即我們通常說的棧指針 SP(Stack Pointer)。指向當前方法棧的頂部(低地址),與通用寄存器低 32 位的訪問方法一樣,你也可以通過 WSP 來訪問 SP 的低 32 位。當 SP 移動到棧區(qū)的最低位置(接近于堆區(qū)),則稱之為”爆棧“。 -
PC(Program Counter):程序計數(shù)器,俗稱PC指針,總是指向即將要執(zhí)行的指令,在arm64中,軟件是不能改寫PC寄存器的。因為在內(nèi)存里數(shù)據(jù)和指令本質(zhì)沒有區(qū)別,那么要加以區(qū)分就是看是否被PC指向過,CPU將pc指向的內(nèi)存單元的內(nèi)容看做指令。mov指令不能更改PC寄存器的值,需要通過bl跳轉(zhuǎn)指令來改變PC寄存器的值。 -
CRSP: 狀態(tài)寄存器 - 浮點和向量寄存器
可以通過register read 寄存器 來打印寄存器地址。
大部分操作系統(tǒng)棧的增長方向都是從上往下(包括iOS/Mac),Stack Pointer指向棧頂部,Frame Pointer指向上一棧幀的Stack Pointer值,通過Frame Pointer就可以遞歸回溯獲取整個調(diào)用棧。
常見命令
- b:用于不返回的跳轉(zhuǎn)
- bl:用于子程序跳轉(zhuǎn),要返回地址,返回地址存于LR中。當發(fā)生bl跳轉(zhuǎn)前,會在寄存器 R14 (即LR)中保存當前PC-4,即bl跳轉(zhuǎn)指令的下一條指令的地址。所以在返回時只要 MOV pc,lr 。
- bl命令可操作PC寄存器
- callq:方法調(diào)用
- ldr: 將內(nèi)存中的值讀取到寄存器中
- cmp: 比較指令
- str: 將寄存器中的值寫入到內(nèi)存中
- cbz: 和 0 比較,如果結(jié)果為零就轉(zhuǎn)移(只能跳到后面的指令)
- cbnz: 和非 0 比較,如果結(jié)果非零就轉(zhuǎn)移(只能跳到后面的指令)
- ret: 子程序(函數(shù)調(diào)用)返回指令,返回地址已默認保存在寄存器 lr (x30) 中
- MOV指令:是數(shù)據(jù)傳送指令,也是最基本的編程指令,用于將一個數(shù)據(jù)從源地址傳送到目標地址(寄存器間的數(shù)據(jù)傳送本質(zhì)上也是一樣的)。其特點是不破壞源地址單元的內(nèi)容,只能用于寄存器與寄存器或者寄存器 與常量之間傳值,不能用于內(nèi)存地址。不區(qū)分大小寫,例如mov和MOV是一樣的.
mov x1, x0 將寄存器x0的值復制到寄存器x1中
add x0, x1, x2 將寄存器x1和x2的值相加后保存到寄存器x0中
sub x0, x1, x2 將寄存器x1和x2的值相減后保存到寄存器x0中
and x0, x0, #0x1 將寄存器x0的值和常量1按位與后保存到x0中
orr x0, x0, #0x1 將寄存器x0的值和常量1按位或后保存到寄存器x0中
str x0, [x0, x8] 將寄存器x0中的值保存到棧內(nèi)存 [x0 + x8]處
ldr x0, [x1, x2] 將寄存器x1和寄存器x2的值相加作為地址,取該內(nèi)存地址的值放入寄存器x0中
stp x29, x30, [sp, #0x10] ; 將 x29, x30 的值存入 sp 偏移 16 個字節(jié)的位置
將內(nèi)存區(qū)域的i值+1并賦值給a的步驟如下
mov x0, i 先將i值放到x0寄存器中
add x0, 1 將x0中的值+1
mov a , x0 賦值給x0