iOS 底层探索篇 —— dyld加载流程(下)

这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战

1 dyld加载流程分析(下)

上文说到_objc_init中调用了_dyld_objc_notify_register(&map_images, load_images, unmap_image)对registerObjCNotifiers中的sNotifyObjCMappedsNotifyObjCInitsNotifyObjCUnmapped,进行了赋值。那么sNotifyObjCMapped,sNotifyObjCInit是在哪里调用的呢

1.1 sNotifyObjCMapped 的调用

在dyld 源码中搜索sNotifyObjCMapped,看到其在notifyBatchPartial中被调用。

在这里插入图片描述

在搜索notifyBatchPartial在哪里被调用,发现其在被赋值后就直接被调用了

在这里插入图片描述

1.2 sNotifyObjCInit 的调用

在dyld 源码中搜索sNotifyObjCInit,发现也是赋值后,sNotifyObjCMapped调用之后调用

在这里插入图片描述

1.3 load 函数的调用

load函数是哪里调用的呢?在load函数打个断点,然后输入bt查看,发现是在load_images里面。

在这里插入图片描述

那么load_images 又是如何调用的load函数呢。 首先找到load_images,看到这里有一个prepare_load_methods

在这里插入图片描述

点击进去看到prepare_load_methods有主类和分类的load方法

在这里插入图片描述

先看主类的load 方法,看到这里会进行递归,将类的所有父类也调用schedule_class_load方法。然后会add_class_to_loadable_list

在这里插入图片描述

点进去看到有getLoadMethod

在这里插入图片描述

在点进去看到这里将类中的load方法imp取了出来

在这里插入图片描述

在看回getLoadMethod,发现其将class 添加到loadable_classes里面,这里的method就是之前返回的load 方法的imp

再来看分类的load方法发现实现差不多,只是添加到了另一个字典loadable_categories里面。

在这里插入图片描述

再回到load_images 方法,看到有call_load_methods方法。

在这里插入图片描述

看到call_load_methods里面有call_class_loadscall_category_loads方法。这里做的就是递归将所有的loadable_classes里面的类和loadable_categories里面的分类的load方法进行调用

在这里插入图片描述

看一下call_class_loads,发现确实是将loadable_classes里面的class和method取出来,然后进行load方法的调用。

在这里插入图片描述

看一下分类的实现方法,也是一摸摸一样样的。

在这里插入图片描述

1.4 CXX 方法的调用

在main函数中添加一个CXX 方法。

在这里插入图片描述

运行后发现,是在load方法之后。

在这里插入图片描述

在CXX 方法里面打个断点,然后输入bt查看方法栈,发现是在doModInitFunctions里面调用的。

在这里插入图片描述

doModInitFunctions是在doInitialization里面调用的,而doInitialization是在notify之前,也就是load方法之前的,为什么这里反而cxx方法在load之后调用呢?这里因为这里的doInitialization是objc的镜像文件初始化,而不是工程的初始化,所以工程里的cxx方法在load后面调用。证明一下,在_objc_init中添加cxx方法。

在这里插入图片描述

然后运行,可以看到在_objc_init中的c++函数确实是在load方法之前的。

在这里插入图片描述

1.5 main 函数的调用

dyld是如何找到main函数并对其进行调用的呢? 又回到最初的起点:dyld_start。看到这里的最后一行有一个跳转到main函数的地方。

在这里插入图片描述

在mac上运行一下证明,在cXX函数打个断点,然后进入汇编模式

在这里插入图片描述

往下走,看到jmpq *%rax,也就是之前说会跳转到main函数的地方。

在这里插入图片描述

读取一下寄存器,发现rax就是main函数。所以jmpq *%rax 也就是调用main函数。

在这里插入图片描述

如果把main函数名字改成别的,那么还能进行调用吗?

在这里插入图片描述

运行一下,发现了报错。可以知道main是写定的函数,写入内存,读取到dyld,如果修改了main函数的名称,那么程序就会报错。

在这里插入图片描述

1.6 dyld流程图

在这里插入图片描述

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享