我们的使用在从桌面点击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(重定向文件)

iOS八股文(八)发起流程 -dyld4源码解析

翻开ipa文件:

iOS八股文(八)发起流程 -dyld4源码解析
这个黑瞿瞿的文件就是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的调用流程图,可以参阅。

iOS八股文(八)发起流程 -dyld4源码解析

加载动态库

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

iOS八股文(八)发起流程 -dyld4源码解析
其间部分源码截图和解析如下:

iOS八股文(八)发起流程 -dyld4源码解析

在objc4源码中的_objc_init 方法处断点,并且运用lldb bt指令检查调用栈。 当然也可以通过查阅libSystem源码libDispatch源码确认调用联络。

iOS八股文(八)发起流程 -dyld4源码解析

调用_dyld_objc_notify_register来注册3个方法

iOS八股文(八)发起流程 -dyld4源码解析

map_images的调用

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

iOS八股文(八)发起流程 -dyld4源码解析
继续寻找setObjCNotifiers方法并定位

iOS八股文(八)发起流程 -dyld4源码解析
可以看到里边对全局变量赋值其实就是保存3个方法,之后调用了withLoadersReadLock并且传入了block,block内部实行了_notifyObjCMapped。那么block的实行就会调用map_images方法。

iOS八股文(八)发起流程 -dyld4源码解析
可以看到在withLoadersReadLock是直接调用的。也就是说,map_images是在objc_init的时分就会调用。

load_images的调用

仍是在runAllInitializersForMain中。

iOS八股文(八)发起流程 -dyld4源码解析
可以看到加载libSystem仍是其他类都用到了这个方法,继续点进去。

iOS八股文(八)发起流程 -dyld4源码解析
其间除了打log外,首要的意图就是调用_notifyObjCInit,这个值是objc通过_dyld_objc_notify_register传给dyld的,然后在setObjCNotifiers方法中存储的(上面提到过)。

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

rebase和bind

mach-o文件中的符号地址都是虚拟地址,在程序发起的时分,系统会生成一个随机数(ASLR),运用虚拟地址加上ASLR才是物理地址,也就是程序正真调用的地址。我们把从虚拟地址换算成物理地址的进程称之为rebase

iOS八股文(八)发起流程 -dyld4源码解析
可以看到start中生成了slide这个相当于alsr

iOS八股文(八)发起流程 -dyld4源码解析
这儿也是去向理了rebase

调用main函数

iOS八股文(八)发起流程 -dyld4源码解析
通过之前的prepare就能获取到main函数,之后直接调用。截止现在,我们的程序就进入我们的源代码中了。

写在最终

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

参阅链接

  • iOS App发起优化(一)—— 了解App的发起流程

  • iOS安全:Mach-O Type

  • 了解iOS app的编译,运转进程

  • iOS APP 发起优化(七):dyld 加载流程详细分析

  • 深化了解Mach-O文件中的Rebase和Bind

  • iOS 底层系列 – ALSR地址空间布局随机化