
應(yīng)用程序加載
準(zhǔn)備條件:dyld-732.8 源碼
編譯過程
源文件 -> 預(yù)編譯 -> 編譯 -> 匯編 -> 鏈接(.a/.lib.so) -> 可執(zhí)行文件


靜態(tài)庫
在鏈接階段,會將匯編生成的目標(biāo)與引用的庫一起鏈接打包到可執(zhí)行文件中

動態(tài)庫
程序編譯并不會鏈接到目標(biāo)代碼中,而是在程序運行時才被載入

-
優(yōu)勢
- 減少打包只有的APP的大小
- 共享內(nèi)存,節(jié)約資源
- 通過更新動態(tài)庫,達到更新程序的目的
-
常見動態(tài)庫
- UIkit
- libdispatch
- libobjc.dyld
APP加載流程

- APP啟動
- 加載
libSystem - Runtime 向
dyld注冊回調(diào)函數(shù) - 加載新的
image(鏡像文件) - 執(zhí)行
map_images、load_images(執(zhí)行這一步之后,會回到第4步之前,ImageLoader 加載 image) - 調(diào)用
main函數(shù)
_dyld 分析
- _dyld 是動態(tài)鏈接器
- _dyld 將所有動態(tài)庫鏈接到內(nèi)存中
- _dyld_start:鏈接開始
程序加載的順序
- load 方法

- load 前還有 _dyld_start

- 找到 arm64 的流程

通過注釋找到
call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue)
- 找到 start 源碼
- 第一步:
rebaseDyld(dyldsMachHeader);。調(diào)整dyld,在磁盤中,所有的dyld的 DATA 段都連在一起,所以需要修復(fù)成真正的指針,以保證程序運行 - 第二步:
__guard_setup(apple);。設(shè)置隨機偏移值,這個是內(nèi)存段的偏移,虛擬地址映射物理地址 - 第三步:
return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);

5. 找到 main 源碼
整個 main 函數(shù)在 5880-6365部分,接近500行代碼
-
第一部分:函數(shù)開始到下面這一段代碼,前面都是環(huán)境判斷、初始化操作;
CRSetCrashLogMessage("dyld: launch started"); setContext(mainExecutableMH, argc, argv, envp, apple); -
第二部分:將dyld添加到uuidArray中以啟用堆棧符號。中間其他代碼都是環(huán)境判斷以及日志處理
addDyldImageToUUIDList(); 第三部分:
reloadAllImages:,這段代碼之后才是真正的開始加載鏡像的一個分水嶺。第四部分:
instantiateFromLoadedImage,為main執(zhí)行文件初始化ImageLoader-
第五部分:
sInsertedDylibCount = sAllImages.size()-1;,記錄插入庫的數(shù)量并描述插入庫的順序。- 先插入庫:inserted libraries
- 然后是主庫:then main
- 然后是其他庫:then others
-
第六部分:
gLinkContext的處理- 鏈接 main 執(zhí)行文件:
gLinkContext.linkingMainExecutable = true; - 申請插入式緩存:
ImageLoader::applyInterposingToDyldCache(gLinkContext); - 通知dyld狀態(tài):
gLinkContext.notifyBatch(dyld_image_state_bound, false); - 斷開 main 執(zhí)行文件的鏈接:
gLinkContext.linkingMainExecutable = false;
- 鏈接 main 執(zhí)行文件:
-
第七部分:
initializeMainExecutable();,初始化主執(zhí)行文件,這里才是真正的初始化、綁定等操作執(zhí)行初始化器:
runInitializers(const LinkContext& context, InitializerTimingList& timingInfo)處理初始化器:
processInitializers(context, thisThread, timingInfo, up);-
遞歸初始化:對images列表中的所有映像調(diào)用recursive init,構(gòu)建未初始化的向上依賴項的新列表。
recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&);
-
第八部分:
notifyMonitoringDyldMain();,通知所有的監(jiān)聽對象(進程),當(dāng)前進程即將進入main()方法。這里通過 mach_msg 進行的通知- sendMessage
- mach_msg

- reloadAllImages

- instantiateFromLoadedImage

- sniffLoadCommands

dyld 流程:main函數(shù)分析
應(yīng)用程序的入口:dyld::_main uintptr_t start(...)
1. 環(huán)境變量相關(guān)處理
- 從環(huán)境變量中讀取可執(zhí)行文件 cdHash
- checkEnvironmentVariables(envp);
- defaultUninitializedFallbackPaths(envp);
2. 加載共享緩存
checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH,mainExecutableSlide);
mapSharedCache();
3. 將 dyld 本身添加到UUID列表
addDyldImageToUUIDList();
4. reloadAllImages:
-
實例化主程序:
instantiateFromLoadedImage-
ImageLoaderMachO::instantiateMainExecutable
-
sniffLoadCommands:確定此 mach-o 文件是否具有壓縮的 LINKEDIT 以及 段數(shù) addImage(image);
-
內(nèi)核會映射到主要可執(zhí)行文件中。我們需要為已經(jīng)映射到主可執(zhí)行文件中的文件中的文件創(chuàng)建一個
ImageLoader *
-
-
加載任何插入的動態(tài)庫
loadInsertedDylib(*lib); -
鏈接庫
- 遍歷:
sInsertedDylibCount ImageLoader* image = sAllImages[i+1];link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);-
this->recursiveApplyInterposing(context);:插入任何動態(tài)加載的鏡像文件
- 遍歷:
5. 運行所有初始化程序
initializeMainExecutable();
runInitializers
初始化準(zhǔn)備:processInitializers(images.count)
遞歸一個個開始初始化:images.images[i]->recursiveInitialization
-
notifySingle 單個鏡像通知-開始行動
- 獲取鏡像文件真實地址:
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); -
初始化objc通知
初始化objc通知 - 遍歷初始化
this->doInitialization(context);-
doImageInit(context);Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide); libSystemInitialized; //libSystem 初始化必須提前 -
C++函數(shù)處理
doModInitFunctions(context);
-
- 獲取鏡像文件真實地址:
6. 通知監(jiān)聽 dyld 的main
notifyMonitoringDyldMain();
