本文首發在個人我的博客: blog.shenyuanluo.com,喜歡的朋友歡迎訂閱。html
31
個R0 ~ R30
,每一個寄存器能夠存取一個 64 位大小的數。 當使用 x0 - x30
訪問時,是一個 64位的數;當使用 w0 - w30
訪問時,是一個 32 位的數,訪問的是寄存器的 低 32
位,如圖:objective-c
(也能夠說是 浮點型寄存器)每一個寄存器的大小是 128
位的。 分別能夠用Bn、Hn、Sn、Dn、Qn
的方式來訪問不一樣的位數;如圖:shell
**注:**word 是
32
位,也就是 4 Byte大小。xcode
8
位16
位32
位64
位128
位bl
指令的下一條指令的內存地址;xzr/wzr
分別表明 64/32 位,其做用就是 0,寫進去表明丟棄結果,讀出來是 0
;CPSR (Current Program Status Register)和其餘寄存器不同,其餘寄存器是用來存放數據的,都是整個寄存器具備一個含義;而 CPSR 寄存器是按位起做用的,即,每一位都有專門的含義,記錄特定的信息;以下圖markdown
注: CPSR 寄存器是 32 位的。數據結構
CPSR
的 低8位(包括 I
、F
、T
和 M[4:0]
)稱爲控制位,程序沒法修改,除非 CPU 運行於 特權模式 下,程序才能修改控制位。架構
N
、Z
、C
、V
均爲條件碼標誌位;其內容可被算術或邏輯運算的結果所改變,而且能夠決定某條指令是否被執行。app
N(Negative)標誌: CPSR 的第 31
位是 N,符號標誌位;記錄相關指令執行後其結果是否爲負數,若是爲負數,則 N = 1
;若是是非負數,則 N = 0
。jsp
Z(Zero)標誌: CPSR
的第 30
位是 Z,0標誌位;記錄相關指令執行後,其結果是否爲0,若是結果爲0,則 Z = 1
;若是結果不爲0,則 Z = 0
。函數
C(Carry)標誌: CPSR 的第 29
位是C,進位標誌位;
C = 1
,不然 C = 0
;CMP
): 當運算時產生了 借位 時(無符號數溢出),C = 0
,不然 C = 1
。V(Overflow)標誌: CPSR 的第 28
位是 V,溢出標誌位;在進行有符號數運算的時候,若是超過了機器所能標識的範圍,稱爲溢出。
操做碼 | 條件碼助記符 | 標誌 | 含義 |
---|---|---|---|
0000 | EQ | Z=1 | 相等 |
0001 | NE(Not Equal) | Z=0 | 不相等 |
0010 | CS/HS(Carry Set/High or Same) | C=1 | 無符號數大於或等於 |
0011 | CC/LO(Carry Clear/LOwer) | C=0 | 無符號數小於 |
0100 | MI(MInus) | N=1 | 負數 |
0101 | PL(PLus) | N=0 | 正數或零 |
0110 | VS(oVerflow set) | V=1 | 溢出 |
0111 | VC(oVerflow clear) | V=0 | 沒有溢出 |
1000 | HI(High) | C=1,Z=0 | 無符號數大於 |
1001 | LS(Lower or Same) | C=0,Z=1 | 無符號數小於或等於 |
1010 | GE(Greater or Equal) | N=V | 有符號數大於或等於 |
1011 | LT(Less Than) | N!=V | 有符號數小於 |
1100 | GT(Greater Than) | Z=0,N=V | 有符號數大於 |
1101 | LE(Less or Equal) | Z=1,N!=V | 有符號數小於或等於 |
1110 | AL | 任何 | 無條件執行(默認) |
1111 | NV | 任何 | 從不執行 |
在 arm64
架構中,每一個指令讀取都是 64 位,即 8
字節 空間。
x0 ~ x7
分別會存放方法的前 8 個參數;若是參數個數超過了8個,多餘的參數會存在棧上,新方法會經過棧來讀取。mov: 將某一寄存器的值複製到另外一寄存器(只能用於寄存器與寄存器或者寄存器與常量之間傳值,不能用於內存地址),如:
mov x1, x0 ; 將寄存器 x0 的值複製到寄存器 x1 中
複製代碼
add: 將某一寄存器的值和另外一寄存器的值 相加 並將結果保存在另外一寄存器中,如:
add x0, x0, #1 ; 將寄存器 x0 的值和常量 1 相加後保存在寄存器 x0 中
add x0, x1, x2 ; 將寄存器 x1 和 x2 的值相加後保存到寄存器 x0 中
add x0, x1, [x2] ; 將寄存器 x1 的值加上寄存器 x2 的值做爲地址,再取該內存地址的內容放入寄存器 x0 中
複製代碼
sub: 將某一寄存器的值和另外一寄存器的值 相減 並將結果保存在另外一寄存器中,如:
sub x0, x1, x2 ; 將寄存器 x1 和 x2 的值相減後保存到寄存器 x0 中
複製代碼
mul: 將某一寄存器的值和另外一個寄存器的值 相乘 並將結果保存在另外一寄存器中,如:
mul x0, x1, x2 ; 將寄存器 x1 和 x2 的值相乘後結果保存到寄存器 x0 中
複製代碼
sdiv:(有符號數,對應 udiv: 無符號數)將某一寄存器的值和另外一個寄存器的值 相除 並將結果保存在另外一寄存器中,如:
sdiv x0, x1, x2 ; 將寄存器 x1 和 x2 的值相除後結果保存到寄存器 x0 中
複製代碼
and: 將某一寄存器的值和另外一寄存器的值 按位與 並將結果保存到另外一寄存器中,如:
and x0, x0, #0xf ; 將寄存器 x0 的值和常量 0xf 按位與後保存到寄存器 x0 中
複製代碼
orr: 將某一寄存器的值和另外一寄存器的值 按位或 並將結果保存到另外一寄存器中,如:
orr x0, x0, #9 ; 將寄存器 x0 的值和常量 9 按位或後保存到寄存器 x0 中
複製代碼
eor: 將某一寄存器的值和另外一寄存器的值 按位異或 並將結果保存到另外一寄存器中,如:
eor x0, x0, #0xf ; 將寄存器 x0 的值和常量 0xf 按位異或後保存到寄存器 x0 中
複製代碼
str: (store register) 將寄存器中的值寫入到內存中,如:
str w9, [sp, #0x8] ; 將寄存器 w9 中的值保存到棧內存 [sp + 0x8] 處
複製代碼
strb: (store register byte) 將寄存器中的值寫入到內存中(只存儲一個字節),如:
strb w8, [sp, #7] ; 將寄存器 w8 中的低 1 字節的值保存到棧內存 [sp + 7] 處
複製代碼
ldr: (load register) 將內存中的值讀取到寄存器中,如:
ldr x0, [x1] ; 將寄存器 x1 的值做爲地址,取該內存地址的值放入寄存器 x0 中
ldr w8, [sp, #0x8] ; 將棧內存 [sp + 0x8] 處的值讀取到 w8 寄存器中
ldr x0, [x1, #4]! ; 將寄存器 x1 的值加上 4 做爲內存地址, 取該內存地址的值放入寄存器 x0 中, 而後將寄存器 x1 的值加上 4 放入寄存器 x1 中
ldr x0, [x1], #4 ; 將寄存器 x1 的值做爲內存地址,取內該存地址的值放入寄存器 x0 中, 再將寄存器 x1 的值加上 4 放入寄存器 x1 中
ldr x0, [x1, x2] ; 將寄存器 x1 和寄存器 x2 的值相加做爲地址,取該內存地址的值放入寄存器 x0 中
複製代碼
ldrsb: (load register byte) 將內存中的值(只讀取一個字節)讀取到寄存器中,如:
ldrsb w8, [sp, #7] ; 將棧內存 [sp + 7] 出的 低 1 字節的值讀取到寄存器 w8 中
複製代碼
stur: 同 str
將寄存器中的值寫入到內存中(通常用於 負
地址運算中),如:
stur w10, [x29, #-0x4] ; 將寄存器 w10 中的值保存到棧內存 [x29 - 0x04] 處
複製代碼
ldur: 同 ldr
將內存中的值讀取到寄存器中(通常用於 負
地址運算中),如:
ldur w8, [x29, #-0x4] ; 將棧內存 [x29 - 0x04] 處的值讀取到 w8 寄存器中
複製代碼
stp: 入棧指令(str
的變種指令,能夠同時操做兩個寄存器),如:
stp x29, x30, [sp, #0x10] ; 將 x29, x30 的值存入 sp 偏移 16 個字節的位置
複製代碼
ldp: 出棧指令(ldr
的變種指令,能夠同時操做兩個寄存器),如:
ldp x29, x30, [sp, #0x10] ; 將 sp 偏移 16 個字節的值取出來,存入寄存器 x29 和寄存器 x30
複製代碼
scvtf: (Signed Convert To Float)帶符號 定點數 轉換爲 浮點數,如:
scvtf d1, w0 ; 將寄存器 w0 的值(頂點數,轉化成 浮點數) 保存到 向量寄存器/浮點寄存器 d1 中
複製代碼
fcvtzs: (Float Convert To Zero Signed)浮點數 轉化爲 定點數 (舍入爲0),如:
fcvtzs w0, s0 ; 將向量寄存器 s0 的值(浮點數,轉換成 定點數)保存到寄存器 w0 中
複製代碼
cbz: 和 0 比較(Compare),若是結果爲零(Zero)就轉移(只能跳到後面的指令);
cbnz: 和非 0 比較(Compare),若是結果非零(Non Zero)就轉移(只能跳到後面的指令);
cmp: 比較指令,至關於 subs
,影響程序狀態寄存器 CPSR ;
cset: 比較指令,知足條件,則並置 1
,不然置 0
,如:
cmp w8, #2 ; 將寄存器 w8 的值和常量 2 進行比較
cset w8, gt ; 若是是大於(grater than),則將寄存器 w8 的值設置爲 1,不然設置爲 0
複製代碼
brk: 能夠理解爲跳轉指令特殊的一種
LSL: 邏輯左移
LSR: 邏輯右移
ASR: 算術右移
ROR: 循環右移
adrp: 用來定位數據段中的數據用, 由於 aslr 會致使代碼及數據的地址隨機化, 用 adrp 來根據 pc 作輔助定位
b: (branch)跳轉到某地址(無返回), 不會改變 lr (x30) 寄存器的值;通常是本方法內的跳轉,如 while
循環,if else
等 ,如:
b LBB0_1 ; 直接跳轉到標籤 ‘LLB0_1’ 處開始執行
複製代碼
bl: 跳轉到某地址(有返回),先將下一指令地址(即函數返回地址)保存到寄存器 lr (x30)中,再進行跳轉 ;通常用於不一樣方法直接的調用 ,如:
bl 0x100cfa754 ; 先將下一指令地址(‘0x100cfa754’ 函數調用後的返回地址)保存到寄存器 ‘lr’ 中,而後再調用 ‘0x100cfa754’ 函數
複製代碼
blr: 跳轉到 某寄存器
(的值)指向的地址(有返回),先將下一指令地址(即函數返回地址)保存到寄存器 lr (x30)中,再進行跳轉 ;如:
blr x20 ; 先將下一指令地址(‘x20’指向的函數調用後的返回地址)保存到寄存器 ‘lr’ 中,而後再調用 ‘x20’ 指向的函數
複製代碼
br: 跳轉到某寄存器(的值)指向的地址(無返回), 不會改變 lr (x30) 寄存器的值。
brk: 能夠理解爲跳轉指令特殊的一種。
ret: 子程序(函數調用)返回指令,返回地址已默認保存在寄存器 lr (x30) 中
每一個函數調用,都會有 入棧 和 出棧 操做。
#include <stdio.h>
void TestPushAndPop()
{
printf("Push an Pop !");
}
複製代碼
經過 Xcode "Product——>Perform Action——>Assemble PushAndPop.c
" 查看其對應的彙編代碼:
也能夠經過 clang
編譯成彙編代碼:
// 注意,如下代碼將默認生成pc版的彙編指令
clang -S PushAndPop.c
// arm64彙編須要以下命令,指定架構和系統頭文件所在的目錄,請務必將isysroot的sdk版本修改成對應的 xcode 中存在的版本!
clang -S -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.1.sdk PushAndPop.c
複製代碼
去除一大堆 不相干東西 獲得對應彙編代碼,以下:
sub sp, sp, #32 ; 更新棧頂寄存器的值,(能夠看出:申請 32 字節佔空間做爲新用)
stp x29, x30, [sp, #16] ; 保存調用該函數前的棧頂寄存器的值和該函數結束返回後下一將執行指令地址值
add x29, sp, #16 ; 更新棧底寄存器的值,(能夠看出:還剩餘 16 字節空間給該函數用)
adrp x0, l_.str@PAGE ; 獲取 ‘l_.str’ 標籤所在的頁的地址
add x0, x0, l_.str@PAGEOFF ; 獲取 ‘l_.str’ 標籤對應頁地址的偏移
bl _printf ; 調用 ‘printf’ 函數進行打印
stur w0, [x29, #-4] ; 將 w0 寄存器的值('bl' 函數調用的返回值)保存到 [x29 - 4] 的內存地址中
ldp x29, x30, [sp, #16] ; 恢復調用該函數以前棧底寄存器的值
add sp, sp, #32 ; 恢復調用該函數以前棧頂寄存器的值
ret ; 返回
複製代碼
對與上面的彙編代碼,分配了
32
本身空間,其中16
字節是用做 入棧操做,剩下的16
字節是用於存儲臨時變量的。疑問: 例子函數命名是沒有臨時變量,爲何還會須要申請佔空間?
解釋: 雖然該函數沒有臨時變量,可是調用
printf
函數後,編譯器自動會加上 該函數返回值 的處理,因爲 arm64 規定了整數型返回值放在x0
寄存器裏,所以會隱藏有一個局部變量int return_value;
的聲明在,該臨時變量佔用4
字節空間;又由於 arm64 下對於使用sp
做爲地址基址尋址的時候,必需要16byte-alignment
(對齊),因此申請了16
字節空間做爲臨時變量使用。具體參見 這裏。
其 入棧操做 彙編代碼流程解析以下:
其 出棧操做 彙編代碼流程解析以下:
注意: 對棧的
分配/釋放
操做只會對棧指針作加減法, 而不會對棧內存中的內容作任何修改(也不會把釋放的棧空間設置爲 0)。