ART世界探險(xiǎn)(2) - 從java byte code說(shuō)起
Dalvik時(shí)代,如果不做JIT的話,只需要了解java字節(jié)碼和Dalivk的字節(jié)碼就夠了。但是,到了ART時(shí)代,我們可能還要至少學(xué)習(xí)兩種新東西:一個(gè)是編譯后端的IR中間代碼。比如,我們假如使用LLVM做為編譯后端的話,需要做從dex到LLVM IR的轉(zhuǎn)換工作。這個(gè)IR可能還不只一層,比如分中層的MIR和底層的LIR。
最后,我們還得了解機(jī)器指令。僅就ARM來(lái)說(shuō),現(xiàn)在是64位時(shí)代了,我們需要了解的就是AArch64和AArch32兩種狀態(tài)下的A64,A32,Thumb2和Thumb四種指令集,還有NEON指令擴(kuò)展等。
Java字節(jié)碼指令集一覽
我們先看一下Android提供的Java指令集簡(jiǎn)表,在這個(gè)網(wǎng)址可以看到:http://androidxref.com/6.0.1_r10/xref/dalvik/docs/java-bytecode.html
一共200條指令。不過(guò)大家千萬(wàn)別被這么多指令嚇到啊,相對(duì)來(lái)說(shuō)絕大部分指令還是非常簡(jiǎn)單的。我們用一講的時(shí)間就可以講個(gè)大概,細(xì)節(jié)將來(lái)遇到再說(shuō)。倒是后面我們講ARM指令的時(shí)候篇幅搞不好要長(zhǎng)一點(diǎn)。
| 字節(jié)碼 | 10進(jìn)制值(相當(dāng)于序號(hào)) | 助記符 |
|---|---|---|
| 0x00 | 0 | nop |
| 0x01 | 1 | aconst_null |
| 0x02 | 2 | iconst_m1 |
| 0x03 | 3 | iconst_0 |
| 0x04 | 4 | iconst_1 |
| 0x05 | 5 | iconst_2 |
| 0x06 | 6 | iconst_3 |
| 0x07 | 7 | iconst_4 |
| 0x08 | 8 | iconst_5 |
| 0x09 | 9 | lconst_0 |
| 0x0a | 10 | lconst_1 |
| 0x0b | 11 | fconst_0 |
| 0x0c | 12 | fconst_1 |
| 0x0d | 13 | fconst_2 |
| 0x0e | 14 | dconst_0 |
| 0x0f | 15 | dconst_1 |
| 0x10 | 16 | bipush |
| 0x11 | 17 | sipush |
| 0x12 | 18 | ldc |
| 0x13 | 19 | ldc_w |
| 0x14 | 20 | ldc2_w |
| 0x15 | 21 | iload |
| 0x16 | 22 | lload |
| 0x17 | 23 | fload |
| 0x18 | 24 | dload |
| 0x19 | 25 | aload |
| 0x1a | 26 | iload_0 |
| 0x1b | 27 | iload_1 |
| 0x1c | 28 | iload_2 |
| 0x1d | 29 | iload_3 |
| 0x1e | 30 | lload_0 |
| 0x1f | 31 | lload_1 |
| 0x20 | 32 | lload_2 |
| 0x21 | 33 | lload_3 |
| 0x22 | 34 | fload_0 |
| 0x23 | 35 | fload_1 |
| 0x24 | 36 | fload_2 |
| 0x25 | 37 | fload_3 |
| 0x26 | 38 | dload_0 |
| 0x27 | 39 | dload_1 |
| 0x28 | 40 | dload_2 |
| 0x29 | 41 | dload_3 |
| 0x2a | 42 | aload_0 |
| 0x2b | 43 | aload_1 |
| 0x2c | 44 | aload_2 |
| 0x2d | 45 | aload_3 |
| 0x2e | 46 | iaload |
| 0x2f | 47 | laload |
| 0x30 | 48 | faload |
| 0x31 | 49 | daload |
| 0x32 | 50 | aaload |
| 0x33 | 51 | baload |
| 0x34 | 52 | caload |
| 0x35 | 53 | saload |
| 0x36 | 54 | istore |
| 0x37 | 55 | lstore |
| 0x38 | 56 | fstore |
| 0x39 | 57 | dstore |
| 0x3a | 58 | astore |
| 0x3b | 59 | istore_0 |
| 0x3c | 60 | istore_1 |
| 0x3d | 61 | istore_2 |
| 0x3e | 62 | istore_3 |
| 0x3f | 63 | lstore_0 |
| 0x40 | 64 | lstore_1 |
| 0x41 | 65 | lstore_2 |
| 0x42 | 66 | lstore_3 |
| 0x43 | 67 | fstore_0 |
| 0x44 | 68 | fstore_1 |
| 0x45 | 69 | fstore_2 |
| 0x46 | 70 | fstore_3 |
| 0x47 | 71 | dstore_0 |
| 0x48 | 72 | dstore_1 |
| 0x49 | 73 | dstore_2 |
| 0x4a | 74 | dstore_3 |
| 0x4b | 75 | astore_0 |
| 0x4c | 76 | astore_1 |
| 0x4d | 77 | astore_2 |
| 0x4e | 78 | astore_3 |
| 0x4f | 79 | iastore |
| 0x50 | 80 | lastore |
| 0x51 | 81 | fastore |
| 0x52 | 82 | dastore |
| 0x53 | 83 | aastore |
| 0x54 | 84 | bastore |
| 0x55 | 85 | castore |
| 0x56 | 86 | sastore |
| 0x57 | 87 | pop |
| 0x58 | 88 | pop2 |
| 0x59 | 89 | dup |
| 0x5a | 90 | dup_x1 |
| 0x5b | 91 | dup_x2 |
| 0x5c | 92 | dup2 |
| 0x5d | 93 | dup2_x1 |
| 0x5e | 94 | dup2_x2 |
| 0x5f | 95 | swap |
| 0x60 | 96 | iadd |
| 0x61 | 97 | ladd |
| 0x62 | 98 | fadd |
| 0x63 | 99 | dadd |
| 0x64 | 100 | isub |
| 0x65 | 101 | lsub |
| 0x66 | 102 | fsub |
| 0x67 | 103 | dsub |
| 0x68 | 104 | imul |
| 0x69 | 105 | lmul |
| 0x6a | 106 | fmul |
| 0x6b | 107 | dmul |
| 0x6c | 108 | idiv |
| 0x6d | 109 | ldiv |
| 0x6e | 110 | fdiv |
| 0x6f | 111 | ddiv |
| 0x70 | 112 | irem |
| 0x71 | 113 | lrem |
| 0x72 | 114 | frem |
| 0x73 | 115 | drem |
| 0x74 | 116 | ineg |
| 0x75 | 117 | lneg |
| 0x76 | 118 | fneg |
| 0x77 | 119 | dneg |
| 0x78 | 120 | ishl |
| 0x79 | 121 | lshl |
| 0x7a | 122 | ishr |
| 0x7b | 123 | lshr |
| 0x7c | 124 | iushr |
| 0x7d | 125 | lushr |
| 0x7e | 126 | iand |
| 0x7f | 127 | land |
| 0x80 | 128 | ior |
| 0x81 | 129 |lor | |
| 0x82 | 130 | ixor |
| 0x83 | 131 | lxor |
| 0x84 | 132 | iinc |
| 0x85 | 133 | i2l |
| 0x86 | 134 | i2f |
| 0x87 | 135 | i2d |
| 0x88 | 136 | l2i |
| 0x89 | 137 | l2f |
| 0x8a | 138 | l2d |
| 0x8b | 139 | f2i |
| 0x8c | 140 | f2l |
| 0x8d | 141 | f2d |
| 0x8e | 142 | d2i |
| 0x8f | 143 | d2l |
| 0x90 | 144 | d2f |
| 0x91 | 145 | i2b |
| 0x92 | 146 | i2c |
| 0x93 | 147 | i2s |
| 0x94 | 148 | lcmp |
| 0x95 | 149 | fcmpl |
| 0x96 | 150 | fcmpg |
| 0x97 | 151 | dcmpl |
| 0x98 | 152 | dcmpg |
| 0x99 | 153 | ifeq |
| 0x9a | 154 | ifne |
| 0x9b | 155 | iflt |
| 0x9c | 156 | ifge |
| 0x9d | 157 | ifgt |
| 0x9e | 158 | ifle |
| 0x9f | 159 | if_icmpeq |
| 0xa0 | 160 | if_icmpne |
| 0xa1 | 161 | if_icmplt |
| 0xa2 | 162 | if_icmpge |
| 0xa3 | 163 | if_icmpgt |
| 0xa4 | 164 | if_icmple |
| 0xa5 | 165 | if_acmpeq |
| 0xa6 | 166 | if_acmpne |
| 0xa7 | 167 | goto |
| 0xa8 | 168 | jsr |
| 0xa9 | 169 | ret |
| 0xaa | 170 | tableswitch |
| 0xab | 171 | lookupswitch |
| 0xac | 172 | ireturn |
| 0xad | 173 | lreturn |
| 0xae | 174 | freturn |
| 0xaf | 175 | dreturn |
| 0xb0 | 176 | areturn |
| 0xb1 | 177 | return |
| 0xb2 | 178 | getstatic |
| 0xb3 | 179 | putstatic |
| 0xb4 | 180 | getfield |
| 0xb5 | 181 | putfield |
| 0xb6 | 182 | invokevirtual |
| 0xb7 | 183 | invokespecial |
| 0xb8 | 184 | invokestatic |
| 0xb9 | 185 | invokeinterface |
| 0xba | 186 | (unused) |
| 0xbb | 187 | new |
| 0xbc | 188 | newarray |
| 0xbd | 189 | anewarray |
| 0xbe | 190 | arraylength |
| 0xbf | 191 | athrow |
| 0xc0 | 192 | checkcast |
| 0xc1 | 193 | instanceof |
| 0xc2 | 194 | monitorenter |
| 0xc3 | 195 | monitorexit |
| 0xc4 | 196 | wide |
| 0xc5 | 197 | multianewarray |
| 0xc6 | 198 | ifnull |
| 0xc7 | 199 | ifnonnull |
| 0xc8 | 200 | goto_w |
| 0xc9 | 201 | jsr_w |
反匯編,學(xué)指令
如果一個(gè)一個(gè)指令地講下來(lái),估計(jì)大家都睡著了。所以我們都過(guò)反匯編我們寫的代碼的方式來(lái)學(xué)習(xí),學(xué)得差不多了,我們?cè)侔阎噶畲幌隆R磺幸詫?shí)用為先,我們嘗試一下吧。
首先我們還是以上一講的empty3例子說(shuō)起。
我們首先用javap工具反匯編一下BuildConfig那個(gè)類:
javap -c com.yunos.system.empty3.BuildConfig
反匯編出來(lái)的代碼如下:
Compiled from "BuildConfig.java"
public final class com.yunos.system.empty3.BuildConfig {
public static final boolean DEBUG;
public static final java.lang.String APPLICATION_ID = "com.yunos.system.empty3";
public static final java.lang.String BUILD_TYPE = "debug";
public static final java.lang.String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final java.lang.String VERSION_NAME = "1.0";
public com.yunos.system.empty3.BuildConfig();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/yunos/system/empty3/BuildConfig;
static {};
Code:
0: ldc #2 // String true
2: invokestatic #3 // Method java/lang/Boolean.parseBoolean:(Ljava/lang/String;)Z
5: putstatic #4 // Field DEBUG:Z
8: return
LineNumberTable:
line 7: 0
}
BuildConfig構(gòu)造方法
我們先看BuildConfig構(gòu)造這段:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
第一條是aload_<n>指令,這個(gè)家族共有4條指令,aload_0, aload_1, aload_2, aload_3.指令代碼從0x2a到0x2d。
這條指令的含義是:從局部變量中,取第n個(gè)的值。取第0個(gè)就是aload_0,第1個(gè)是aload_1。
如果取第4個(gè)怎么辦?這時(shí)候一個(gè)字節(jié)的指令不夠用了,另有一個(gè)aload指令,后面接一個(gè)字節(jié)提供數(shù)字。
相當(dāng)于,aload_0是aload 0指令的簡(jiǎn)寫,但是aload 0占兩個(gè)字節(jié),而aload_0占一個(gè)字節(jié)。
第二條指令是invokespecial,調(diào)用對(duì)象實(shí)例的方法,尤其是調(diào)用super方法,私有方法和構(gòu)造方法。因?yàn)檫@里是要調(diào)用父類的初始化方法,所以正好該是invokespecial.
第三條是return,不帶值返回。
一共就這3條指令,還是挺好理解的吧?
BuildConfig的靜態(tài)部分
學(xué)習(xí)了3條指令的,我們?cè)賹W(xué)一個(gè)4條指令的:
static {};
Code:
0: ldc #2 // String true
2: invokestatic #3 // Method java/lang/Boolean.parseBoolean:(Ljava/lang/String;)Z
5: putstatic #4 // Field DEBUG:Z
8: return
LineNumberTable:
line 7: 0
第一條,ldc,從常量池中將常量讀出來(lái)壓入棧中。JVM是基于棧的,操作數(shù)都是從棧上取,結(jié)果也壓到棧里面去。
第二條,invokestatic,這是我們學(xué)到的第二條invoke類指令了,上一條是invokespecial,這個(gè)顧名思義,就是調(diào)用靜態(tài)方法專用的指令。
第三條,putstatic,將棧中的值,放到靜態(tài)域中。
第四條,return,無(wú)數(shù)據(jù)返回。
流程很簡(jiǎn)單,先從常量池將true讀出來(lái)放到棧里,然后調(diào)用Boolean.parseBoolean,參數(shù)就是剛才放入棧的true字符串,解析好之后的值又入棧。接著,putstatic從棧里讀取這個(gè)boolean的值,寫到DEBUG這個(gè)域中。最后返回。
class的基本結(jié)構(gòu)
因?yàn)槭侨腴T文章,暫時(shí)我們先不講class文件各模塊的細(xì)節(jié),只是先有個(gè)感性認(rèn)識(shí):
- 常量池:class中有常量池,有很多指令是操作常量池的。將常量池中的值讀出來(lái)放到棧中。
- 方法:class文件中,方法是有專門存儲(chǔ)模塊的,invoke集指令去調(diào)用的時(shí)候,從中去查找。
- 域:不管是靜態(tài)域還是對(duì)象實(shí)例中的普通域,我們有很多指令是用來(lái)操作它們的。
- 棧:JVM最重要的結(jié)構(gòu)就是這個(gè)棧,大部分的操作都是通過(guò)這個(gè)棧來(lái)操作。后面學(xué)習(xí)Dalvik指令的時(shí)候我們會(huì)看到,比起JVM中基本都是棧操作的這種指令,Dalvik大量使用了寄存器。
運(yùn)算指令
下面我們?cè)倏戳硪淮箢惖闹噶?,運(yùn)算相關(guān)的指令。
我們還是老辦法,先寫個(gè)例子,然后再反匯編,看它背后的故事。我們先寫個(gè)最簡(jiǎn)單的加法運(yùn)算:
package com.yunos.xulun.testcppjni2;
public class TestART {
public static int add(int a, int b){
return a+b;
}
}
反匯編之后是這樣的:
Compiled from "TestART.java"
public class com.yunos.xulun.testcppjni2.TestART {
public com.yunos.xulun.testcppjni2.TestART();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/yunos/xulun/testcppjni2/TestART;
public static int add(int, int);
Code:
0: iload_0
1: iload_1
2: iadd
3: ireturn
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 a I
0 4 1 b I
}
默認(rèn)生成的構(gòu)造方法,以后我們就略過(guò)不提了。
這個(gè)加法運(yùn)算,一共4條指令:
- iload_0,從棧頂?shù)?個(gè)位置取一個(gè)整數(shù)
- iload_1,從棧頂?shù)?個(gè)位置取一個(gè)整數(shù)
- iadd,將這兩個(gè)整數(shù)相加
- ireturn,返回一個(gè)整數(shù)。
運(yùn)算指令多,是因?yàn)橹噶罴?jí)沒(méi)辦法做泛型,針對(duì)每種類型數(shù)據(jù)都得做一條指令,所以,加法這一個(gè)操作,就得4條指令,分別對(duì)應(yīng)整型,長(zhǎng)整型,單精度,雙精度:
| 指令碼 | 序號(hào) | 助記符 |
|---|---|---|
| 0x60 | 96 | iadd |
| 0x61 | 97 | ladd |
| 0x62 | 98 | fadd |
| 0x63 | 99 | dadd |
指令中,第一個(gè)字符為i的對(duì)應(yīng)整型,l是長(zhǎng)整型,f是單精度,d是雙精度。
當(dāng)然不光加法是這樣,減法,乘法,除法也是一樣。從棧上讀數(shù),轉(zhuǎn)化成什么類型,也是4種類型都要支持。
類型轉(zhuǎn)換
那么一個(gè)問(wèn)題來(lái)了,既然只有4種類型的計(jì)算指令,其它類型怎么辦?
JVM提供了一堆類型轉(zhuǎn)換的指令來(lái)滿足這個(gè)需求。
有一些類型直接連轉(zhuǎn)換都省了,比如short和byte,在JVM里,就是當(dāng)int來(lái)處理。
我們做個(gè)試驗(yàn):
public static int sub(int a, short b){
return a-b;
}
反匯編了之后發(fā)現(xiàn),一個(gè)int跟short,或者是兩個(gè)short相減,跟兩個(gè)int做減法就沒(méi)有區(qū)別:
public static int sub(int, short);
Code:
0: iload_0
1: iload_1
2: isub
3: ireturn
LineNumberTable:
line 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 a I
0 4 1 b S
所以,以后大家就用int吧,不是數(shù)組的話,short跟byte也是int。
為什么?因?yàn)闂>褪且詉nt為單位的??!寄存器的還值得拆成兩個(gè)用,棧真就不需要了。
我們?cè)倏匆粋€(gè)帶類型轉(zhuǎn)換的:
public static long mul(int a, byte b){
return a*b;
}
反匯編之后,出現(xiàn)一條將整型轉(zhuǎn)成長(zhǎng)整型的i2l指令。
public static long mul(int, byte);
Code:
0: iload_0
1: iload_1
2: imul
3: i2l
4: lreturn
LineNumberTable:
line 13: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 a I
0 5 1 b B
因?yàn)榉祷刂狄彩情L(zhǎng)整型了,所以返回指令變成lreturn了。
趁熱打鐵,我們強(qiáng)勢(shì)切入Dalvik指令
對(duì)JVM指令有了初步的理解之后,我們絕不沾沾自喜,迅速看看Dalvik指令是什么樣子的。
先從記憶中把BuildConfig那段翻出來(lái),JVM是這樣的:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
我們看看,轉(zhuǎn)成Dalvik是什么樣的:
00052c: |[00052c] com.yunos.system.empty3.BuildConfig.<init>:()V
00053c: 7010 1100 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0011
000542: 0e00 |0003: return-void
看完了之后有沒(méi)有會(huì)心一笑?invokespecial指令換了個(gè)名,叫invoke-direct,帶一個(gè)v0寄存器的參數(shù),所以aload_0省了。
return換了個(gè)更貼切的名字:return-void。我們前面學(xué)過(guò)了ireturn,lreturn,這個(gè)不帶值的return,確實(shí)叫return-void很合適。
再對(duì)比另一段:
0: ldc #2 // String true
2: invokestatic #3 // Method java/lang/Boolean.parseBoolean:(Ljava/lang/String;)Z
5: putstatic #4 // Field DEBUG:Z
8: return
對(duì)應(yīng)過(guò)來(lái)是:
000508: |[000508] com.yunos.system.empty3.BuildConfig.<clinit>:()V
000518: 1a00 4a00 |0000: const-string v0, "true" // string@004a
00051c: 7110 1000 0000 |0002: invoke-static {v0}, Ljava/lang/Boolean;.parseBoolean:(Ljava/lang/String;)Z // method@0010
000522: 0a00 |0005: move-result v0
000524: 6a00 0200 |0006: sput-boolean v0, Lcom/yunos/system/empty3/BuildConfig;.DEBUG:Z // field@0002
000528: 0e00 |0008: return-void
ldc變成了const-string,就是換個(gè)名,多個(gè)v0寄存器。invokestatic多了個(gè)"-",也是多了個(gè)寄存器參數(shù)。
因?yàn)閕nvoke-static返回的值是在棧里,所以需要一條額外的move-result指令將棧頂值放入寄存器。
putstatic變成了sput,加上類型,變成sput-boolean。
最后return-void.
好,我們?cè)倏聪?,加,減,乘的那幾個(gè):
0f477c: |[0f477c] com.yunos.xulun.testcppjni2.TestART.add:(II)I
0f478c: 9000 0102 |0000: add-int v0, v1, v2
0f4790: 0f00 |0002: return v0
iadd變成了add-int指令,帶有三個(gè)寄存器參數(shù),v1和v2是兩個(gè)加數(shù),和放在v0中。
ireturn變成return v0
減法以此類推:
0f47ac: |[0f47ac] com.yunos.xulun.testcppjni2.TestART.sub:(IS)I
0f47bc: 9100 0102 |0000: sub-int v0, v1, v2
0f47c0: 0f00 |0002: return v0
乘法的增加一條類型轉(zhuǎn)換:
0f4794: |[0f4794] com.yunos.xulun.testcppjni2.TestART.mul:(IB)J
0f47a4: 9200 0203 |0000: mul-int v0, v2, v3
0f47a8: 8100 |0002: int-to-long v0, v0
0f47aa: 1000 |0003: return-wide v0
i2l換了個(gè)馬甲叫int-to-long。
lreturn變成了return-wide。
最后收尾,ARM指令
我們最后看下幾個(gè)計(jì)算函數(shù)生成的機(jī)器代碼吧:
add的機(jī)器代碼
CODE: (code_offset=0x0050151c size_offset=0x00501518 size=76)...
0x0050151c: d1400bf0 sub x16, sp, #0x2000 (8192)
0x00501520: b940021f ldr wzr, [x16]
suspend point dex PC: 0x0000
0x00501524: f81e0fe0 str x0, [sp, #-32]!
0x00501528: f9000ffe str lr, [sp, #24]
0x0050152c: b9002be1 str w1, [sp, #40]
0x00501530: b9002fe2 str w2, [sp, #44]
0x00501534: 79400250 ldrh w16, [tr] (state_and_flags)
0x00501538: 35000130 cbnz w16, #+0x24 (addr 0x50155c)
0x0050153c: b9402be0 ldr w0, [sp, #40]
0x00501540: b9402fe1 ldr w1, [sp, #44]
0x00501544: 0b010002 add w2, w0, w1
0x00501548: b90013e2 str w2, [sp, #16]
0x0050154c: b94013e0 ldr w0, [sp, #16]
0x00501550: f9400ffe ldr lr, [sp, #24]
0x00501554: 910083ff add sp, sp, #0x20 (32)
0x00501558: d65f03c0 ret
0x0050155c: f9421e5e ldr lr, [tr, #1080] (pTestSuspend)
0x00501560: d63f03c0 blr lr
suspend point dex PC: 0x0000
0x00501564: 17fffff6 b #-0x28 (addr 0x50153c)
核心就這一條add w2, w0, w1,其余都是折騰棧和寄存器。指令用的是w[n]而不是x[n],進(jìn)行的是32位的加法。
減法
CODE: (code_offset=0x0050160c size_offset=0x00501608 size=76)...
0x0050160c: d1400bf0 sub x16, sp, #0x2000 (8192)
0x00501610: b940021f ldr wzr, [x16]
suspend point dex PC: 0x0000
0x00501614: f81e0fe0 str x0, [sp, #-32]!
0x00501618: f9000ffe str lr, [sp, #24]
0x0050161c: b9002be1 str w1, [sp, #40]
0x00501620: b9002fe2 str w2, [sp, #44]
0x00501624: 79400250 ldrh w16, [tr] (state_and_flags)
0x00501628: 35000130 cbnz w16, #+0x24 (addr 0x50164c)
0x0050162c: b9402be0 ldr w0, [sp, #40]
0x00501630: b9402fe1 ldr w1, [sp, #44]
0x00501634: 4b010002 sub w2, w0, w1
0x00501638: b90013e2 str w2, [sp, #16]
0x0050163c: b94013e0 ldr w0, [sp, #16]
0x00501640: f9400ffe ldr lr, [sp, #24]
0x00501644: 910083ff add sp, sp, #0x20 (32)
0x00501648: d65f03c0 ret
0x0050164c: f9421e5e ldr lr, [tr, #1080] (pTestSuspend)
0x00501650: d63f03c0 blr lr
suspend point dex PC: 0x0000
0x00501654: 17fffff6 b #-0x28 (addr 0x50162c)
除了加法換成了減法:sub w2, w0, w1,其余基本一樣啊。
乘法
CODE: (code_offset=0x0050158c size_offset=0x00501588 size=88)...
0x0050158c: d1400bf0 sub x16, sp, #0x2000 (8192)
0x00501590: b940021f ldr wzr, [x16]
suspend point dex PC: 0x0000
0x00501594: f81e0fe0 str x0, [sp, #-32]!
0x00501598: f9000ffe str lr, [sp, #24]
0x0050159c: b9002be1 str w1, [sp, #40]
0x005015a0: b9002fe2 str w2, [sp, #44]
0x005015a4: 79400250 ldrh w16, [tr] (state_and_flags)
0x005015a8: 35000190 cbnz w16, #+0x30 (addr 0x5015d8)
0x005015ac: b9402be0 ldr w0, [sp, #40]
0x005015b0: b9402fe1 ldr w1, [sp, #44]
0x005015b4: 1b017c02 mul w2, w0, w1
0x005015b8: b9000fe2 str w2, [sp, #12]
0x005015bc: b9400fe0 ldr w0, [sp, #12]
0x005015c0: 93407c01 sxtw x1, w0
0x005015c4: f800c3e1 stur x1, [sp, #12]
0x005015c8: f840c3e0 ldur x0, [sp, #12]
0x005015cc: f9400ffe ldr lr, [sp, #24]
0x005015d0: 910083ff add sp, sp, #0x20 (32)
0x005015d4: d65f03c0 ret
0x005015d8: f9421e5e ldr lr, [tr, #1080] (pTestSuspend)
0x005015dc: d63f03c0 blr lr
suspend point dex PC: 0x0000
0x005015e0: 17fffff3 b #-0x34 (addr 0x5015ac)
首先,是mul指令:mul w2, w0, w1
另外,還有一條是將32位整數(shù)轉(zhuǎn)成64位的長(zhǎng)整型,請(qǐng)注意,32位的w寄存器之外,64位的x寄存器出來(lái)干活了。
sxtw x1, w0:是將w0中的32位值擴(kuò)展成64位的值,結(jié)果放在x1 64位寄存器中。
基本概念我們先說(shuō)這么多,分支,異常等高級(jí)話題,下面分別討論。
最后我們會(huì)cover到完整的指令集。