1、cmp(Compare)比较指令
CMP 把一个寄存器的内容和另一个寄存器的内容或立即数进行比较。但不存储结果,只是正确的更改标志。
一般CMP做完判断后会进行跳转,后面通常会跟上B指令!
- BL 标号:跳转到标号处执行
- B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转
- B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转
- B.EQ 标号:比较结果是等于,执行标号,否则不跳转
- B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
1.1、if条件判断
int g = 12;
void func(int a,int b){
if (a > b) {
g = a;
}else{
g = b;
}
}
int main(int argc, char * argv[]) {
func(1, 2);
return 0;
}
func汇编
002--if的识别`func:
0x10419dfec <+0>: sub sp, sp, #0x10 ; =0x10
-> 0x10419dff0 <+4>: str w0, [sp, #0xc]
0x10419dff4 <+8>: str w1, [sp, #0x8]
0x10419dff8 <+12>: ldr w8, [sp, #0xc]
0x10419dffc <+16>: ldr w9, [sp, #0x8]
0x10419e000 <+20>: subs w8, w8, w9
0x10419e004 <+24>: b.le 0x10419e018 ; <+44> at main.m:17:13
0x10419e008 <+28>: ldr w8, [sp, #0xc]
0x10419e00c <+32>: adrp x9, 7
0x10419e010 <+36>: str w8, [x9, #0x498]
0x10419e014 <+40>: b 0x10419e024 ; <+56> at main.m:19:1
0x10419e018 <+44>: ldr w8, [sp, #0x8]
0x10419e01c <+48>: adrp x9, 7
0x10419e020 <+52>: str w8, [x9, #0x498]
0x10419e024 <+56>: add sp, sp, #0x10 ; =0x10
0x10419e028 <+60>: ret
1.2、do while循环
判断条件b.lt
void func() {
int nSum = 0;
int i = 0;
do {
nSum = nSum + 1;
i ++;
} while (i < 100);
}
汇编代码
003--Loop`func:
-> 0x1007fdf6c <+0>: sub sp, sp, #0x10 ; =0x10
0x1007fdf70 <+4>: str wzr, [sp, #0xc]
0x1007fdf74 <+8>: str wzr, [sp, #0x8]
// do while 循环
0x1007fdf78 <+12>: ldr w8, [sp, #0xc]
0x1007fdf7c <+16>: add w8, w8, #0x1 ; =0x1
0x1007fdf80 <+20>: str w8, [sp, #0xc]
0x1007fdf84 <+24>: ldr w8, [sp, #0x8]
0x1007fdf88 <+28>: add w8, w8, #0x1 ; =0x1
0x1007fdf8c <+32>: str w8, [sp, #0x8]
0x1007fdf90 <+36>: ldr w8, [sp, #0x8]
// 与100 比较
0x1007fdf94 <+40>: subs w8, w8, #0x64 ; =0x64
0x1007fdf98 <+44>: b.lt 0x1007fdf78 ; <+12> at main.m:14:16
0x1007fdf9c <+48>: add sp, sp, #0x10 ; =0x10
0x1007fdfa0 <+52>: ret
1.3、while循环
判断条件b.ge
void func() {
int nSum = 0;
int i = 0;
while (i < 100) {
nSum = nSum + 1;
i++;
}
}
汇编代码
003--Loop`func:
-> 0x100c65f68 <+0>: sub sp, sp, #0x10 ; =0x10
0x100c65f6c <+4>: str wzr, [sp, #0xc]
0x100c65f70 <+8>: str wzr, [sp, #0x8]
0x100c65f74 <+12>: ldr w8, [sp, #0x8]
// 与100 比较 while循环
0x100c65f78 <+16>: subs w8, w8, #0x64 ; =0x64
0x100c65f7c <+20>: b.ge 0x100c65f9c ; <+52> at main.m:17:1
0x100c65f80 <+24>: ldr w8, [sp, #0xc]
0x100c65f84 <+28>: add w8, w8, #0x1 ; =0x1
0x100c65f88 <+32>: str w8, [sp, #0xc]
0x100c65f8c <+36>: ldr w8, [sp, #0x8]
0x100c65f90 <+40>: add w8, w8, #0x1 ; =0x1
0x100c65f94 <+44>: str w8, [sp, #0x8]
0x100c65f98 <+48>: b 0x100c65f74 ; <+12> at main.m:13:12
// 循环结束
0x100c65f9c <+52>: add sp, sp, #0x10 ; =0x10
0x100c65fa0 <+56>: ret
1.4、for 循环
for 循环和while循环相似
2、Switch
1、假设switch语句的分支比较少的时候(例如3,少于4的时候没有意义)没有必要使用此结构,相当于if。
2、各个分支常量的差值较大的时候,编译器会在效率还是内存进行取舍,这个时候编译器还是会编译成类似于if,else的结构。
3、在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节)。
2.1、case条件为三个的时候
void funcA(int a){
switch (a) {
case 1:
printf("1");
break;
case 2:
printf("2");
break;
case 3:
printf("3");
break;
default:
printf("0");
break;
}
}
对应汇编代码
004--选择`funcA:
-> 0x100255eec <+0>: sub sp, sp, #0x20 ; =0x20
0x100255ef0 <+4>: stp x29, x30, [sp, #0x10]
0x100255ef4 <+8>: add x29, sp, #0x10 ; =0x10
0x100255ef8 <+12>: stur w0, [x29, #-0x4]
0x100255efc <+16>: ldur w8, [x29, #-0x4]
0x100255f00 <+20>: str w8, [sp, #0x8]
0x100255f04 <+24>: subs w8, w8, #0x1 ; =0x1
// case 1
0x100255f08 <+28>: b.eq 0x100255f28 ; <+60> at main.m
0x100255f0c <+32>: ldr w8, [sp, #0x8]
0x100255f10 <+36>: subs w8, w8, #0x2 ; =0x2
// case 2
0x100255f14 <+40>: b.eq 0x100255f38 ; <+76> at main.m
0x100255f18 <+44>: ldr w8, [sp, #0x8]
0x100255f1c <+48>: subs w8, w8, #0x3 ; =0x3
// case 3
0x100255f20 <+52>: b.eq 0x100255f48 ; <+92> at main.m
0x100255f24 <+56>: b 0x100255f58 ; <+108> at main.m
0x100255f28 <+60>: adrp x0, 2
0x100255f2c <+64>: add x0, x0, #0xe0f ; =0xe0f
0x100255f30 <+68>: bl 0x100256310 ; symbol stub for: printf
0x100255f34 <+72>: b 0x100255f64 ; <+120> at main.m:30:1
0x100255f38 <+76>: adrp x0, 2
0x100255f3c <+80>: add x0, x0, #0xe16 ; =0xe16
0x100255f40 <+84>: bl 0x100256310 ; symbol stub for: printf
0x100255f44 <+88>: b 0x100255f64 ; <+120> at main.m:30:1
0x100255f48 <+92>: adrp x0, 2
0x100255f4c <+96>: add x0, x0, #0xe1d ; =0xe1d
0x100255f50 <+100>: bl 0x100256310 ; symbol stub for: printf
0x100255f54 <+104>: b 0x100255f64 ; <+120> at main.m:30:1
0x100255f58 <+108>: adrp x0, 2
0x100255f5c <+112>: add x0, x0, #0xe24 ; =0xe24
0x100255f60 <+116>: bl 0x100256310 ; symbol stub for: printf
0x100255f64 <+120>: ldp x29, x30, [sp, #0x10]
0x100255f68 <+124>: add sp, sp, #0x20 ; =0x20
0x100255f6c <+128>: ret
2.2、case大于3个
1、现将参数,减去最新case
2、通过表直接映射到具体的case
void funcA(int a){
switch (a) {//
case 1:
printf("a");
break;
case 2:
printf("b");
break;
case 3:
printf("c");
break;
case 5:
printf("d");
break;
default:
printf("0");
break;
}
}
funcA(4);
对应的汇编代码
004--选择`funcA:
0x100a2deb4 <+0>: sub sp, sp, #0x20 ; =0x20
0x100a2deb8 <+4>: stp x29, x30, [sp, #0x10]
0x100a2debc <+8>: add x29, sp, #0x10 ; =0x10
0x100a2dec0 <+12>: stur w0, [x29, #-0x4]
0x100a2dec4 <+16>: ldur w8, [x29, #-0x4]
0x100a2dec8 <+20>: subs w8, w8, #0x1 ; =0x1
0x100a2decc <+24>: str x8, [sp]
0x100a2ded0 <+28>: subs x8, x8, #0x4 ; =0x4
0x100a2ded4 <+32>: b.hi 0x100a2df34 ; <+128> at main.m
// 开始查表
0x100a2ded8 <+36>: ldr x11, [sp]
0x100a2dedc <+40>: adrp x10, 0
0x100a2dee0 <+44>: add x10, x10, #0xf4c ; =0xf4c
0x100a2dee4 <+48>: adr x8, #0x0 ; <+48> at main.m:12:5
0x100a2dee8 <+52>: ldrsw x9, [x10, x11, lsl #2]
-> 0x100a2deec <+56>: add x8, x8, x9
// 获取跳转地址br
0x100a2def0 <+60>: br x8
0x100a2def4 <+64>: adrp x0, 2
0x100a2def8 <+68>: add x0, x0, #0xdff ; =0xdff
0x100a2defc <+72>: bl 0x100a2e300 ; symbol stub for: printf
0x100a2df00 <+76>: b 0x100a2df40 ; <+140> at main.m:30:1
0x100a2df04 <+80>: adrp x0, 2
0x100a2df08 <+84>: add x0, x0, #0xe06 ; =0xe06
0x100a2df0c <+88>: bl 0x100a2e300 ; symbol stub for: printf
0x100a2df10 <+92>: b 0x100a2df40 ; <+140> at main.m:30:1
0x100a2df14 <+96>: adrp x0, 2
0x100a2df18 <+100>: add x0, x0, #0xe0d ; =0xe0d
0x100a2df1c <+104>: bl 0x100a2e300 ; symbol stub for: printf
0x100a2df20 <+108>: b 0x100a2df40 ; <+140> at main.m:30:1
0x100a2df24 <+112>: adrp x0, 2
0x100a2df28 <+116>: add x0, x0, #0xe14 ; =0xe14
0x100a2df2c <+120>: bl 0x100a2e300 ; symbol stub for: printf
0x100a2df30 <+124>: b 0x100a2df40 ; <+140> at main.m:30:1
0x100a2df34 <+128>: adrp x0, 2
0x100a2df38 <+132>: add x0, x0, #0xe1b ; =0xe1b
0x100a2df3c <+136>: bl 0x100a2e300 ; symbol stub for: printf
0x100a2df40 <+140>: ldp x29, x30, [sp, #0x10]
0x100a2df44 <+144>: add sp, sp, #0x20 ; =0x20
0x100a2df48 <+148>: ret
ldrsw指令
ldrsw x9, [x10, x11, lsl #2]
新的地址值 = x10的地址值 + (x11的值左移两位)
x9 = 取出新的地址值所指向的内容
adrp指令
0x104089f34 <+128>: adrp x0, 2
0x104089f38 <+132>: add x0, x0, #0xe1b ; =0xe1b
- 将PC寄存器的低12位清零,得到 0x0000000104089000 (二进制的1位对应16进制的4位,计算机中的内存地址一般用16进制表示,0x开头表示16进制数,所以后16进制数的后三位清零)
注:PC寄存器中存储的地址是当前要执行的指令地址,低12位清零,代表当前内存页的起始地址
2、立即数2代表页数,arm64中每一页为4K,即2的12次方。一个数乘以2的12次方,计算机中即是左移12位,即低12位为0。16进制中即低3位为0.
3、将上述步骤一和步骤二中得到的16进制数相加,得到0x000000010408b000,将0x000000010408b000放入 x0 寄存器
4、将x0寄存器中的地址值加上0xe1b,得到的结果放入到x0寄存器。
网友评论