OC底层原理12-应用程序的加载上

594 阅读3分钟

前言

代码如何加载到内存的 我们探究一下

代码准备

1.应用程序的加载原理

库:可执行的二进制文件-> 能够被操作系统加载到内存->静态库 和 动态库

编译过程

Xnip2021-07-10_14-51-17.jpg

可执行文件是什么呢

打开工程 command+b 入下图操作 最后得到的就是可执行文件

Xnip2021-07-10_14-54-06.jpg

Xnip2021-07-10_14-54-24.jpg

Xnip2021-07-10_14-54-41.jpg

验证一下 可执行文件 执行了main函数里面的代码

Xnip2021-07-10_15-04-42.jpg

可执行文件通过动态链接器dyld加载到内存的加载过程入下图

Xnip2021-07-10_19-43-21.jpg

Runtime向注册回调函数是

Xnip2021-07-10_19-46-39.jpg

加载新的image是指把库映射了一份到内存 通过代码 我们打印一下 image list 看加载了哪些dyld 通过地址找到CoreFoundation这个库 库的路径:对应dyld源码DYLD_ROOT_PATH

Xnip2021-07-10_19-52-45.jpg

通过执行map_imagesLoad_images(这两个函数下篇在分析)之后 调用main函数

2.dyld引出

我们怎么找到dyld 首先打开工程 断点main函数

Xnip2021-07-10_20-01-56.jpg 我们发现左边是0 main 1 startmain之前走了这个1 start 1 start做了什么呢

Xnip2021-07-10_20-03-12.jpg

欢迎来到 libdyld.dylib 的世界 添加系统断点start 走一波 发现没断点住 但是[ViewController load]main函数之前打印的 如果我们在这打印断点 看是否有发现呢 如下图 Xnip2021-07-10_20-08-45.jpg

通过bt打印栈的信息 先进后出 所以最开始的是 dyld``_dyld_start 我们打开dyld源码(最新版本 dyld-852 下载链接在最上面)

应用加载流程如下

  • dyld`_dyld_start ->
  • dyld`dyldbootstrap::start ->
  • dyld`dyld::_main ->
  • dyld`dyld::useSimulatorDyld ->
  • dyld_sim`dyld::_main ->
  • dyld_sim`dyld::initializeMainExecutable() ->
  • dyld_sim`ImageLoader::runInitializers ->
  • dyld_sim`ImageLoader::processInitializers ->
  • dyld_sim`ImageLoader::recursiveInitialization ->
  • dyld_sim`dyld::notifySingle ->
  • libobjc.A.dylib`load_images

3.dyld流程上

通过dyld源码搜索_dyld_start 我们进入汇编

Xnip2021-07-10_20-24-11.jpg

发现会进入这个函数 call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue) 我们全局搜索一下

Xnip2021-07-10_20-27-33.jpg 该函数返回一个 dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); 我们进入看一下

4.dyld流程中的main函数主流程

Xnip2021-07-10_20-56-12.jpg

因为main函数的代码比较长main函数最后返回result 通过反推 发现会执行 sMainExecutable这个函数

Xnip2021-07-10_21-03-40.jpg

发现sMainExecutable = instantiateFromLoadedImage 这个函数实例化主程序

Xnip2021-07-10_21-04-54.jpg

通过这个函数添加镜像文件

Xnip2021-07-10_21-08-01.jpg

通过这个函数添加machO一些格式 machO的格式如下图:

Xnip2021-07-10_21-09-24.jpg

通过sniffLoadCommands这个函数添加具体格式例如load_command

Xnip2021-07-10_21-12-42.jpg

下一步加载插入的动态库

Xnip2021-07-10_21-18-55.jpg

Xnip2021-07-10_21-19-02.jpg

下一步 link主程序

Xnip2021-07-10_21-20-32.jpg

下一步 link 插入动态库

Xnip2021-07-10_21-21-37.jpg

下一步 weakBind弱引用绑定

Xnip2021-07-10_21-22-55.jpg

下一步 初始化initializeMainExecutable 跑起来

Xnip2021-07-10_21-28-24.jpg

下一步通知dyld可以进入main()函数了

Xnip2021-07-10_21-29-18.jpg

5.dyld流程-主程序运行

我们主要分析initializeMainExecutable images开始初始化

Xnip2021-07-10_21-43-15.jpg

进入 runInitializers -> processInitializers初始化准备

Xnip2021-07-10_21-44-26.jpg

进入processInitializers->recursiveInitialization递归初始化

Xnip2021-07-10_21-45-52.jpg

进入recursiveInitialization->context.notifySingle单个通知注入

Xnip2021-07-10_21-48-29.jpg

-> doInitialization 调用init方法 -> context.notifySingle通知初始化完成

Xnip2021-07-10_21-50-06.jpg

进入notifySingle->找到sNotifyObjCInit

Xnip2021-07-10_21-52-25.jpg

通过sNotifyObjCInit->registerObjCNotifiers

Xnip2021-07-10_21-58-22.jpg

通过registerObjCNotifiers->_dyld_objc_notify_register

Xnip2021-07-10_21-58-53.jpg

_dyld_objc_notify_register 就是runtime注册回调函数

6.dyld流程-images初始化流程

我们通过objc4-818.2源码 断点_objc_init

Xnip2021-07-10_22-08-33.jpg

libdispatch.dylib``_os_object_init

Xnip2021-07-10_22-11-52.jpg

_os_object_init->调用了_objc_init

Xnip2021-07-10_22-13-23.jpg

libdispatch.dylib``libdispatch_init->_os_object_init

Xnip2021-07-10_22-16-38.jpg

libSystem.B.dylib``libSystem_initializer->libdispatch_init

Xnip2021-07-10_22-18-12.jpg

Xnip2021-07-10_22-18-42.jpg

dyld``ImageLoaderMachO::doModInitFunctions:->初始化libSystem_initializer(通过 strcmp(installPath, libSystemPath(context)

Xnip2021-07-10_22-28-37.jpg

Xnip2021-07-10_22-28-57.jpg

ImageLoaderMachO::doInitialization->doModInitFunctions

Xnip2021-07-10_22-30-56.jpg

回到递归recursiveInitialization-> doInitialization

Xnip2021-07-10_22-31-45.jpg

总结app启动流程图

dyld的加载流程-2.png