Objc_MsgSend 消息快速查找之Cache 查找

开发 前端
根据不同架构找到 buckets 中 sel 对应的 index,p10 = buckets,p11 = mask / _bucketsAndMaybeMask(arm64_64 是 _bucketsAndMaybeMask),p12 = index。

[[410478]]

本文转载自微信公众号「网罗开发」,作者HotPotCat 。转载本文请联系网罗开发公众号。

上一篇文章分析了 objc_msgSend 的汇编实现,这边文章继续分析 objc_msgSend 中缓存的查找逻辑以及汇编代码是如何进入 c/c++ 代码的。

1. CacheLookup 查找缓存

1.1 CacheLookup源码分析

  1. //NORMAL, _objc_msgSend, __objc_msgSend_uncached 
  2. .macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant 
  3.     //   requirements: 
  4.     //   //缓存不存在返回NULL,x0设置为0 
  5.     //   GETIMP: 
  6.     //     The cache-miss is just returning NULL (setting x0 to 0) 
  7.     //   参数说明 
  8.     //   NORMAL and LOOKUP: 
  9.     //   - x0 contains the receiver 
  10.     //   - x1 contains the selector 
  11.     //   - x16 contains the isa 
  12.     //   - other registers are set as per calling conventions 
  13.     // 
  14. //调用过来的p16存储的是cls,将cls存储在x15. 
  15.     mov x15, x16            // stash the original isa 
  16. //_objc_msgSend 
  17. LLookupStart\Function
  18.     // p1 = SEL, p16 = isa 
  19. //arm64 64 OSX/SIMULATOR 
  20. #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS 
  21.     //isa->cache,首地址也就是_bucketsAndMaybeMask 
  22.     ldr p10, [x16, #CACHE]              // p10 = mask|buckets 
  23.     //lsr逻辑右移 p11 = _bucketsAndMaybeMask >> 48 也就是 mask 
  24.     lsr p11, p10, #48           // p11 = mask 
  25.     //p10 = _bucketsAndMaybeMask & 0xffffffffffff = buckets(保留后48位) 
  26.     and p10, p10, #0xffffffffffff   // p10 = buckets 
  27.     //x12 = cmd & mask   w1为第二个参数cmd(self,cmd...),w11也就是p11 也就是执行cache_hash。这里没有>>7位的操作 
  28.     and w12, w1, w11            // x12 = _cmd & mask 
  29. //arm64 64 真机这里p11计算后是_bucketsAndMaybeMask 
  30. #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 
  31.     ldr p11, [x16, #CACHE]          // p11 = mask|buckets 
  32. //arm64 + iOS + !模拟器 + 非mac应用 
  33. #if CONFIG_USE_PREOPT_CACHES 
  34. //iphone 12以后指针验证 
  35. #if __has_feature(ptrauth_calls) 
  36.     //tbnz 测试位不为0则跳转。与tbz对应。p11 第0位不为0则跳转 LLookupPreopt\Function。 
  37.     tbnz    p11, #0, LLookupPreopt\Function 
  38.     //p10 = _bucketsAndMaybeMask & 0x0000ffffffffffff = buckets 
  39.     and p10, p11, #0x0000ffffffffffff   // p10 = buckets 
  40. #else 
  41.     //p10 = _bucketsAndMaybeMask & 0x0000fffffffffffe = buckets 
  42.     and p10, p11, #0x0000fffffffffffe   // p10 = buckets 
  43.     //p11 第0位不为0则跳转 LLookupPreopt\Function。 
  44.     tbnz    p11, #0, LLookupPreopt\Function 
  45. #endif 
  46.     //eor 逻辑异或(^) 格式为:EOR{S}{cond} Rd, Rn, Operand2 
  47.     //p12 = selector ^ (selector >> 7) select 右移7位&自己给到p12 
  48.     eor p12, p1, p1, LSR #7 
  49.     //p12 = p12 & (_bucketsAndMaybeMask >> 48) = index & mask值 = buckets中的下标 
  50.     and p12, p12, p11, LSR #48      // x12 = (_cmd ^ (_cmd >> 7)) & mask 
  51. #else 
  52.     //p10 = _bucketsAndMaybeMask & 0x0000ffffffffffff = buckets 
  53.     and p10, p11, #0x0000ffffffffffff   // p10 = buckets 
  54.     //p12 = selector & (_bucketsAndMaybeMask >>48) = sel & mask = buckets中的下标 
  55.     and p12, p1, p11, LSR #48       // x12 = _cmd & mask 
  56. #endif // CONFIG_USE_PREOPT_CACHES 
  57. //arm64 32 
  58. #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4 
  59.     //后4位为mask前置0的个数的case 
  60.     ldr p11, [x16, #CACHE]              // p11 = mask|buckets 
  61.     and p10, p11, #~0xf         // p10 = buckets 相当于后4位置为0,取前32位 
  62.     and p11, p11, #0xf          // p11 = maskShift 取的是后4位,为mask前置位的0的个数 
  63.     mov p12, #0xffff 
  64.     lsr p11, p12, p11           // p11 = mask = 0xffff >> p11 
  65.     and p12, p1, p11            // x12 = _cmd & mask 
  66. #else 
  67. #error Unsupported cache mask storage for ARM64. 
  68. #endif 
  69.     //通过上面的计算 p10 = buckets,p11 = mask(arm64真机是_bucketsAndMaybeMask), p12 = index 
  70.     // p13(bucket_t) = buckets + 下标 << 4   PTRSHIFT arm64 为3.  <<4 位为16字节 buckets + 下标 *16 = buckets + index *16 也就是直接平移到了第几个元素的地址。 
  71.     add p13, p10, p12, LSL #(1+PTRSHIFT) 
  72.                         // p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT)) 
  73.     //这里就直接遍历查找了,因为arm64下cache_next相当于遍历(这里只扫描了前面) 
  74.                         // do { 
  75.     //p17 = imp, p9 = sel 
  76. 1:  ldp p17, p9, [x13], #-BUCKET_SIZE   //     {imp, sel} = *bucket-- 
  77.     //sel - _cmd != 0 则跳转 3:,也就意味着没有找到就跳转到__objc_msgSend_uncached 
  78.     cmp p9, p1              //     if (sel != _cmd) { 
  79.     b.ne    3f              //         scan more 
  80.                         //     } else { 
  81.     //找到则调用或者返回imp,Mode为 NORMAL 
  82. 2:  CacheHit \Mode              // hit:    call or return imp  命中 
  83.                         //     } 
  84. //__objc_msgSend_uncached 
  85. //缓存中找不到方法就走__objc_msgSend_uncached逻辑了。 
  86.     //cbz 为0跳转 sel == nil 跳转 \MissLabelDynamic 
  87. 3:  cbz p9, \MissLabelDynamic       //     if (sel == 0) goto Miss; 有空位没有找到说明没有缓存 
  88.     //bucket_t - buckets 由于是递减操作 
  89.     cmp p13, p10            // } while (bucket >= buckets) //⚠️ 这里一直是往前找,后面的元素在后面还有一次循环。 
  90.     //无符号大于等于 则跳转1:f b 分别代表front与back 
  91.     b.hs    1b 
  92.  
  93. //没有命中cache  查找 p13 = mask对应的元素,也就是倒数第二个 
  94. #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS 
  95.     //p13 = buckets + (mask << 4) 平移找到对应mask的bucket_t。UXTW 将w11扩展为64位后左移4 
  96.     add p13, p10, w11, UXTW #(1+PTRSHIFT) 
  97.                         // p13 = buckets + (mask << 1+PTRSHIFT) 
  98. #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 
  99.     //p13 = buckets + (mask >> 44) 这里右移44位,少移动4位就不用再左移了。因为maskZeroBits的存在 就找到了mask对应元素的地址 
  100.     add p13, p10, p11, LSR #(48 - (1+PTRSHIFT)) 
  101.                         // p13 = buckets + (mask << 1+PTRSHIFT) 
  102.                         // see comment about maskZeroBits 
  103. #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4 
  104.     //p13 = buckets + (mask << 4) 找到对应mask的bucket_t。 
  105.     add p13, p10, p11, LSL #(1+PTRSHIFT) 
  106.                         // p13 = buckets + (mask << 1+PTRSHIFT) 
  107. #else 
  108. #error Unsupported cache mask storage for ARM64. 
  109. #endif 
  110.     //p12 = buckets + (p12<<4) index对应的bucket_t 
  111.     add p12, p10, p12, LSL #(1+PTRSHIFT) 
  112.                         // p12 = first probed bucket 
  113.  
  114.     //之前已经往前查找过了,这里从后往index查找 
  115.                         // do { 
  116.     //p17 = imp p9 = sel 
  117. 4:  ldp p17, p9, [x13], #-BUCKET_SIZE   //     {imp, sel} = *bucket-- 
  118.     //sel - _cmd 
  119.     cmp p9, p1              //     if (sel == _cmd) 
  120.     //sel == _cmd跳转CacheHit 
  121.     b.eq    2b              //         goto hit 
  122.     //sel != nil 
  123.     cmp p9, #0              // } while (sel != 0 && 
  124.     // 
  125.     ccmp    p13, p12, #0, ne        //     bucket > first_probed) 
  126.     //有值跳转4: 
  127.     b.hi    4b 
  128.  
  129. LLookupEnd\Function
  130. LLookupRecover\Function
  131. //仍然没有找到缓存,缓存彻底不存在 __objc_msgSend_uncached() 
  132.     b   \MissLabelDynamic 

核心逻辑:

  • 根据不同架构找到 buckets 中 sel 对应的 index,p10 = buckets,p11 = mask / _bucketsAndMaybeMask(arm64_64 是 _bucketsAndMaybeMask),p12 = index。

arm64_64 的情况下如果 _bucketsAndMaybeMask 第 0 位为 1 则执行 LLookupPreopt\Function。

  • p13 = buckets + index << 4 找到 cls 对应的 buckets 地址,地址平移找到对应 bucket_t
  • do-while 循环扫描 buckets[index] 的前半部分(后半部分逻辑不在这里)。
    • 如果存在 sel 为空,则说明是没有缓存的,就直接 `__objc_msgSend_uncached()``。
    • 命中直接 CacheHit \Mode,这里 Mode 为 NORMAL。
  • 平移获得 p13 = buckets[mask] 对应的元素,也就是最后一个元素(arm64 下最后一个不存自身地址,也就相当于 buckets[count - 1])。
  • p13 = buckets + mask << 4 找到 mask 对应的 buckets 地址,地址平移找到对应 bucket_t
  • do-while 循环扫描 buckets[mask] 的前面元素,直到 index(不包含 index)。
    • 命中 CacheHit \Mode
    • 如果存在 sel 为空,则说明是没有缓存的,就直接结束循环。
  • 最终仍然没有找到则执行 __objc_msgSend_uncached()
  1. CACHE 是 cache_t 相对 isa 的偏移。#define CACHE (2 * SIZEOF_POINTER)
  2. maskZeroBits 始终是 4 位 0,p13 = buckets + (_bucketsAndMaybeMask >> 44)右移 44 位后就不用再 <<4 找到对应 bucket_t 的地址了。这是因为 maskZeroBits 在 arm64_64 下存在的原因。
  3. f b 分别代表 front 与 back,往下往上的意思。

1.2 CacheLookup 伪代码实现

  1. //NORMAL, _objc_msgSend, __objc_msgSend_uncached 
  2. void CacheLookup(Mode,Function,MissLabelDynamic,MissLabelConstant) { 
  3.     //1. 根据架构不同集算sel在buckets中的index 
  4.     if (arm64_64 && OSX/SIMULATOR) { 
  5.         p10 = isa->cache //_bucketsAndMaybeMask 
  6.         p11 = _bucketsAndMaybeMask >> 48//mask 
  7.         p10 = _bucketsAndMaybeMask & 0xffffffffffff//buckets 
  8.         x12 = sel & mask //index 也就是执行cache_hash 
  9.     } else if (arm64_64) {//真机 //这个分支下没有计算mask 
  10.         p11 = isa->cache //_bucketsAndMaybeMask 
  11.         if (arm64 + iOS + !模拟器 + 非mac应用) { 
  12.             if (开启指针验证 ) { 
  13.                 if (_bucketsAndMaybeMask 第0位 != 0) { 
  14.                     goto LLookupPreopt\Function 
  15.                 } else { 
  16.                     p10 = _bucketsAndMaybeMask & 0x0000ffffffffffff//buckets 
  17.                 } 
  18.             } else { 
  19.                 p10 = _bucketsAndMaybeMask & 0x0000fffffffffffe //buckets 
  20.                 if (_bucketsAndMaybeMask 第0位 != 0) { 
  21.                     goto LLookupPreopt\Function 
  22.                 } 
  23.             } 
  24.             //计算index 
  25.             p12 = selector ^ (selector >> 7) 
  26.             p12 = p12 & (_bucketsAndMaybeMask & 48) = p12 & mask//index 
  27.         } else { 
  28.             p10 = _bucketsAndMaybeMask & 0x0000ffffffffffff //buckets 
  29.             p12 = selector & (_bucketsAndMaybeMask >>48) //index 
  30.         } 
  31.     } else if (arm64_32) { 
  32.         p11 = _bucketsAndMaybeMask 
  33.         p10 =  _bucketsAndMaybeMask &(~0xf)//buckets 相当于后4位置为0,取前32位 
  34.         p11 = _bucketsAndMaybeMask & 0xf //mask前置位0的个数 
  35.         p11 =  0xffff >> p11 //获取到mask的值 
  36.         x12 = selector & mask //index 
  37.     } else { 
  38.         #error Unsupported cache mask storage for ARM64. 
  39.     } 
  40.      
  41.     //通过上面的计算 p10 = buckets,p11 = mask/_bucketsAndMaybeMask, p12 = index 
  42.     p13 = buckets + index << 4 //找到cls对应的buckets地址。地址平移找到对应bucket_t。 
  43.      
  44.     //2.找缓存(这里只扫描了前面) 
  45.     do { 
  46.         p13 = *bucket-- //赋值后指向前一个bucket 
  47.         p17 = bucket.imp 
  48.         p9 = bucket.sel 
  49.         if (p9 != selector) { 
  50.             if (p9 == 0) {//说明没有缓存 
  51.                 __objc_msgSend_uncached() 
  52.             } 
  53.         } else {//缓存命中,走命中逻辑 call or return imp 
  54.             CacheHit \Mode 
  55.         } 
  56.     } while(bucket >= buckets) //buckets是首地址,bucket是index对应的buckct往前移动 
  57.    
  58.     //查找完后还没有缓存? 
  59.     //查找 p13 = mask对应的元素,也就是最后一个元素 
  60.     if (arm64_64 && OSX/SIMULATOR) { 
  61.         p13 = buckets + (mask << 4) 
  62.     } else if (arm64_64) {//真机 
  63.         p13 = buckets + (_bucketsAndMaybeMask >> 44)//这里右移44位,少移动4位就不用再左移了。这里就找到了对应index的bucket_t。 
  64.     } else if (arm64_32) { 
  65.         p13 = buckets + (mask << 4) 
  66.     } else { 
  67.         #error Unsupported cache mask storage for ARM64. 
  68.     } 
  69.      
  70.     //index的bucket_t 从mask对应的buckets开始再往前找 
  71.     p12 = buckets + (index<<4) 
  72.     do { 
  73.         p17 = imp; 
  74.         p9 = sel; 
  75.         *p13--; 
  76.         if (p9 == selector) {//命中 
  77.             CacheHit \Mode 
  78.         } 
  79.     } while (p9 != nil && bucket > p12)//从后往前 p9位nil则证明没有存,也就不存在缓存了。 
  80.      
  81.     //仍然没有找到缓存,缓存彻底不存在。 
  82.     __objc_msgSend_uncached() 

2. LLookupPreopt\Function

在 arm64_64 真机的情况下,如果 _bucketsAndMaybeMask 的第 0 位为 1 则会执行 LLookupPreopt\Function 的逻辑。简单看了下汇编发现与 cache_t 中的 _originalPreoptCache 有关。

2.1 LLookupPreopt\Function 源码分析

  1. LLookupPreopt\Function
  2. #if __has_feature(ptrauth_calls) 
  3.     //p10 = _bucketsAndMaybeMask & 0x007ffffffffffffe = buckets 
  4.     and p10, p11, #0x007ffffffffffffe   // p10 = x 
  5.     //buckets x16为cls 验证 
  6.     autdb   x10, x16            // auth as early as possible 
  7. #endif 
  8.  
  9.     // x12 = (_cmd - first_shared_cache_sel) 
  10.     //(_cmd >> 12 + PAGE) << 12 + PAGEOFF 第一个sel 
  11.     adrp    x9, _MagicSelRef@PAGE 
  12.     ldr p9, [x9, _MagicSelRef@PAGEOFF] 
  13.     //差值index 
  14.     sub p12, p1, p9 
  15.  
  16.     // w9  = ((_cmd - first_shared_cache_sel) >> hash_shift & hash_mask) 
  17. #if __has_feature(ptrauth_calls) 
  18.     // bits 63..60 of x11 are the number of bits in hash_mask 
  19.     // bits 59..55 of x11 is hash_shift 
  20.  
  21.     // 取到 hash_shift... 
  22.     lsr x17, x11, #55           // w17 = (hash_shift, ...) 
  23.     //w9 = index >> hash_shift 
  24.     lsr w9, w12, w17            // >>= shift 
  25.     //x17 = _bucketsAndMaybeMask >>60 //mask_bits 
  26.     lsr x17, x11, #60           // w17 = mask_bits 
  27.     mov x11, #0x7fff 
  28.     //x11 = 0x7fff >> mask_bits //mask 
  29.     lsr x11, x11, x17           // p11 = mask (0x7fff >> mask_bits) 
  30.     //x9 = x9 & mask 
  31.     and x9, x9, x11         // &= mask 
  32. #else 
  33.     // bits 63..53 of x11 is hash_mask 
  34.     // bits 52..48 of x11 is hash_shift 
  35.     lsr x17, x11, #48           // w17 = (hash_shift, hash_mask) 
  36.     lsr w9, w12, w17            // >>= shift 
  37.     and x9, x9, x11, LSR #53        // &=  mask 
  38. #endif 
  39.     //x17 = el_offs | (imp_offs << 32) 
  40.     ldr x17, [x10, x9, LSL #3]      // x17 == sel_offs | (imp_offs << 32) 
  41.     // cmp x12  x17 是否找到sel 
  42.     cmp x12, w17, uxtw 
  43.  
  44. .if \Mode == GETIMP 
  45.     b.ne    \MissLabelConstant      // cache miss 
  46.     //imp = isa - (sel_offs >> 32) 
  47.     sub x0, x16, x17, LSR #32       // imp = isa - imp_offs 
  48.     //注册imp 
  49.     SignAsImp x0 
  50.     ret 
  51. .else 
  52.     b.ne    5f              // cache miss 
  53.     //imp(x17) =  (isa - sel_offs>> 32) 
  54.     sub x17, x16, x17, LSR #32      // imp = isa - imp_offs 
  55. .if \Mode == NORMAL 
  56.     //跳转imp 
  57.     br  x17 
  58. .elseif \Mode == LOOKUP 
  59.     //x16 = isa | 3 //这里为或的意思 
  60.     orr x16, x16, #3 // for instrumentation, note that we hit a constant cache 
  61.     //注册imp 
  62.     SignAsImp x17 
  63.     ret 
  64. .else 
  65. .abort  unhandled mode \Mode 
  66. .endif 
  67.     //x9 = buckets-1 
  68. 5:  ldursw  x9, [x10, #-8]          // offset -8 is the fallback offset 
  69.     //计算回调isa  x16 = x16 + x9 
  70.     add x16, x16, x9            // compute the fallback isa 
  71.     //使用新isa重新查找缓存 
  72.     b   LLookupStart\Function       // lookup again with a new isa 
  73. .endif 
  • 找到 imp 就跳转/返回。
  • 没有找到返回下一个 isa 重新 CacheLookup。
  • 这块进入的查找共享缓存, 与 cache_t 的 _originalPreoptCache 有关。maskZeroBits 这 4 位就是用来判断是否有 _originalPreoptCache 的。

@TODO 真机调试的时候进不到这块流程,这块分析的还不是很透彻,后面再补充。

3. CacheHit

在查找缓存命中后会执行 CacheHit。

3.1 CacheHit源码分析

  1. #define NORMAL 0 
  2. #define GETIMP 1 
  3. #define LOOKUP 2 
  4.  
  5. // CacheHit: x17 = cached IMP, x10 = address of buckets, x1 = SEL, x16 = isa 
  6. .macro CacheHit 
  7. //这里传入的为NORMAL 
  8. .if $0 == NORMAL 
  9.     //调用imp TailCallCachedImp(imp,buckets,sel,isa) 
  10.     TailCallCachedImp x17, x10, x1, x16 // authenticate and call imp 
  11. .elseif $0 == GETIMP 
  12.     //返回imp 
  13.     mov p0, p17 
  14.     //imp == nil跳转9: 
  15.     cbz p0, 9f          // don't ptrauth a nil imp 
  16.     //有imp执行AuthAndResignAsIMP(imp,buckets,sel,isa)最后给到x0返回。 
  17.     AuthAndResignAsIMP x0, x10, x1, x16 // authenticate imp and re-sign as IMP 
  18. 9:  ret             // return IMP 
  19. .elseif $0 == LOOKUP 
  20.     // No nil check for ptrauth: the caller would crash anyway when they 
  21.     // jump to a nil IMP. We don't care if that jump also fails ptrauth. 
  22.     //找imp(imp,buckets,sel,isa) 
  23.     AuthAndResignAsIMP x17, x10, x1, x16    // authenticate imp and re-sign as IMP 
  24.     //isa与x15比较 
  25.     cmp x16, x15 
  26.     //cinc如果相等 就将x16+1,否则就设成0. 
  27.     cinc    x16, x16, ne            // x16 += 1 when x15 != x16 (for instrumentation ; fallback to the parent class) 
  28.     ret             // return imp via x17 
  29. .else 
  30. .abort oops 
  31. .endif 
  32. .endmacro 
  • 这里其实走的是 NORMAL 逻辑,NORMAL 的 case 直接验证并且跳转 imp。
  • TailCallCachedImp 内部执行的是 imp^cls,对 imp 进行了解码。
  • GETIMP 返回 imp。
  • LOOKUP 查找注册 imp 并返回。

3.1 CacheHit 伪代码实现

  1. //x17 = cached IMP, x10 = address of buckets, x1 = SEL, x16 = isa 
  2. void CacheHit(Mode) { 
  3.     if (Mode == NORMAL) { 
  4.         //imp = imp^cls 解码 
  5.         TailCallCachedImp x17, x10, x1, x16    // 解码跳转imp 
  6.     } else if (Mode == GETIMP) { 
  7.         p0 = IMP 
  8.         if (p0 == nil) { 
  9.             return 
  10.         } else { 
  11.             AuthAndResignAsIMP(imp,buckets,sel,isa)//resign cached imp as IMP 
  12.         } 
  13.     } else if (Mode == LOOKUP) { 
  14.         AuthAndResignAsIMP(x17, buckets, sel, isa)//resign cached imp as IMP 
  15.         if (isa == x15) { 
  16.             x16 += 1 
  17.         } else { 
  18.             x16 = 0 
  19.         } 
  20.     } else { 
  21.         .abort oops//报错 
  22.     } 

4. __objc_msgSend_uncached

在缓存没有命中的情况下会走到 __objc_msgSend_uncached() 的逻辑:

  1. STATIC_ENTRY __objc_msgSend_uncached 
  2. UNWIND __objc_msgSend_uncached, FrameWithNoSaves 
  3.  
  4. // THIS IS NOT A CALLABLE C FUNCTION 
  5. // Out-of-band p15 is the class to search 
  6. //查找imp 
  7. MethodTableLookup 
  8. //跳转imp 
  9. TailCallFunctionPointer x17 
  10.  
  11. END_ENTRY __objc_msgSend_uncached 
  • MethodTableLookup 查找 imp
  • TailCallFunctionPointer 跳转 imp
  1. .macro MethodTableLookup 
  2.      
  3.     SAVE_REGS MSGSEND 
  4.  
  5.     // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER) 
  6.     // receiver and selector already in x0 and x1 
  7.     //x2 = cls 
  8.     mov x2, x16 
  9.     //x3 = LOOKUP_INITIALIZE|LOOKUP_RESOLVER //是否初始化,imp没有实现尝试resolver 
  10. //_lookUpImpOrForward(receiver,selector,cls,LOOKUP_INITIALIZE | LOOKUP_RESOLVER) 
  11.     mov x3, #3 
  12.     bl  _lookUpImpOrForward 
  13.  
  14.     // IMP in x0 
  15.     mov x17, x0 
  16.  
  17.     RESTORE_REGS MSGSEND 
  18.  
  19. .endmacro 
  • 调用 _lookUpImpOrForward 查找 imp。这里就调用到了 c/c++ 的代码了:
  1. IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior) 

最终会调用 _lookUpImpOrForward 进入 c/c++ 环境逻辑。

对于架构的一些理解:

  • LP64 //64位
  • x86_64 // interl 64位
  • i386 // intel 32位
  • arm // arm指令 32 位
  • arm64 //arm64指令
  • arm64 && LP64 //arm64 64位
  • arm64 && !LP64 //arm64 32 位

当然也可以通过真机跟踪汇编代码读取寄存器进行,与源码分析的是一致的,走其中的一个分支。

5. objc_msgSend流程图

总结

 

  • 判断 receiver 是否存在。
  • 通过 isa 获取 cls。
  • cls 内存平移 0x10 获取 cache 也就是 _bucketsAndMaybeMask。
  • 通过 buckets & bucketsMask 获取 buckets地址。
  • 通过 bucketsMask >> maskShift 获取 mask。
  • 通过 sel & mask 获取第一次查找的 index。
  • buckets + index << 4 找到 index 对应的地址。
  • do-while 循环判断找缓存,这次从 [index~0] 查找 imp。
  • 取到 buckets[mask] 继续 do-while 循环,从 [mask~index) 查找 imp。两次查找过程中如果有 sel 为空则会结束查找。走 __objc_msgSend_uncached 的逻辑。
  • 找到 imp 就解码跳转 imp。

 

责任编辑:武晓燕 来源: 网罗开发
相关推荐

2015-08-25 14:25:54

objc_msgsen

2019-03-26 08:15:45

iOS尾调用Objective-C

2015-08-13 10:28:07

Cobjc_msgSen汇编

2009-11-18 16:37:20

2012-07-06 13:35:42

金山WPSWPS移动版

2015-08-13 11:36:52

Windows 10上帝模式

2021-11-23 09:58:05

文件Linux命令

2023-10-11 16:36:06

谷歌AI搜索

2019-06-26 20:10:38

LinuxWindows计算机端口

2013-02-21 09:39:42

微软虚拟化开放

2013-12-18 16:43:16

顺序多核

2021-05-09 20:22:41

顺序查找二叉查找数据结构

2015-04-17 16:30:46

swiftOC

2018-07-30 10:50:15

存储管理查找排序

2011-03-17 17:10:49

iptablesmatchpolicy

2011-10-31 10:24:03

Linux死机

2009-07-07 11:28:36

2017-12-26 11:13:41

Linuxfind文件查找

2021-09-13 12:15:57

Linux文件命令

2011-11-08 09:46:10

点赞
收藏

51CTO技术栈公众号