iOS ARM64 匯編入門(mén)

這里主要介紹 iOS 平臺(tái)上 ARM64 架構(gòu)的寄存器和指令以及內(nèi)存堆棧十厢。

堆棧

在 iOS 中,椢嫫耄空間是向下生長(zhǎng)的蛮放,也就是從高地址向低地址生長(zhǎng),棧頂在低地址奠宜,棧底在高地址包颁,比如:
棧頂?shù)刂罚?x00
棧底地址:0x1C

寄存器

ARM64 主要有34個(gè)寄存器,其中包括 31 個(gè)通用寄存器(x0-x30)以及 SP挎塌,PC徘六,CPSR,可以通過(guò)在調(diào)試時(shí)輸入 register read 命令查看

寄存器是64位的榴都,可以使用W0-W30來(lái)訪問(wèn)前32位空間

x0-x7

主要用來(lái)存放函數(shù)傳遞的參數(shù)待锈,以及臨時(shí)變量,超過(guò)8個(gè)參數(shù)的存放在棧上嘴高,其中 x0 也用來(lái)存放函數(shù)的返回值

FP(x29)

通常存放棧幀地址(棧底指針)竿音,指向當(dāng)前函數(shù)棧的底部

LR(x30)

通常稱(chēng) x30 為程序鏈接寄存器,因?yàn)檫@個(gè)寄存器會(huì)記錄著當(dāng)前方法的調(diào)用方地址拴驮,也就是當(dāng)前方法返回后程序繼續(xù)執(zhí)行的下一條指令地址

SP

棧頂指針春瞬,指向當(dāng)前函數(shù)棧的頂部

PC

程序計(jì)數(shù)器,指向即將要執(zhí)行的下一條指令地址套啤,軟件無(wú)法修改

CPSR

狀態(tài)寄存器宽气,有些指令會(huì)修改 CPSR 的值,比如 CPM潜沦,有些指令會(huì)根據(jù) CPSR 的值來(lái)做跳轉(zhuǎn)操作萄涯。
CPSR 的每一位值都有特殊的意義,其中最常見(jiàn)的是 NZCV 標(biāo)志位

NZCV是狀態(tài)寄存器的條件標(biāo)志位唆鸡,分別代表運(yùn)算過(guò)程中產(chǎn)生的狀態(tài)涝影,其中:
N, negative condition flag,一般代表運(yùn)算結(jié)果是負(fù)數(shù)
Z, zero condition flag, 指令結(jié)果為0時(shí)Z=1争占,否則Z=0燃逻;
C, carry condition flag, 無(wú)符號(hào)運(yùn)算有溢出時(shí),C=1臂痕。
V, oVerflow condition flag 有符號(hào)運(yùn)算有溢出時(shí)伯襟,V=1。

常用指令

常用指令可大致分為運(yùn)算指令握童,尋址指令逗旁,跳轉(zhuǎn)指令等

運(yùn)算指令

mov x1,x0          ;傳送:x1 = x0
add x0,x1,x2       ;加法:x0 = x1 + x2
sub x0,x1,x2       ;減法:x0 = x1 - x2
mul x0,x1,x2.      ;乘法:x0 = x1 * x2
and x0,x0,#0xF     ;位與:x0 = x0 & 0xF
orr x0,x0,#9       ;位或:x0 = x0 | 9
eor x0,x0,#0xF     ;異或:x0 = x0 ^ 0xF;
lsl x0,#1          ;邏輯左移:x0 = x0 << 1
lsr x0,#1          ;邏輯右移:x0 = x0 >> 1,左邊統(tǒng)一補(bǔ)0
asr x0,#1          ;算術(shù)右移:x0 = x0 >> 1,左邊補(bǔ)符號(hào)位

尋址指令

分為兩種片效,存和取
L 打頭的基本都是取值指令红伦,如 LDR(Load Register)、LDP(Load Pair)
S 打頭的基本都是存值指令淀衣,如 STR(Store Register)昙读、STP(Store Pair)

ldr    x0,[x1]               ;從 x1 指向的地址里面取出一個(gè)64位大小的數(shù)存入x0
ldp    x1,x2,[x10, #0x10]    ;從 x10+0x10 指向的地址里面取出2個(gè)64位的數(shù),分別存入x1膨桥、x2
str    x5,[sp, #24]          ;往內(nèi)存中寫(xiě)數(shù)據(jù)(偏移值為正), 把 x5 的值(64位的數(shù)值)存到 sp+24 指向的地址內(nèi)存上
stur   w0,[x29, #0x8]        ;往內(nèi)存中寫(xiě)數(shù)據(jù)(偏移值為負(fù))蛮浑,將 w0 的值存儲(chǔ)到 x29 - 0x8 這個(gè)地址里
stp    x29,x30,[sp, #-16]!   ;把 x29、x30 的值存到 sp-16 的地址上只嚣,并且把sp-=16  Note:后面有個(gè)感嘆號(hào)的沮稚,然后沒(méi)有stup這個(gè)指令哈
ldp    x29,x30,[sp],#16      ;從 sp 地址取出16 byte數(shù)據(jù),分別存入x29册舞、x30,然后 sp+=16

跳轉(zhuǎn)指令

bl/b bl 是有返回的跳轉(zhuǎn)蕴掏;b 是無(wú)返回的跳轉(zhuǎn),BL的L也可以理解為L(zhǎng)r
跳轉(zhuǎn)指令可以配合條件狀態(tài)(根據(jù) CPSR 相應(yīng)位置的值)

B    ;跳轉(zhuǎn)指令调鲸,可帶條件跳轉(zhuǎn)與cmp配合使用
BL   ;帶返回的跳轉(zhuǎn)指令盛杰, 返回地址保存到LR(X30)
BLR  ; 帶返回的跳轉(zhuǎn)指令,跳轉(zhuǎn)到指令后邊跟隨寄存器中保存的地址(例:blr    x8 ;跳轉(zhuǎn)到x8保存的地址中去執(zhí)行)
RET  ;子程序返回指令藐石,返回地址默認(rèn)保存在LR(X30)

比較指令

cmp x1,x0  ;將x1和x0(可以是立即數(shù))的值相減即供,并根據(jù)結(jié)果設(shè)置 CPSR 標(biāo)志位
CBZ  ;比較(Compare),如果結(jié)果為零(Zero)就轉(zhuǎn)移(只能跳到后面的指令)
CBNZ ;比較于微,如果結(jié)果非零(Non Zero)就轉(zhuǎn)移(只能跳到后面的指令)

入門(mén)Demo

以一個(gè)簡(jiǎn)單的 C 程序?yàn)槔旱眨缦麓a:

int sum(int a, int b)
{
    int c = a + b + 1;
    return c;
}
int main() 
{
    int a = 10;
    int b = 20;
    int rs = sum(a,b);
    if (rs > 30) {
        return 0;
    }
    else {
        return 1;
    }    
}

有2個(gè)函數(shù),main 函數(shù)和 sum 函數(shù)株依,通過(guò) clang 命令成匯編代碼

clang -arch arm64 -S testAsm.c

最后生成的匯編代碼如下:

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 15    sdk_version 10, 15, 4
    .globl  _sum                    ; -- Begin function sum
    .p2align    2
_sum:                                   ; @sum
    .cfi_startproc
; %bb.0:
    sub sp, sp, #16             ; =16
    .cfi_def_cfa_offset 16
    str w0, [sp, #12]
    str w1, [sp, #8]
    ldr w8, [sp, #12]
    ldr w9, [sp, #8]
    add w8, w8, w9
    add w8, w8, #1              ; =1
    str w8, [sp, #4]
    ldr w0, [sp, #4]
    add sp, sp, #16             ; =16
    ret
    .cfi_endproc
                                        ; -- End function
    .globl  _main                   ; -- Begin function main
    .p2align    2
_main:                                  ; @main
    .cfi_startproc
; %bb.0:
    sub sp, sp, #32             ; =32
    stp x29, x30, [sp, #16]     ; 16-byte Folded Spill
    add x29, sp, #16            ; =16
    .cfi_def_cfa w29, 16
    .cfi_offset w30, -8
    .cfi_offset w29, -16
    stur    wzr, [x29, #-4]
    mov w8, #10
    str w8, [sp, #8]
    mov w8, #20
    str w8, [sp, #4]
    ldr w0, [sp, #8]
    ldr w1, [sp, #4]
    bl  _sum
    str w0, [sp]
    ldr w8, [sp]
    cmp w8, #30                 ; =30
    b.le    LBB1_2
; %bb.1:
    stur    wzr, [x29, #-4]
    b   LBB1_3
LBB1_2:
    mov w8, #1
    stur    w8, [x29, #-4]
LBB1_3:
    ldur    w0, [x29, #-4]
    ldp x29, x30, [sp, #16]     ; 16-byte Folded Reload
    add sp, sp, #32             ; =32
    ret
    .cfi_endproc
                                        ; -- End function
.subsections_via_symbols

先看 sum 函數(shù)的代碼(簡(jiǎn)單起見(jiàn)祸穷,這里去除一些不(看)重(不)要(懂)的代碼)

_sum:                    ; sum 函數(shù)入口
    sub sp, sp, #16      ; sp = sp-16,棧頂指針向下移16字節(jié)勺三,相當(dāng)于分配了16個(gè)字節(jié)的棧空間(iOS中需曾,棧是向下(低地址)生長(zhǎng)的)
    str w0, [sp, #12]    ; 將 w0(x0的低32位) 存放到 s p12的位置(參數(shù)a)
    str w1, [sp, #8]     ; 將 w1(x1的低32位) 存放到 sp+8 的位置(參數(shù)b)
    ldr w8, [sp, #12]    ; 將 sp+12 位置的值讀到 w8 上吗坚,也就是 w8=a
    ldr w9, [sp, #8]     ; 將 sp+8 位置的值讀到 w9 上,也就是 w9=b
    add w8, w8, w9       ; 兩數(shù)相加 w8 = w8 + w9
    add w8, w8, #1       ; w8 = w8 + 1
    str w8, [sp, #4]     ; 將 w8 保存到 sp+4 的位置
    ldr w0, [sp, #4]     ; 將 sp+4 的位置讀到 w0 上(前面說(shuō)過(guò),x0也用來(lái)存放函數(shù)返回值)
    add sp, sp, #16      ; 將棧頂指針向上移16字節(jié)(恢復(fù)棧指針呆万,相當(dāng)于釋放堆棧)
    ret                  ; 函數(shù)返回

總體上邏輯比較簡(jiǎn)單商源,基本上沒(méi)什么難懂的地方

這里有個(gè)不明白的地方就是

str w8, [sp, #4]
ldr w0, [sp, #4]

為什么不直接

mov w0, w8

有知道的大佬還請(qǐng)指教一下,為了寄存器內(nèi)容可以恢復(fù)么谋减?

再來(lái)看 main 函數(shù)的牡彻,同樣去除一些不重要的代碼

_main:                        ; main函數(shù)入口
    sub sp, sp, #32           ; sp = sp - 32,將棧頂指針向下移動(dòng) 32 字節(jié)
    stp x29, x30, [sp, #16]   ; 將 x29,x30 存儲(chǔ)到 sp+16 的位置(x29,x30 參考前面介紹庄吼,這里相當(dāng)于做個(gè)備份缎除,因?yàn)楹竺嬉薷倪@2個(gè)寄存器)
    add x29, sp, #16          ; x29 = sp + 16(相當(dāng)于移動(dòng)棧底指針,堆棧大小 16 字節(jié))
    stur    wzr, [x29, #-4]   ; 將 wzr(也叫零寄存器总寻,這是一個(gè)特殊的寄存器器罐,值為0)存儲(chǔ)到 x29 - 4 的位置,相當(dāng)于把這快內(nèi)存清0
    mov w8, #10               ; 將 w8 置為 10
    str w8, [sp, #8]          ; 將 w8 存儲(chǔ)到 sp+8 的位置
    mov w8, #20               ; 將 w8 置為 20
    str w8, [sp, #4]          ; 將 w8 存儲(chǔ)到 sp+4 的位置  
    ldr w0, [sp, #8]          ; 將 sp+8 讀到 w0(w0 = 10)
    ldr w1, [sp, #4]          ; 將 sp+4 讀到 w1 (w1 = 20渐行,前面說(shuō)過(guò)x0-x7是存放參數(shù)的)
    bl  _sum                  ; 跳轉(zhuǎn)到 sum 函數(shù) 
    str w0, [sp]              ; 存儲(chǔ)函數(shù)返回值(w0)到 sp
    ldr w8, [sp]              ; 讀取 sp 的值到 w8
    cmp w8, #30               ; 將 w8 的內(nèi)容和30比較轰坊,同時(shí)設(shè)置 CPSR 寄存器
    b.le    LBB1_2            ; 根據(jù) CPSR 結(jié)果來(lái)決定是跳轉(zhuǎn) LBB1_2(其中條件le的意思是小于等于的意思,具體可以參考我下面的鏈接)
    stur    wzr, [x29, #-4]   ; 將 x29-4 位置內(nèi)存清0 
    b   LBB1_3                ; 跳轉(zhuǎn)到 LBB1_3
LBB1_2:
    mov w8, #1                ; 將 w8 置 1  
    stur    w8, [x29, #-4]    ; 將 w8 保存到 x29-4 的位置
LBB1_3:
    ldur    w0, [x29, #-4]    ; 讀取 x29-4 位置的值到 w0 作為函數(shù)返回值
    ldp x29, x30, [sp, #16]   ; 將 sp+16 位置的值讀取到 x29,x30(恢復(fù)x29,x30)
    add sp, sp, #32           ; sp = sp + 32祟印,恢復(fù)棧頂指針肴沫,釋放堆棧
    ret                       ; 函數(shù)返回 

main 函數(shù)和 sum 函數(shù)的分析基本就到這里,基本邏輯還是比較簡(jiǎn)單的蕴忆,這里有個(gè)最大的區(qū)別就是 main 函數(shù)有 x29,x30 的備份&恢復(fù)操作颤芬,原因是 sum 函數(shù)是葉子函數(shù),而 main 函數(shù)是非葉子函數(shù)孽文,非葉子函數(shù)有對(duì)其他函數(shù)的調(diào)用驻襟,需要開(kāi)辟新的堆棧空間芋哭,以及其他函數(shù)返回后需要繼續(xù)執(zhí)行下一條指令沉衣,這需要修改 x29,x30的值,所以要先備份起來(lái)减牺,而葉子函數(shù)就不需要豌习。

參考

ARM64 匯編基礎(chǔ)
iOS開(kāi)發(fā)同學(xué)的arm64匯編入門(mén)
iOS 常用匯編指令集

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拔疚,隨后出現(xiàn)的幾起案子肥隆,更是在濱河造成了極大的恐慌,老刑警劉巖稚失,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栋艳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡句各,警方通過(guò)查閱死者的電腦和手機(jī)吸占,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)凿宾,“玉大人矾屯,你說(shuō)我怎么就攤上這事〕鹾瘢” “怎么了件蚕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我排作,道長(zhǎng)牵啦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任纽绍,我火速辦了婚禮蕾久,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拌夏。我一直安慰自己僧著,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布障簿。 她就那樣靜靜地躺著盹愚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪站故。 梳的紋絲不亂的頭發(fā)上皆怕,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音西篓,去河邊找鬼愈腾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛岂津,可吹牛的內(nèi)容都是我干的虱黄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼吮成,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼橱乱!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起粱甫,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泳叠,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后茶宵,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體危纫,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年乌庶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了种蝶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡安拟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宵喂,到底是詐尸還是另有隱情糠赦,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站拙泽,受9級(jí)特大地震影響淌山,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜顾瞻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一泼疑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧荷荤,春花似錦退渗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至古毛,卻和暖如春翻翩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稻薇。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工嫂冻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人塞椎。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓桨仿,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親忱屑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蹬敲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容