應(yīng)用程序加載

1.jpeg

做了這么久的ioser,你真的了解我們應(yīng)用程序加載的一個(gè)主流程么蜘渣?我們做的app是怎么運(yùn)行起來(lái)的呢日矫?下面我們探索下在我們看不到的地方底層加載流程赔退。

前言

我們知道我們的app是我們用代碼一行一行敲出來(lái)的清寇,我們這些代碼自己是沒(méi)辦法跑起來(lái)的训堆,只有把他們加載到內(nèi)存露戒,然后他們?cè)趦?nèi)存中被某種機(jī)制調(diào)用正式啟動(dòng)進(jìn)入到main函數(shù)椒功。這個(gè)整個(gè)過(guò)程會(huì)經(jīng)歷很多個(gè)環(huán)節(jié)捶箱,會(huì)涉及到很多的庫(kù)的調(diào)用和加載。首先要把我們寫(xiě)的代碼和一些需要用到的庫(kù)都編譯成可執(zhí)行文件动漾。然后才是這個(gè)可執(zhí)行文件的加載啟動(dòng)丁屎。如下圖:

編譯過(guò)程:
編譯過(guò)程.png

可執(zhí)行文件(exec)你雙擊或者直接拖到終端就可以運(yùn)行起來(lái)。ps:iphone模擬器或者真機(jī)編譯的需要處理一些簽名等問(wèn)題所以可以直接用mac環(huán)境的項(xiàng)目來(lái)嘗試

補(bǔ)充動(dòng)靜態(tài)庫(kù)的區(qū)別:
動(dòng)靜態(tài)庫(kù)鏈接.png

由上面動(dòng)靜態(tài)庫(kù)鏈接圖可見(jiàn) 靜態(tài)庫(kù)鏈接是把自己復(fù)制一份然后鏈接其他的庫(kù)旱眯,這樣就會(huì)出現(xiàn)同一個(gè)庫(kù)重復(fù)出現(xiàn)的情況晨川。而動(dòng)態(tài)庫(kù)則不會(huì),動(dòng)態(tài)庫(kù)鏈接是一個(gè)動(dòng)態(tài)庫(kù)直接鏈接多個(gè)其他庫(kù)而不會(huì)出現(xiàn)復(fù)制自己的情況删豺。所以大部分蘋(píng)果的系統(tǒng)庫(kù)都是動(dòng)態(tài)庫(kù)有利于優(yōu)化內(nèi)存空間共虑。

應(yīng)用程序加載原理

在前言中我們知道如何把我們的代碼和一些庫(kù)怎么編譯成為了可執(zhí)行文件,但是這個(gè)執(zhí)行文件是怎么加載到內(nèi)存中的呢呀页?前面我們探索了objc_init的方法妈拌,也去讀了部分objc源碼,那它們是如何調(diào)用和啟動(dòng)的呢蓬蝶?下面我們就探索下這個(gè)流程尘分。

思考:既然我們是要探索app啟動(dòng)之前做了什么,我們暫時(shí)也不知道從哪兒可以入手去查找這個(gè)流程疾党,那么我們是否可以利用倒推法音诫,從app啟動(dòng)進(jìn)入mian函數(shù)那時(shí)刻起 反著查看匯編或者堆棧看是否能找到一些線(xiàn)索呢雪位?

下面我們就這樣去操作一下竭钝。首先創(chuàng)建一個(gè)app工程(ZYProjectTwelfth001),然后在viewController里添加一個(gè)+ (void)load;方法雹洗。因?yàn)槲覀兦懊娴奈恼戮椭v到了load方法在main函數(shù)之前香罐。所以這也可以幫助我們判斷在main函數(shù)前都做了什么。然后斷點(diǎn)到main函數(shù)然后查看下匯編时肿。

2.png
3.png

debug->Always show Disassembly查看匯編

4.png

既然我們?cè)谶@個(gè)堆棧里可以發(fā)現(xiàn)在main函數(shù)前調(diào)用了start函數(shù)并且發(fā)現(xiàn)它是在dyld里調(diào)用的庇茫,那我們可以嘗試符號(hào)斷點(diǎn)這個(gè)函數(shù)。然而我斷點(diǎn)過(guò)了 并不能斷住螃成。所以我猜測(cè)這個(gè)函數(shù)在底層并不叫start函數(shù)或者有其他原因?qū)е滤荒軇e斷點(diǎn)旦签。這條路行不通那我們就利用在mian 函數(shù)前的load函數(shù)做文章。我們打一個(gè)斷點(diǎn)到load函數(shù)寸宏。然后利用bt查看堆棧信息宁炫。

5.png

從上面的信息我們也可以發(fā)現(xiàn)在mian 之前的那個(gè)start確實(shí)是來(lái)自dyld_dyld_start方法。而且在這個(gè)方法之后調(diào)用了很多其他的方法流程氮凝。例如:
initializeMainExecutable()
ImageLoader::runInitializers
ImageLoader::processInitializers
ImageLoader::recursiveInitialization
dyld::notifySingle
libobjc.A.dylib load_images

那我們下面就去dyld的源碼探究一番羔巢,看看能不能找到這些東西。我這里用的是dyld-852需要的可以點(diǎn)擊去下載。

在真正探索dyld之前我先利用前輩的一張圖來(lái)解釋下什么是dyld竿秆。我們經(jīng)常聽(tīng)到dyld確實(shí)他就是一個(gè)動(dòng)態(tài)編譯器启摄。他的作用就是在app啟動(dòng)后加載各種庫(kù)和鏡像文件.

12.png

初步了解了dyld,我們繼續(xù)上面的查找流程

6.png

全局搜索_dyld_start這個(gè)方法(因?yàn)?code>c++語(yǔ)法關(guān)系我們先搜索前面的dyldbootstrap然后再去搜索start方法,這種叫二級(jí)命名空間)如圖:

7.png

在這個(gè)start方法最后的return幽钢,我們看到了我們熟悉的mian 方法(不過(guò)這里的mian方法可不是我們自己程序里的那個(gè)mian方法哦)歉备,我們跟進(jìn)去看看。

8.png

我的天搅吁,這個(gè)方法一千多行代碼威创,我只能折疊才能直觀(guān)點(diǎn)的截圖展示出來(lái)落午。下面我們就硬著頭皮進(jìn)去看看吧谎懦。還是老方法對(duì)于這種代碼量大的方法我們直接看return。我們把關(guān)于return 結(jié)果的result相關(guān)的代碼貼出來(lái):

uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[], uintptr_t* startGlue)
{

/*
*省略了前面的全部代碼
*/
#if TARGET_OS_OSX
        if ( gLinkContext.driverKit ) {
            result = (uintptr_t)sEntryOverride;
            if ( result == 0 )
                halt("no entry point registered");
            *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
        }
        else
#endif
        {
            // find entry point for main executable
            result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
            if ( result != 0 ) {
                // main executable uses LC_MAIN, we need to use helper in libdyld to call into main()
                if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
                    *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
                else
                    halt("libdyld.dylib support not present for LC_MAIN");
            }
            else {
                // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
                result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
                *startGlue = 0;
            }
        }
    }
    catch(const char* message) {
        syncAllImages();
        halt(message);
    }
    catch(...) {
        dyld::log("dyld: launch failed\n");
    }

    CRSetCrashLogMessage("dyld2 mode");
#if !TARGET_OS_SIMULATOR
    if (sLogClosureFailure) {
        // We failed to launch in dyld3, but dyld2 can handle it. synthesize a crash report for analytics
        dyld3::syntheticBacktrace("Could not generate launchClosure, falling back to dyld2", true);
    }
#endif

    if (sSkipMain) {
        notifyMonitoringDyldMain();
        if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
            dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2);
        }
        ARIADNEDBG_CODE(220, 1);
        result = (uintptr_t)&fake_main;
        *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
    }

    return result;
}

我們代碼中發(fā)現(xiàn) return 的這個(gè)result的賦值就只有幾個(gè)地方溃斋,而且在一個(gè)sMainExecutable這個(gè)東西賦值的次數(shù)最多界拦,而且取地址fake_main賦值我們進(jìn)去查看發(fā)現(xiàn)是個(gè)空函數(shù)。所以我們接下來(lái)再次用倒推法梗劫,我們搜索sMainExecutable看看跟他有關(guān)的代碼享甸。

uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[], uintptr_t* startGlue)
{
               /*
                *省略了前面的全部代碼
                */
        /* ****************** 弱綁定 ********************/
        
        // <rdar://problem/12186933> do weak binding only after all inserted images linked
        sMainExecutable->weakBind(gLinkContext);
        gLinkContext.linkingMainExecutable = false;

        sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);

        CRSetCrashLogMessage("dyld: launch, running initializers");
    #if SUPPORT_OLD_CRT_INITIALIZATION
        // Old way is to run initializers via a callback from crt1.o
        if ( ! gRunInitializersOldWay ) 
            initializeMainExecutable(); 
    #else
        // run all initializers
        initializeMainExecutable(); 
    #endif

        // notify any montoring proccesses that this process is about to enter main()
        notifyMonitoringDyldMain();
        if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
            dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2);
        }
        ARIADNEDBG_CODE(220, 1);

              /*
                *省略了后面的全部代碼 后面的代碼就是上方關(guān)于返回值result                的代碼
               */
}

我們這上面這段代碼看到了我們想要的東西。就是我們?cè)?code>load()方法斷點(diǎn)的時(shí)候查看的堆棧信息里的東西關(guān)于images綁定/bind梳侨、鏈接/link蛉威、加載/loadMainExecutable的處理等走哺。這證明我們的方向沒(méi)有錯(cuò)蚯嫌。我們繼續(xù)往上溯源查找這個(gè)sMainExecutable的相關(guān)代碼直接從方法開(kāi)始部分進(jìn)行:

第一部分:
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[], uintptr_t* startGlue)
{
     /*
     *省略了前面的全部代碼
     */
   /* **********************初始化dyld 主程序****************************/

  CRSetCrashLogMessage(sLoadingCrashMessage);
  // instantiate ImageLoader for main executable
  sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
  gLinkContext.mainExecutable = sMainExecutable;
  gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);

  /*
  *省略了后面的全部代碼
  */
}

這一部分看到了sMainExecutable的初始化方法instantiateFromLoadedImage,并且在這一段之前的所有代碼都是在加載處理各種平臺(tái)信息丙躏,架構(gòu)信息等择示。可以稱(chēng)之為準(zhǔn)備信息階段吧晒旅。這里就不貼出來(lái)了栅盲,因?yàn)樘嗔恕?/strong>

第二部分:
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[], uintptr_t* startGlue)
{
    /*
     *省略了前面的全部代碼
     */

  char dyldPathBuffer[MAXPATHLEN+1];
        
  int len = proc_regionfilename(getpid(), (uint64_t)(long)addressInDyld, dyldPathBuffer, MAXPATHLEN);
        
  if ( len > 0 ) {
    dyldPathBuffer[len] = '\0'; // proc_regionfilename() does not zero terminate returned string
    if ( strcmp(dyldPathBuffer, gProcessInfo->dyldPath) != 0 )  
        gProcessInfo->dyldPath = strdup(dyldPathBuffer);
      }

        /* **********************循環(huán)加載插入的libraries*************************/
      // load any inserted libraries
      if    ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
            
           for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)   
               loadInsertedDylib(*lib);
             }
          // record count of inserted libraries so that a flat search will look at     
         // inserted libraries, then main, then others.
        sInsertedDylibCount = sAllImages.size()-1;

        // link main executable
        
        gLinkContext.linkingMainExecutable = true;
#if SUPPORT_ACCELERATE_TABLES
          if ( mainExcutableAlreadyRebased ) {
            // previous link() on main executable has already adjusted its internal pointers for ASLR
            // work around that by rebasing by inverse amount
            sMainExecutable->rebase(gLinkContext, -mainExecutableSlide);
        }
#endif
        /* **********************鏈接主程序*************************/
        
        link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
        sMainExecutable->setNeverUnloadRecursive();
        if ( sMainExecutable->forceFlat() ) {
            gLinkContext.bindFlat = true;
            gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
        }

        /* **********************鏈接 所有 插入的 libraries*************************/
        // link any inserted libraries
        // do this after linking main executable so that any dylibs pulled in by inserted 
        // dylibs (e.g. libSystem) will not be in front of dylibs the program uses
        if ( sInsertedDylibCount > 0 ) {
            for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
                ImageLoader* image = sAllImages[i+1];
                link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
                image->setNeverUnloadRecursive();
            }
            if ( gLinkContext.allowInterposing ) {
                // only INSERTED libraries can interpose
                // register interposing info after all inserted libraries are bound so chaining works
                for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
                    ImageLoader* image = sAllImages[i+1];
                    image->registerInterposing(gLinkContext);
                }
            }
        }

        if ( gLinkContext.allowInterposing ) {
            // <rdar://problem/19315404> dyld should support interposition even without DYLD_INSERT_LIBRARIES
            for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) {
                ImageLoader* image = sAllImages[I];
                if ( image->inSharedCache() )
                    continue;
                image->registerInterposing(gLinkContext);
            }
        }
        

  /*
  *省略了后面的全部代碼
  */
}

第二部分是插入動(dòng)態(tài)庫(kù)鏈接過(guò)程,先插入了所有動(dòng)態(tài)庫(kù)废恋,然后連接了主程序以及所有動(dòng)態(tài)庫(kù)谈秫。

第三部分:
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[], uintptr_t* startGlue)
{

/*
*省略了前面的全部代碼
*/

// apply interposing to initial set of images
        for(int i=0; i < sImageRoots.size(); ++i) {
            sImageRoots[i]->applyInterposing(gLinkContext);
        }
        ImageLoader::applyInterposingToDyldCache(gLinkContext);

        /* **********************綁定 通知主程序interposing 已經(jīng)注冊(cè)完畢*************************/
        
        // Bind and notify for the main executable now that interposing has been registered
        uint64_t bindMainExecutableStartTime = mach_absolute_time();
        sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
        uint64_t bindMainExecutableEndTime = mach_absolute_time();
        ImageLoaderMachO::fgTotalBindTime += bindMainExecutableEndTime - bindMainExecutableStartTime;
        gLinkContext.notifyBatch(dyld_image_state_bound, false);

        // Bind and notify for the inserted images now interposing has been registered
        if ( sInsertedDylibCount > 0 ) {
            for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
                ImageLoader* image = sAllImages[i+1];
                image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true, nullptr);
            }
        }
        
        /* ****************** 弱綁定 ********************/
        
        // <rdar://problem/12186933> do weak binding only after all inserted images linked
        sMainExecutable->weakBind(gLinkContext);
        gLinkContext.linkingMainExecutable = false;

        sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);

        CRSetCrashLogMessage("dyld: launch, running initializers");
        
/*
* 省略下面代碼 就是從 上面綁定開(kāi)始的代碼
*/
        
}

第三部分是做弱綁定以及通知

第四部分:
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[], uintptr_t* startGlue)
{

/*
*省略了前面的全部代碼
*/

/* ****************** 不是iphone ********************/
    #if SUPPORT_OLD_CRT_INITIALIZATION
        // Old way is to run initializers via a callback from crt1.o
        if ( ! gRunInitializersOldWay ) 
            initializeMainExecutable(); 
    #else
        
        /* ****************** 是iphone ********************/
        /* ****************** 運(yùn)行所有的初始化 重點(diǎn) ********************/
        // run all initializers
        initializeMainExecutable(); 
    #endif

        /* ****************** 通知所有監(jiān)控進(jìn)程/dyld 該進(jìn)程即將進(jìn)入main() ********************/
        
        // notify any montoring proccesses that this process is about to enter main()
        notifyMonitoringDyldMain();
        
        if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
            dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2);
        }
        ARIADNEDBG_CODE(220, 1);

#if TARGET_OS_OSX
        if ( gLinkContext.driverKit ) {
            result = (uintptr_t)sEntryOverride;
            if ( result == 0 )
                halt("no entry point registered");
            *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
        }
        else
#endif
        {
            // find entry point for main executable
            result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
            if ( result != 0 ) {
                // main executable uses LC_MAIN, we need to use helper in libdyld to call into main()
                if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
                    *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
                else
                    halt("libdyld.dylib support not present for LC_MAIN");
            }
            else {
                // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
                result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
                *startGlue = 0;
            }
        }
    }
/*
* 省略下面代碼 就是從 上面綁定開(kāi)始的代碼
*/
        
}

第四部分是開(kāi)始運(yùn)行所有的初始化鱼鼓,并且通知所有的dyld監(jiān)控進(jìn)程即將進(jìn)入到mian函數(shù)了拟烫。這里也是我們研究的重點(diǎn),因?yàn)閺倪@里initializeMainExecutable(); 開(kāi)始就是真正的從dyld進(jìn)入到了objc的入口了蚓哩。

重點(diǎn):initializeMainExecutable();

void initializeMainExecutable()
{
    // record that we've reached this step
    gLinkContext.startedInitializingMainExecutable = true;

    
    /* ****************   運(yùn)行所有鏡像文件 runInitializers  *******************/
    
    // 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]);
        }
    }
    
    /* ****************   運(yùn)行所有主程序初始化 和他所攜帶的一切 sMainExecutable->runInitializers *******************/
    
    // 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]);
}

在這個(gè)方法中我們看到首先for循環(huán)運(yùn)行了所有鏡像文件的初始化:sImageRoots[i]->runInitializers构灸;然后就運(yùn)行主城的初始化和主程序攜帶的一切東西:sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);。并且發(fā)現(xiàn)調(diào)用的都是同一個(gè)方法。我們跟蹤這個(gè)runInitializers方法:

ImageLoader::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() };

    /* ************    處理喜颁、準(zhǔn)備所有的  Initializers   *************/
    processInitializers(context, thisThread, timingInfo, up);
    
    /* ***********   發(fā)送通知       **************/
    context.notifyBatch(dyld_image_state_initialized, false);

    mach_port_deallocate(mach_task_self(), thisThread);
    uint64_t t2 = mach_absolute_time();
    fgTotalInitTime += (t2 - t1);
}

在這個(gè)方法我們看到主要是processInitializers 方法的處理稠氮、準(zhǔn)備所有的Initializers;和context.notifyBatch(dyld_image_state_initialized, false);發(fā)送通知半开。

我們看下processInitializers:

// <rdar://problem/14412057> 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;
    
    /* ***************     對(duì)圖像列表中的所有圖像調(diào)用遞歸init隔披,構(gòu)建一個(gè)未初始化的向上依賴(lài)項(xiàng)的新列表。        ******************/
    // Calling recursive init on all images in images list, building a new list of
    // uninitialized upward dependencies.
    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.
    if ( ups.count > 0 )
        processInitializers(context, thisThread, timingInfo, ups);
}

在上面這個(gè)方法我們看到主要是利用一個(gè)for循環(huán)來(lái)遞歸init所有鏡像列表中的鏡像寂拆。我們繼續(xù)跟蹤下recursiveInitialization方法:

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);

    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);

            /* **********  告訴objc知道我們要初始化這個(gè)鏡像 注入通知 context.notifySingle   ***************/
            // 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);
            
            /* **********  doInitialization  initialize this image 這里進(jìn)入調(diào)用objc_init()   ***************/
            // 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();
}

ImageLoader::recursiveInitialization這個(gè)方法主要點(diǎn)在告訴objc知道我們要初始化這個(gè)鏡像 注入通知context.notifySingle奢米,并且調(diào)用了doInitialization方法來(lái) initialize this image

我們先看看context.notifySingle這個(gè)通知到底做了什么:

context.notifySingle():

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);
            }
        }
    }
    
    /* ******接收到dyld_image_state_dependents_initialized 通知 *********/
    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 對(duì)鏡像文件的通知處理     *****************/
        
        /* ***********      static _dyld_objc_notify_init       sNotifyObjCInit;     *****************/
        (*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);
    }
}

找到這個(gè)notifySingle()方法的實(shí)現(xiàn)纠永。并且發(fā)現(xiàn)對(duì)應(yīng)我們上面方法通知的類(lèi)型dyld_image_state_dependents_initialized下方的通知是利用(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());這樣一個(gè)通知來(lái)處理的鬓长。所以我們繼續(xù)查找下sNotifyObjCInit。全局查找發(fā)現(xiàn)了以下代碼:

static _dyld_objc_notify_init       sNotifyObjCInit;

// _dyld_objc_notify_init
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
    // record functions to call
    sNotifyObjCMapped   = mapped;
    sNotifyObjCInit     = init;
    sNotifyObjCUnmapped = unmapped;

    // call 'mapped' function with all images mapped so far
    try {
        notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
    }
    catch (const char* msg) {
        // ignore request to abort during registration
    }

    // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
    for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
        ImageLoader* image = *it;
        if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
            dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
            (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
        }
    }
}

也就是說(shuō)sNotifyObjCInit只是_dyld_objc_notify_init定義出來(lái)的尝江。并且在registerObjCNotifiers()方法賦值:sNotifyObjCInit = init;涉波。既然如此我們繼續(xù)反推溯源,追蹤registerObjCNotifiers()方法炭序。

_dyld_objc_notify_register()

// _dyld_objc_notify_register
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped)
{
    dyld::registerObjCNotifiers(mapped, init, unmapped);
}

經(jīng)過(guò)全局搜索找到了上面的方法調(diào)用了registerObjCNotifiers()啤覆。可是當(dāng)我們?cè)俅稳ニ阉?code>_dyld_objc_notify_register()方法的時(shí)候并沒(méi)有可以繼續(xù)提供我們追蹤的方法了惭聂。至此已經(jīng)沒(méi)有路可走了窗声。那我們就轉(zhuǎn)頭先看看doInitialization方法:

我們繼續(xù)跟蹤這個(gè)doInitialization方法:

ImageLoaderMachO::doInitialization:

bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
    CRSetCrashLogMessage2(this->getPath());

    // mach-o has -init and static initializers
    doImageInit(context);
    doModInitFunctions(context);
    
    CRSetCrashLogMessage2(NULL);
    
    return (fHasDashInit || fHasInitializers);
}

這個(gè)方法我們也可以看到主要是對(duì)mach-o的一個(gè)讀取初始化doImageInit(context);以及一些方法的初始化調(diào)用doModInitFunctions(context);。我們就來(lái)看看這個(gè)doModInitFunctions辜纲。

ImageLoaderMachO::doModInitFunctions:

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 = &sectionsStart[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);
        }
    }
}

到這個(gè)方法我們就有點(diǎn)蒙了笨觅。到這里再也沒(méi)有很清晰的步驟和線(xiàn)路能讓我們直接追蹤下去了∏惹福可是到這里我們也只是找到了我們前言部分利用bt打印的dyld的一些步驟屋摇。至于系統(tǒng)在dyld運(yùn)行之后怎么進(jìn)入objc的步驟已然沒(méi)法從這里探索得知了。這個(gè)時(shí)候我們不如換個(gè)方向幽邓。我們直接去objc源碼里運(yùn)行然后打斷點(diǎn)到objc_init方法炮温。看看在進(jìn)入這個(gè)方法之前堆棧的一些運(yùn)行這樣我們說(shuō)不定可以知道在進(jìn)入objc之前都做了什么牵舵。如下圖:

9.png

從上圖我們可知姥饰,果然這種方法是可行的菱属。我們發(fā)現(xiàn)在進(jìn)入ocjc_init之前還經(jīng)歷了兩個(gè)庫(kù)楷拳,一個(gè)是libSystem讨衣,一個(gè)是libdispatch。并且剛好libSystem庫(kù)的libSystem_initializer方法緊跟著上面的ImageLoaderMachO::doModInitFunctions方法没炒。所以我們的流程貌似又接回來(lái)了涛癌。我們直接去下載一個(gè)libSystem庫(kù)。和一個(gè)之后要用的libdispatch庫(kù)。

10.png

打開(kāi)libdispatch庫(kù)我們搜索堆棧信息中的_os_object_init方法:
_os_object_init(void)

void
_os_object_init(void)
{
    _objc_init();
    Block_callbacks_RR callbacks = {
        sizeof(Block_callbacks_RR),
        (void (*)(const void *))&objc_retain,
        (void (*)(const void *))&objc_release,
        (void (*)(const void *))&_os_objc_destructInstance
    };
    _Block_use_RR2(&callbacks);
#if DISPATCH_COCOA_COMPAT
    const char *v = getenv("OBJC_DEBUG_MISSING_POOLS");
    if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
    v = getenv("DISPATCH_DEBUG_MISSING_POOLS");
    if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
    v = getenv("LIBDISPATCH_DEBUG_MISSING_POOLS");
    if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
#endif
}

在這個(gè)方法我們看到確實(shí)調(diào)用了objc_init方法初始化進(jìn)入了objc源碼拳话。我們可以驗(yàn)證下objc_init確實(shí)是調(diào)用objc源碼的先匪。全局搜索objc_init發(fā)現(xiàn)以下代碼:

#if __has_include(<objc/objc-internal.h>)
#include <objc/objc-internal.h>
#else
extern id _Nullable objc_retain(id _Nullable obj) __asm__("_objc_retain");
extern void objc_release(id _Nullable obj) __asm__("_objc_release");
extern void _objc_init(void);
extern void _objc_atfork_prepare(void);
extern void _objc_atfork_parent(void);
extern void _objc_atfork_child(void);
#endif // __has_include(<objc/objc-internal.h>)
#include <objc/objc-exception.h>
#include <Foundation/NSString.h>

從上面的代碼可以證明在_os_object_init里調(diào)用的_objc_init()確實(shí)是從objc引入過(guò)來(lái)的。這樣我們就把objc源碼libDispatch聯(lián)系起來(lái)了弃衍。接下來(lái)我們繼續(xù)查找_os_object_init是被誰(shuí)調(diào)用的呀非。

DISPATCH_EXPORT DISPATCH_NOTHROW
void
libdispatch_init(void)
{
    /*
       * 省略前面的代碼
       */
    _dispatch_hw_config_init();
    _dispatch_time_init();
    _dispatch_vtable_init();
    _os_object_init();
    _voucher_init();
    _dispatch_introspection_init();
}

通過(guò)全局搜索我們發(fā)現(xiàn)是libdispatch_init方法里調(diào)用了_os_object_init();這也剛好驗(yàn)證了我們上面在objc源碼bt打印的堆棧步驟信息。

在上面的堆棧信息打印中我們發(fā)現(xiàn)調(diào)用libdispatch_init方法之前的堆棧已然不在libdispatch這個(gè)庫(kù)了镜盯,而是libSystem庫(kù)岸裙。所以我們?cè)俅无D(zhuǎn)到libSystem庫(kù)查找libdispatch_init方法。

進(jìn)入libSystem庫(kù)全局搜索libdispatch_init發(fā)現(xiàn)以下代碼:

extern void libdispatch_init(void);     // from libdispatch.dylib

以及調(diào)用

// libsyscall_initializer() initializes all of libSystem.dylib
// <rdar://problem/4892197>
__attribute__((constructor))
static void
libSystem_initializer(int argc,
              const char* argv[],
              const char* envp[],
              const char* apple[],
              const struct ProgramVars* vars)
{
    static const struct _libkernel_functions libkernel_funcs = {
        .version = 4,
        // V1 functions

       /*
        * 省略前面部分代碼
        */
    libdispatch_init();
    _libSystem_ktrace_init_func(LIBDISPATCH);

        /*
         * 省略后面部分代碼
         */
    _libSystem_ktrace0(ARIADNE_LIFECYCLE_libsystem_init | DBG_FUNC_END);

    /* <rdar://problem/11588042>
     * C99 standard has the following in section 7.5(3):
     * "The value of errno is zero at program startup, but is never set
     * to zero by any library function."
     */
    errno = 0;
}

我們從上面的代碼可以看出:libDispatch庫(kù)init方法確實(shí)是在libSystyem庫(kù)引入并且調(diào)用速缆。

到這里我們已經(jīng)從把兩頭的方法和步驟排查了一遍降允,理通了從libSystem庫(kù)->libDispatch庫(kù)->objc庫(kù) 的流程,并且在dyld庫(kù)我們也理通了從dyldbootstrap_start->_dyld_objc_notify_register->doModInitFunctions激涤。但是從dyld庫(kù)libSystem庫(kù)的步驟我們還是沒(méi)有理清楚拟糕。所以我們接下來(lái)回到dyld繼續(xù)查看下 有沒(méi)有地方涉及到libSystem的地方尤其是在doModInitFunctions方法之后判呕。

我們回到上面我們斷路的ImageLoaderMachO::doModInitFunctions方法倦踢,因?yàn)榭炊褩P畔⒁彩沁@個(gè)方法之后就進(jìn)入了libSystem庫(kù)了。

ps:因?yàn)樯厦嬉呀?jīng)貼了完整的方法這里就把重點(diǎn)進(jìn)入libSystem的代碼貼出來(lái)侠草。

ImageLoaderMachO::doModInitFunctions

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) {

//省略部分代碼
                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 ) {
                        //省略部分代碼
                    }
                    else if ( type == S_INIT_FUNC_OFFSETS ) {

                        //省略部分代碼

                            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);
        }
    }
}

在上面的簡(jiǎn)化方法中辱挥,我們仔細(xì)看可以發(fā)現(xiàn)有這樣一句代碼和注釋?zhuān)?/p>

if ( ! dyld::gProcessInfo->libSystemInitialized ) { 
// <rdar://problem/17973316> libSystem initializer must run first
/*
*省略代碼
*/
}

表示在執(zhí)行下面的if之前必須先要初始化libSystem。而剛好在下面有這樣一句代碼:

Initializer func = (Initializer)((uint8_t*)this->machHeader() + funcOffset);

machHeader中初始化了一個(gè)方法边涕,并且在下面調(diào)用了:

#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;    
}

從上面可以看出 在沒(méi)有初始化libSystem的時(shí)候是沒(méi)辦法執(zhí)行某些代碼的晤碘。并且后面也直接從machHeader初始化了一個(gè)方法fun。而且調(diào)用這個(gè)fun之后就可以把dyld::gProcessInfo->libSystemInitialized = true;設(shè)置為true了功蜓。所以我們可以確定這里就是初始化libSystem的一處地方园爷。這樣也就串聯(lián)起來(lái)了我們的整個(gè)流程。這個(gè)流程跨越了dyld->libSystem->libDispatch->objc四大系統(tǒng)庫(kù)式撼。

整個(gè)應(yīng)用程序加載的流程圖如下:
dyld&libSystem&libDispatch&objc流程圖.png

文章至此結(jié)束童社,這篇文章花了我不少時(shí)間,希望能給自己理解這個(gè)流程更有幫助著隆,如果能給你帶來(lái)些許啟發(fā)就更讓我欣喜了扰楼。

遇事不決,可問(wèn)春風(fēng)美浦。站在巨人的肩膀上學(xué)習(xí)弦赖,如有疏忽或者錯(cuò)誤的地方還請(qǐng)多多指教。謝謝浦辨!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蹬竖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌币厕,老刑警劉巖庆冕,帶你破解...
    沈念sama閱讀 221,331評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異劈榨,居然都是意外死亡访递,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)同辣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)拷姿,“玉大人,你說(shuō)我怎么就攤上這事旱函∠斐玻” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,755評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵棒妨,是天一觀(guān)的道長(zhǎng)踪古。 經(jīng)常有香客問(wèn)我,道長(zhǎng)券腔,這世上最難降的妖魔是什么伏穆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,528評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮纷纫,結(jié)果婚禮上枕扫,老公的妹妹穿的比我還像新娘。我一直安慰自己辱魁,他們只是感情好烟瞧,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,526評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著染簇,像睡著了一般参滴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锻弓,一...
    開(kāi)封第一講書(shū)人閱讀 52,166評(píng)論 1 308
  • 那天砾赔,我揣著相機(jī)與錄音,去河邊找鬼弥咪。 笑死过蹂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的聚至。 我是一名探鬼主播酷勺,決...
    沈念sama閱讀 40,768評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼扳躬!你這毒婦竟也來(lái)了脆诉?” 一聲冷哼從身側(cè)響起甚亭,我...
    開(kāi)封第一講書(shū)人閱讀 39,664評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎击胜,沒(méi)想到半個(gè)月后亏狰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,205評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡偶摔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,290評(píng)論 3 340
  • 正文 我和宋清朗相戀三年暇唾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辰斋。...
    茶點(diǎn)故事閱讀 40,435評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡策州,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宫仗,到底是詐尸還是另有隱情够挂,我是刑警寧澤,帶...
    沈念sama閱讀 36,126評(píng)論 5 349
  • 正文 年R本政府宣布藕夫,位于F島的核電站孽糖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏毅贮。R本人自食惡果不足惜办悟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,804評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嫩码。 院中可真熱鬧誉尖,春花似錦、人聲如沸铸题。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,276評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)丢间。三九已至,卻和暖如春驹针,著一層夾襖步出監(jiān)牢的瞬間烘挫,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工柬甥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留饮六,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,818評(píng)論 3 376
  • 正文 我出身青樓苛蒲,卻偏偏與公主長(zhǎng)得像卤橄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子臂外,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,442評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 想要了解應(yīng)用程序加載窟扑,我們需要了解下面幾個(gè)問(wèn)題: 我們寫(xiě)的代碼是如何加載到內(nèi)存的喇颁? 我們使用的動(dòng)靜態(tài)庫(kù)是如何加載到...
    鏡像閱讀 174評(píng)論 0 0
  • 應(yīng)用程序的加載分析 作為一個(gè)開(kāi)發(fā)者,對(duì)于iOS應(yīng)用程序啟動(dòng)過(guò)程有很多疑問(wèn)嚎货,本篇就應(yīng)用程序是如何加載的橘霎,做相關(guān)分析 ...
    Rachel_雷蕾閱讀 344評(píng)論 0 1
  • 《iOS底層原理文章匯總》[http://www.reibang.com/p/15af435341ce] 前面所...
    一畝三分甜閱讀 888評(píng)論 0 4
  • 今天我們研究的是應(yīng)用程序的加載過(guò)程,先做一下準(zhǔn)備工作殖属。新建一個(gè)iphone工程姐叁,添加下面代碼: ViewContr...
    Irino閱讀 573評(píng)論 0 1
  • 本文的目的主要是分析dyld的加載流程 首先我們先運(yùn)行個(gè)代碼 來(lái)引入我們今天的主題~~ 運(yùn)行結(jié)果: 運(yùn)行程序,查看...
    北京_小海閱讀 570評(píng)論 1 3