參考
- dyld源碼
- 個人注釋的源碼
- 參考文章
- http://www.itdecent.cn/p/3a238256a190 (這篇細節(jié)講得很不錯)
- http://www.itdecent.cn/p/885c8077b27d
- https://juejin.cn/post/6844904040149729294#heading-16 (有些細節(jié)沒有講清楚,比如_objc_init的調(diào)用時機)
- http://www.itdecent.cn/p/ceb33cff30f6
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