iOS-基础的汇编指令(二)

351 阅读7分钟

全局变量和常量

获取全局变量和常量时,都是获取地址,一般会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会对代码进行优化:

  1. 假设switch语句的分支比较少的时候(例如3,少于4的时候没有意义)没有必要使用此结构,相当于if。
  2. 各个分支常量的差值较大的时候,编译器会在效率还是内存进行取舍,这个时候编译器还是会编译成类似于if,else的结构。
  3. 在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节),根据给定值与最小case的差值,作为偏移量,从表中查结果,不用每次都去比较了。