# 07.dyld

> [DYLD源码](https://opensource.apple.com/tarballs/dyld/)

## 一、验证

### 1.1 在main函数处下断点

我们新建一个项目，在main函数起始处下个断点。

![dyld](/files/-MZvK8Os7Pd1UzYQ8g3c)

查看堆栈信息`bt`

```
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000100d9a190 DyleDemo`main(argc=1, argv=0x000000016f06b8e0) at main.m:12:16
    frame #1: 0x00000001afbb48f0 libdyld.dylib`start + 4
(lldb)
```

很明显，这里的堆栈信息不足以供我们进行分析。我们需要在更早的地方下断点。

### 1.2 在+load下断点

![dyld](/files/-MZvK8OujyCRS9NSDO4k)

```
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x0000000100911e78 DyleDemo`+[ViewController load](self=ViewController, _cmd="load") at ViewController.m:18:1
    frame #1: 0x00000001afadf35c libobjc.A.dylib`load_images + 984
    frame #2: 0x0000000100aca190 dyld`dyld::notifySingle(dyld_image_states, ImageLoader const*, ImageLoader::InitializerTimingList*) + 448
    frame #3: 0x0000000100ada0d8 dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 512
    frame #4: 0x0000000100ad8520 dyld`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 184
    frame #5: 0x0000000100ad85e8 dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 92
    frame #6: 0x0000000100aca658 dyld`dyld::initializeMainExecutable() + 216
    frame #7: 0x0000000100aceeb0 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 4400
    frame #8: 0x0000000100ac9208 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 396
    frame #9: 0x0000000100ac9038 dyld`_dyld_start + 56
```

因为load方法在main之前就已经执行了，在这里我们可以看到更丰富的堆栈信息。

### 1.3 一些关键流程

> 跟具体的可以结合`dyld`源码进行了解

#### 1.3.1 dyld start

`rebaseDyld` 重定位 调用 `main` dyld的核心逻辑

#### 1.3.2 dyld main

> Return address of main() in target program which \_\_dyld\_start jumps to

**a. 加载动态库共享缓存**

> 何为共享缓存？UIKit、ARKit等系统库就是在共享缓存中的。

三种情况的加载

* 仅加载到当前进程（设置不放入共享缓存）
* 已经加载了共享缓存
* 第一次加载共享缓存

**b. dyld3 ClosureMode(闭包模式)**

> iOS11 之后引入了dyld3和闭包模式 闭包模式加载速度更快效率更高。 iOS13 之后Framework和三方库也会通过闭包模式加载

先去缓存中找`mainClosure` 没找到就创建一个

* launchWithClosure
  * 使用上面的`mainClosure`启动`main`
  * 并返回`main`函数地址

## 二、流程总结

* 程序执行从\_dyld\_start开始
* 进入dyld\_main函数
* 配置环境变量以及rebase\_dyld
* 加载共享缓存
  * 系统的动态库都在这里了
* dyld2/dyld(ClosureMode)`决定以哪种模式继续进行`
* 实例化主程序`ImageLoaderMachO`，加入到`allImages`中
* 到此共享缓存加载完毕，设置版本化的dylib覆盖
  * Now that shared cache is loaded, setup an versioned dylib overrides
* 加载插入的动态库
  * 越狱插件在这里加入
* 记录插入的库的数量，以便统一搜索将先查看插入的库，然后是main，然后是其他。
  * record count of inserted libraries so that a flat search will look at inserted libraries, then main, then others.
* 链接主程序
  * `绑定符号（非懒加载、弱符号）等`
* 链接所有插入的库
  * 链接主可执行文件后执行此操作，以使插入的dylib（例如libSystem）不在程序使用的dylib的前面
  * 只有插入的库可以插入。绑定所有插入的库后，注册插入信息，以便链接工作
* 绑定并通知`主程序可执行文件`，现在插入的已被注册
* 绑定并通知`现在已插入的库`已插入的Image已被注册
* 执行所有初始化方法：`ImageLoader`
  * 初始化所有插入的库、运行主要可执行文件的初始化程序及其依赖
  * `ImageLoader`:
    * `runInitializers`:
      * `processInitializers`:
        * `ecursiveInitialization`:
          * `notifySingle`:
            * 此函数执行一个回调，此回调是`_objc_init`初始化时服饰的一个函数`load_images`
              * `load_images`里执行`class_load_metgods`函数
                * `class_load_metgods`里调用`call_class_loads`函数：循环调用各个类的`load`函数
          * `doModInitFunction`
            * 内部会调用全局C++对象的构造函数`__attribute__((constructor))`的C函数
* 通知任何监视过程此过程即将进入main()
* 查找主要可执行文件的指针并`return`

## 三、主程序中的Load先执行还是framework中的先执行？分类呢？子类呢？

### 3.1 创建一个Demo，创建两个Framework。

![1](/files/-M_G7HpCqeJqGB-_izQC)

* Main函数内添加Log

```swift
int main(int argc, char * argv[]) {
    printf("Main函数");
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
```

* 添加C++构造函数

```cpp
__attribute__((constructor)) void funcA() {
    printf(__FUNCTION__);
    printf("\n");
}

__attribute__((constructor)) void funcB() {
    printf(__FUNCTION__);
    printf("\n");
}
```

* Frameworks中添加Log

![](/files/-M_G7HpDxPJwvqdjveb9)

* 运行看Log

```
2021-05-09 19:59:09.301905+0800 DyleDemo[34074:1711187] TestSecond-TestClassSecond-Load
2021-05-09 19:59:09.302965+0800 DyleDemo[34074:1711187] TestF-TestClass-Load
2021-05-09 19:59:09.303008+0800 DyleDemo[34074:1711187] ViewController-Load
2021-05-09 19:59:09.303027+0800 DyleDemo[34074:1711187] SomeClass-Load
funcA
funcB
2021-05-09 19:59:09.303178+0800 DyleDemo[34074:1711187] Main函数
```

* 调整一下Framework的链接顺序

![](/files/-M_G7HpEpUp1PAmHGhhq)

* 再看下下log

```
2021-05-09 20:02:42.713300+0800 DyleDemo[34120:1713030] TestSecond-TestClassSecond-Load
2021-05-09 20:02:42.713925+0800 DyleDemo[34120:1713030] TestF-TestClass-Load
2021-05-09 20:02:42.713961+0800 DyleDemo[34120:1713030] ViewController-Load
2021-05-09 20:02:42.713983+0800 DyleDemo[34120:1713030] SomeClass-Load
funcA
funcB
2021-05-09 20:02:42.714103+0800 DyleDemo[34120:1713030] Main函数
```

#### 小结

Frameworks(有多个的话按照链接顺序来) -> 主程序内的Load -> C++构造函数 -> Main

### 3.2 我们再加两个分类玩玩吧！

* Log如下

```
2021-05-09 20:14:38.455892+0800 DyleDemo[34267:1718526] TestSecond-TestClassSecond-Load
2021-05-09 20:14:38.456556+0800 DyleDemo[34267:1718526] TestF-TestClass-Load
2021-05-09 20:14:38.456594+0800 DyleDemo[34267:1718526] ViewController-Load
2021-05-09 20:14:38.456615+0800 DyleDemo[34267:1718526] SomeClass-Load
2021-05-09 20:14:38.456633+0800 DyleDemo[34267:1718526] UIViewController+Test Load
2021-05-09 20:14:38.456651+0800 DyleDemo[34267:1718526] UIView+Test Load
funcA
funcB
2021-05-09 20:14:38.456782+0800 DyleDemo[34267:1718526] Main函数
```

当前的顺序是这样的

![](/files/-M_G7HpFaA47ILLHDn-z)

* 调整一下顺序能让分类提前加载Load么？我们试一下！

![](/files/-M_G7HpGQJioNYze4GTY)

```
2021-05-09 20:20:04.294895+0800 DyleDemo[34337:1721873] TestSecond-TestClassSecond-Load
2021-05-09 20:20:04.295441+0800 DyleDemo[34337:1721873] TestF-TestClass-Load
2021-05-09 20:20:04.295476+0800 DyleDemo[34337:1721873] ViewController-Load
2021-05-09 20:20:04.295495+0800 DyleDemo[34337:1721873] SomeClass-Load
2021-05-09 20:20:04.295511+0800 DyleDemo[34337:1721873] UIView+Test Load
2021-05-09 20:20:04.295526+0800 DyleDemo[34337:1721873] UIViewController+Test Load
funcA
funcB
2021-05-09 20:20:04.295640+0800 DyleDemo[34337:1721873] Main函数
```

#### 小结

项目内的分类在最后调用Load，在C++构造函数之前

### 3.3 Framework中的分类呢？

我们在两个Framework中分别添加一个分类

![](/files/-M_G7HpHkDkzOQXc7Hvr)

* Log如下

```
2021-05-09 20:22:41.298458+0800 DyleDemo[34371:1723177] TestSecond-TestClassSecond-Load
2021-05-09 20:22:41.299001+0800 DyleDemo[34371:1723177] TestSecond-TestClassSecond+Cate-Load
2021-05-09 20:22:41.299032+0800 DyleDemo[34371:1723177] TestF-TestClass-Load
2021-05-09 20:22:41.299048+0800 DyleDemo[34371:1723177] TestF-TestClass+Cate-Load
2021-05-09 20:22:41.299076+0800 DyleDemo[34371:1723177] ViewController-Load
2021-05-09 20:22:41.299093+0800 DyleDemo[34371:1723177] SomeClass-Load
2021-05-09 20:22:41.299109+0800 DyleDemo[34371:1723177] UIView+Test Load
2021-05-09 20:22:41.299145+0800 DyleDemo[34371:1723177] UIViewController+Test Load
funcA
funcB
2021-05-09 20:22:41.299451+0800 DyleDemo[34371:1723177] Main函数
```

#### 小结

和在主工程中类似，每个Framework内部的分类在Framework最后调用Load。

### 3.4 再来个子类看下

这次我们在Framework中建一个子类看看

![](/files/-M_G7HpIxtAAhSFUfpHB)

* Log

```
2021-05-09 20:31:31.124655+0800 DyleDemo[34472:1726758] TestSecond-TestClassSecond-Load
2021-05-09 20:31:31.125259+0800 DyleDemo[34472:1726758] TestSecond-TestClassSecond+Cate-Load
2021-05-09 20:31:31.125288+0800 DyleDemo[34472:1726758] TestF-TestClass-Load
2021-05-09 20:31:31.125306+0800 DyleDemo[34472:1726758] TestF-TestSubClass-Load
2021-05-09 20:31:31.125323+0800 DyleDemo[34472:1726758] TestF-TestClass+Cate-Load
2021-05-09 20:31:31.125350+0800 DyleDemo[34472:1726758] ViewController-Load
2021-05-09 20:31:31.125367+0800 DyleDemo[34472:1726758] SomeClass-Load
2021-05-09 20:31:31.125427+0800 DyleDemo[34472:1726758] UIView+Test Load
2021-05-09 20:31:31.125575+0800 DyleDemo[34472:1726758] UIViewController+Test Load
funcA
funcB
2021-05-09 20:31:31.125796+0800 DyleDemo[34472:1726758] Main函数
```

#### 小结

子类的Load先于分类


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ni-xiang/07.dyld.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
