美文网首页
方法查找流程 objc-msg-x86_64.s msg_se

方法查找流程 objc-msg-x86_64.s msg_se

作者: 答案不止一个 | 来源:发表于2020-09-16 12:14 被阅读0次

    先记录一部分笔记,剩下的继续学习补充

    //  movb(8位)、movw(16位)、movl(32位)、movq(64位)
    // 最后的b,w,l,q 是区分不同的位数
    

    cache_getImp

    • a1 = class whose cache is to be searched a1 是class
    • a2 = selector to search for a2 是sel
    /********************************************************************
     * IMP cache_getImp(Class cls, SEL sel)
     *
     * On entry:    a1 = class whose cache is to be searched
     *      a2 = selector to search for
     *
     * If found, returns method implementation.
     * If not found, returns NULL.
     ********************************************************************/
    
        STATIC_ENTRY _cache_getImp
    
    // do lookup
        // 将class 复制给 r10 位 CacheLookup 做准备
        movq    %a1, %r10       // move class to r10 for CacheLookup
        // returns IMP on success
        CacheLookup NORMAL, GETIMP, _cache_getImp
    
    // _cache_getImp 查找缓存是没有找到的跳转
    LCacheMiss_cache_getImp:
    // cache miss, return nil
        xorl    %eax, %eax
        ret
    
        END_ENTRY _cache_getImp
    

    CacheLookup

    调用方式: CacheLookup NORMAL, GETIMP, _cache_getImp
    所以 0 是就是 NORMAL,1 就是GETIMP $2就是 _cache_getImp
    第一个参数 normal 或者 Strat 表示 isa 和cm的参数的位置不同
    第二个参数 GETIMP : 返回计算后的img , CALL 直接调用imp。 LOOKUP 将计算得到的imp 存入r11
    第三个参数: 这是一个标记, 比如cachemiss 是跳转到哪里

    参考

    • r11 64-bit register
    • r11d 是Lower 32 bits
    .macro  CacheLookup
        //
        // Restart protocol:
        //
        //   As soon as we're past the LLookupStart$1 label we may have loaded
        //   an invalid cache pointer or mask.
        //
        //   When task_restartable_ranges_synchronize() is called,
        //   (or when a signal hits us) before we're past LLookupEnd$1,
        //   then our PC will be reset to LCacheMiss$1 which forcefully
        //   jumps to the cache-miss codepath which have the following
        //   requirements:
        //
        //   GETIMP:
        //     The cache-miss is just returning NULL (setting %rax to 0)
        //
        //   NORMAL and STRET:
        //   - a1 or a2 (STRET) contains the receiver  class 
        //   - a2 or a3 (STRET) contains the selector  
        //   - r10 contains the isa
        //   - other registers are set as per calling conventions
        //
    LLookupStart$2:
    
    // 将sel 复制给 r11
    .if $0 != STRET
        movq    %a2, %r11       // r11 = _cmd 
    .else
        movq    %a3, %r11       // r11 = _cmd
    .endif
        // r11d -> r11去8位数据     24(%r10) r10  isa的指针 偏移 24个字节(isa 8, superclass 8 , class->cache.buckets 8) class->cache.mask
        // 根据 cmd 和 _cmd & class->cache.mask 计算hashkey 并复制给r11
        andl    24(%r10), %r11d     // r11 = _cmd & class->cache.mask  
        
        // $$4  -> 4   这是根据hashkey << 4 计算hashkey个  16位/2字节   bucket是2个字节大小(sel 8, imp 8)
        // 根据hashkey 之后计算 hashkey对应的偏移量
        shlq    $$4, %r11       // r11 = offset = (_cmd & mask)<<4
        
        // 类似 24(%r10)  16(%r10) 就是 class->cache.bucket
        //  根据偏移量,找到对应hashkey的 bucket地址
        addq    16(%r10), %r11      // r11 = class->cache.buckets + offset
        
        // 判断当权位置是否等于sel hash冲突判断
    .if $0 != STRET
        cmpq    cached_sel(%r11), %a2   // if (bucket->sel != _cmd)
    .else
        cmpq    cached_sel(%r11), %a3   // if (bucket->sel != _cmd)
    .endif
        
        // 如果不想等 则去下一个查找 (根据cache的存储逻辑)
        jne     1f          //     scan more
        // 相等 跳转CacheHit $0 , $1 ; $0,$1 是传入的参数
        CacheHit $0, $1         // call or return imp
    
    //  检查是否已经到了最后一位/或者下一位是空的。就返回查不到
    //  否则, 计算下一个bucket的地址
    1:  
        // loop
        // r11 是刚才计算到hashkey对应的 bucket
        // cache_sel 位0  =>   0(%r11)  就是 bucket.sel
        cmpq    $$1, cached_sel(%r11)
        
        // if (bucket->sel <= 1) 就是说查不到, 跳转到退出
        jbe 3f          // if (bucket->sel <= 1) wrap or miss
        
        // 计算下一个入职的bucket地址 复制给r11
        addq    $$16, %r11      // bucket++
    
    // 
    2: 
        // 比较 cmd 和当前 bucket->sel 是否相同
    .if $0 != STRET
        cmpq    cached_sel(%r11), %a2   // if (bucket->sel != _cmd)
    .else
        cmpq    cached_sel(%r11), %a3   // if (bucket->sel != _cmd)
    .endif
        // 不相同 hashkey冲突,继续乡下解决
        jne     1b          //     scan more
        // 相同,则就是跳转cachehit
        CacheHit $0, $1         // call or return imp
    
    // 查找下一个的bucket出现:
    // 空的存储  -> 跳转cacheMiss
    // end标志  -> end标志的imp (第一个bucket的地址)复制给r11 ,继续查找
    3:
        // 根据上面的判断,cmpq $$1, cached_sel(%r11) 再判断
        // 如果是小于, 这属于 下一个是空的,找不到了
        // wrap or miss
        jb  LCacheMiss$2        // if (bucket->sel < 1) cache miss
        
        //  否则 下一个 bucket->sel == 1 表示到了最后的结尾,其结尾的imp 存的是开始的bucket地址
        // 将下一个bucket设置位 第一个bucket 的地址,再次查询比较
        movq    cached_imp(%r11), %r11  // bucket->imp is really first bucket
        jmp     2f
    
        // Clone scanning loop to miss instead of hang when cache is corrupt.
        // The slow path may detect any corruption and halt later.
    
    1:
        // loop
        cmpq    $$1, cached_sel(%r11)
        jbe 3f          // if (bucket->sel <= 1) wrap or miss
    
        addq    $$16, %r11      // bucket++
    2:  
    .if $0 != STRET
        cmpq    cached_sel(%r11), %a2   // if (bucket->sel != _cmd)
    .else
        cmpq    cached_sel(%r11), %a3   // if (bucket->sel != _cmd)
    .endif
        jne     1b          //     scan more
        CacheHit $0, $1         // call or return imp
    
    3:
        // double wrap or miss
        jmp LCacheMiss$2
    
    LLookupEnd$2:
    .endmacro
    

    CacheHit

    就是找到了imp。然后根据类型返回或者执行.其中r11 是在之前CacheLookup中找到的 bucket的地址

    1. CALL : 执行IMP,并返回imp 放在r11
    2. LOOKUP: 返回IMP ^ isa
    .macro CacheHit
        // r11 是在 
    .if $1 == GETIMP
        // r11  是bucket  r10  isa
        // #define cached_imp   8  cached_imp(%r11)就是bucket的imp地址
        // %rax
        % rax 作为函数返回值使用。
        movq    cached_imp(%r11), %rax  // return imp
        //  判断imp 是否为0
        cmpq    $$0, %rax   // $$0  就是直接取值 0 就是判断imp 是否为nil
        // 为0 直接返回
        jz  9f          // don't xor a nil imp 
        //  如果rmp 不为nil ,则按照原先存的的逻辑,, 在xor 计算回来。参考cache_t存储imp的存储逻辑
        // r10 是isa
        xorq    %r10, %rax      // xor the isa with the imp
    9:  ret
    
    .else
    .if $1 == CALL
        movq    cached_imp(%r11), %r11  // load imp
        xorq    %r10, %r11          // xor imp and isa
    .if $0 != STRET
        // ne already set for forwarding by `xor`
    .else
        cmp %r11, %r11      // set eq for stret forwarding
    .endif
        // 跳转到imp的地址 去执行imp
        jmp *%r11           // call imp
    
    .elseif $1 == LOOKUP
        movq    cached_imp(%r11), %r11
        xorq    %r10, %r11      
        ret
        
    .else
    .abort oops
    .endif
    
    .endif
    
    .endmacro
    

    GetIsaFast

    调用: GetIsaFast NORMAL
    根据isa的 nonpointer 位的标示,获取正确的isa地址
    将isa的地址 保存到 r10上

    • a1 or a2 (STRET) contains the receiver
    • a2 or a3 (STRET) contains the selector

    MethodTableLookup

    去执行 lookUpImpOrForward 方法 。能查到方法,放回方法。
    没有查到方法,返回的是_objc_msgForward_impcache 消息转发流程

    // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
    // 参数的赋值  a1 obj, a2 sel  a3 cls  a4 behavior
    if $0 == NORMAL
        // receiver already in a1
        // selector already in a2
    .else
        movq    %a2, %a1
        movq    %a3, %a2
    .endif
        movq    %r10, %a3
        movl    $$3, %a4d 
        call    _lookUpImpOrForward
    

    其他的是传参数

    __objc_msgSend_uncached

    STATIC_ENTRY __objc_msgSend_uncached
        UNWIND __objc_msgSend_uncached, FrameWithNoSaves
        
        // THIS IS NOT A CALLABLE C FUNCTION
        // Out-of-band r10 is the searched class
    
        // r10 is already the class to search
        MethodTableLookup NORMAL    // r11 = IMP
        jmp *%r11           // goto *imp
    
        END_ENTRY __objc_msgSend_uncached
    

    _objc_msgLookup

    先去缓存查找 CacheLookup NORMAL, LOOKUP, _objc_msgLookup

    1. 缓存中找到imp,放入r11 然后判断是否是null 再将isa 放入r10
    2. 找不到跳转 LCacheMiss_objc_msgLookup
      cachemiss_objc_msgLookup 再去方法列表中查找 -> __objc_msgLookup_uncached -> MethodTableLookup
    ENTRY _objc_msgLookup
        //  #define NORMAL 0
        NilTest NORMAL
    
        // 就是获取对应的isa的值 (如果是优化过的,需要使用ISA_MASK去获取)然后赋值到 r10上
        GetIsaFast NORMAL       // r10 = self->isa
        // returns IMP on success
        // 去缓存查找
        CacheLookup NORMAL, LOOKUP, _objc_msgLookup
        // returns an IMP in r11 that returns zero.
        NilTestReturnIMP NORMAL
        // 再将isa 放入r10
        GetIsaSupport NORMAL
        
    // cache miss: go search the method lists
    LCacheMiss_objc_msgLookup:
        // isa still in r10
        jmp __objc_msgLookup_uncached
    
        END_ENTRY _objc_msgLookup
    

    _objc_msgSend

        ENTRY _objc_msgSend
        UNWIND _objc_msgSend, NoFrame
    
        NilTest NORMAL
        
        //对象/类 isa数据根据 nonpointer位,获取其class的地址 / mataclass 的地址
        GetIsaFast NORMAL       // r10 = self->isa
        
        // calls IMP on success
        // 去cache中查找 ,如果找到直接执行imp,并放回imp, (参考 CacheHit NORMAL CALL)
        // 如果没有查找到imp, 就会 跳转到 LCacheMiss_objc_msgSend ($2 _objc_msgSend) 
        CacheLookup NORMAL, CALL, _objc_msgSend
        
        // 判断返回是否是nil
        NilTestReturnZero NORMAL
        
        GetIsaSupport NORMAL
    
    // cache miss: go search the method lists
    // 
    LCacheMiss_objc_msgSend:
        // isa still in r10
        // __objc_msgSend_uncached 就回去执行MethodTableLookup 
        // 然后执行 lookUpImpOrForward 就是去查方法列表,
        // 然后执行返回的方法
        jmp __objc_msgSend_uncached 
    
        END_ENTRY _objc_msgSend
    

    总结

    msg_send.jpg

    重点:

    1. 先查找缓存,找到直接执行
    2. cache_t 的方法存储逻辑,以及查找逻辑
    3. cacheHit 根据参数 $1 参数 处理(CALL 直接执行, GET_IMP 判断是否nil,返回IMP,LOOKUP (不判断nil, r11 存储IMP))。
    4. LCacheMiss_objc_msgSend 去类的方法列表中去查,执行 _lookUpImpOrForward, behavior 设为LOOKUP_INITIALIZE | LOOKUP_RESOLVER

    相关文章

      网友评论

          本文标题:方法查找流程 objc-msg-x86_64.s msg_se

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