苹果在 iOS16
之后使用 dyld4
取代了 dyld3
, 但是目前网上的资料都是介绍dyld3启动流程, 本文讲解最新的dyld4的原理和核心流程
官网dyld4介绍 github.com/apple-oss-d…
dyld4
dyld(the dynamic link editor)是苹果的动态链接器
,是苹果操作系统一个重要组成部分,在系统内核 XNU 完成 Mach-O 文件的加载,做好程序准备工作之后,交由 dyld 负责余下的工作如动态库加载, 进程信息初始化。
dyld 最新版本是dyld4
dyld4
同时支持pre-built loader
(提前缓存) 来提高启动速度
也支持 just-in-time loader
(实时加载)来应对App或系统信息
变化的情况
启动
内核将所有KernelArgs
(内核参数)推送到堆栈上并跳转到 dyld 的入口点start()
来启动进程。
void start(const KernelArgs* kernArgs, void* prevDyldMH)
KernelArgs(内核参数)
KernelArgs
指向内核在堆栈上传递给 dyld 信息的指针(例如 argc
、argv
、envp
和apple parameter
等)。
进程状态
分为两类
-
DyldProcessConfig
保留进程的
固定
状态信息(例如安全策略,dyld缓存,日志记录标志,平台等)。
-
DyldRuntimeState
保存在进程生命周期内
变化
的状态信息。它包括Loader、注册的通知函数和所有锁。
Loader objects
每个加载的 mach-o
文件(可执行程序, 动态库) 对应一个 dyld4::Loader
对象跟踪。
分为如下四类mach-o
- 可执行程序
- libsystem.dylib
- libdyld.dylib
- 其他dylib
// Loader 加载器(Prebuilt or JustInTime 两种 loader)
const Loader* mainExecutableLoader = nullptr; // 主程序加载方式
Vector<ConstAuthLoader> loaded; // 其他动态库加载方式
const Loader* libSystemLoader = nullptr; // libsystem.dylib 加载方式
const Loader* libdyldLoader = nullptr; // libdyld.dylib 加载方式
Loader
使用 loadAddress()
来获取 mach_o
信息 .
Loader 有两种
- PrebuiltLoader: 使用 dyld cache和缓存文件, 速度更快。
- JustInTimeLoader: 速度慢, 但是更新及时
PrebuiltLoader
包含 mach-o 文件的预先计算
(缓存
)的信息,包括路径、验证信息、依赖的 dylib 和预先计算的绑定目标数组。
PrebuiltLoaderSet
一个进程中最多有两个 PrebuiltLoaderSet
- 一个在 dyld 缓存中,包含
每个 dylib
的PrebuiltLoader,该 dylib 是 dyld 共享缓存的一部分。 - 另一个是
App
的PrebuiltLoaderSet, 包含mainLoader- App的PrebuiltLoaderSet来自两个位置:
dyld 缓存或文件
。
- App的PrebuiltLoaderSet来自两个位置:
JustInTimeLoader
JustInTimeLoader 通过MachOAnalyzer
根据需要实时解析mach-o文件。
加载所有images之后,dyld 将 JustInTimeLoader 对象“克隆”到 PrebuiltLoader 对象,然后将这些对象打包到 PrebuiltLoaderSet 中并写入磁盘, 之后就可以使用 PrebuiltLoader 加载缓存了,提高了App运行速度。
App Loader 过程
- 启动时,dyld 查找 PrebuiltLoader, 如果找到,则调用其 isValid()判断有效, 则使用Prebuilt。
- 如果没有,则创建一个新 JustInTimeLoader, JustInTimeLoader 通过解析 mach-o 来查找其依赖项。
- 对于每个依赖项,都会执行上面相同的步骤。
SyscallDelgate
SyscallDelgate 处理所有syscal(如打开或映射文件)。
- 低级(即posix级)方法: 如打开,关闭,mmap。
- 更高级别的方法: withReadOnlyMappedFile等