dyld源碼閱讀

參考

dyld版本

dyld-852.2

源碼

1. dyld啟動

- _dyld_start () // 匯編實現(xiàn)
- dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) ()
- dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue)

::表示命名空間下的某個函數(shù)或?qū)傩浴?/p>

2. 在dyld::_main()中執(zhí)行的操作

2.1 設(shè)置上下文

這里會設(shè)置一些要執(zhí)行的函數(shù)的地址, 如notifySingle等,這些函數(shù)會在后續(xù)某些階段進行調(diào)用。

setContext(mainExecutableMH, argc, argv, envp, apple);
2.2 加載共享緩存
mapSharedCache(mainExecutableSlide);

程序啟動運行時會依賴很多系統(tǒng)動態(tài)庫,而系統(tǒng)動態(tài)庫會通過dyld加載到內(nèi)存中,
最開始系統(tǒng)內(nèi)核讀取程序可執(zhí)行文件的Header段信息做一些準備工作,之后就會將工作交給dyld。
由于不止一個程序需要使用系統(tǒng)動態(tài)庫,所以不可能在每個程序加載時都去加載所有的系統(tǒng)動態(tài)庫,
為了優(yōu)化程序啟動速度和利用動態(tài)庫緩存,蘋果從iOS3.1之后,
將所有系統(tǒng)庫(私有與公有)編譯成一個大的緩存文件,這就是dyld_shared_cache
,該緩存文件存在iOS系統(tǒng)下的/System/Library/Caches/com.apple.dyld/目錄下

2.3 實例化主執(zhí)行文件
ImageLoaderMachO* sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
2.4 加載插入動態(tài)庫

越獄插件會使用

for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) 
    loadInsertedDylib(*lib);
2.5 鏈接主程序

此時會鏈接動態(tài)庫, 進行rebase操作, 同時會記錄啟動時間

- link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);

- image->link(gLinkContext, forceLazysBound, false, neverUnload, loaderRPaths, path);

2.5.1 遞歸加載依賴的動態(tài)庫

主程序依賴的動態(tài)庫會被加載,依賴層級較低的會先加載

this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath);
2.5.2 遞歸進行重定位rebase

與ASLR,全稱為 Address Space Layout Randomization,地址空間布局隨機化相關(guān)

this->recursiveRebaseWithAccounting(context);
2.5.3 遞歸進行符號綁定bind
this->recursiveBindWithAccounting(context, forceLazysBound, neverUnload);
2.6 鏈接插入動態(tài)庫(通常越獄插件使用)
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();
    }
    // 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);
    }
}

2.7 初始化主執(zhí)行文件
initializeMainExecutable(); 

2.7.1 初始化主執(zhí)行文件
sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);

processInitializers(context, thisThread, timingInfo, up);

2.7.1.1 遞歸進行初始化

for (uintptr_t i=0; i < images.count; ++i) {
    // xds: 遞歸初始化所有image, 第一個初始化的是libSystem
    images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups);
}

2.7.1.2 首先初始化更低層級的庫

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

重點

libSystem會首先初始化,libSystem會調(diào)用_objc_init進行objc的初始化,_objc_init中會調(diào)用_dyld_objc_notify_register注冊回調(diào):

  • mapped回調(diào): 注冊的時候會立即進行調(diào)用(notifyBatchPartial函數(shù)),對所有映射的鏡像進行objc setup操作;搜索(*sNotifyObjCMapped)(objcImageCount, paths, mhs);

  • init回調(diào):注冊的時候會對所有鏡像立即進行調(diào)用;另外會在鏡像初始化時調(diào)用;搜索(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());

  • unmapped回調(diào):卸載時調(diào)用;

mapped回調(diào)具體操作看objc源碼閱讀

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

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;

    try {
        notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
    }
    catch (const char* msg) {
        // ignore request to abort during registration
    }

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

2.7.1.3 notifySingle執(zhí)行_dyld_objc_notify_register的init回調(diào)

+load方法在此時調(diào)用

context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);

2.7.1.4 初始化當前鏡像

bool hasInitializers = this->doInitialization(context);

2.7.1.4.1 調(diào)用_mod_init_func

即 C++ Static Initializers 和 attribute((constructor))

doModInitFunctions(context);

2.7.2 打印各個階段的時間

ImageLoader::printStatistics((unsigned int)allImagesCount(), initializerTimes[0]);
2.8 找到主執(zhí)行文件的main函數(shù)
// find entry point for main executable
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();

_dyld_objc_notify_register 的調(diào)用時機

看這篇http://www.itdecent.cn/p/3a238256a190的3.4,是在初始化第一個庫libSystem時,libSystem 又會去初始化 libDispatch,在 libDispatch 初始化方法里面又會有一步 _os_object_init,在 _os_object_init 內(nèi)部就會調(diào)起 _objc_init

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容