引言:
眾所周知,我們的iOS應(yīng)用是通過Dyld進(jìn)行加載的,那么Dyld是如何加載我們的應(yīng)用的,它的流程是怎樣的,下面我們把dyld的加載分為幾個步驟做個簡短的分析。
1 dyld的start啟動
首先我們創(chuàng)建一個Demo工程,在我們的AppDelegate.m文件里加入+(load)方法并斷點(diǎn),如下圖所示:

運(yùn)行Demo App后,可以得到所下圖

從圖2中我們可以看到,我們的App是從_dyld_start開始的,我們點(diǎn)擊dyld_start,看到匯編的第18行代碼,這里調(diào)用了dyldbootstrap::start(macho_header const, int, char const, long, macho_header const, unsigned long*)這行代碼,我們再使用bt命令看下詳細(xì)的堆棧,如下圖

從這里可以看出一切的開始是從_dyld_start開始的,這個dyld_start在可以在dyld的源碼里找(注:這里使用的dyld的源碼的版本是dyld-832.7.3),下面我們打開dyld的源碼進(jìn)行分析
我們在dyld的源碼里找 dyldbootstrap這個命名空間,按住shift+comand+j進(jìn)入文件,所下圖所示

從這里可以看到是在dyldInitialization.cpp文件里,然后我們在文件里搜索start,找到uintptr_tstart(constdyld3::MachOLoaded* appsMachHeader,intargc,constchar* argv[],
constdyld3::MachOLoaded* dyldsMachHeader,uintptr_t* startGlue)函數(shù),如圖所示:

我們從start函數(shù)逐步往下分析整個流程,我們先看下參數(shù),appsMachHeader是我們App的MachHeader,dyldsMachHeader是dyld的MachHeader。
第121行代碼是告訴我們的degbugServer,我的dyld開始起動了。
第125行代碼rebaseDyld(dyldsMachHeader) 重定位我們的dyld。
第136,143行是棧溢出保護(hù)和初始化dyld,之后就是調(diào)用dyld的main函數(shù)(這是最核心的),我們著重分析下dyld的main函數(shù)流程。

main函數(shù)的前幾行代碼都是代碼檢測相關(guān)的,不是核心內(nèi)容
我們下面來看主程序的配置相關(guān),如圖

這些是配置主程序的MachHeader(就是Macho的頭),主程序的Slide(就是主程序的ASLR的偏移值,每次啟動都是不一樣的)
下面調(diào)用setContext(mainExecutableMH, argc, argv, envp, apple);保存我們配置的信息

然后通過configureProcessRestrictions(mainExecutableMH, envp)這個函數(shù)配置進(jìn)程受限制(AMFI相關(guān)(Apple Mobile File Integrity蘋果移動文件保護(hù))),下圖都是進(jìn)程受限相關(guān)的配置,比如是否強(qiáng)制使用dyld3(dyld是在iOS11推出來的,加載高效)

下圖打印我們的環(huán)境變量,這個環(huán)境變理可通過 Environment Variables配置

以下都是dyld的啟動,配置,以及主程序的相關(guān)配置和一些代碼檢測的流程,下面我們來分析共享緩存
2 dyld加載共享緩存

點(diǎn)擊進(jìn)去checkSharedRegionDisable發(fā)現(xiàn)有一個“iOS cannot run without shared region”說明,這是表明我們的iOS是一定有共享緩存的。
接著調(diào)用mapSharedCache傳進(jìn)去主程序的Slide,這個函數(shù)調(diào)用了loadDyldCache加載我們的dyld緩存,如下圖所示

滿足options.forcePrivate 這個條件的話,只加載當(dāng)前進(jìn)程
reuseExistingCache如果緩存已經(jīng)加載不再處理,如果第一次加載執(zhí)行mapCacheSystemWide這個函數(shù)
通過以上分析,可以得出結(jié)論,動態(tài)庫的共享緩存是最先被加載的(我們自己開發(fā)的動態(tài)庫不可以)。從iOS11引入了dyld3的ClosureMode(閉包模式加載更快),下面我們來分析一下
3 dyld3的閉包模式

這里先判斷閉包模式是否打開,如果沒有的話將會走dyld2的流程,打開走dyld3的流程(dyld2,dyld3的加載流程一致),下面我們來分析dyld3的閉包模式

先從共享緩存中查找這個實例,如果拿到就先驗證

這里判斷是否查找成功,并且驗證閉包的有效性,如果失效,sLaunchModeUsed設(shè)置為NULL

這里如果沒找到,再去緩存中查一次,如果mainClosure為空,這里就調(diào)用buildLaunchClosure創(chuàng)建閉包實例
最終拿到這個mainClosure實例啟動這個實例,如下圖所示

如果啟動失敗或者閉包過期,這里就再重新調(diào)用buildLaunchClosure創(chuàng)建并調(diào)用launchWithClosure重新啟動一次。
啟動成功后設(shè)置gLinkContext.startedInitializingMainExecutable = true;這個主程序加載成功了。
同時返回結(jié)果result(即主程序的main),如下圖所示:

接著就會實例化我們的主程序了,下面我們來分析是如何加載的。
4 dyld加載主程序
接下看下怎么實例化主程序的,如下圖所示

第6862行代碼會調(diào)用instantiateFromLoadedImage函數(shù)實例化我們的主程序,我們來看下instantiateFromLoadedImage這個函數(shù),下圖所示:

這里通過ImageLoaderMachO這個函數(shù)傳image的macho_header,ASLR的偏移值,路徑生成ImageLoader對象,然后調(diào)用addImage這個函數(shù)加入我們的鏡像文件,同時返回ImageLoader這個對象。(通過dyld加載的第一個鏡像是我們的主程序),我們來看下instantiateMainExecutable的這個函數(shù)的流程,如圖所示:

這里調(diào)用sniffLoadCommands獲取loadCommands,如圖:

compressed是根據(jù)Macho中的LG_DYLD_INFO_ONLY和LG_LOAD_DYLINKER來獲取的。
segCount是SEGMENT的數(shù)量,最大不能超過255。
libCount是LC_LOAD_DYLIB加載動態(tài)庫的個數(shù),最大不能超過4095。
*codeSigCmd是代碼簽名。
*encryptCmd是代碼加密信息。
ImageLoaderMachO這個函數(shù)調(diào)用sniffLoadCommands這個之后會根據(jù)compressed這個變量判斷調(diào)用ImageLoaderMachOCompressed或者ImageLoaderMachOClassic這兩個函數(shù)實例化。
實例化完畢之后添加到AllImage中。

接著檢測當(dāng)前主程否是當(dāng)前設(shè)備的,如上圖所示,到這里我們的主程序?qū)嵗Y(jié)束,接著我們來分析動態(tài)庫的加載。
5 dyld加載動態(tài)庫

這里先檢查動態(tài)庫的版本和路徑,接著加載動態(tài)庫,如圖所示:

這里先根據(jù)環(huán)境變量判斷動態(tài)插入庫不為空,接著遍歷loadInsertedDylib

這里調(diào)用load插入動態(tài)庫,接著開始鏈接主程序,

先配置gLinkContext.linkingMainExecutable = true;這個變量為true.
接著調(diào)用link函數(shù)進(jìn)行鏈接,我們來看看是如何鏈接的:



這里先記錄起始時間,在最后在記錄結(jié)束時間,把加載時間記錄下來,這個就是dyld加載應(yīng)用的時長。
這里鏈接插入動態(tài)庫完成了。

之后把這些實例化的鏡像文件加入到AllImages中(這里是從i+1開始的,因為主程序已經(jīng)先加載了),之后再調(diào)用link進(jìn)行鏈接,這里跟主程序的鏈接是一樣的。

這里的條件不滿足的話,將會持續(xù)的調(diào)用reloadAllImage,這里執(zhí)行之后,就開始綁定動態(tài)庫了,如圖所示:

這里遍歷AllImages綁定插入動態(tài)庫,之后進(jìn)行弱符號綁定。
接著調(diào)用initializeMainExecutable初始化主程序的Main方法,如圖所示:

下面我們來分析主程序Main方法加載的流程。
6 load方法與初始化方法的加載
我們先進(jìn)入initializeMainExecutable()這個函數(shù),看下它的實現(xiàn)

這里有一個runInitializers函數(shù),我們再進(jìn)去

這里會調(diào)用processInitializers這個函數(shù),我們再跟進(jìn)去看看

接著我們再跟下recursiveInitialization這個函數(shù)


這里調(diào)用了notifySingle這個函數(shù),我們需要再跟進(jìn)去一下,


而這里沒有找到loadImge的函數(shù)調(diào)用,這里到底是怎么回事,我們通過匯編可以看到load_image是在libobjc.dylib中,也就是說在objc的源碼中,那它是怎么調(diào)用的,我們來看下代碼。
在1019行中 (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()) 這個回調(diào)進(jìn)行關(guān)聯(lián)的。
這里首先判斷sNotifyObjCInit這個是否為空,我們在這文件里搜下,發(fā)現(xiàn)是在registerObjCNotifiers這里調(diào)用的時候賦值的,如下圖:

我們搜下registerObjCNotifiers這個函數(shù)發(fā)現(xiàn)是在dyldAPIS.cpp中的

這個函數(shù)調(diào)用的,這里有傳遞init進(jìn)來,那么又是誰調(diào)用的_dyld_objc_notify_register這個函數(shù)呢,搜了之后,發(fā)現(xiàn)dyld里沒用調(diào)用的,那么怎么辦呢,我們可以在Demo工程中下一個符號斷點(diǎn)_dyld_objc_notify_register,結(jié)果發(fā)現(xiàn)是在libobjc.dylib中的_objc_init調(diào)用的,下面打開objc的的源代碼,按信shift+command+o找到定義,再按住shift+command+j找到源文件是在objc-os.mm文件中,如圖所示:

這里可以看到_dyld_objc_notify_register這個函數(shù)是在_objc_init調(diào)用的,這里有一個load_images,我們再看下

這里面有一個call_load_methods方法,點(diǎn)進(jìn)去看下

這里會do while調(diào)用call_class_loads方法來加載所有類的+(void)load方法,load方法加載完成后調(diào)用了call_category_loads這個方法,加載類加的loads方法,這也是為什么類別的方法與原類的方法重名后,會覆蓋原類的方法。
我們回到dyld的源代碼找到ImageLoader.cpp文件中的recursiveInitialization函數(shù)中調(diào)用notifySingle這里走到了objc中,objc把所有的load加載完成后,會調(diào)用doInitialization這個函數(shù),進(jìn)去看下

這里doModInitFunctions調(diào)用這個函數(shù),這個函數(shù)的作用是什么,我們來看下,

這里就是在加載我們的構(gòu)造函數(shù),我們在Demo的main.m上面加入構(gòu)造函數(shù)
attribute((constructor)) void test1() {
printf("test調(diào)用了");
}
經(jīng)過調(diào)試,它比main函數(shù)先調(diào)用。
我們再回到dyld的main函數(shù),找到這里,如圖

這里通過LC_MAIN找到程序入口給result,最后返回主程序的main地址。dyld的加載就結(jié)束了。
下面是dyld的initializeMainExecutable初始化主程序的思維導(dǎo)圖

結(jié)語:到這里dyld的流程分析就結(jié)束了,有所遺漏或者錯誤的地方,請指正,大家可以相互學(xué)習(xí)交流,共同進(jìn)步!
補(bǔ)充 objc與dyld是如何關(guān)聯(lián)?
接上文,我們知道notifySingle是在libobjc.dyld動態(tài)庫中找到了,那么它在dyld源碼中是怎么賦值的呢,經(jīng)過我們的一系列的搜索查找,終于找到了,如圖:

第4803行代碼中,我們看到了notifySingle的函數(shù)指針給了gLinkContext.notifySingle,我們貼一下這個函數(shù)的代碼:
static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
//dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath());
std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sSingleHandlers);
if ( handlers != NULL ) {
dyld_image_info info;
info.imageLoadAddress = image->machHeader();
info.imageFilePath = image->getRealPath();
info.imageFileModDate = image->lastModified();
for (std::vector<dyld_image_state_change_handler>::iterator it = handlers->begin(); it != handlers->end(); ++it) {
const char* result = (*it)(state, 1, &info);
if ( (result != NULL) && (state == dyld_image_state_mapped) ) {
//fprintf(stderr, " image rejected by handler=%p\n", *it);
// make copy of thrown string so that later catch clauses can free it
const char* str = strdup(result);
throw str;
}
}
}
if ( state == dyld_image_state_mapped ) {
// <rdar://problem/7008875> Save load addr + UUID for images from outside the shared cache
// <rdar://problem/50432671> Include UUIDs for shared cache dylibs in all image info when using private mapped shared caches
if (!image->inSharedCache()
|| (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion)) {
dyld_uuid_info info;
if ( image->getUUID(info.imageUUID) ) {
info.imageLoadAddress = image->machHeader();
addNonSharedCacheImageUUID(info);
}
}
}
if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
uint64_t t0 = mach_absolute_time();
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
uint64_t t1 = mach_absolute_time();
uint64_t t2 = mach_absolute_time();
uint64_t timeInObjC = t1-t0;
uint64_t emptyTime = (t2-t1)*100;
if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) {
timingInfo->addTime(image->getShortName(), timeInObjC);
}
}
// mach message csdlc about dynamically unloaded images
if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) {
notifyKernel(*image, false);
const struct mach_header* loadAddress[] = { image->machHeader() };
const char* loadPath[] = { image->getPath() };
notifyMonitoringDyld(true, 1, loadAddress, loadPath);
}
}
其中(sNotifyObjCInit)(image->getRealPath(), image->machHeader());*
這行代碼有一個sNotifyObjCInit這個類型,我們?nèi)炙阉饕幌?,如圖:

發(fā)現(xiàn)它是一個_dyld_objc_notify_init這個類型 ,我們接著往下搜索,如圖:

發(fā)現(xiàn)sNotifyObjCInit是在這里通過函數(shù)的參數(shù)進(jìn)行的初始化。
我們再搜索一下registerObjCNotifiers這個函數(shù)是在什么時候調(diào)用的,如圖所示:

發(fā)現(xiàn)是在_dyld_objc_notify_register這個函數(shù)調(diào)用的,我們再搜索下這個函數(shù),沒有找到它的定義,只有調(diào)用,這是怎么回事?
從這里找不到的時候,我們通過匯編可以看到是在libObjc.dylib中,我們打開objc的源碼,搜索,看到如下圖的代碼:

在這里我們看到了_dyld_objc_notify_register這個函數(shù)的調(diào)用,我們斷點(diǎn)調(diào)試一下,看下堆棧,如圖:

我們經(jīng)過一系列的分析,frame13到fame7的流程我們是知道的,而從frame0到fame6這個流程是怎么回事,我們還不得而知,我們接著分析,從堆棧我們可以看到_objc_init的是由_os_object_init發(fā)起的,是在libdispatch.dylib這個動態(tài)庫中,我們打開libdispatch.dylib這個庫的源碼,通過搜索_os_object_init,如圖:

這里有調(diào)用_objc_init這個函數(shù)通過搜索發(fā)現(xiàn)是引入objc庫中的_objc_init,如圖:

通過之前的堆棧可以看出libdispatch_init調(diào)用了_os_object_init,我們再搜下libdispatch_init這個函數(shù),找到如下代碼:
void
libdispatch_init(void)
{
dispatch_assert(sizeof(struct dispatch_apply_s) <=
DISPATCH_CONTINUATION_SIZE);
if (_dispatch_getenv_bool("LIBDISPATCH_STRICT", false)) {
_dispatch_mode |= DISPATCH_MODE_STRICT;
}
#if DISPATCH_DEBUG || DISPATCH_PROFILE
#if DISPATCH_USE_KEVENT_WORKQUEUE
if (getenv("LIBDISPATCH_DISABLE_KEVENT_WQ")) {
_dispatch_kevent_workqueue_enabled = false;
}
#endif
#endif
#if HAVE_PTHREAD_WORKQUEUE_QOS
dispatch_qos_t qos = _dispatch_qos_from_qos_class(qos_class_main());
_dispatch_main_q.dq_priority = _dispatch_priority_make(qos, 0);
#if DISPATCH_DEBUG
if (!getenv("LIBDISPATCH_DISABLE_SET_QOS")) {
_dispatch_set_qos_class_enabled = 1;
}
#endif
#endif
#if DISPATCH_USE_THREAD_LOCAL_STORAGE
_dispatch_thread_key_create(&__dispatch_tsd_key, _libdispatch_tsd_cleanup);
#else
_dispatch_thread_key_create(&dispatch_priority_key, NULL);
_dispatch_thread_key_create(&dispatch_r2k_key, NULL);
_dispatch_thread_key_create(&dispatch_queue_key, _dispatch_queue_cleanup);
_dispatch_thread_key_create(&dispatch_frame_key, _dispatch_frame_cleanup);
_dispatch_thread_key_create(&dispatch_cache_key, _dispatch_cache_cleanup);
_dispatch_thread_key_create(&dispatch_context_key, _dispatch_context_cleanup);
_dispatch_thread_key_create(&dispatch_pthread_root_queue_observer_hooks_key,
NULL);
_dispatch_thread_key_create(&dispatch_basepri_key, NULL);
#if DISPATCH_INTROSPECTION
_dispatch_thread_key_create(&dispatch_introspection_key , NULL);
#elif DISPATCH_PERF_MON
_dispatch_thread_key_create(&dispatch_bcounter_key, NULL);
#endif
_dispatch_thread_key_create(&dispatch_wlh_key, _dispatch_wlh_cleanup);
_dispatch_thread_key_create(&dispatch_voucher_key, _voucher_thread_cleanup);
_dispatch_thread_key_create(&dispatch_deferred_items_key,
_dispatch_deferred_items_cleanup);
#endif
pthread_key_create(&_os_workgroup_key, _os_workgroup_tsd_cleanup);
#if DISPATCH_USE_RESOLVERS // rdar://problem/8541707
_dispatch_main_q.do_targetq = _dispatch_get_default_queue(true);
#endif
_dispatch_queue_set_current(&_dispatch_main_q);
_dispatch_queue_set_bound_thread(&_dispatch_main_q);
#if DISPATCH_USE_PTHREAD_ATFORK
(void)dispatch_assume_zero(pthread_atfork(dispatch_atfork_prepare,
dispatch_atfork_parent, dispatch_atfork_child));
#endif
_dispatch_hw_config_init();
_dispatch_time_init();
_dispatch_vtable_init();
_os_object_init();
_voucher_init();
_dispatch_introspection_init();
}
這里有調(diào)用_os_object_init這個函數(shù)。
libdispatch_init又由誰調(diào)起的,我們通過堆棧分析發(fā)現(xiàn),是在libSystem.B.dylib這個庫中調(diào)用,我們打開這個庫的源碼,搜索發(fā)現(xiàn),如下圖代碼:

這里有調(diào)用libdispatch_init這個函數(shù),這個流程是通的,那么libSystem_initializer是由誰調(diào)起的呢,我們接著分析。
從通過之前的堆棧分析,下面就來到了dyld的doModInitFunctions這個函數(shù),我們先貼下這個函數(shù)的代碼:
void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
{
if ( fHasInitializers ) {
const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
const struct load_command* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
const uint8_t type = sect->flags & SECTION_TYPE;
if ( type == S_MOD_INIT_FUNC_POINTERS ) {
Initializer* inits = (Initializer*)(sect->addr + fSlide);
const size_t count = sect->size / sizeof(uintptr_t);
// <rdar://problem/23929217> Ensure __mod_init_func section is within segment
if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
dyld::throwf("__mod_init_funcs section has malformed address range for %s\n", this->getPath());
for (size_t j=0; j < count; ++j) {
Initializer func = inits[j];
// <rdar://problem/8543820&9228031> verify initializers are in image
if ( ! this->containsAddress(stripPointer((void*)func)) ) {
dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
}
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
// <rdar://problem/17973316> libSystem initializer must run first
const char* installPath = getInstallPath();
if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) )
dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath());
}
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
{
dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
}
bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
// now safe to use malloc() and other calls in libSystem.dylib
dyld::gProcessInfo->libSystemInitialized = true;
}
}
}
else if ( type == S_INIT_FUNC_OFFSETS ) {
const uint32_t* inits = (uint32_t*)(sect->addr + fSlide);
const size_t count = sect->size / sizeof(uint32_t);
// Ensure section is within segment
if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
dyld::throwf("__init_offsets section has malformed address range for %s\n", this->getPath());
if ( seg->initprot & VM_PROT_WRITE )
dyld::throwf("__init_offsets section is not in read-only segment %s\n", this->getPath());
for (size_t j=0; j < count; ++j) {
uint32_t funcOffset = inits[j];
// verify initializers are in image
if ( ! this->containsAddress((uint8_t*)this->machHeader() + funcOffset) ) {
dyld::throwf("initializer function offset 0x%08X not in mapped image for %s\n", funcOffset, this->getPath());
}
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
// <rdar://problem/17973316> libSystem initializer must run first
const char* installPath = getInstallPath();
if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) )
dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath());
}
Initializer func = (Initializer)((uint8_t*)this->machHeader() + funcOffset);
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
#if __has_feature(ptrauth_calls)
func = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
#endif
bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
{
dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
}
bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
// now safe to use malloc() and other calls in libSystem.dylib
dyld::gProcessInfo->libSystemInitialized = true;
}
}
}
}
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
}
其中有一行if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) )這行代碼就是所有文件加載之前,必須把libSystem加載進(jìn)來,它的優(yōu)先級最高,這行也就是libSystem.B.dylib這個庫的加載,Initializer func = (Initializer)((uint8_t)this->machHeader() + funcOffset);這行代碼就是libSystem_initializer這個函數(shù)的加載。
我們接著再搜索下doModInitFunctions這個函數(shù),找到它的調(diào)用,如圖:

是在doInitialization*這個函數(shù)調(diào)用的,我們再搜下這個函數(shù),找到如下代碼:
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);
if ( fState < dyld_image_state_dependents_initialized-1 ) {
uint8_t oldState = fState;
// break cycles
fState = dyld_image_state_dependents_initialized-1;
try {
// initialize lower level libraries first
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);
}
}
}
// record termination order
if ( this->needsTermination() )
context.terminationRecorder(this);
// let objc know we are about to initialize this 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
bool hasInitializers = this->doInitialization(context);
// let anyone know we finished initializing this image
fState = dyld_image_state_initialized;
oldState = fState;
context.notifySingle(dyld_image_state_initialized, this, NULL);
if ( hasInitializers ) {
uint64_t t2 = mach_absolute_time();
timingInfo.addTime(this->getShortName(), t2-t1);
}
}
catch (const char* msg) {
// this image is not initialized
fState = oldState;
recursiveSpinUnLock();
throw;
}
}
recursiveSpinUnLock();
}
發(fā)現(xiàn)這行代碼調(diào)用了bool hasInitializers = this->doInitialization(context);,
doInitialization函數(shù),這行就是初始化我們鏡像文件的。
notifySingle與doInitialization是怎么關(guān)聯(lián)的呢,我們接著分析。
我們知道notifySingle會間接調(diào)用_dyld_objc_notify_register這個函數(shù),
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
會初始化這些參數(shù)。
doInitialization必然會走到_objc_init這個函數(shù),而_objc_init又必然走到_dyld_objc_notify_register這個函數(shù),它的一個參數(shù)map_images,那么map_images(sNotifyObjCMapped這個是dyld的叫法,其實是一回事)這個函數(shù)又是什么時候調(diào)用的,我們繼續(xù)往下分析。
我們在dyld的源碼中搜下sNotifyObjCMapped這個,如圖所示:

是在notifyBatchPartial這個函數(shù)調(diào)起的,我們再搜索notifyBatchPartial這個函數(shù),如圖所示:

是在registerObjCNotifiers這個函數(shù)發(fā)起的調(diào)用,經(jīng)過我們的再次分析sNotifyObjCInit這個函數(shù)是在notifySingle發(fā)起的調(diào)用,而它又是在recursiveInitialization被調(diào)用的(recursiveInitialization是遞歸調(diào)用)。
doInitialization一旦初始化,就會對map_images初始化,同時load也會初始化,緊接著就會對map_images的調(diào)用,接著對就走notifySingle,notifySingle就會對load調(diào)用
以上就是個人的一些見解,請多多指證。