探究APP启动流程
从main函数探究
我们知道main()函数是入口函数,那么我们探究APP的启动流程就先从main函数开始,我们直接断点main函数尝试一下
image我们可以看到从main函数入手看不到堆栈调用,说明main函数被其他调用,而且不处于APP启动的堆栈中。
根据经验,load()函数加载比较早,我们从load()函数入手是不是能有所收获呢
从load()函数探究
从load()函数下断点,开始调试
image在这里可以发现很多堆栈信息_dyld_start
我们准备了dyld源码,准备一探究竟
我们看到前面就是获取一些参数,然后调用dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
那么这个函数应该重要,我们跟进去看看
这个里面做一些dyld的启动引导,直接进入了dyld::_main()
函数,继续跟进去看看
我们找到了这个函数,有点长,800+行,我们分块进行分析
1、配置环境
image2、加载共享缓存
image3、为可执行程序实例化ImageLoader
image4、加载dyld插入的库文件
image5、链接插入的库文件
image6、镜像文件链接之后建立弱绑定
image7、运行主程序
image8、我们继续跟进去看initializeMainExecutable()
的实现,我们发现里面最主要的代码就是runInitializers()
方法
9、在runInitializers()
里面我们看到processInitializers()
方法,继续跟进
10、processInitializers()
里面最主要就是对镜像文件进行递归调用,我们继续跟进recursiveInitialization()
我们发现,这个里面的代码,最主要的就是notifySingle()
和this->doInitialization(context);
,先看notifySingle()方法
有没有我们想找的
11、我们发现notifySingle()方法里面主要是设置macho的header和path等等一些信息,我们继续跟进sNotifyObjCInit()
12、我们全局搜索之后,发现在registerObjCNotifiers()
方法里面被调用,那我们继续跟踪看看registerObjCNotifiers()
被谁调用
13、此方法只有在一个地方被调用过就是_dyld_objc_notify_register()
,这个函数太熟悉了,就是在我们在runtime里面objc_init里面的方法,一模一样,难道是巧合吗?我们找来runtime的源码继续探究
从libobjc.A.dylib库继续探究
我们找来libobjc.A.dylib库,在void _objc_init(void)
方法后面下断点,直接运行,然后打印一下当前堆栈
我们发现调用_objc_init
的方法来自于libdispatch.dylib
库里面的_os_object_init
方法,我们证实一下流程。
从libdispatch.dylib库证实调用流程
1、从苹果开源库中下载libdispatch.dylib,打开工程后全局搜_os_object_init
我们发现第一行代码就是_objc_init
,证实了流程。我们继续寻找libdispatch_init
方法,看看是否调用了_os_object_init
方法
2、全局搜索之后,我们在倒数第三行发现了_os_object_init();
方法
我们继续跟踪libdispatch_init
方法的调用
从libSystem.B.dylib库证实调用流程
1、从苹果开源库找到libSystem.B.dylib的源码,全局搜索libSystem_initializer
方法
我们在第239行的位置,找到了libdispatch_init();
的调用
总结
整个流程分析下来,大概流程图如下所示
image