OC底层探索(十九):map_images

1,159 阅读3分钟

入口

之前已经了解到map_imagesdyldregisterObjCNotifiers,中调用的,具体的实现还在libobjc源码中, 在objc_init中注册到dyld,_dyld_objc_notify_register(&map_images, load_images, unmap_image);

image.png

image.png

分析

map_image

void
map_images(unsigned count, const char * const paths[],

           const struct mach_header * const mhdrs[])
{
    // 互斥锁
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}
  • 参数分析
  • count = objcImageCount,image的Count
  • paths[] = imageFilePath,文件路径
  • mhdrs[] = imageLoadAddress, 地址

image.png

map_images_nolock

void map_images_nolock(unsigned mhCount, const char * const mhPaths[],

                  const struct mach_header * const mhdrs[])
{
    // 主要变量
    // 第一次
    static bool firstTime = YES;
    // hList 数组
    header_info *hList[mhCount];
    // header 个数
    uint32_t hCount;
    //SEL count
    size_t selrefCount = 0;
// 主要逻辑
if (firstTime) {
   // 处理共享缓存
  preopt_init();
}


// mhdr 地址
// mhPaths 文件路劲
// 添加到 header_info 单项链表中,FirstHeader LastHeader
uint32_t i = mhCount;
while (i--) {
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
// 将header_info 添加到 hList中
hList[hCount++] = hi;
}


if (firstTime) {

// 初始化并注册内部的 namedSelectors 表 
sel_init(selrefCount);

// AutoreleasePoolPage  初始化, 是双向链表,自动释放池
// SideTableMap 初始化 -> weak_table_t弱引用表  RefcountMap 引用表
// AssociationsManager 初始化, 存储的是锁,锁的散列表, 也就是关联列表
 arr_init();
 
 
 if (hCount > 0) {
//重要,读取images, hList:header_info *[header_info *], 装的是 header_info 的指针
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}

// 调用镜像中的所有 load方法
for (auto func : loadImageFuncs) {
    for (uint32_t i = 0; i < mhCount; i++) {
        func(mhdrs[i]);
        }
}
}
  • 第一次,处理共享缓存
  • 遍历循环创建 header_info链表,并将header_info添加到hList
  • 调用 _read_images
  • 调用镜像文件中所有load方法

read_images


void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses) {
    // 主要逻辑
    // 只走一次
    if (!doneOnce) {
      doneOnce = YES;
      // 初始化 taggedPointer 混淆器
      initializeTaggedPointerObfuscator();
      
      int namedClassesSize = 
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;

        // 创建 NXMapTable
        // NXStrValueMapPrototype =  _mapStrHash(字符串hash),字符串是否相等(_mapStrIsEqual)
        // 除共享缓存之外的所有类
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
    }
    
    //修复预编译阶段的 @selector 引用
    // 遍历hList
    for (EACH_HEADER) {
      // MachO 只读区 __DATA_CONST __objc_selrefs section中获取 sels 
      SEL *sels = _getObjc2SelectorRefs(hi, &count);
      // 遍历所有的 sels
      (i = 0; i < count; i++) { 
          // 将 SEL 转化为 const char * 类型, sel的指针
          const char *name = sel_cname(sels[i]);
          
          // dyld 查看name指向的有没有SEL, 有的话直接返回 dyld查找到的SEL
          // 没有的话,将name copy一份 插入到 nameSelctors中
          SEL sel = sel_registerNameNoLock(name, isBundle);
          
          // 当注册过后的 sel 的地址 与 当前的地址不同时
          if (sels[i] != sel) {
             sels[i] = sel
          }
      }
    }
    ts.log("IMAGE TIMES: fix up selector references");
    
    // 发现为未知的类,容错处理,一般不会走
    for (EACH_HEADER) { 
    // 1. dyld共享缓存不存在或者过期
    // 2. 模拟器运行
    // 3. 覆盖了共享缓存
    // 4, image 缺少 weak superclasses
    // 5, 有未知的类
    if (! mustReadClasses(hi, hasDyldRoots)) {
            // Image is sufficiently optimized that we need not call readClass()
            continue;
        }
        // mach-o 获取类
        classref_t const *classlist = _getObjc2ClassList(hi, &count);
        for (i = 0; i < count; i++) { 
          // 读取类
          Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized)
          // 类有改动,但没有移除
          if (newCls != cls  &&  newCls) {
          }
        }
    }
    ts.log("IMAGE TIMES: discover classes");
    
    
    ts.log("IMAGE TIMES: remap classes");
    ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
    
    
    ts.log("IMAGE TIMES: discover protocols");
    // mach-o : __objc_protolist section
    
    NXMapTable *protocol_map = protocols();
    protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
    for protolist {
    readProtocol(protolist[i], cls, protocol_map, 
                         isPreoptimized, isBundle);
    }
    
    
    ts.log("IMAGE TIMES: fix up @protocol references");
    for (EACH_HEADER) {
        if (launchTime && hi->isPreoptimized())
            continue;
        protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapProtocolRef(&protolist[i]);
        }
    }
    
    // 如果已经 load_image, 就加载分类
    if (didInitialAttachCategories) {
        for (EACH_HEADER) {
            load_categories_nolock(hi);
        }
    }
    ts.log("IMAGE TIMES: discover categories");
    
    
    // 实现 非懒加载的类(类里实现的了 load方法的和 有静态初始化方法的)
    for (EACH_HEADER) {
        classref_t const *classlist = hi->nlclslist(&count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            // 将类和元类加到 allocatedClasses 中
            addClassTableEntry(cls);
            if (cls->isSwiftStable()) {
            }
            realizeClassWithoutSwift(cls, nil);
        }
    }
    ts.log("IMAGE TIMES: realize non-lazy classes");
    

    ts.log("IMAGE TIMES: realize future classes");
}
  • 可以通过环境变量OBJC_PRINT_IMAGE_TIMES = YES打印
  • EACH_HEADER 遍历 hList
  • 第一次,初始化 taggedPointer 混淆器,然后三分之四负载因子开辟内存,创建gdb_objc_realized_classes , gdb_objc_realized_classes 用词不当,实际是不在共享缓存的所有类,不管有没有实现
  1. ts.log("IMAGE TIMES: fix up selector references");
    • 修复 mach-o @selector,遍历hList
    • Mach-O中取出 sels, 注册 sel,插入到 nameSelctiors
    • 当前sel地址不等于注册后的sel地址,则修正
  2. ts.log("IMAGE TIMES: discover classes");
    • 查找类,未知类的话,就从mach-o中重新读取,加载到内存中
    • 遍历读取类,addNamedClass 将类 插入到 gdb_objc_realized_classes
    • addClassTableEntry(cls), 将类和元类插入到allocatedClasses
  3. ts.log("IMAGE TIMES: remap classes");
    • remapped_class_map查找
    • 重新映射没有被镜像文件加载进来的类
    • 一般不会走
  4. ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
    • 修复老的 消息发送
    • 真机不会走
  5. ts.log("IMAGE TIMES: discover protocols");
    • 读取mach-o中的协议到内存中
    • mach-o中读取,protocollist
    • readProtocol, 创建protocol_t插入到protocol_map
  6. ts.log("IMAGE TIMES: fix up @protocol references");
    • 修复 mach-o 中协议的指针地址
    • remapProtocolRef 重新映射内存中的协议,不相同则重新赋值
  7. ts.log("IMAGE TIMES: discover categories");
    • didInitialAttachCategories 默认是 false
    • 加载分类,前提是image已经走了load_image
  8. ts.log("IMAGE TIMES: realize non-lazy classes");
    • 实现非懒加载的类
    • 当类实现+load的方法,就是非懒加载方法
    • 所以+load会导致类提前实现
    • addClassTableEntry, 将类和元类加到allocatedClasses
    • realizeClassWithoutSwift 实现类,重点,也就是ro, rw, 类别的处理
  9. ts.log("IMAGE TIMES: realize future classes");
    • 如果有未处理的类就实现新的解析未知类,以防止CF修改他们
    • 一般不会走

总结

  • map_imagesdyld注册是调用
  • 主要是将mach-o映射到内存中
  • 修复了 mach-osel与内存中nameSelctiors映射
  • mach-oprotocol读取到内存protocol_map
  • 如果类实现了load方法, 就会实现类,非懒加载