一、dyld简介
在iOS系统中,几乎所有的程序都会用到动态库,静态库等,而这些库在加载的时候都需要用到dyld程序进行链接,dyld是苹果的动态链接器,是苹果操作系统的一个重要组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作。
二、准备
创建一个空的项目,在ViewController这个类中添加一个load方法,打个断点,然后在xcode左侧查看项目的堆栈调用信息,如下:
图1我们发现在load方法之前,加载了好多函数呀,这些函数都干了什么啦,这些是我们接下来需要探索的东西。
补充
我们也可以通过lldb进行查看如下:
镜像加载图
dyld初探
下载最新的dyld源码本篇文章基于dyld-750.6进行分析,我们在源码中搜索_dyld_start,然后发现在dyldStartup.s文件中有具体的调用,无论模拟器、真机都会调用dyldbootstrap::start这个方法,我们继续查找,找到如下:
部分源码如下:
uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[],
const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue)
{
// Emit kdebug tracepoint to indicate dyld bootstrap has started <rdar://46878536>
dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);
// if kernel had to slide dyld, we need to fix up load sensitive locations
// we have to do this before using any global variables
rebaseDyld(dyldsMachHeader);
// kernel sets up env pointer to be just past end of agv array
const char** envp = &argv[argc+1];
// kernel sets up apple pointer to be just past end of envp array
const char** apple = envp;
while(*apple != NULL) { ++apple; }
++apple;
// set up random value for stack canary
__guard_setup(apple);
#if DYLD_INITIALIZER_SUPPORT
// run all C++ initializers inside dyld
runDyldInitializers(argc, argv, envp, apple);
#endif
// now that we are done bootstrapping dyld, call dyld's main
uintptr_t appsSlide = appsMachHeader->getSlide();
return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}
在进入dyld的__main函数之前,会进行dyld的重定向、dyld内部加载所有的c++初始化器、地址偏移等工作。
我们进行dyld的__main函数中,发现里面干了好多事情,流程如下:
dyld流程分析图.png
1.我们发现__main函数中,主要会对环境以及平台信息进行处理,比如我们xcode设置了一些环境变量之类的,都可以打印出来。
2.getHostInfo函数:获取cpu相关信息
3.checkSharedRegionDisable函数:判断是否可以加载系统共享缓存库,加载共享缓存库
4.instantiateFromLoadedImage函数:实例化主程序,也就是machO这个可执行文件、链接动态库和插入库
5.loadInsertedDylib:加载所有插入库
6.weakBind:符号绑定
7.initializeMainExecutable:初始化依赖库、三方库、load、c++构造函数,这个函数内部源码如下:
void initializeMainExecutable()
{
// record that we've reached this step
gLinkContext.startedInitializingMainExecutable = true;
// run initialzers for any inserted dylibs
ImageLoader::InitializerTimingList initializerTimes[allImagesCount()];
initializerTimes[0].count = 0;
const size_t rootCount = sImageRoots.size();
if ( rootCount > 1 ) {
for(size_t i=1; i < rootCount; ++i) {
sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]);
}
}
// run initializers for main executable and everything it brings up
sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
// register cxa_atexit() handler to run static terminators in all loaded images when this process exits
if ( gLibSystemHelpers != NULL )
(*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL, NULL);
// dump info if requested
if ( sEnv.DYLD_PRINT_STATISTICS )
ImageLoader::printStatistics((unsigned int)allImagesCount(), initializerTimes[0]);
if ( sEnv.DYLD_PRINT_STATISTICS_DETAILS )
ImageLoaderMachO::printStatisticsDetails((unsigned int)allImagesCount(), initializerTimes[0]);
}
里面关键的函数是runInitializers,顺着这个函数我们可以找到notifySingle,doInitialization
查找如下:runInitializers->processInitializers->recursiveInitialization->notifySingle(加载load方法)
doInitialization:内部会调用全局C++对象的构造函数,即attribute((constructor))这样的函数。
最后通过notifyMonitoringDyldMain:通知main函数dyld引导已经完成了,可以进入main函数了。
补充
我们发现notifySingle这个函数中会调用sNotifyObjCInit,它是在registerObjCNotifiers函数中进行赋值,_dyld_objc_notify_register函数中又调用了registerObjCNotifiers,但是我们却没有找到_dyld_objc_notify_register函数调用的位置,最后在objc的源码中发现了我们想要的结果。
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
runtime_init();
exception_init();
cache_init();
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
注释中有解释,Called by libSystem BEFORE library initialization time,library初始化之前被libSystem库调用。
查看load_images源码实现
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
loadAllCategories();
}
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
最后一行代码是调用所有的+load方法
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
call_load_methods函数中循环调用类的+load方法
然后会调用分类的+load方法
网友评论