美文网首页
汇编(四)

汇编(四)

作者: 浅墨入画 | 来源:发表于2021-04-11 19:22 被阅读0次

    前言:

    内存分区是编译器帮我们做的,属于硬件相关。
    macho文件是由代码、数据、macho的描述信息等组成
    进行下面的学习之前请先了解一下内存五大区
    接下来让我们一起来探索全局变量、常量 、静态区的内存

    一. 全局变量和常量

    创建空工程Demo,为了防止ViewController中OC代码的干扰,我们在main.m文件中编写代码探讨

    // main.m文件代码如下
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    int g = 12;
    
    int func(int a,int b){
        printf("haha");
        int c = a + g;
        return c;
    }
    
    int main(int argc, char * argv[]) {
        func(1, 2);
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    
    image.png

    继续执行上面汇编代码到这一步

    image.png
    // func函数的汇编代码如下
    Demo`func:
    ->  0x104b82170 <+0>:  sub    sp, sp, #0x20             ; =0x20 
        0x104b82174 <+4>:  stp    x29, x30, [sp, #0x10]
        0x104b82178 <+8>:  add    x29, sp, #0x10            ; =0x10 
        // 1 2 两个参数分别入栈
        0x104b8217c <+12>: stur   w0, [x29, #-0x4]
        0x104b82180 <+16>: str    w1, [sp, #0x8]
        // x0寄存器中存的是地址,这个地址是4个字节,存的是 haha
        // 下面这两条指令是怎么得到0x0000000104b83f8d内存地址呢?
        // adrp 全称address page,按页寻找内存地址  这里是先将1这个值左移12位得到0x1000,再把pc寄存器0x104b82184低12位清0得到0x104b82000,两者再相加得到0x104b83000
        // 0x104b83000 再添加0xf8d 得到0x0000000104b83f8d
        0x104b82184 <+20>: adrp   x0, 1
        0x104b82188 <+24>: add    x0, x0, #0xf8d            ; =0xf8d 
        // 调用printf函数
        0x104b8218c <+28>: bl     0x104b825ac               ; symbol stub for: printf
        // 这里拿到参数a 也就是 1 
        0x104b82190 <+32>: ldur   w8, [x29, #-0x4]
        // 这里通过偏移得到 0x104b89570,也就是参数g值为12 
        0x104b82194 <+36>: adrp   x9, 7
        0x104b82198 <+40>: add    x9, x9, #0x570            ; =0x570 
        // 把x9 读到w10寄存器
        0x104b8219c <+44>: ldr    w10, [x9]
        0x104b821a0 <+48>: add    w8, w8, w10
        0x104b821a4 <+52>: str    w8, [sp, #0x4]
        0x104b821a8 <+56>: ldr    w8, [sp, #0x4]
        // 把计算的结果给到x0寄存器,作为返回值
        0x104b821ac <+60>: mov    x0, x8
        0x104b821b0 <+64>: ldp    x29, x30, [sp, #0x10]
        0x104b821b4 <+68>: add    sp, sp, #0x20             ; =0x20 
        0x104b821b8 <+72>: ret 
    

    上面执行完 0x104b82184 <+20>: adrp x0, 1得到0x104b83000,fff是4095加上0就是4096 刚好对应4k,而一页大小是4096,所以这里的0x104b83000刚好是某一页的开始,adrp就意味着是先找到某一页的地址,再加上偏移量#0xf8d找到x0 haha的地址

    // 终端执行PAGESIZE得到4096,说明一页大小是4096
    hello-world$ PAGESIZE
    4096
    
    • 页号是编译器编译的时候确定的
    • 0x104b82184 <+20>: adrp x0, 1的参照是pc寄存器0x104b82184,以当前这个代码段的地址作为参照
    • 其中偏移值作为参照有两种方式, 第一种从文件起始位置偏移,第二种是当前代码地址偏移,这里是通过当前代码地址偏移进行计算

    这里为什么不能直接加到字符串的位置?

    • 因为0x104b82184地址中的前4位 104b是随机获取的,这里只能通过偏移值来找到字符串位置

    这里我们做个练习加深对adrp指令的理解?0x1002bf888 <+20>: adrp x0, 2

    • 这里0x是16进制,低12位这里的位指的是bit,也就是后面三个数清0
    • 这里是先将2这个值左移12位得到0x2000
    • 再把pc寄存器0x1002bf888低12位清0得到0x1002bf000
    • 两者再相加得到0x1002c1000

    小结
    全局adrp与常量drp是一样的,都是通过基值加上偏移来获取。上面两处adrp没法区分是全局变量还是常量,但是可以拿到内存地址在macho中查看是全局变量还是常量。

    二. 还原高级代码

    下面我们通过反汇编来还原高级代码,这里需要借助工具Hopper,首先修改代码如下

    // main.m文件代码如下
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    int g = 12;
    
    int func(int a,int b){
        printf("haha");
        int c = a + g + b;
        return c;
    }
    
    int main(int argc, char * argv[]) {
        func(10, 20);
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    

    编译Demo工程,选中Products目录下Demo.app -> Show in Finder -> 右键显示包内容 -> 找到可执行文件Demo -> 把Demo拖入Hopper

    image.png image.png

    下面我们通过上面工具的汇编代码来还原高级代码

    image.png image.png image.png
            ; ================ B E G I N N I N G   O F   P R O C E D U R E ================
                         _func:
    // 函数的开始
    0000000100006168         sub        sp, sp, #0x20                               ; CODE XREF=_main+32
    000000010000616c         stp        x29, x30, [sp, #0x10]
    0000000100006170         add        x29, sp, #0x10
    
    // 从这里可以看出函数为两个参数
    0000000100006174         stur       w0, [x29, #-0x4]
    0000000100006178         str        w1, [sp, #0x8]
    // 还原出代码为 func(int a,int b)
    func(int a,int b) { }
    
    // 这一段代码调用的是 printf函数
    000000010000617c         adrp       x0, #0x100007000                            ; argument #1 for method imp___stubs__printf
    // 工具直接定位出常量 haha
    0000000100006180         add        x0, x0, #0xf8d                              ; "haha"
    0000000100006184         bl         imp___stubs__printf
    // 注意这里的0x100007000 是工具已经帮我们计算好了偏移量之后的值,执行完add 计算出内存地址 0000000100007f8d
    // 根据Hopper工具,我们可以得出地址为0000000100007f8d  值为haha
    // 还原出代码为printf("haha");
    printf("haha");
    
    0000000100006188         ldur       w8, [x29, #-0x4]
    // 还原出代码为 int w8 = a;
    int w8 = a;
    
    000000010000618c         adrp       x9, #0x10000d000
    // 这里也定位出全局变量_g
    0000000100006190         add        x9, x9, #0x570                              ; _g
    // 根据Hopper工具,我们可以得出地址为000000010000d570 值为0x0c也就是12
    // 还原出代码为 int g = 12; 注意⚠️ 因为是全局变量(使用烂苹果软件MachOView看出是在data区被改动,所以是全局变量),要写在func函数外面
    g;
    
    0000000100006194         ldr        w10, x9
    // 还原出代码为 int w10 = g;
    int w10 = g;
    
    0000000100006198         add        w8, w8, w10
    // 还原出代码为 w8 += w10;
    w8 += w10;
    
    000000010000619c         ldr        w10, [sp, #0x8]
    // 还原出代码为 int w10 = b;
    w10 = b;
    
    00000001000061a0         add        w8, w8, w10
    // 还原出代码为 w8 += w10;
    w8 += w10;
    
    00000001000061a4         str        w8, [sp, #0x4]
    00000001000061a8         ldr        w8, [sp, #0x4]
    00000001000061ac         mov        x0, x8
    // 还原出代码为 return w8;
    return w8;
    
    // 函数的结束
    00000001000061b0         ldp        x29, x30, [sp, #0x10]
    00000001000061b4         add        sp, sp, #0x20
    00000001000061b8         ret
    

    根据上面汇编代码还原简化为下面代码

    int g = 12;
    func(int a,int b) {
        printf("haha");
        int w8 = a;
        int w10 = g;
        w8 += w10;
        w10 = b;
        w8 += w10;
        return w8;
    }
    
    // 接着简化代码如下
    int g = 12;
    func(int a,int b) {
        printf("haha");
        return b + g + a;
    }
    

    通过最终还原的代码与上面代码比对,发现汇编代码还原高级语言代码成功。注意⚠️ 还原代码必须从下往上还原,这就是逆向

    三. if的识别

    创建空工程002--if的识别,我们还在main.m文件中编写代码探讨

    // main.m文件代码如下
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    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 UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    

    编译002--if的识别工程,找到可执行文件拖入Hopper,如果不是真机编译,会出现下图情况

    image.png image.png

    接下来我们用真机编译,可执行文件拖入Hopper

    image.png
    // func汇编代码如下
       ; ================ B E G I N N I N G   O F   P R O C E D U R E ================
                         _func:
    0000000100006188         sub        sp, sp, #0x10                               ; CODE XREF=_main+32
    000000010000618c         str        w0, [sp, #0xc]
    0000000100006190         str        w1, [sp, #0x8]
    0000000100006194         ldr        w8, [sp, #0xc]
    0000000100006198         ldr        w9, [sp, #0x8]
    // 通过尾数判断func函数,无返回值
    // 上面还原出代码为 void func(int a, int b) {  int w8 = a; int w9 = b; }
    void func(int a, int b) { 
        int w8 = a;
        int w9 = b;
    }
    
    // 两个参数的比较,cmp执行的是减法 不影响目标寄存器w8 w9,只看减法的结果,修改标记寄存器
    000000010000619c         cmp        w8, w9
    // b.le 相当于 小于等于
    00000001000061a0         b.le       loc_1000061b8
    // 还原出代码 if (w8 - w9) { }
    if (w8 - w9) {  // 小于等于
    }
    
    // 执行大于逻辑
    00000001000061a4         ldr        w8, [sp, #0xc]
    00000001000061a8         adrp       x9, #0x10000d000
    00000001000061ac         add        x9, x9, #0x568                              ; _g
    00000001000061b0         str        w8, x9
    00000001000061b4         b          loc_1000061c8
    // 还原出代码 g = b;
    g = b;
    
    // 执行小于等于的逻辑
                         loc_1000061b8:
    00000001000061b8         ldr        w8, [sp, #0x8]                              ; CODE XREF=_func+24
    00000001000061bc         adrp       x9, #0x10000d000
    00000001000061c0         add        x9, x9, #0x568                              ; _g
    // 根据Hopper工具,我们可以得出地址为000000010000d568 值为0x0c也就是12,还原出代码为 int g = 12;
    // 把w8 写入 x9,也就是写入g中
    // 这里只能看出w8是4个字节,因为是低32位,这里有可能是Int 也有可能是uInt
    00000001000061c4         str        w8, x9
    // 还原出代码 g = w8;
    g = w8;
    
                         loc_1000061c8:
    00000001000061c8         add        sp, sp, #0x10                               ; CODE XREF=_func+44
    00000001000061cc         ret
                         ; endp
    
    // 最终通过汇编代码还原出的高级代码如下
    int g = 12;
    void func2(int a,int b){
        if (a > b) {
            g = a;
        }else{
            g = b;
        }
    }
    

    小结

    cmp(Compare)比较指令

    CMP 把一个寄存器的内容和另一个寄存器的内容或立即数进行比较。但不存储结果,只是正确的更改标志。一般CMP做完判断后会进行跳转,后面通常会跟上B指令!

    • BL 标号:跳转到标号处执行
    • B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转
    • B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转
    • B.EQ 标号:比较结果是等于,执行标号,否则不跳转
    • B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
    • B.LT 标号:比较结果是小于,执行标号,否则不跳转
    • B.LE(less equal) 标号:比较结果是小于等于,执行标号,否则不跳转
    • B.NE(not equal) 标号:比较结果是不等于,执行标号,否则不跳转

    注意⚠️ cmp指令下面执行的是 else 条件

    四. 循环

    创建空工程003--Loop,我们还在main.m文件中编写代码探讨

    // main.m文件代码如下
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    int main(int argc, char * argv[]) {
        int nSum = 0;
        int i = 0;
        do {
            nSum = nSum + 1;
            i++;
        } while (i < 100);  
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    

    真机编译,可执行文件拖入Hopper,获取汇编代码

    // main汇编代码如下
                              _main:
    00000001000061b8         sub        sp, sp, #0x40
    00000001000061bc         stp        x29, x30, [sp, #0x30]
    00000001000061c0         add        x29, sp, #0x30
    00000001000061c4         stur       wzr, [x29, #-0x4]
    00000001000061c8         stur       w0, [x29, #-0x8]
    00000001000061cc         stur       x1, [x29, #-0x10]
    00000001000061d0         stur       wzr, [x29, #-0x14]
    // 把0放入一个内存地址中
    00000001000061d4         str        wzr, [sp, #0x18]
    
                         loc_1000061d8:
    // 这里是do的操作
    00000001000061d8         ldur       w8, [x29, #-0x14]                           ; CODE XREF=_main+64
    00000001000061dc         add        w8, w8, #0x1
    00000001000061e0         stur       w8, [x29, #-0x14]
    // 读出来给到w8寄存器
    00000001000061e4         ldr        w8, [sp, #0x18]
    // w8寄存器加1
    00000001000061e8         add        w8, w8, #0x1
    00000001000061ec         str        w8, [sp, #0x18]
    00000001000061f0         ldr        w8, [sp, #0x18]
    // 比较指令这里是while条件,0x64就是100,和100比较,小于就跳回来
    00000001000061f4         cmp        w8, #0x64
    00000001000061f8         b.lt       loc_1000061d8
    
    00000001000061fc         ldur       w0, [x29, #-0x8]
    0000000100006200         ldur       x1, [x29, #-0x10]
    0000000100006204         adrp       x8, #0x10000d000
    0000000100006208         add        x8, x8, #0x398                              ; objc_cls_ref_AppDelegate
    000000010000620c         ldr        x8, x8
    0000000100006210         str        w0, [sp, #0x14]
    0000000100006214         mov        x0, x8
    0000000100006218         str        x1, [sp, #0x8]
    000000010000621c         bl         imp___stubs__objc_opt_class
    0000000100006220         bl         imp___stubs__NSStringFromClass
    0000000100006224         mov        x29, x29
    0000000100006228         bl         imp___stubs__objc_retainAutoreleasedReturnValue
    000000010000622c         ldr        w9, [sp, #0x14]
    0000000100006230         str        x0, sp
    0000000100006234         mov        x0, x9
    0000000100006238         ldr        x1, [sp, #0x8]
    000000010000623c         movz       x8, #0x0
    0000000100006240         mov        x2, x8
    0000000100006244         ldr        x3, sp
    0000000100006248         bl         imp___stubs__UIApplicationMain
    000000010000624c         stur       w0, [x29, #-0x4]
    0000000100006250         ldr        x0, sp
    0000000100006254         bl         imp___stubs__objc_release
    0000000100006258         ldur       w0, [x29, #-0x4]
    000000010000625c         ldp        x29, x30, [sp, #0x30]
    0000000100006260         add        sp, sp, #0x40
    0000000100006264         ret
                            ; endp
    

    修改main.m代码如下

    int main(int argc, char * argv[]) {
        int nSum = 0;
        int i = 0;
        while (i < 100) {
            nSum = nSum + 1;
            i++;
        } ;
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    
    // 核心汇编代码如下
                         loc_1000061d4:
    00000001000061d4         ldr        w8, [sp, #0x18]                             ; CODE XREF=_main+68
    00000001000061d8         cmp        w8, #0x64
    // 不满足条件跳转执行loc_1000061fc
    00000001000061dc         b.ge       loc_1000061fc
    
    00000001000061e0         ldur       w8, [x29, #-0x14]
    00000001000061e4         add        w8, w8, #0x1
    00000001000061e8         stur       w8, [x29, #-0x14]
    00000001000061ec         ldr        w8, [sp, #0x18]
    00000001000061f0         add        w8, w8, #0x1
    00000001000061f4         str        w8, [sp, #0x18]
    00000001000061f8         b          loc_1000061d4
                         loc_1000061fc:
    00000001000061fc         ldur       w0, [x29, #-0x8]                            ; CODE XREF=_main+40
    0000000100006200         ldur       x1, [x29, #-0x10]
    0000000100006204         adrp       x8, #0x10000d000
    0000000100006208         add        x8, x8, #0x398                              ; objc_cls_ref_AppDelegate
    000000010000620c         ldr        x8, x8
    0000000100006210         str        w0, [sp, #0x14]
    0000000100006214         mov        x0, x8
    0000000100006218         str        x1, [sp, #0x8]
    000000010000621c         bl         imp___stubs__objc_opt_class
    0000000100006220         bl         imp___stubs__NSStringFromClass
    0000000100006224         mov        x29, x29
    ... 
    

    再次修改main.m代码如下

    int main(int argc, char * argv[]) {
        int nSum = 0;
        for (int i = 0; i < 100; i++) {
            nSum = nSum + 1;
        }
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    
    // 核心汇编代码如下
                         loc_1000061d4:
    00000001000061d4         ldr        w8, [sp, #0x14]                             ; CODE XREF=_main+72
    00000001000061d8         cmp        w8, #0x64
    00000001000061dc         b.ge       loc_1000061fc
    
    00000001000061e0         ldur       w8, [x29, #-0x14]
    00000001000061e4         add        w8, w8, #0x1
    00000001000061e8         stur       w8, [x29, #-0x14]
    00000001000061ec         ldr        w8, [sp, #0x14]
    00000001000061f0         add        w8, w8, #0x1
    00000001000061f4         str        w8, [sp, #0x14]
    00000001000061f8         b          loc_1000061d4
    
                         loc_1000061fc:
    00000001000061fc         ldur       w0, [x29, #-0x8]                            ; CODE XREF=_main+44
    0000000100006200         ldur       x1, [x29, #-0x10]
    0000000100006204         adrp       x8, #0x10000d000
    0000000100006208         add        x8, x8, #0x398                              ; objc_cls_ref_AppDelegate
    000000010000620c         ldr        x8, x8
    0000000100006210         str        w0, [sp, #0x10]
    ...
    // 可以看出来跟while 汇编代码一摸一样,所以两者可以代替
    

    五. 选择(上)

    创建空工程004--选择,我们还在main.m文件中编写代码探讨

    // main.m文件代码如下
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    void funcA(int a){
        switch (a) {//
            case 1:
                printf("打坐");
                break;
            case 2:
                printf("加红");
                break;
            case 3:
                printf("加蓝");
                break;
            default:
                printf("啥都不干");
                break;
        }
    }
    int main(int argc, char * argv[]) {
        funcA(1);
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    
    // funcA汇编代码如下
    004--选择`funcA:
        0x100962114 <+0>:   sub    sp, sp, #0x20             ; =0x20 
        0x100962118 <+4>:   stp    x29, x30, [sp, #0x10]
        0x10096211c <+8>:   add    x29, sp, #0x10            ; =0x10 
        // wo 寄存器保存一下,然后再取出来
        0x100962120 <+12>:  stur   w0, [x29, #-0x4]
    ->  0x100962124 <+16>:  ldur   w8, [x29, #-0x4]
        // 比较,相等就跳转
        0x100962128 <+20>:  cmp    w8, #0x1                  ; =0x1 
        0x10096212c <+24>:  str    w8, [sp, #0x8]
        0x100962130 <+28>:  b.eq   0x100962158               ; <+68> at main.m
        0x100962134 <+32>:  b      0x100962138               ; <+36> at main.m
        0x100962138 <+36>:  ldr    w8, [sp, #0x8]
        // 比较,相等就跳转
        0x10096213c <+40>:  cmp    w8, #0x2                  ; =0x2 
        0x100962140 <+44>:  b.eq   0x100962168               ; <+84> at main.m
        0x100962144 <+48>:  b      0x100962148               ; <+52> at main.m
        0x100962148 <+52>:  ldr    w8, [sp, #0x8]
        // 比较,相等就跳转
        0x10096214c <+56>:  cmp    w8, #0x3                  ; =0x3 
        0x100962150 <+60>:  b.eq   0x100962178               ; <+100> at main.m
        0x100962154 <+64>:  b      0x100962188               ; <+116> at main.m
        // 拿出常量打印
        0x100962158 <+68>:  adrp   x0, 1
        0x10096215c <+72>:  add    x0, x0, #0xf6d            ; =0xf6d 
        0x100962160 <+76>:  bl     0x10096258c               ; symbol stub for: printf
        0x100962164 <+80>:  b      0x100962194               ; <+128> at main.m:30:1
        0x100962168 <+84>:  adrp   x0, 1
        0x10096216c <+88>:  add    x0, x0, #0xf74            ; =0xf74 
        0x100962170 <+92>:  bl     0x10096258c               ; symbol stub for: printf
        0x100962174 <+96>:  b      0x100962194               ; <+128> at main.m:30:1
        0x100962178 <+100>: adrp   x0, 1
        0x10096217c <+104>: add    x0, x0, #0xf7b            ; =0xf7b 
        0x100962180 <+108>: bl     0x10096258c               ; symbol stub for: printf
        0x100962184 <+112>: b      0x100962194               ; <+128> at main.m:30:1
        0x100962188 <+116>: adrp   x0, 1
        0x10096218c <+120>: add    x0, x0, #0xf82            ; =0xf82 
        0x100962190 <+124>: bl     0x10096258c               ; symbol stub for: printf
        0x100962194 <+128>: ldp    x29, x30, [sp, #0x10]
        0x100962198 <+132>: add    sp, sp, #0x20             ; =0x20 
        0x10096219c <+136>: ret    
    // 可以发现上面汇编代码与if 汇编是一样的
    

    修改main.m代码如下

    void funcA(int a){
        switch (a) {//
            case 1:
                printf("打坐");
                break;
            case 2:
                printf("加红");
                break;
            case 3:
                printf("加蓝");
                break;
            case 4:
                printf("打怪");
                break;
            default:
                printf("啥都不干");
                break;
        }
    }
    
    // funcA汇编代码如下
    004--选择`funcA:
        0x1024660f0 <+0>:   sub    sp, sp, #0x20             ; =0x20 
        0x1024660f4 <+4>:   stp    x29, x30, [sp, #0x10]
        0x1024660f8 <+8>:   add    x29, sp, #0x10            ; =0x10 
        // 参数入栈
        0x1024660fc <+12>:  stur   w0, [x29, #-0x4]
    ->  0x102466100 <+16>:  ldur   w8, [x29, #-0x4]
        // 这里没有了类似if的汇编,也没有了很多个cmp 
        // subs 让参数减1,这里会影响状态寄存器减1
        0x102466104 <+20>:  subs   w8, w8, #0x1              ; =0x1 
        0x102466108 <+24>:  mov    x9, x8
        0x10246610c <+28>:  ubfx   x9, x9, #0, #32
        // 判断是否等于3
        0x102466110 <+32>:  cmp    x9, #0x3                  ; =0x3 
        0x102466114 <+36>:  str    x9, [sp]
        // 跳转
        0x102466118 <+40>:  b.hi   0x102466174               ; <+132> at main.m
        0x10246611c <+44>:  adrp   x8, 0
        0x102466120 <+48>:  add    x8, x8, #0x18c            ; =0x18c 
        0x102466124 <+52>:  ldr    x11, [sp]
        0x102466128 <+56>:  ldrsw  x10, [x8, x11, lsl #2]
        0x10246612c <+60>:  add    x9, x8, x10
        0x102466130 <+64>:  br     x9
        0x102466134 <+68>:  adrp   x0, 1
        0x102466138 <+72>:  add    x0, x0, #0xf69            ; =0xf69 
        0x10246613c <+76>:  bl     0x102466588               ; symbol stub for: printf
        0x102466140 <+80>:  b      0x102466180               ; <+144> at main.m:30:1
        0x102466144 <+84>:  adrp   x0, 1
        0x102466148 <+88>:  add    x0, x0, #0xf70            ; =0xf70 
        0x10246614c <+92>:  bl     0x102466588               ; symbol stub for: printf
        0x102466150 <+96>:  b      0x102466180               ; <+144> at main.m:30:1
        0x102466154 <+100>: adrp   x0, 1
        0x102466158 <+104>: add    x0, x0, #0xf77            ; =0xf77 
        0x10246615c <+108>: bl     0x102466588               ; symbol stub for: printf
        0x102466160 <+112>: b      0x102466180               ; <+144> at main.m:30:1
        0x102466164 <+116>: adrp   x0, 1
        0x102466168 <+120>: add    x0, x0, #0xf7e            ; =0xf7e 
        0x10246616c <+124>: bl     0x102466588               ; symbol stub for: printf
        0x102466170 <+128>: b      0x102466180               ; <+144> at main.m:30:1
        0x102466174 <+132>: adrp   x0, 1
        0x102466178 <+136>: add    x0, x0, #0xf85            ; =0xf85 
        0x10246617c <+140>: bl     0x102466588               ; symbol stub for: printf
        0x102466180 <+144>: ldp    x29, x30, [sp, #0x10]
        0x102466184 <+148>: add    sp, sp, #0x20             ; =0x20 
        0x102466188 <+152>: ret    
    

    小结

    • switch 如果条件语句小于等于3个,汇编代码就是if else 的形式。
    • 当参数超过3个,就会建立一张连续的数据表,把每一条判断语句做的事情放入表中相应地址,每8个字节放一个地址 ,通过一次运算得到一个地址值,然后执行对应代码,不需要多次if else 判断,这就是底层的优化
    • 这里上面是减1和3比较得到一个index值,通过index值从数据表中找内存地址,找到之后直接执行对应的逻辑

    注意⚠️ switch 判断条件比较多的话,一直if else 会浪费时间,由于上面都是连续的数字,这个时候就会创建表,投入空间来换时间。如果上面判断条件不是连续的,底层就不会创建表来进行优化,又会执行if else 的判断。

    // 下面这种形式,虽然条件语句是4个,但判断条件不是连续的,系统底层不会创建表来进行优化
    void funcA(int a){
        switch (a) {
            case 1:
                printf("打坐");
                break;
            case 200:
                printf("加红");
                break;
            case 35:
                printf("加蓝");
                break;
            case 41:
                printf("打怪");
                break;
            default:
                printf("啥都不干");
                break;
        }
    }
    
    • 连续的表,系统底层会找规律来创建表进行优化
    • 如果判断逻辑是连续的并且超过了3个,尽可能使用switch语句,因为底层会进行优化

    上面是怎么生成数据表?又是怎么找到对应的地址值?限于篇幅,我们之后继续探讨......

    相关文章

      网友评论

          本文标题:汇编(四)

          本文链接:https://www.haomeiwen.com/subject/xtkakltx.html