dyld和objc关联

283 阅读3分钟

今天我们来探究一下App在启动后是如何将类信息通过dyld关联到objc的?

_objc_init 分析

首先通过Apple open source 获取到苹果的官方源码objc4-781并配置,全局搜索_objc_init函数。

_objc_init函数是dyld关联objc的入口函数,下面来介绍一下这个函数中实现的一些函数

environ_init()

读取影响运行时的环境变量。如果需要,还可以打印环境变量帮助

进入environ_init函数,通过添加如下代码打印所有的环境变量

打印得到的部分环境变量如下:

关闭打印环境变量的代码,设置环境变量OBJC_PRINT_LOAD_METHODS

运行程序,打印出所有实现了load方法的类

我们在LGPerson类中实现了load方法,所以也打印出来了,如果没有实现是不会打印的。

还可以通过终端命令export OBJC_HELP=1打印所有的环境变量

通过在xcode中配置一些环境变量我们可以打印出项目中哪些类实现了load方法等情况,来快速的查看项目的相关信息。

tls_init()

关于线程key的绑定 - 比如每线程数据的析构函数

static_init()

运行C ++静态构造函数。在dyld调用我们的静态构造函数之前,libc 会调用 _objc_init(), 因此我们必须自己做

runtime_init()

runtime运行时环境初始化,里面主要是:unattachedCategories,allocatedClasses

exception_init ()

初始化libobjc的异常处理系统

cache_init()

缓存条件初始化

_imp_implementationWithBlock_init()

启动回调机制。通常这不会做什么,因为所有的初始化都 是惰性的,但是对于某些进程,我们会迫不及待地加载trampolines dylib。

_dyld_objc_notify_register(&map_images, load_images, unmap_image)

此函数是_objc_init函数的核心,此函数传递了三个参数,其中map_images,load_images是核心函数,它们的功能如下图:

我们所写的代码,通过编绎,得到macho的可执行文件,dyld通过函数_dyld_objc_notify_register关联objc将macho文件写入内存;在dyld中通过回调函数map_imagesload_images将macho中的data数据回调回来进行镜像文件的映射。

map_images 分析

分析map_images_nolock函数,我们发现_read_images函数是此函数的核心,它的功能是读取镜像文件。

_read_images 分析

通过分析_read_images

1.条件控制进行一次的加载

initializeTaggedPointerObfuscator函数是对NSString,NSNumber等小对象的处理; gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize)初始化一个MapTable表来存储加载的类。

2.修复预编译阶段的 @selector 的混乱问题

3.错误混乱的类处理,通过readClass函数将类信息读取出来

  • 在3597行断点打印cls时,打印的是一个地址0x000000010052cfe8
  • 在3598行断点打印cls时,打印的是类信息OS_object. 说明readClass函数将类信息和类地址进行了关联.

4.修复重映射一些没有被镜像文件加载进来的类

5.修复一些消息

6.当我们类里面有协议的时候 : readProtocol

7.修复没有被加载的协议

8.分类处理

9.非懒加载的类的加载处理

  • addClassTableEntry将类添加到表中,方便后面的快速读取;
  • realizeClassWithoutSwift加载类信息。

10.没有被处理的类 优化那些被侵犯的类

总结

_objc_init函数通过调用dyld_dyld_objc_notify_register函数,传入两个函数map_imagesload_images,将macho镜像文件的data回调回来;map_images通过调用map_images_nolock函数,再调用_read_images函数;在_read_images函数中关联类信息,处理方法、属性、协议和类的其它信息。通过这一系列的操作,就成功的将macho文件中的data数据映射到了内存中,实现了dyldobjc的关联。