認識ARM64匯編

[TOC]

之前說過學習匯編就是學習寄存器和指令,查看代碼請連接真機聋呢。

寄存器

arm64匯編中寄存器是64bit的,使用X[n]表示,低32位以w[n]表示

15450350776264.jpg

64位架構中有3164位的通用寄存器盛嘿。

可以通過register read查看

(lldb) register read
General Purpose Registers:
        x0 = 0x0000000000000001
        x1 = 0x00000002805d8a20
        x2 = 0x00000002837e7980
        x3 = 0x00000002805d8a20
        x4 = 0x0000000000000001
        x5 = 0x0000000000000001
        x6 = 0x0000000100d54000  
        x7 = 0x0000000000000000
        x8 = 0x0000200000000000
        x9 = 0x000161a1d63f7945 (0x00000001d63f7945) (void *)0x01d63f7cb0000001
       x10 = 0x0000000000000000
       x11 = 0x000000000000006d
       x12 = 0x000000000000006d
       x13 = 0x0000000000000000
       x14 = 0x0000000000000000
       x15 = 0x00000001ca634e6d  "touchesBegan:withEvent:"
       x16 = 0x000000019c4cd47c  libobjc.A.dylib`objc_storeStrong
       x17 = 0x0000000000000000
       x18 = 0x0000000000000000
       x19 = 0x0000000100f17810
       x20 = 0x00000002837e7920
       x21 = 0x00000002805d8a20
       x22 = 0x00000001ca634e6d  "touchesBegan:withEvent:"
       x23 = 0x0000000100e11a30
       x24 = 0x00000002837e7980
       x25 = 0x0000000100e11a30
       x26 = 0x00000002805d8a20
       x27 = 0x00000001ca62d900  "countByEnumeratingWithState:objects:count:"
       x28 = 0x00000002805d8a20
        fp = 0x000000016f47d730
        lr = 0x00000001009866dc  ArmAssembly`-[ViewController touchesBegan:withEvent:] + 84 at ViewController.m:38
        sp = 0x000000016f47d720
        pc = 0x0000000100986720  ArmAssembly`foo1 + 16 at ViewController.m:46
      cpsr = 0x80000000

(lldb) 

x0~x7:一般是函數的參數,大于8個的會通過堆棧傳參括袒。

/*
 * ArmAssembly`foo2:
 0x101062760 <+0>:  sub    sp, sp, #0x20             
 0x101062764 <+4>:  str    w0, [sp, #0x1c]
 0x101062768 <+8>:  str    w1, [sp, #0x18]
 0x10106276c <+12>: str    w2, [sp, #0x14]
 0x101062770 <+16>: str    w3, [sp, #0x10]
 0x101062774 <+20>: str    w4, [sp, #0xc]
 0x101062778 <+24>: str    w5, [sp, #0x8]
 0x10106277c <+28>: str    w6, [sp, #0x4]
 ->  0x101062780 <+32>: add    sp, sp, #0x20             
 0x101062784 <+36>: ret

 */
void foo7(int a,int b,int c ,int d ,int e,int f,int g) {
    
}
/*
 *ArmAssembly`foo8:
 0x10024275c <+0>:  sub    sp, sp, #0x20             
 0x100242760 <+4>:  str    w0, [sp, #0x1c]
 0x100242764 <+8>:  str    w1, [sp, #0x18]
 0x100242768 <+12>: str    w2, [sp, #0x14]
 0x10024276c <+16>: str    w3, [sp, #0x10]
 0x100242770 <+20>: str    w4, [sp, #0xc]
 0x100242774 <+24>: str    w5, [sp, #0x8]
 0x100242778 <+28>: str    w6, [sp, #0x4]
 0x10024277c <+32>: str    w7, [sp]
 ->  0x100242780 <+36>: add    sp, sp, #0x20             
 0x100242784 <+40>: ret

 */
void foo8(int a,int b,int c ,int d ,int e,int f,int g,int h) {
    
}


/*
 * ArmAssembly`foo9:
 0x100dc2718 <+0>:  sub    sp, sp, #0x30  
           
 0x100dc271c <+4>:  ldr    w8, [sp, #0x30] 次兆; 大于8個參數的時候 已經是從內存棧中去取數據了
 
 0x100dc2720 <+8>:  str    w0, [sp, #0x2c]
 0x100dc2724 <+12>: str    w1, [sp, #0x28]
 0x100dc2728 <+16>: str    w2, [sp, #0x24]
 0x100dc272c <+20>: str    w3, [sp, #0x20]
 0x100dc2730 <+24>: str    w4, [sp, #0x1c]
 0x100dc2734 <+28>: str    w5, [sp, #0x18]
 0x100dc2738 <+32>: str    w6, [sp, #0x14]
 0x100dc273c <+36>: str    w7, [sp, #0x10]
 0x100dc2740 <+40>: str    w8, [sp, #0xc]
 0x100dc2744 <+44>: add    sp, sp, #0x30             
 0x100dc2748 <+48>: ret

 */
void foo9(int a,int b,int c ,int d ,int e,int f,int g,int h,int i) {
    
}

可以看到當參數的個數大于8個的時候就不會從寄存器中去取參數了。

x0:一般表示返回值

int fooReturnValue() {
    return  10;
}

匯編代碼

ArmAssembly`fooReturnValue:
    0x100ece7b4 <+0>: mov    w0, #0xa
->  0x100ece7b8 <+4>: ret    

通過lldb指令

(lldb) register read x0
      x0 = 0x000000000000000a
(lldb) 

確實是10

pc:表示當前執(zhí)行的指令的地址

這個類似8086匯編里面的ip

15450984590679.jpg

比如我們斷點該改代碼處锹锰,查看pc寄存器的值


(lldb) register read pc
      pc = 0x0000000100b6e7b8  ArmAssembly`fooReturnValue + 4 at ViewController.m:140
(lldb) 

lr:鏈接寄存器芥炭,存放著函數的返回地址

lr也就是x30,這個里面存放著函數的返回地址

比如有下面2個方法恃慧,方法fooFp方法內部調用fooFp2

void fooFp() {
    int a = 4;
    int b = 5;
    fooFp2();
}

void fooFp2() {
    int a = 2;
    int b = 3;
}

fooFp的匯編代碼

ArmAssembly`fooFp:
 0x100dbe76c <+0>:  sub    sp, sp, #0x20             ; =0x20
 0x100dbe770 <+4>:  stp    x29, x30, [sp, #0x10]
 0x100dbe774 <+8>:  add    x29, sp, #0x10            ; =0x10
 
 0x100dbe778 <+12>: mov    w8, #0x5
 0x100dbe77c <+16>: orr    w9, wzr, #0x4
 0x100dbe780 <+20>: stur   w9, [x29, #-0x4]
 0x100dbe784 <+24>: str    w8, [sp, #0x8]
 0x100dbe788 <+28>: bl     0x100dbe798               ; fooFp2 at ViewController.m:154
 
 0x100dbe78c <+32>: ldp    x29, x30, [sp, #0x10]
 0x100dbe790 <+36>: add    sp, sp, #0x20             ; =0x20
 0x100dbe794 <+40>: ret
 

0x100dbe788 <+28>: bl 0x100dbe798這行就是在調用方法fooFp2园蝠,如果調用完fooFp2后函數應該要繼續(xù)執(zhí)行,那么肯定是要到0x100dbe788 <+28>: bl 0x100dbe798下一行也就是地址0x100dbe78c處痢士,我們到fooFp2中查看下lr寄存器的值

(lldb) register read lr
      lr = 0x0000000100dbe78c  ArmAssembly`fooFp + 32 at ViewController.m:170
(lldb) 

當然彪薛, 本質上還是將lr的值給了pc寄存器了,也就是ret指令做的事情。

棧寄存器

分為sp棧頂寄存器和fp棧底寄存器善延。(如果熟悉8086匯編的話類似于分別類似于spbp

還是用上面的方法fooFpfooFp2來說明這2個寄存器训唱。

不嫌啰嗦這邊還是復制下fooFp的匯編代碼


ArmAssembly`fooFp:
 0x100dbe76c <+0>:  sub    sp, sp, #0x20             ; 申請棧空間
 0x100dbe770 <+4>:  stp    x29, x30, [sp, #0x10]     ; 保護寄存器的值
 0x100dbe774 <+8>:  add    x29, sp, #0x10            ; 改變fp寄存器的值挚冤,用于執(zhí)行棧底
 
 0x100dbe778 <+12>: mov    w8, #0x5
 0x100dbe77c <+16>: orr    w9, wzr, #0x4
 0x100dbe780 <+20>: stur   w9, [x29, #-0x4]
 0x100dbe784 <+24>: str    w8, [sp, #0x8]
 0x100dbe788 <+28>: bl     0x100dbe798               ; fooFp2 at ViewController.m:154
 
 0x100dbe78c <+32>: ldp    x29, x30, [sp, #0x10]     ; 恢復之前保存的fp和lr的值
 0x100dbe790 <+36>: add    sp, sp, #0x20             ; 恢復sp指針
 0x100dbe794 <+40>: ret 
 

整個過程如下圖

15451047620385.jpg

cpsr:程序狀態(tài)寄存器

cpsr(Current Program Status Register )

spsr (Saved Program Status Register) 異常狀況下使用

xzr:零寄存器

表示zero register况增,一般用來默認值,里面存儲的值都是0训挡。

  1. xzr:64位的
  2. wzr:32位的

指令

尋址方式大概規(guī)則說明

ADD R0澳骤,R0,#1  // R0 = R0 + #1 表示寄存器R0的值 + 1再賦值給R0

ADD R0澜薄,R1为肮,R2  // R0 = R1+R2 

ADD R0,R1肤京,[R2] // R0←R1+[R2]

LDR R0颊艳,[R1,#4]  // 忘分;R0←[R1+4]

LDR R0棋枕,[R1,#4]妒峦!  // R0←[R1+4]重斑、R1←R1+4

LDR R0,[R1] 肯骇,#4  // 窥浪;R0←[R1]、R1←R1+4

LDR R0笛丙,[R1漾脂,R2] // R0←[R1+R2]

  1. []一般表示是取值的意思,[R2]表示取出R2所存的內存地址比如是0x10000所對應的值比如是66胚鸯。
  2. LDR R0 [R1骨稿,#4] :寄存器 R1 的內容加上4形成操作數的有效地址,從而取得操作數存入寄存器 R0 中蠢琳。
  3. LDR R0啊终,[R1,#4]傲须!:將寄存器 R1 的內容加上 4 形成操作數的有效地址蓝牲,從而取得操作數存入寄 存器 R0 中,然后泰讽,R1 的內容自增 4 個字節(jié)例衍。
  4. LDR R0昔期,[R1] ,#4:以寄存器 R1 的內容作為操作數的有效地址佛玄,從而取得操作數存入寄存器 R0 中硼一,然后,R1 的內容自增 4 個字節(jié)梦抢。
  5. LDR R0般贼,[R1,R2]:將寄存器 R1 的內容加上寄存器 R2 的內容形成操作數的有效地址奥吩,從而取得 操作數存入寄存器 R0 中哼蛆。

計算指令

ADD 加法

ADD R0,R1霞赫,R2 // R0 = R1 + R2
ADD R0腮介,R1,#256  // R0 = R1 + 256 

ADD R0端衰,R2叠洗,R3,LSL#1 //  R0 = R2 + (R3 << 1)

SUB 減法

SUB R0旅东,R1灭抑,R2 // R0 = R1 - R2
SUB R0,R1玉锌,#256 //R0 = R1 - 256 
SUB R0名挥,R2,R3主守,LSL#1 // R0 = R2 - (R3 << 1)

邏輯運算

AND邏輯與、EOR邏輯異或榄融、ORR邏輯或参淫、LSL邏輯左移、LSR邏輯右移

內存指令

一般是ST開頭的為存數據,比如說STR愧杯、STP涎才、STUR
LD開頭的表示取數據,比如說LDR力九、LDP耍铜、lDUR

str    w8, [sp, #0x8] // 表示 將w8存放到sp+0x8表示的內存中
stur   w9, [x29, #-0x4] // 表示 將w9存放到x29, #-0x4表示的內存中
stp x1, x2, [sp, #-16] // 表示 從sp-0x16對應地址的開始存放 x1、x2的表示的值

ldp    x29, x30, [sp, #0x10] //表示 從sp+0x10對應地址的開始取出值賦值給x29,x30

P:可以理解為pair跌前;
u: 表示負數

跳轉指令

b棕兼、bl一般搭配cmp指令使用

b:無條件跳轉,一般是什么函數內部的if抵乓、switch條件判斷伴挚;
bl:帶函數返回值的跳轉靶衍,一般是調用其他的函數;

0x100432624 <+88>:  cmp    w10, #0x1                 ; =0x1 
0x100432628 <+92>:  b.le   0x100432630               ; 
    

<1>. CMP:將寄存器 R1 的值與立即數 0x1 相減茎芋,并根據結果設置 CPSR 的標志位

標志位的可能值如下表

條件碼 助記符后綴 標志 含義
0000 EQ Z 置位 相等 ==
0001 NE Z 清零 不相等 !=
0010 CS C 置位 無符號數大于或等于
0011 CC C 清零 無符號數小于
0100 MI N 置位 負數
0101 PL N 清零 正數或零
0110 VS V 置位 溢出
0111 VC V 清零 未溢出
1000 HI C 置位 Z 清零 無符號數大于
1001 LS C 清零 Z 置位 無符號數小于或等于
1010 GE N 等于 V 帶符號數大于或等于
1011 LT N 不等于 V 帶符號數小于
1100 GT Z 清零且(N 等于 V) 帶符號數大于
1101 LE Z 置位或(N 不等于 V) 帶符號數小于或等于
1110 AL 忽略 無條件執(zhí)行

<2>. b.le 0x100432630:表示如果w10 <= 0x1那么就執(zhí)行0x100432630

ret指令

  1. 函數返回
  2. lr(x30)寄存器器的值賦值給pc寄存器器颅眶。

了解了這些基本知識讀一些匯編代碼應該沒問題了,沒有提到的自己查下資料也差不多了田弥。

ARM-GNU匯編

如果你只會arm匯編去看runtime匯編源碼的時候會發(fā)現還是有些東西不明白涛酗,比如下面的代碼

#if SUPPORT_TAGGED_POINTERS
    .data
    .align 3
    .globl _objc_debug_taggedpointer_classes
_objc_debug_taggedpointer_classes:
    .fill 16, 8, 0
    .globl _objc_debug_taggedpointer_ext_classes
_objc_debug_taggedpointer_ext_classes:
    .fill 256, 8, 0
#endif

    ENTRY _objc_msgSend
    UNWIND _objc_msgSend, NoFrame

    cmp p0, #0          // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
    b.le    LNilOrTagged        //  (MSB tagged pointer looks negative)
#else
    b.eq    LReturnZero
#endif
    ldr p13, [x0]       // p13 = isa
    GetClassFromIsa_p16 p13     // p16 = class
LGetIsaDone:
    CacheLookup NORMAL      // calls imp or objc_msgSend_uncached

[...]

ARM匯編開發(fā)指用ARM提供的匯編指令,進行ARM程序的開發(fā)偷厦。
ARM匯編開發(fā)商叹,有兩種開發(fā)方式,一種是使用ARM匯編沪哺,一種是使用ARM GNU匯編沈自。兩種匯編開發(fā),使用的匯編指令是完全一樣的辜妓,區(qū)別是宏指令枯途,偽指令,偽操作不一樣籍滴。其實兩種開發(fā)方式的區(qū)別在于所使用的編譯工具不一樣酪夷。
對于ARM匯編,使用的是ARM公司開發(fā)的編譯器孽惰,而ARM GNU匯編晚岭,是使用GNU為ARM指令集開發(fā)的編譯器,也就是arm-gcc勋功。

2種方式的不同之處就是偽操作的不同坦报,蘋果遵循的是GNU匯編規(guī)范的。點擊這個可以查看各個偽操作的意思狂鞋,比如:

.global:全局聲明片择;
.macro:定義一個宏;
.align:對齊方式
.text:指定程序在哪個段
...

關于匯編還有很多骚揍,比如書寫匯編代碼字管,內聯匯編,有了目前的基礎信不,相信學起來也是很快的嘲叔。

感謝

  1. https://zh.wikipedia.org/wiki/ARM%E6%9E%B6%E6%A7%8B#cite_note-v8arch-1
  2. https://www.arm.com/files/downloads/ARMv8_Architecture.pdf
  3. http://www.lujun.org.cn/?p=3943
  4. https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/Assembler/000-Introduction/introduction.html#//apple_ref/doc/uid/TP30000851-CH211-SW1

(完)。

demo地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末抽活,一起剝皮案震驚了整個濱河市硫戈,隨后出現的幾起案子,更是在濱河造成了極大的恐慌酌壕,老刑警劉巖掏愁,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件歇由,死亡現場離奇詭異,居然都是意外死亡果港,警方通過查閱死者的電腦和手機沦泌,發(fā)現死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辛掠,“玉大人谢谦,你說我怎么就攤上這事÷荞茫” “怎么了回挽?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長猩谊。 經常有香客問我千劈,道長,這世上最難降的妖魔是什么牌捷? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任墙牌,我火速辦了婚禮,結果婚禮上暗甥,老公的妹妹穿的比我還像新娘喜滨。我一直安慰自己,他們只是感情好撤防,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布虽风。 她就那樣靜靜地躺著,像睡著了一般寄月。 火紅的嫁衣襯著肌膚如雪辜膝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天漾肮,我揣著相機與錄音内舟,去河邊找鬼。 笑死初橘,一個胖子當著我的面吹牛,可吹牛的內容都是我干的充岛。 我是一名探鬼主播保檐,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼崔梗!你這毒婦竟也來了夜只?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蒜魄,失蹤者是張志新(化名)和其女友劉穎扔亥,沒想到半個月后场躯,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡旅挤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年踢关,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粘茄。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡签舞,死狀恐怖,靈堂內的尸體忽然破棺而出柒瓣,到底是詐尸還是另有隱情儒搭,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布芙贫,位于F島的核電站搂鲫,受9級特大地震影響,放射性物質發(fā)生泄漏磺平。R本人自食惡果不足惜魂仍,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望褪秀。 院中可真熱鬧蓄诽,春花似錦、人聲如沸媒吗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闸英。三九已至锯岖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間甫何,已是汗流浹背出吹。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辙喂,地道東北人捶牢。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像巍耗,于是被迫代替她去往敵國和親秋麸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內容