引入
我们都知道app运行起来load方法调用在main函数之前。
load方法之前做了些什么事情?苹果这些动态库是怎么加载到内存中的?动态库之前是怎么联系起来的?
带着这些问题我们引入一个非常牛B的东西dyld链接器。
dyld简介
dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统一个重要组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作。而且它是开源的,任何人可以通过苹果官网下载它的源码来阅读理解它的运作方式,了解系统加载动态库的细节。
准备工作
编译和库
IOS程序员一般是写上层代码,最熟悉的就是.h和.m文件,在编译的时候.h和.m文件具体经过哪些流程最终生成了可执行文件
- 源文件:
.h、.m、.cpp等文件 - 预处理:在预处理的时候,注释被删除、条件编译被处理、头文件展开、宏被替换
- 编译:进行词法分析语法分析以及中间层
IR文件,最后生成汇编文件.s文件 - 汇编:将
.s文件转换成机器语言生成.o文件 - 链接:将所有的
.o文件以及链接的第三方库,生成一个macho类型的可执行文件
编译过程
dyld链接器在哪个过程工作请看下图
dyld引入和流程分析
首先我们应该明确dyld链接是在main函数之前。我们创建一个新的app工程在main函数下断点打印堆栈看下。
堆栈显示了libdyld.dylibstart -> main`函数,但中间过程我们不清楚。找一个在main函数之前的方法load方法在下断点打印堆栈。
-
load堆栈流程 1.
_dyld_start-> 2.dyldbootstrap::start-> 3.dyld::_main-> 4.dyld::initializeMainExecutable()-> 5.runInitializers-> 6.processInitializers-> 7.recursiveInitialization-> 8.dyld::notifySingle-> 9.load_images-> 10.[ViewController load] -
流程比较多我们一一来分析,总体思路就是抓住dyld是干什么的?
链接器,分析dyld源码还是比较困然的,希望给自己点耐心。既然开始分析dyld了下面就一起来看一下源码dyld,dyld源码在准备工作中有链接,请先下载好。
_dyld_start
- 打开dyld源码定位
_dyld_start方法
- bl 指令是调用函数
dyldbootstrap::start dyldbootstrap::startC++语法我们先搜索命名空间dyldbootstrap里面的start方法
dyldbootstrap::start
- 之前都是对dyld的引导处理代码以及处理c++代码
- 返回值是调用了
dyld::_main我们定位dyld::_main函数
dyld::_main
- dyld::_main 总代码1000行左右大部分都是条件准备:环境,平台信息,路径。主机信息。我们不一一分析。我们重点不是这些。有兴趣的可以上wwdc看细节。
getHostInfo(mainExecutableMH, mainExecutableSlide)平台信息的处理;
- dyld条件准备阶段
mapSharedCache共享缓存
在iOS系统中,每个程序依赖的动态库都需要通过dyld(位于/usr/lib/dyld)一个一个加载到内存,然而,很多系统库几乎是每个程序都会用到的,如果在每个程序运行的时候都重复的去加载一次,势必造成运行缓慢,为了优化启动速度和提高程序性能,共享缓存机制就应运而生。所有默认的动态链接库被合并成一个大的缓存文件,放到/System/Library/Caches/com.apple.dyld/目录下
- 我们关注到两个函数
checkSharedRegionDisable检查共享缓存是否禁用没有共享区域iOS无法运行,mapSharedCache调用loadDyldCache加载缓存
mapSharedCache共享缓存加载
共享缓存加载又分为三种情况:
- 仅加载到当前进程,调用
mapCachePrivate()。 - 共享缓存已加载,不做任何处理。
- 当前进程首次加载共享缓存,调用
mapCacheSystemWide()。
instantiateFromLoadedImage为主程序初始化镜像加载器
segment段的个数最大是256个command的个数最大是4096个- 确保必须依赖了
libSystem库
segment和command以及macho,通过MachOView工具认识下可执行文件
loadInsertedDylib 插入动态库
通过loadInsertedDylib插入动态库,此时有的动态库数量是所有的镜像文件减一
link链接主程序
void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool
preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath{
...
//递归加载所有的动态库
this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath);
context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly);
...
__block uint64_t t2, t3, t4, t5;
{
dyld3::ScopedTimer(DBG_DYLD_TIMING_APPLY_FIXUPS, 0, 0, 0);
t2 = mach_absolute_time();
//递归重定位
this->recursiveRebaseWithAccounting(context);
context.notifyBatch(dyld_image_state_rebased, false);
t3 = mach_absolute_time();
if ( !context.linkingMainExecutable )
//递归绑定非懒加载
this->recursiveBindWithAccounting(context, forceLazysBound, neverUnload);
t4 = mach_absolute_time();
if ( !context.linkingMainExecutable )
//弱绑定
this->weakBind(context);
t5 = mach_absolute_time();
}
}
link中调用了ImageLoader::link方法,ImageLoader负责加载image文件
- 递归加载所有的动态库
- 递归
image重定位 - 递归绑定非懒加载
- 弱绑定
recursiveLoadLibraries递归绑定动态库
void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool
preflightOnly, const RPathChain& loaderRPaths, const char* loadPath){
...
// get list of libraries this image needs
//获取当前的image依赖的动态库
DependentLibraryInfo libraryInfos[fLibraryCount];
this->doGetDependentLibraries(libraryInfos);
// get list of rpaths that this image adds
//获取当前的image依赖的动态库的文件路径
std::vector<const char*> rpathsFromThisImage;
this->getRPaths(context, rpathsFromThisImage);
const RPathChain thisRPaths(&loaderRPaths, &rpathsFromThisImage);
// 加载image依赖的动态库
for(unsigned int i=0; i < fLibraryCount; ++i){
...
dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(),
&thisRPaths, cacheIndex);
// 保存加载的动态库
setLibImage(i, dependentLib, depLibReExported, requiredLibInfo.upward);
...
`}`
//告诉image依赖的动态库去加载各自需要的动态库
for(unsigned int i=0; i < libraryCount(); ++i) {
ImageLoader* dependentImage = libImage(i);
if ( dependentImage != NULL ) {
dependentImage->recursiveLoadLibraries(context, preflightOnly,
thisRPaths, libraryInfos[i].name);
}
}
}
- 获取当前
image依赖的动态库和动态库的文件路径 - 加载
image依赖的动态库,并保存起来 - 告诉image依赖的动态库去加载各自需要的动态库
weakBind 弱绑定主程序
- 前期所有的准备都是
initializeMainExecutable()下面重点来分析他
initializeMainExecutable初始化主程序
- 先运行动态库的初始化方法,再运行主程序的初始化方法
- 全局搜
runInitializers
void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo)
{
uint64_t t1 = mach_absolute_time();
mach_port_t thisThread = mach_thread_self();
ImageLoader::UninitedUpwards up;
up.count = 1;
up.imagesAndPaths[0] = { this, this->getPath() };
// 递归当前image的镜像列表实例化
processInitializers(context, thisThread, timingInfo, up);
context.notifyBatch(dyld_image_state_initialized, false);
mach_port_deallocate(mach_task_self(), thisThread);
uint64_t t2 = mach_absolute_time();
fgTotalInitTime += (t2 - t1);
}
- 全局搜
processInitializers
// upward dylib initializers can be run too soon
// To handle dangling dylibs which are upward linked but not downward, all upward linked dylibs
// have their initialization postponed until after the recursion through downward dylibs
// has completed.
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t
thisThread,InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
{
uint32_t maxImageCount = context.imageCount()+2;
ImageLoader::UninitedUpwards upsBuffer[maxImageCount];
ImageLoader::UninitedUpwards& ups = upsBuffer[0];
ups.count = 0;
// Calling recursive init on all images in images list, building a new list of
// uninitialized upward dependencies.
//递归所有镜像列表中的所有`image`,如果有没有初始化就去初始化
for (uintptr_t i=0; i < images.count; ++i) {
images.imagesAndPaths[i].first->recursiveInitialization(context,
thisThread, images.imagesAndPaths[i].second, timingInfo, ups);
}
// If any upward dependencies remain, init them.
// 为了保证所有的向上依赖关系都初始化,再次把没有初始化的image去初始化
if ( ups.count > 0 )
processInitializers(context, thisThread, timingInfo, ups);
}
- 全局搜索
recursiveInitialization
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t
this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
recursive_lock lock_info(this_thread);
recursiveSpinLock(lock_info);
...
// initialize lower level libraries first
//优先初始化依赖最深的库比如libsystem,必须先等这些最基础库初始化完成
for(unsigned int i=0; i < libraryCount(); ++i) {
ImageLoader* dependentImage = libImage(i);
if ( dependentImage != NULL ) {
// don't try to initialize stuff "above" me yet
if ( libIsUpward(i) ) {
uninitUps.imagesAndPaths[uninitUps.count] = { dependentImage,
libPath(i) };
uninitUps.count++;
}
else if ( dependentImage->fDepth >= fDepth ) {
dependentImage->recursiveInitialization(context, this_thread,
libPath(i), timingInfo, uninitUps);
}
}
}
// 将要初始化的image 依赖镜像初始化 依赖文件的初始化
uint64_t t1 = mach_absolute_time();
fState = dyld_image_state_dependents_initialized;
oldState = fState;
//下句柄通知
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
// initialize this image
// 初始化image
bool hasInitializers = this->doInitialization(context);
// image初始化完成
// let anyone know we finished initializing this image
fState = dyld_image_state_initialized;
oldState = fState;
//下句柄通知
context.notifySingle(dyld_image_state_initialized, this, NULL);
recursiveSpinUnLock();
}
-
recursiveInitialization主要做了三件事情。 -
1.初始化系统库
-
2.初始化系统库依赖的镜像文件
-
- 初始化镜像文件
doInitialization以及通知注入初始化完成
- 初始化镜像文件
-
context.notifySingle 中的context 是
const LinkContext它是struct 类型调用了函数
void (*notifySingle)(dyld_image_states, const ImageLoader* image, InitializerTimingList*);
- 下面重点分析
notifySingle流程 和doInitialization
notifySingle 通知注入
全局搜索 notifySingle
gLinkContext.notifySingle = ¬ifySingle;
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader())这是函数调用,而且参数是跟image有关的,现在只要搞清楚在哪里赋值就可以了。全局搜索sNotifyObjCInit,源码如下
-
在registerObjCNotifiers 中的第二个参数
init它的类型是_dyld_objc_notify_init给sNotifyObjCInit赋值,那么_dyld_objc_notify_init又等于什么了? -
全局搜
_dyld_objc_notify_init没有找到一处关于_dyld_objc_notify_init的赋值。解决办法只能通过下符号断点跟流程看看
- 堆栈信息看出
_dyld_objc_notify_register由libobjc.A.dylib_objc_init` 发起,我们跟进libobjc源码定位看下。
-
流程分析由
doInitialization-->doModInitFunctions-->libSystem_initializer-->libdispatch_init-->_os_object_init-->_objc_init-->_dyld_objc_notify_register-->registerObjCNotifiers -
libSystem_initializer方法在libSystem系统库中 -
libdispatch_init和_os_object_init方法在libdispatch系统库中 -
_objc_init方法在libobjc系统库中 -
从这里我们就能得出了完整的闭环有dyld调起libSystem初始化以及调起libdispatch,libobjc一系列的动态库,等到镜像文件和依赖文件初始化完成,发出通知链接libobjc去做方法和类等各项工作。
dyld流程图
还在制作中。
总结
load 方法的调用流程
_dyld_start-->dyldbootstrap::start-->dyld::_main-->intializeMainExecutable-->runInitializers-->processInitializers-->runInitializers-->recursiveInitialization-->notifySingle-->load_images-->+[ViewController load]
_objc_init方法的调用流程
doInitialization-->doModInitFunctions-->libSystem_initializer-->libdispatch_init-->_os_object_init-->_objc_init-->_dyld_objc_notify_register-->registerObjCNotifiers
这两个调用流程通过doInitialization和 notifySingle 完美形成一个完整的闭环