iOS 8 消息快速查找

520 阅读5分钟

真机调试分析

  • 新建NBPerson

@interface NBPerson : NSObject
- (void)sayNB;
@end

@implementation NBPerson
- (void)sayNB{
    NSLog(@"%s",__func__);
}
@end

  • 断点调试

截屏2021-08-17 上午11.09.05.png

  • control into step进入汇编

56BFEA44-681F-4B58-A712-985FCA6EA973.png

  • 继续下一步control into step找到 objc_msgSend

03F3E4D7-4E3A-4EC5-9FDD-06A1A0F5671E.png

  • 然后断点到

E8E74A57-8C43-4BD4-A2FB-381F07CE4A1C.png

  • 代码详解
libobjc.A.dylib`objc_msgSend:
    //1,判断receiver是否存在
->  0x1c130a0e0 <+0>:   cmp    x0, #0x0                  ; =0x0 
    0x1c130a0e4 <+4>:   b.le   0x1c130a1a4               ; <+196>
    //将实例对象的isa复制给 x13寄存器
    0x1c130a0e8 <+8>:   ldr    x13, [x0]
    //将isa&ISA_MASK得到类NBPerson
    0x1c130a0ec <+12>:  and    x16, x13, #0x7ffffffffffff8
    0x1c130a0f0 <+16>:  xpacd  x16
    0x1c130a0f4 <+20>:  mov    x15, x16
    //将NBPerson的isa地址加16个字节,得到cache_t
    0x1c130a0f8 <+24>:  ldr    x11, [x16, #0x10]
    //w11的0号位置不为0,跳转不为0,继续向下执行
    0x1c130a0fc <+28>:  tbnz   w11, #0x0, 0x1c130a158    ; <+120>
    //将x11&0xffffffffffff可以保留cache_t的低48位,即拿到的buckets
    0x1c130a100 <+32>:  and    x10, x11, #0xffffffffffff
    //x12=_cmd&(_cmd>>7),mask = x11>>48,x12 = x12&masek
    //x12位第一个检索的位置,即firs_preopt
    0x1c130a104 <+36>:  eor    x12, x1, x1, lsr #7
    0x1c130a108 <+40>:  and    x12, x12, x11, lsr #48
    //buckets + first_preopt 得到第一个探查的bucket_t的地址
    0x1c130a10c <+44>:  add    x13, x10, x12, lsl #4
    //将bucket_t的imp和sel取出,分别存在P17和P9
    0x1c130a110 <+48>:  ldp    x17, x9, [x13], #-0x10
    //比较sel与 _cmd不相等就跳转,否则命中缓存buckets = buckets&_cmd buckets = buckets & isa
    0x1c130a114 <+52>:  cmp    x9, x1
    0x1c130a118 <+56>:  b.ne   0x1c130a128               ; <+72>
    0x1c130a11c <+60>:  eor    x10, x10, x1
    0x1c130a120 <+64>:  eor    x10, x10, x16
    0x1c130a124 <+68>:  brab   x17, x10
    //判断x9是否存在
    0x1c130a128 <+72>:  cbz    x9, 0x1c130a4e0           ; _objc_msgSend_uncached
    //比较bucket_t与buckets
    0x1c130a12c <+76>:  cmp    x13, x10
    0x1c130a130 <+80>:  b.hs   0x1c130a110               ; <+48>
    
    //取末位缓存,取第一次查找的位置,将末尾bucket_t的imp和sel赋值给x17和x9末位bucket
    0x1c130a134 <+84>:  add    x13, x10, x11, lsr #44
    0x1c130a138 <+88>:  add    x12, x10, x12, lsl #4
    0x1c130a13c <+92>:  ldp    x17, x9, [x13], #-0x10
    //比较sel与_cmd,相等:命中缓存 不相等:判定sel是否存在,判定当前搜索的bucket_t是否已经搜索过了,如果大于,继续循环搜索,否则跳转0x1c130a4e0,进入慢速查找流程
    0x1c130a140 <+96>:  cmp    x9, x1
    0x1c130a144 <+100>: b.eq   0x1c130a11c               ; <+60>
    0x1c130a148 <+104>: cmp    x9, #0x0                  ; =0x0 
    0x1c130a14c <+108>: ccmp   x13, x12, #0x0, ne
    0x1c130a150 <+112>: b.hi   0x1c130a13c               ; <+92>
    0x1c130a154 <+116>: b      0x1c130a4e0               ; _objc_msgSend_uncached
    //x11&preoptBucketsMask,取buckets
    0x1c130a158 <+120>: and    x10, x11, #0x7ffffffffffffe
    0x1c130a15c <+124>: autdb  x10, x16
    0x1c130a160 <+128>: adrp   x9, 225804
    0x1c130a164 <+132>: add    x9, x9, #0xef0            ; =0xef0 
    0x1c130a168 <+136>: sub    x12, x1, x9
    0x1c130a16c <+140>: lsr    x17, x11, #55
    0x1c130a170 <+144>: lsr    w9, w12, w17
    0x1c130a174 <+148>: lsr    x17, x11, #60
    0x1c130a178 <+152>: mov    x11, #0x7fff
    0x1c130a17c <+156>: lsr    x11, x11, x17
    0x1c130a180 <+160>: and    x9, x9, x11
    0x1c130a184 <+164>: ldr    x17, [x10, x9, lsl #3]
    0x1c130a188 <+168>: cmp    x12, w17, uxtw
    0x1c130a18c <+172>: b.ne   0x1c130a198               ; <+184>
    0x1c130a190 <+176>: sub    x17, x16, x17, lsr #32
    0x1c130a194 <+180>: br     x17
    0x1c130a198 <+184>: ldursw x9, [x10, #-0x8]
    0x1c130a19c <+188>: add    x16, x16, x9
    0x1c130a1a0 <+192>: b      0x1c130a0f8               ; <+24>
    0x1c130a1a4 <+196>: b.eq   0x1c130a1c8               ; <+232>
    0x1c130a1a8 <+200>: and    x10, x0, #0x7
    0x1c130a1ac <+204>: asr    x11, x0, #55
    0x1c130a1b0 <+208>: cmp    x10, #0x7                 ; =0x7 
    0x1c130a1b4 <+212>: csel   x12, x11, x10, eq
    0x1c130a1b8 <+216>: adrp   x10, 307745
    0x1c130a1bc <+220>: add    x10, x10, #0x820          ; =0x820 
    0x1c130a1c0 <+224>: ldr    x16, [x10, x12, lsl #3]
    0x1c130a1c4 <+228>: b      0x1c130a0f4               ; <+20>
    0x1c130a1c8 <+232>: mov    x1, #0x0
    0x1c130a1cc <+236>: movi   d0, #0000000000000000
    0x1c130a1d0 <+240>: movi   d1, #0000000000000000
    0x1c130a1d4 <+244>: movi   d2, #0000000000000000
    0x1c130a1d8 <+248>: movi   d3, #0000000000000000
    0x1c130a1dc <+252>: ret    

汇编指令学习

指令寄存器值说明
mov x0,#0xffff0xffff将0xffff放到寄存器x0
movk x0,#0x3a88,lsl #160x3a88ffff将0x3a44放到寄存器x0,从16bit位开始存放(movk 保持其他位置不变,将值存入指定bit位)
tbnz x0,#0x0,0x1c130a158......判断x0的1号位置是否为0,不为零,跳转到0x1c130a158,为0,向下执行
eor x10, x10, x1x10将x10与x1逻辑异或,结果存在x10
ldr x13, [x0]x13将x0寄存器中的值取出放在x13寄存器
and x10, x11, #0xffffffffffffx10将x11 & 0xffffffffffff,结果存在x10
cmp x9, x1......比较x9和x1的大小
b.ne 0x1c130a198......不相等,跳转至0x0x1c130a198
b.hs 0x199c731f8......无符号小于,跳转至0x199c731f8
b.hi 0x199c73224......无符号大于,跳转至0x199c73224
b.hi 0x199c73224......无符号大于,跳转至0x199c73224
b.eq 0x199c73204......等于,跳转至0x199c73204
cbz x9, 0x199c73600......判断x9是否存在,不存在,则跳转0x199c73600