全局变量和常量
获取全局变量和常量时,都是获取地址,一般会adrp add两条指令组合调用。
0x1047c2188 adrp x0, 2 //找到某一页,0x1047c2000+2000=0x1047c4000
add X0, #0xf8d //加上偏移量,x0=0x1047c4000+0xf8d=0x1047c4f8d
if判断和循环
if判断
调用comp 和b.xx 指令,其实是做减法,不影响目标寄存器,只影响标志寄存器。
- BL 标号:跳转到标号处执行
- B.LT 标号:比价结果是小于(less than),执行标号,否则不跳转
- B.LE 标号:比较结果是小于等于(less than or qeual to),执行标号,否则不跳转
- B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转
- B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转
- B.EQ 标号:比较结果是等于(equal to),执行标号,否则不跳转
- B.NE 标号:比较结果是不等于(not equal to),执行标号,否则不跳转
- B.LS 标号:比较结果是无符号小于等于,执行标号,否则不跳转
- B.LO 标号:比较结果是无符号小于,执行标号,否则不跳转
- B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
- B.HS 标号:比较结果是无符号大于等于,执行标号,否则不跳转
void compare(){
int a = 5;
int b = 12;
if (a > b) {
NSLog(@"a大于b");
}else if (a == b) {
NSLog(@"a等于b");
}else{
NSLog(@"a小于b");
}
}
ZZZ`compare:
0x1047c589c <+0>: sub sp, sp, #0x20 ; =0x20
0x1047c58a0 <+4>: stp x29, x30, [sp, #0x10]
0x1047c58a4 <+8>: add x29, sp, #0x10 ; =0x10
0x1047c58a8 <+12>: mov w8, #0x5
-> 0x1047c58ac <+16>: stur w8, [x29, #-0x4]
0x1047c58b0 <+20>: mov w8, #0xc
0x1047c58b4 <+24>: str w8, [sp, #0x8]
0x1047c58b8 <+28>: ldur w8, [x29, #-0x4] ;w8=0x5
0x1047c58bc <+32>: ldr w9, [sp, #0x8] ;w9=0xc
0x1047c58c0 <+36>: cmp w8, w9 //比较w8, w9
0x1047c58c4 <+40>: b.le 0x1047c58d8 ; <+60> at main.m:21:15 如果小于等于,跳转到0x1047c58d8,否则往下执行
0x1047c58c8 <+44>: adrp x0, 3
0x1047c58cc <+48>: add x0, x0, #0x190 ; =0x190
0x1047c58d0 <+52>: bl 0x1047c5cb4 ; symbol stub for: NSLog 打印 a大于b
0x1047c58d4 <+56>: b 0x1047c5904 ; <+104> at main.m:26:1 跳转到0x1047c5904
0x1047c58d8 <+60>: ldur w8, [x29, #-0x4]
0x1047c58dc <+64>: ldr w9, [sp, #0x8]
0x1047c58e0 <+68>: cmp w8, w9
0x1047c58e4 <+72>: b.ne 0x1047c58f8 ; <+92> at main.m 如果不等于,跳转到0x1047c58f8
0x1047c58e8 <+76>: adrp x0, 3
0x1047c58ec <+80>: add x0, x0, #0x1b0 ; =0x1b0
0x1047c58f0 <+84>: bl 0x1047c5cb4 ; symbol stub for: NSLog 打印等于
0x1047c58f4 <+88>: b 0x1047c5904 ; <+104> at main.m:26:1 跳转到0x1047c5904
0x1047c58f8 <+92>: adrp x0, 3
0x1047c58fc <+96>: add x0, x0, #0x1d0 ; =0x1d0
0x1047c5900 <+100>: bl 0x1047c5cb4 ; symbol stub for: NSLog 打印不等于
0x1047c5904 <+104>: ldp x29, x30, [sp, #0x10]
0x1047c5908 <+108>: add sp, sp, #0x20 ; =0x20
0x1047c590c <+112>: ret
循环
- do while 循环:判断条件在后面,满足条件往外跳
- for循环和while循环很像,判断条件在里面,不满足就往外面跳。
Switch
先来一个switch的C与汇编代码的对比
void test(){
int a=5;
switch (a) {
case 3:
NSLog(@"苹果");
break;
case 5:
NSLog(@"香蕉");
break;
case 8:
NSLog(@"橘子");
break;
case 10:
NSLog(@"桃子");
break;
default:
NSLog(@"不知道");
break;
}
}
ZZZ`test:
0x100505830 <+0>: sub sp, sp, #0x20 ; =0x20
0x100505834 <+4>: stp x29, x30, [sp, #0x10]
0x100505838 <+8>: add x29, sp, #0x10 ; =0x10
0x10050583c <+12>: mov w8, #0x5 ;w8=5
-> 0x100505840 <+16>: stur w8, [x29, #-0x4] ;把5存到[x29, #-0x4]
0x100505844 <+20>: ldur w8, [x29, #-0x4] ;取出[x29, #-0x4]上的值,给到w8
0x100505848 <+24>: subs w8, w8, #0x3 ; =0x3 w8=w8-0x3=2,可以发现汇编用给定的5减去case中最下的case
0x10050584c <+28>: mov x9, x8 ;x9=0x2
0x100505850 <+32>: ubfx x9, x9, #0, #32 ;把x9的高32位清零,赋值给x9,x9=0x2
0x100505854 <+36>: cmp x9, #0x7 ;x9和0x7比较,这里2=5-3,7=10-3,也就是最大的case-最小的case
0x100505858 <+40>: str x9, [sp] //2存到[sp]内存上
0x10050585c <+44>: b.hi 0x1005058b8 ; <+136> at main.m hi是无符号大于,相当于判断是否在
条件的取值范围内,如果不在,直接跳转到break处
0x100505860 <+48>: adrp x8, 0
0x100505864 <+52>: add x8, x8, #0x8d0 ; =0x8d0 x8=0x1005058d0,此处为当前函数最下方下面的地址
0x100505868 <+56>: ldr x11, [sp] ;从[sp]上取值赋给x11,上面可知此处存的值是2,x11=2
0x10050586c <+60>: ldrsw x10, [x8, x11, lsl #2] ;指令意思是:x11左移2位,加上x8,这块内存的值赋给x10.
x10=[x8+0x200]=[0x1005058d0+0x200]=[0x100505ad0],
经过查看寄存器的值x10=0xffffffffffffffb8
0x100505870 <+64>: add x9, x8, x10 ;x9=0x1005058d0+0xffffffffffffffb8=0x0000000105005888
0x100505874 <+68>: br x9
0x100505878 <+72>: adrp x0, 3
0x10050587c <+76>: add x0, x0, #0x1f0 ; =0x1f0
0x100505880 <+80>: bl 0x100505c94 ; symbol stub for: NSLog
0x100505884 <+84>: b 0x1005058c4 ; <+148> at main.m:47:1
0x100505888 <+88>: adrp x0, 3
0x10050588c <+92>: add x0, x0, #0x210 ; =0x210
0x100505890 <+96>: bl 0x100505c94 ; symbol stub for: NSLog,调用NSLog函数
0x100505894 <+100>: b 0x1005058c4 ; <+148> at main.m:47:1,跳转至0x1005058c4,结束函数
0x100505898 <+104>: adrp x0, 3
0x10050589c <+108>: add x0, x0, #0x230 ; =0x230
0x1005058a0 <+112>: bl 0x100505c94 ; symbol stub for: NSLog
0x1005058a4 <+116>: b 0x1005058c4 ; <+148> at main.m:47:1
0x1005058a8 <+120>: adrp x0, 3
0x1005058ac <+124>: add x0, x0, #0x250 ; =0x250
0x1005058b0 <+128>: bl 0x100505c94 ; symbol stub for: NSLog
0x1005058b4 <+132>: b 0x1005058c4 ; <+148> at main.m:47:1
0x1005058b8 <+136>: adrp x0, 3
0x1005058bc <+140>: add x0, x0, #0x270 ; =0x270
0x1005058c0 <+144>: bl 0x100505c94 ; symbol stub for: NSLog
0x1005058c4 <+148>: ldp x29, x30, [sp, #0x10]
0x1005058c8 <+152>: add sp, sp, #0x20 ; =0x20
0x1005058cc <+156>: ret
经过多次的实验,发现switch会对代码进行优化:
- 假设switch语句的分支比较少的时候(例如3,少于4的时候没有意义)没有必要使用此结构,相当于if。
- 各个分支常量的差值较大的时候,编译器会在效率还是内存进行取舍,这个时候编译器还是会编译成类似于if,else的结构。
- 在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节),根据给定值与最小case的差值,作为偏移量,从表中查结果,不用每次都去比较了。