iOS八股文(八)启动流程 -dyld4源码解析

3,742 阅读4分钟

我们的应用在从桌面点击App icon 到App展示界面,手机都做了哪些事情? main函数是如何被调用的?

带着这些问题,研究一下app的启动流程。

首先先普及一些基础概念。

冷启动与热启动

这个概念也是业界提出的名词,其实区别很简单,就看启动之前手机后台是否有app存活。 主要区别:

名称区别
冷启动启动时,App的进程不在系统里,需要开启新进程。
热启动启动时,App的进程还在系统里,不需要开启新进程。

Mach-O

mach-o是iOS/macOS二进制文件的格式,mach-o又分为几种不同的类型。本文介绍了常见的mach-o文件类型以及它们的不同之处。 在Xcode->Build Setting -> Mach-O Type中,我们可以选择下面几种类型:

  • Executable(产物为ipa包)
  • Dynamic Library(产物为动态库)
  • Bundle(产物为bundle文件)
  • Static Library(产物为静态库)
  • Relocatable Object File(重定向文件)

image.png

打开ipa文件:

image.png 这个黑瞿瞿的文件就是mach-o格式的可执行文件。

可执行文件生成流程

  • 预编译 处理源代码里面以#开头的代码,例如宏定义、import文件、删除注释。
  • 编译 词法分析 语法分析 语义分析 生成汇编文件
  • 汇编 把汇编代码变成机器命令 生成.o文件
  • 链接程序 生成Mach-O文件,把静态库加入到Mach-O的文件中,把动态库信息写入Mach-O的文件。

经过上述步骤,我们开发的代码会生成一个Mach-O格式的可执行文件。详情可阅读这里

APP启动流程

点击app icon 后,系统调用exec()函数,系统将对应的Mach-O文件加载进内存,同时再将dyld加载进内存。dyld就会进行动态链接。 其中dyld的主要工作有一下几点:

  • 递归加载可执行文件所依赖所有动态库
  • 进行rebase和bingding操作
  • 调用main函数

关于dyld苹果已经开源dyld源码,我们可以通过阅读源码来了解dyld在pre-main阶段的流程。

截止目前(2022年5月)dyld已经更新至dyld4(941)了,iOS15以上系统都是使用的dyld4。在网上找到了dyld3的调用流程图,可以参考。

dyld流程分析图.png

加载动态库

接下来,可以再看看最新的dyld4启动过程中,关于加载动态库的调用流程。

image.png 其中部分源码截图和解析如下:

image.png

在objc4源码中的_objc_init 方法处断点,并且使用lldb bt命令查看调用栈。 当然也可以通过查阅libSystem源码libDispatch源码确认调用关系。

image.png

调用_dyld_objc_notify_register来注册3个方法 image.png

map_images的调用

来到dyld源码中搜索_dyld_objc_notify_register方法并定位。

image.png 继续寻找setObjCNotifiers方法并定位

image.png 可以看到里面对全局变量赋值其实就是保存3个方法,之后调用了withLoadersReadLock并且传入了block,block内部执行了_notifyObjCMapped。那么block的执行就会调用map_images方法。

image.png 可以看到在withLoadersReadLock是直接调用的。也就是说,map_images是在objc_init的时候就会调用。

load_images的调用

还是在runAllInitializersForMain中。 image.png 可以看到加载libSystem还是其他类都用到了这个方法,继续点进去。

image.png 其中除了打log外,主要的目的就是调用_notifyObjCInit,这个值是objc通过_dyld_objc_notify_register传给dyld的,然后在setObjCNotifiers方法中存储的(上面提到过)。

可以看到dyld在加载应用程序的时候也是需要objc的。dyld像是一个首脑派发加载任务给objc,objc把动态库加载进内存。

rebase和bind

mach-o文件中的符号地址都是虚拟地址,在程序启动的时候,系统会生成一个随机数(ASLR),使用虚拟地址加上ASLR才是物理地址,也就是程序正真调用的地址。我们把从虚拟地址换算成物理地址的过程称之为rebaseimage.png 可以看到start中生成了slide这个相当于alsr

image.png 这里也是去处理了rebase

调用main函数

image.png 通过之前的prepare就能获取到main函数,之后直接调用。截止目前,我们的程序就进入我们的源代码中了。

写在最后

这篇文章重点是dyld4(dyld-941.5)的源码解析部分,以及为之后的内容做铺垫。很多内容不影响整体理解作者删繁就简,如有遗漏可联系作者。下一遍文章计划对load_images详细讲解。

参考链接🔗