07--應(yīng)用加載01--應(yīng)用加載流程[_dyld]

TOC

應(yīng)用程序加載

準(zhǔn)備條件:dyld-732.8 源碼

編譯過程

源文件 -> 預(yù)編譯 -> 編譯 -> 匯編 -> 鏈接(.a/.lib.so) -> 可執(zhí)行文件

編譯過程
編譯過程

靜態(tài)庫

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

靜態(tài)庫

動態(tài)庫

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

動態(tài)庫
  • 優(yōu)勢

    • 減少打包只有的APP的大小
    • 共享內(nèi)存,節(jié)約資源
    • 通過更新動態(tài)庫,達到更新程序的目的
  • 常見動態(tài)庫

    • UIkit
    • libdispatch
    • libobjc.dyld

APP加載流程

APP加載流程
  1. APP啟動
  2. 加載 libSystem
  3. Runtime 向 dyld 注冊回調(diào)函數(shù)
  4. 加載新的 image(鏡像文件)
  5. 執(zhí)行 map_images、load_images(執(zhí)行這一步之后,會回到第4步之前,ImageLoader 加載 image)
  6. 調(diào)用 main 函數(shù)

_dyld 分析

  • _dyld 是動態(tài)鏈接器
  • _dyld 將所有動態(tài)庫鏈接到內(nèi)存中
  • _dyld_start:鏈接開始

程序加載的順序

  1. load 方法
load方法
  1. load 前還有 _dyld_start
_dyld_start
  1. 找到 arm64 的流程
image

通過注釋找到

call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue)
  1. 找到 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);
start 源碼

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ù)量并描述插入庫的順序。

    1. 先插入庫:inserted libraries
    2. 然后是主庫:then main
    3. 然后是其他庫:then others
  • 第六部分:gLinkContext 的處理

    1. 鏈接 main 執(zhí)行文件:gLinkContext.linkingMainExecutable = true;
    2. 申請插入式緩存:ImageLoader::applyInterposingToDyldCache(gLinkContext);
    3. 通知dyld狀態(tài):gLinkContext.notifyBatch(dyld_image_state_bound, false);
    4. 斷開 main 執(zhí)行文件的鏈接:gLinkContext.linkingMainExecutable = false;
  • 第七部分:initializeMainExecutable();,初始化主執(zhí)行文件,這里才是真正的初始化、綁定等操作

    1. 執(zhí)行初始化器:runInitializers(const LinkContext& context, InitializerTimingList& timingInfo)

    2. 處理初始化器:processInitializers(context, thisThread, timingInfo, up);

    3. 遞歸初始化:對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 進行的通知

    1. sendMessage
    2. mach_msg
main 源碼
  1. reloadAllImages
reloadAllImages
  1. instantiateFromLoadedImage
instantiateFromLoadedImage
  1. sniffLoadCommands
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:

  1. 實例化主程序:instantiateFromLoadedImage

    • ImageLoaderMachO::instantiateMainExecutable

      • sniffLoadCommands:確定此 mach-o 文件是否具有壓縮的 LINKEDIT 以及 段數(shù)
      • addImage(image);
    • 內(nèi)核會映射到主要可執(zhí)行文件中。我們需要為已經(jīng)映射到主可執(zhí)行文件中的文件中的文件創(chuàng)建一個 ImageLoader *

  2. 加載任何插入的動態(tài)庫

    loadInsertedDylib(*lib);
    
  3. 鏈接庫

    • 遍歷: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. 運行所有初始化程序

  1. initializeMainExecutable();

  2. runInitializers

  3. 初始化準(zhǔn)備:processInitializers(images.count)

  4. 遞歸一個個開始初始化:images.images[i]->recursiveInitialization

  5. notifySingle 單個鏡像通知-開始行動

    1. 獲取鏡像文件真實地址:(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
    2. 初始化objc通知


      初始化objc通知
    3. 遍歷初始化 this->doInitialization(context);
      1. doImageInit(context);

        Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide);
        libSystemInitialized; //libSystem 初始化必須提前
        
      2. C++函數(shù)處理

        doModInitFunctions(context);
        

6. 通知監(jiān)聽 dyld 的main

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

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