iOS 分析dyld工作過程 dyld-941.5源碼

dyld源碼
蘋果官方資源opensource
objc4-838可編譯聯(lián)調(diào)源碼

一模她、了解相關(guān)概念

1.靜態(tài)庫(kù)督勺、動(dòng)態(tài)庫(kù)

通常程序都會(huì)依賴系統(tǒng)一些庫(kù), 庫(kù)是什么呢? 其實(shí)庫(kù)就是一些可執(zhí)行的二進(jìn)制文件, 能被操作系統(tǒng)加載到內(nèi)存里面中舍哄。庫(kù)分為兩種:靜態(tài)庫(kù) / 動(dòng)態(tài)庫(kù)

  • 靜態(tài)庫(kù):是一堆.o文件的集合。格式.a, .lib等。鏈接階段時(shí)靜態(tài)庫(kù)會(huì)被完整地復(fù)制, 一起打包在可執(zhí)行文件中,被多次使用就有多份冗余拷貝掌逛。
    • 優(yōu)點(diǎn): 編譯完成之后, 鏈接到目標(biāo)程序中, 同時(shí)打包到可執(zhí)行文件里面, 不會(huì)有外部依賴。
    • 缺點(diǎn): 靜態(tài)庫(kù)會(huì)有兩份, 所以會(huì)導(dǎo)致目標(biāo)程序體積增大, 對(duì)內(nèi)存, 性能, 速度消耗很大穆咐。并且相同靜態(tài)庫(kù)每個(gè)app中都會(huì)拷貝一份颤诀。
  • 動(dòng)態(tài)庫(kù):是一個(gè)已經(jīng)完全鏈接后的鏡像。格式.framework等对湃。程序編譯時(shí)并不會(huì)鏈接到目標(biāo)程序中崖叫,目標(biāo)程序只會(huì)存儲(chǔ)指向動(dòng)態(tài)庫(kù)的引用,在程序運(yùn)行時(shí)才被載入拍柒。蘋果大部分都是動(dòng)態(tài)庫(kù)心傀。
    • 優(yōu)點(diǎn): 不需要拷貝到目標(biāo)程序, 減少App包的體積;多個(gè)App可以使用同一個(gè)動(dòng)態(tài)庫(kù), 共享內(nèi)存, 節(jié)約資源拆讯;由于運(yùn)行時(shí)才會(huì)去加載, 那么可以在App不使用時(shí)隨時(shí)對(duì)庫(kù)進(jìn)行替換或更新, 更新靈活脂男。
    • 缺點(diǎn): 動(dòng)態(tài)載入會(huì)帶來(lái)一部分性能損失, 同時(shí)動(dòng)態(tài)庫(kù)也會(huì)使得程序依賴于外部環(huán)境。一旦動(dòng)態(tài)庫(kù)沒有或消失, 程序會(huì)出現(xiàn)問題种呐。
1.Xcode編譯command + B產(chǎn)生可執(zhí)行文件的過程:
  • a.預(yù)編譯
    處理源代碼文件中以#開頭的預(yù)編譯的命令(#import/#include等等)宰翅;把包含的文件插入到指定的位置;替換代碼中的宏定義爽室;刪除代碼里的注釋等等汁讼。產(chǎn)生.i文件。
  • b.編譯
    詞法分析阔墩;語(yǔ)法分析嘿架;語(yǔ)義分析;生成相應(yīng)的匯編代碼文件啸箫。 產(chǎn)生.s文件(匯編文件)
  • c.匯編
    將匯編代碼文件翻譯成機(jī)器指令耸彪,機(jī)器指令會(huì)生成.o文件。
  • d.鏈接
    把之前所有操作的文件鏈接到程序里面來(lái), 對(duì).o文件中引用其他庫(kù)的地方進(jìn)行引用, 生成最后的 可執(zhí)行文件忘苛。動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)區(qū)別其實(shí)就是鏈接的區(qū)別蝉娜。
    • 靜態(tài)鏈接: 鏈接器對(duì).o文件進(jìn)行符號(hào)收集唱较、解析和重定位;把所有的.o粘合在一起從而形成可執(zhí)行文件召川。(在靜態(tài)鏈接之后就不會(huì)再有靜態(tài)庫(kù)了)
    • 動(dòng)態(tài)鏈接: dyld動(dòng)態(tài)鏈接(下面有介紹)
2.點(diǎn)擊App啟動(dòng)的過程:
  • a.系統(tǒng)調(diào)用 exec() 分配內(nèi)存绊汹、開辟進(jìn)程
  • b.app對(duì)應(yīng)的可執(zhí)行文件加載到內(nèi)存
  • c.dyld動(dòng)態(tài)鏈接器加載到內(nèi)存:load dyld
  • d.dyld動(dòng)態(tài)鏈接器進(jìn)行動(dòng)態(tài)鏈接:rebase->bing->Objc->initalizers
  • e.調(diào)起main()函數(shù)

啟動(dòng)時(shí)間的劃分可以把main()函數(shù)作為關(guān)鍵點(diǎn)分割成兩塊

main()函數(shù)啟動(dòng)分塊.png

t1階段main()之前的處理所需時(shí)間扮宠,稱為pre-main

t1階段.png

t2階段main()main()之后處理所需時(shí)間
t2階段耗時(shí)的主要是業(yè)務(wù)代碼

3.Mach-O是什么狐榔?

Mach-O: Mach Object文件格式的縮寫坛增,它是一種用于可執(zhí)行文件,目標(biāo)文件.o薄腻,動(dòng)態(tài)庫(kù)收捣,內(nèi)核轉(zhuǎn)儲(chǔ)的文件格式。作為a.out格式的替代庵楷,Mach-O提供了更強(qiáng)的擴(kuò)展性罢艾,并提升了符號(hào)表中信息的訪問速度。

三種Mach-O格式的文件:可執(zhí)行文件尽纽、dylib動(dòng)態(tài)庫(kù)咐蚯、Bundle資源文件包

Mach-O由四部分組成:Mach-O頭部Load Command弄贿、section春锋、Other Data,可以通過MachOView可查看可執(zhí)行文件信息差凹。

二期奔、什么是dyld

本文主要介紹dyld源碼執(zhí)行流程。
app啟動(dòng)加載過程中類和分類加載都不可避免的觸及dyld危尿,所以了解dyld源碼可以讓我們更好的理解app的工作原理呐萌。

什么是dyld?
dyld(the dynamic link editor 動(dòng)態(tài)鏈接器):是蘋果操作系統(tǒng)的一個(gè)重要的組成部分谊娇。在iOS/Mac OSX系統(tǒng)中肺孤,僅有很少量的進(jìn)程只需要內(nèi)核就能完成加載,基本上所有的進(jìn)程都是動(dòng)態(tài)鏈接的邮绿,所以Mach-O鏡像文件中會(huì)有很多對(duì)外部的庫(kù)和符號(hào)的引用渠旁,但是這些引用并不能直接用,在啟動(dòng)時(shí)還必須要通過這些引用進(jìn)行內(nèi)容的填補(bǔ)船逮,這個(gè)填補(bǔ)工作就是由動(dòng)態(tài)鏈接器dyld來(lái)完成的顾腊,也就是符號(hào)綁定。

在app被編譯打包成可執(zhí)行文件格式的Mach-O文件后挖胃,在程序啟動(dòng)時(shí)交由dyld負(fù)責(zé)鏈接加載程序杂靶。

dyld是開源的梆惯,可以通過蘋果官網(wǎng)下載它的dyld源碼,本文的對(duì)應(yīng)的版本是941.5吗垮。dyld4是iOS15發(fā)布的垛吗。

  • dyld3: 相比dyld2新增預(yù)構(gòu)建/閉包, 目的是將一些啟動(dòng)數(shù)據(jù)創(chuàng)建為閉包存到本地,下次啟動(dòng)將不再重新解析數(shù)據(jù)烁登,而是直接讀取閉包內(nèi)容
  • dyld4: 采用pre-build + just-in-time 預(yù)構(gòu)建/閉包+實(shí)時(shí)解析的雙解析模式, 將根據(jù)緩存有效與否選擇合適的模式進(jìn)行解析, 同時(shí)也處理了閉包失效時(shí)候需要重建閉包的性能問題怯屉。

三、dyld工作流程

dyld流程

新建一個(gè)工程饵沧,在ViewController.m里重寫+(void)load方法锨络,在load方法上打個(gè)斷點(diǎn)調(diào)試,運(yùn)行app后在斷點(diǎn)處輸出其函數(shù)調(diào)用的堆棧:

dyld4模擬器 入口
dyld4真機(jī) 入口

新版本dyld: start開始接下來(lái)走 dyld4中prepare方法, 源碼入口需要在start中開始探索狼牺。
(舊版本dyld: _dyld_start開始, 接下來(lái)走dyldbootstrap, 源碼入口需要在dyld_start開始羡儿。)

dyld start匯編
1.start函數(shù)是dyld的開始
// dyld的入口點(diǎn)。內(nèi)核加載dyld并跳轉(zhuǎn)到__dyld_start是钥,它設(shè)置一些寄存器并調(diào)用這個(gè)函數(shù)掠归。
// 注意:這個(gè)函數(shù)永遠(yuǎn)不會(huì)返回,它調(diào)用exit()悄泥。因此虏冻,堆棧保護(hù)程序是無(wú)用的,因?yàn)橛肋h(yuǎn)不會(huì)執(zhí)行epilog码泞。標(biāo)記函數(shù)no-return禁用堆棧保護(hù)兄旬。堆棧保護(hù)器也會(huì)導(dǎo)致armv7k代碼生成的問題,因?yàn)樗ㄟ^prolog中的GOT槽訪問隨機(jī)值余寥,但dyld還沒有rebased领铐。
void start(const KernelArgs* kernArgs) __attribute__((noreturn)) __asm("start");
void start(const KernelArgs* kernArgs)
{
    // 發(fā)出kdebug跟蹤點(diǎn)來(lái)指示dyld引導(dǎo)程序已經(jīng)啟動(dòng) <rdar://46878536>
    // 注意:這是在dyld rebased之前調(diào)用的,所以kdebug_trace_dyld_marker()不能使用任何全局變量
    dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);

    // 走所有的fixups chains 和 rebase dyld
    // 注意:withChainStarts()和fixupAllChainedFixups()不能使用任何靜態(tài)數(shù)據(jù)指針宋舷,因?yàn)樗鼈冞€沒有rebased
    const MachOAnalyzer* dyldMA = getDyldMH(); // 獲取Mach-O分析器
    assert(dyldMA->hasChainedFixups());
    uintptr_t           slide = (long)dyldMA; // 所有基于修復(fù)鏈的圖像的基址為0绪撵,因此slide == load address
    __block Diagnostics diag;
    dyldMA->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
        dyldMA->fixupAllChainedFixups(diag, starts, slide, dyld3::Array<const void*>(), nullptr);
    });
    diag.assertNoError();

    // 現(xiàn)在,我們可以調(diào)用使用DATA的函數(shù)
    mach_init(); // mach消息初始化

    // 為stack canary設(shè)置隨機(jī)值
    __guard_setup(kernArgs->findApple()); // 棧溢出保護(hù)

    // 設(shè)置以便open_with_subsystem()工作
    _subsystem_init(kernArgs->findApple());

    // 在__DATA_CONST中將ProcessConfig對(duì)象變?yōu)橹蛔x之前祝蝠,使用placement new來(lái)構(gòu)造它
    ProcessConfig& config = *new ((ProcessConfig*)sConfigBuffer) ProcessConfig(kernArgs, sSyscallDelegate);

    // 使__DATA_CONST只讀(內(nèi)核映射為r/w)
    dyldMA->forEachSegment(^(const MachOAnalyzer::SegmentInfo& segInfo, bool& stop) {
        if ( segInfo.readOnlyData ) {
            const uint8_t* start = (uint8_t*)(segInfo.vmAddr + slide);
            size_t         size  = (size_t)segInfo.vmSize;
            sSyscallDelegate.mprotect((void*)start, size, PROT_READ);
        }
    });

#if !SUPPPORT_PRE_LC_MAIN
    // 堆棧分配RuntimeLocks音诈。它們不能位于通常是只讀的Allocator池中
    RuntimeLocks  sLocks;
#endif

    // 創(chuàng)建Allocator和APIs/RuntimeState對(duì)象
    APIs& state = APIs::bootstrap(config, sLocks);

    // 加載程序的所有依賴項(xiàng)并將它們綁定在一起
    MainFunc appMain = prepare(state, dyldMA);

    // 現(xiàn)在使所有的dyld分配的數(shù)據(jù)結(jié)構(gòu)只讀
    state.decWritable();

    // 調(diào)用main(),如果它返回绎狭,調(diào)用exit()并返回結(jié)果
    // 注意:這是經(jīng)過組織的细溅,以便在程序的主線程中回溯時(shí)只在“main”下面顯示“start”。
    int result = appMain(state.config.process.argc, state.config.process.argv, state.config.process.envp, state.config.process.apple);

    // 如果我們到達(dá)這里儡嘶,main()返回(而不是程序調(diào)用exit())
#if TARGET_OS_OSX
    // <rdar://74518676> libSystemHelpers不是為模擬器設(shè)置的喇聊,因此直接調(diào)用_exit()
    if ( MachOFile::isSimulatorPlatform(state.config.process.platform) )
        _exit(result);
#endif
    state.libSystemHelpers->exit(result);
}
  • 走所有的 fixups chainsrebase dyld
  • mach消息初始化:mach_init()
  • 棧溢出保護(hù):__guard_setup(kernArgs->findApple());
  • 構(gòu)建進(jìn)程配置:config = ProcessConfig(kernArgs, sSyscallDelegate);
class VIS_HIDDEN ProcessConfig
{
public:
                            // used in unit tests to config and test ProcessConfig objects
                            ProcessConfig(const KernelArgs* kernArgs, SyscallDelegate&);

#if !BUILDING_DYLD
    void                    reset(const MachOAnalyzer* mainExe, const char* mainPath, const DyldSharedCache* cache);
#endif

    //
    // Contains config info derived from Kernel arguments
    //
    class Process
    {
    public:
                                    Process(const KernelArgs* kernArgs, SyscallDelegate&);

        const MachOAnalyzer*        mainExecutable;
        const char*                 mainExecutablePath;
        const char*                 mainUnrealPath;             // raw path used to launch
        uint32_t                    mainExecutableSDKVersion;
        uint32_t                    mainExecutableSDKVersionSet;
        uint32_t                    mainExecutableMinOSVersion;
        uint32_t                    mainExecutableMinOSVersionSet;
        dyld3::Platform             basePlatform;
        dyld3::Platform             platform;
        const char*                 dyldPath;
        int                         argc;
        const char* const*          argv;
        const char* const*          envp;
        const char* const*          apple;
        const char*                 progname;
        DyldCommPage                commPage;
        const GradedArchs*          archs;
        int                         pid;
        bool                        isTranslated;
        bool                        catalystRuntime; // Mac Catalyst app or iOS-on-mac app
        bool                        enableDataConst; // Temporarily allow disabling __DATA_CONST for bringup
        bool                        proactivelyUseWeakDefMap;

        const char*                 appleParam(const char* key) const;
        const char*                 environ(const char* key) const;
        const char*                 strdup(const char*) const; // allocates into read-only region
        void*                       roalloc(size_t) const;     // allocates into read-only region
    private:
        friend class ProcessConfig;

        uint32_t                    findVersionSetEquivalent(dyld3::Platform versionPlatform, uint32_t version) const;
        const char*                 pathFromFileHexStrings(SyscallDelegate& sys, const char* encodedFileInfo);
        const char*                 getDyldPath(SyscallDelegate& sys);
        const char*                 getMainPath(SyscallDelegate& syscall);
        const char*                 getMainUnrealPath(SyscallDelegate& syscall);
        dyld3::Platform             getMainPlatform();
        const GradedArchs*          getMainArchs(SyscallDelegate& osDelegate);
        bool                        usesCatalyst();
    };

    //
    // Contains security related config info
    //
    class Security
    {
    public:
                                    Security(Process& process, SyscallDelegate&);

        bool                        internalInstall;
        bool                        allowAtPaths;
        bool                        allowEnvVarsPrint;
        bool                        allowEnvVarsPath;
        bool                        allowEnvVarsSharedCache;
        bool                        allowClassicFallbackPaths;
        bool                        allowInsertFailures;
        bool                        allowInterposing;
        bool                        skipMain;

     private:
        uint64_t                    getAMFI(const Process& process, SyscallDelegate& syscall);
        void                        pruneEnvVars(Process& process);
   };

    //
    // Contains logging related config info
    //
    class Logging
    {
    public:
                                    Logging(const Process& process, const Security& security, SyscallDelegate&);

        bool                        libraries;
        bool                        segments;
        bool                        fixups;
        bool                        initializers;
        bool                        apis;
        bool                        notifications;
        bool                        interposing;
        bool                        loaders;
        bool                        searching;
        bool                        env;
        int                         descriptor;
        bool                        useStderr;
        bool                        useFile;

     private:
    };

    //
    // Contains dyld cache related config info
    //
    class DyldCache
    {
    public:
                                    DyldCache(Process&, const Security&, const Logging&, SyscallDelegate&);

        const DyldSharedCache*          addr;
        uintptr_t                       slide;
        const char*                     path;
        const objc_opt::objc_opt_t*     objCCacheInfo;
        const SwiftOptimizationHeader*  swiftCacheInfo;
        dyld3::Platform                 platform;
        uint32_t                        osVersion;
        uint32_t                        dylibCount;

        bool                        indexOfPath(const char* dylibPath, uint32_t& dylibIndex) const;
        void                        makeDataConstWritable(const Logging&, const SyscallDelegate&, bool writable) const;

    private:
        friend class ProcessConfig;
        bool                        uuidOfFileMatchesDyldCache(const Process& process, const SyscallDelegate& syscall, const char* path) const;
        void                        setPlatformOSVersion(const Process& proc);
        void                        setupDyldCommPage(Process& process, const Security& security, SyscallDelegate& syscall);
    };

    //
    // Contains path searching config info
    //
    class PathOverrides
    {
    public:
                                        PathOverrides(const Process&, const Security&, const Logging&, const DyldCache&, SyscallDelegate&);

        enum Type { pathDirOverride, versionedOverride, suffixOverride, catalystPrefix, simulatorPrefix, rawPath,
              rpathExpansion, loaderPathExpansion, executablePathExpansion, implictRpathExpansion, customFallback, standardFallback };

        void                            forEachPathVariant(const char* requestedPath, dyld3::Platform platform, bool disableCustomFallbacks, bool& stop,
                                                           void (^handler)(const char* possiblePath, Type type, bool& stop)) const;
        void                            forEachInsertedDylib(void (^handler)(const char* dylibPath, bool &stop)) const;
        bool                            dontUsePrebuiltForApp() const;
        bool                            hasInsertedDylibs() const { return (_insertedDylibCount != 0); }
        uint32_t                        insertedDylibCount() const { return _insertedDylibCount; }
        const char*                     simRootPath() const { return _simRootPath; }

        static const char*              getLibraryLeafName(const char* path);
        static const char*              typeName(Type);

    private:
        void                            setEnvVars(const char* envp[], const char* mainExePath);
        void                            addEnvVar(const Process& process, const Security& security, const char* keyEqualsValue, bool isLC_DYLD_ENV, char* crashMsg);
        void                            processVersionedPaths(const Process& proc, SyscallDelegate&, const DyldCache&, dyld3::Platform, const GradedArchs&);
        void                            forEachEnvVar(void (^handler)(const char* envVar)) const;
        void                            forEachExecutableEnvVar(void (^handler)(const char* envVar)) const;

        void                            setString(const Process& process, const char*& var, const char* value);
        static void                     forEachInColonList(const char* list1, const char* list2, void (^callback)(const char* path, bool& stop));
        const char*                     getFrameworkPartialPath(const char* path) const;
        void                            handleListEnvVar(const char* key, const char** list, void (^handler)(const char* envVar)) const;
        void                            handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const;
        void                            forEachDylibFallback(dyld3::Platform platform, bool disableCustom, void (^handler)(const char* fallbackDir, Type type, bool& stop)) const;
        void                            forEachFrameworkFallback(dyld3::Platform platform, bool disableCustom, void (^handler)(const char* fallbackDir, Type type, bool& stop)) const;
        void                            forEachImageSuffix(const char* path, Type type, bool& stop, void (^handler)(const char* possiblePath, Type type, bool& stop)) const;
        void                            addSuffix(const char* path, const char* suffix, char* result) const;
        void                            checkVersionedPath(const Process&, const char* path, SyscallDelegate& delegate, const DyldCache&, dyld3::Platform platform, const GradedArchs& archs);
        void                            addPathOverride(const Process&, const char* installName, const char* overridePath);

        enum class FallbackPathMode { classic, restricted, none };
        struct DylibOverride { DylibOverride* next; const char* installName; const char* overridePath; };

        const char*                     _dylibPathOverridesEnv       = nullptr;
        const char*                     _frameworkPathOverridesEnv   = nullptr;
        const char*                     _dylibPathFallbacksEnv       = nullptr;
        const char*                     _frameworkPathFallbacksEnv   = nullptr;
        const char*                     _versionedDylibPathsEnv      = nullptr;
        const char*                     _versionedFrameworkPathsEnv  = nullptr;
        const char*                     _dylibPathOverridesExeLC     = nullptr;
        const char*                     _frameworkPathOverridesExeLC = nullptr;
        const char*                     _dylibPathFallbacksExeLC     = nullptr;
        const char*                     _frameworkPathFallbacksExeLC = nullptr;
        const char*                     _versionedFrameworkPathExeLC = nullptr;
        const char*                     _versionedDylibPathExeLC     = nullptr;
        const char*                     _insertedDylibs              = nullptr;
        const char*                     _imageSuffix                 = nullptr;
        const char*                     _simRootPath                 = nullptr;  // simulator only
        DylibOverride*                  _versionedOverrides          = nullptr;  // linked list of VERSIONED overrides
        FallbackPathMode                _fallbackPathMode            = FallbackPathMode::classic;
        uint32_t                        _insertedDylibCount          = 0;
    };


    // wrappers for macOS that causes special three libsystem dylibs to not exist if they match what is in dyld cache
    bool simulatorFileMatchesDyldCache(const char* path) const;
    bool fileExists(const char* path, FileID* fileID=nullptr, bool* notAFile=nullptr) const;

    // if there is a dyld cache and the supplied path is in the dyld cache at that path or a symlink, return canonical path
    const char* canonicalDylibPathInCache(const char* dylibPath) const;


    // all instance variables are organized into groups
    SyscallDelegate         syscall;
    Process                 process;
    Security                security;
    Logging                 log;
    DyldCache               dyldCache;
    PathOverrides           pathOverrides;
};
  • 創(chuàng)建Allocator和APIs/RuntimeState對(duì)象: APIs& state = APIs::bootstrap(config, sLocks);

APIs繼承自RuntimeState

class VIS_HIDDEN APIs : public RuntimeState
{
public:
#if BUILDING_DYLD
    static      APIs&                         bootstrap(const ProcessConfig& c, RuntimeLocks& locks);
#else
    static      APIs&                         bootstrap(const ProcessConfig& c);
#endif

    //
    // private call from libdyld.dylib into dyld to tell that libSystem.dylib is initialized
    //
    virtual     void                        _libdyld_initialize(const LibSystemHelpers* helpers);


    //
    // APIs from macOS 10.2
    //
    virtual     uint32_t                    _dyld_image_count();
    virtual     const mach_header*          _dyld_get_image_header(uint32_t index);
    virtual     intptr_t                    _dyld_get_image_vmaddr_slide(uint32_t index);
    virtual     const char*                 _dyld_get_image_name(uint32_t index);
    virtual     void                        _dyld_register_func_for_add_image(void (*func)(const mach_header* mh, intptr_t vmaddr_slide));
    virtual     void                        _dyld_register_func_for_remove_image(void (*func)(const mach_header* mh, intptr_t vmaddr_slide));
    virtual     int32_t                     NSVersionOfLinkTimeLibrary(const char* libraryName);
    virtual     int32_t                     NSVersionOfRunTimeLibrary(const char* libraryName);
    virtual     int                         _NSGetExecutablePath(char* buf, uint32_t* bufsize);
    virtual     void                        _dyld_fork_child();


    //
    // APIs from macOS 10.4
    //
    virtual     int                         dladdr(const void*, Dl_info* result);
    virtual     void*                       dlopen(const char* path, int mode);
    virtual     int                         dlclose(void* handle);
    virtual     char*                       dlerror();
    virtual     void*                       dlsym(void* handle, const char* symbol);
    virtual     bool                        dlopen_preflight(const char* path);


    //
    // APIs deprecated in macOS 10.5 and not on any other platform
    //
    virtual     NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage* objectFileImage);
    virtual     NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* address, size_t size, NSObjectFileImage* objectFileImage);
    virtual     bool                        NSDestroyObjectFileImage(NSObjectFileImage objectFileImage);
    virtual     bool                        NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName);
    virtual     void*                       NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t* size);
    virtual     const char*                 NSNameOfModule(NSModule m);
    virtual     const char*                 NSLibraryNameForModule(NSModule m);
    virtual     NSModule                    NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options);
    virtual     bool                        NSUnLinkModule(NSModule module, uint32_t options);
    virtual     bool                        NSIsSymbolNameDefined(const char* symbolName);
    virtual     bool                        NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint);
    virtual     bool                        NSIsSymbolNameDefinedInImage(const mach_header* image, const char* symbolName);
    virtual     NSSymbol                    NSLookupAndBindSymbol(const char* symbolName);
    virtual     NSSymbol                    NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint);
    virtual     NSSymbol                    NSLookupSymbolInModule(NSModule module, const char* symbolName);
    virtual     NSSymbol                    NSLookupSymbolInImage(const mach_header* image, const char* symbolName, uint32_t options);
    virtual     void*                       NSAddressOfSymbol(NSSymbol symbol);
    virtual     NSModule                    NSModuleForSymbol(NSSymbol symbol);
    virtual     void                        NSLinkEditError(NSLinkEditErrors* c, int* errorNumber, const char** fileName, const char** errorString);
    virtual     bool                        NSAddLibrary(const char* pathName);
    virtual     bool                        NSAddLibraryWithSearching(const char* pathName);
    virtual     const mach_header*          NSAddImage(const char* image_name, uint32_t options);
    virtual     bool                        _dyld_image_containing_address(const void* address);
    virtual     void                        _dyld_lookup_and_bind(const char* symbol_name, void** address, NSModule* module);
    virtual     void                        _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module);
    virtual     void                        _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module);


    //
    // Added macOS 10.6
    //
    virtual     intptr_t                    _dyld_get_image_slide(const mach_header* mh);
    virtual     const char*                 dyld_image_path_containing_address(const void* addr);
#if !__USING_SJLJ_EXCEPTIONS__
    virtual     bool                        _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info);
#endif

    //
    // Added iOS 6, macOS 10.8
    //
    virtual     uint32_t                    dyld_get_sdk_version(const mach_header* mh);
    virtual     uint32_t                    dyld_get_min_os_version(const mach_header* mh);
    virtual     uint32_t                    dyld_get_program_sdk_version();
    virtual     uint32_t                    dyld_get_program_min_os_version();


    //
    // Added iOS 7, macOS 10.9
    //
    virtual     bool                        dyld_process_is_restricted();


    //
    // Added iOS 8, macOS 10.10
    //
    virtual     bool                        dyld_shared_cache_some_image_overridden();
    virtual     void                        _tlv_atexit(void (*termFunc)(void* objAddr), void* objAddr);
    virtual     void                        _tlv_bootstrap();
    virtual     void                        _tlv_exit();


    //
    // Added iOS 9, macOS 10.11, watchOS 2.0
    //
    virtual     int                         dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info));
    virtual     const mach_header*          dyld_image_header_containing_address(const void* addr);
    virtual     const char*                 dyld_shared_cache_file_path();
    virtual     uint32_t                    dyld_get_program_sdk_watch_os_version();
    virtual     uint32_t                    dyld_get_program_min_watch_os_version();


    //
    // Added iOS 10, macOS 10.12, watchOS 3.0
    //
    virtual     void                        _dyld_objc_notify_register(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped);
    virtual     bool                        _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid);
    virtual     bool                        _dyld_get_shared_cache_uuid(uuid_t uuid);
    virtual     bool                        _dyld_is_memory_immutable(const void* addr, size_t length);
    virtual     int                         dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info));


    //
    // Added iOS 11, macOS 10.13, bridgeOS 2.0
    //
    virtual     const void*                 _dyld_get_shared_cache_range(size_t* length);
    virtual     uint32_t                    dyld_get_program_sdk_bridge_os_version();
    virtual     uint32_t                    dyld_get_program_min_bridge_os_version();


    //
    // Added iOS 12, macOS 10.14
    //
    virtual     dyld_platform_t             dyld_get_active_platform();
    virtual     dyld_platform_t             dyld_get_base_platform(dyld_platform_t platform);
    virtual     bool                        dyld_is_simulator_platform(dyld_platform_t platform);
    virtual     bool                        dyld_sdk_at_least(const mach_header* mh, dyld_build_version_t version);
    virtual     bool                        dyld_minos_at_least(const mach_header* mh, dyld_build_version_t version);
    virtual     bool                        dyld_program_sdk_at_least(dyld_build_version_t version);
    virtual     bool                        dyld_program_minos_at_least(dyld_build_version_t version);
    virtual     void                        dyld_get_image_versions(const mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version));
    virtual     void                        _dyld_images_for_addresses(unsigned count, const void* addresses[], dyld_image_uuid_offset infos[]);
    virtual     void                        _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable));


    //
    // Added iOS 13, macOS 10.15
    //
    virtual     void                        _dyld_atfork_prepare();
    virtual     void                        _dyld_atfork_parent();
    virtual     bool                        dyld_need_closure(const char* execPath, const char* dataContainerRootDir);
    virtual     bool                        dyld_has_inserted_or_interposing_libraries(void);
    virtual     bool                        _dyld_shared_cache_optimized(void);
    virtual     bool                        _dyld_shared_cache_is_locally_built(void);
    virtual     void                        _dyld_register_for_bulk_image_loads(void (*func)(unsigned imageCount, const mach_header* mhs[], const char* paths[]));
    virtual     void                        _dyld_register_driverkit_main(void (*mainFunc)(void));
    virtual     void                        _dyld_missing_symbol_abort();
    virtual     const char*                 _dyld_get_objc_selector(const char* selName);
    virtual     void                        _dyld_for_each_objc_class(const char* className, void (^)(void* classPtr, bool isLoaded, bool* stop));
    virtual     void                        _dyld_for_each_objc_protocol(const char* protocolName, void (^)(void* protocolPtr, bool isLoaded, bool* stop));


    //
    // Added iOS 14, macOS 11
    //
    virtual     uint32_t                    _dyld_launch_mode();
    virtual     bool                        _dyld_is_objc_constant(DyldObjCConstantKind kind, const void* addr);
    virtual     bool                        _dyld_has_fix_for_radar(const char* rdar);
    virtual     const char*                 _dyld_shared_cache_real_path(const char* path);
    virtual     bool                        _dyld_shared_cache_contains_path(const char* path);
    virtual     void*                       dlopen_from(const char* path, int mode, void* addressInCaller);
#if !__i386__
    virtual     void *                      dlopen_audited(const char * path, int mode);
#endif
    virtual     const struct mach_header*   _dyld_get_prog_image_header();


    //
    // Added iOS 15, macOS 12
    //
    virtual     void                                obsolete() __attribute__((noreturn));
    virtual     void                                _dyld_visit_objc_classes(void (^callback)(const void* classPtr));
    virtual     uint32_t                            _dyld_objc_class_count(void);
    virtual     bool                                _dyld_objc_uses_large_shared_cache(void);
    virtual     _dyld_protocol_conformance_result   _dyld_find_protocol_conformance(const void *protocolDescriptor,
                                                                                    const void *metadataType,
                                                                                    const void *typeDescriptor) const;
    virtual     _dyld_protocol_conformance_result   _dyld_find_foreign_type_protocol_conformance(const void *protocol,
                                                                                                 const char *foreignTypeIdentityStart,
                                                                                                 size_t foreignTypeIdentityLength) const;
    virtual     uint32_t                            _dyld_swift_optimizations_version() const;
    virtual     void                                runAllInitializersForMain();
    virtual     void                                _dyld_before_fork_dlopen();
    virtual     void                                _dyld_after_fork_dlopen_parent();
    virtual     void                                _dyld_after_fork_dlopen_child();


private:
#if BUILDING_DYLD
                            APIs(const ProcessConfig& c, RuntimeLocks& locks, Allocator* alloc) : RuntimeState(c, locks, *alloc) {}
#else
                            APIs(const ProcessConfig& c, Allocator* alloc) : RuntimeState(c, *alloc) {}
#endif

    // internal helpers
    uint32_t                getSdkVersion(const mach_header* mh);
    dyld_build_version_t    mapFromVersionSet(dyld_build_version_t version);
    uint32_t                linkedDylibVersion(const dyld3::MachOFile* mf, const char* installname);
    uint32_t                deriveVersionFromDylibs(const dyld3::MachOFile* mf);
    void                    forEachPlatform(const dyld3::MachOFile* mf, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version));
    uint32_t                basePlaform(uint32_t reqPlatform) const;
    bool                    findImageMappedAt(const void* addr, const MachOLoaded** ml, bool* neverUnloads = nullptr, const char** path = nullptr, const void** segAddr = nullptr, uint64_t* segSize = nullptr, uint8_t* segPerms = nullptr);
    void                    clearErrorString();
    void                    setErrorString(const char* format, ...) __attribute__((format(printf, 2, 3)));
    const Loader*           findImageContaining(void* addr);
    bool                    flatFindSymbol(const char* symbolName, void** symbolAddress, const mach_header** foundInImageAtLoadAddress);
    bool                    validLoader(const Loader* maybeLoader);
    void                    forEachImageVersion(const mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version));
};
//
// Note: need to force vtable ptr auth so that libdyld.dylib from base OS and driverkit use same ABI
//
class [[clang::ptrauth_vtable_pointer(process_independent, address_discrimination, type_discrimination)]] RuntimeState
{
public:
    const ProcessConfig&            config;
    Allocator&                      longTermAllocator;
    const Loader*                   mainExecutableLoader = nullptr;
    Vector<ConstAuthLoader>         loaded;
    const Loader*                   libSystemLoader      = nullptr;
    const Loader*                   libdyldLoader        = nullptr;
    const void*                     libdyldMissingSymbol = 0;
#if BUILDING_DYLD
    RuntimeLocks&                   _locks;
#endif
    dyld4::ProgramVars*             vars                 = nullptr;
    const LibSystemHelpers*         libSystemHelpers     = nullptr;
    Vector<InterposeTupleAll>       interposingTuplesAll;
    Vector<InterposeTupleSpecific>  interposingTuplesSpecific;
    uint64_t                        weakDefResolveSymbolCount = 0;
    WeakDefMap*                     weakDefMap                = nullptr;

#if BUILDING_DYLD
                                RuntimeState(const ProcessConfig& c, RuntimeLocks& locks, Allocator& alloc)
#else
                                RuntimeState(const ProcessConfig& c, Allocator& alloc = *Allocator::bootstrap())
#endif
                                    : config(c), longTermAllocator(alloc), loaded(&alloc),
#if BUILDING_DYLD
                                    _locks(locks),
#endif
                                    interposingTuplesAll(&alloc), interposingTuplesSpecific(&alloc),
                                    _notifyAddImage(&alloc), _notifyRemoveImage(&alloc),
                                    _notifyLoadImage(&alloc), _notifyBulkLoadImage(&alloc),
                                    _tlvInfos(&alloc), _loadersNeedingDOFUnregistration(&alloc),
                                    _missingFlatLazySymbols(&alloc), _dynamicReferences(&alloc),
                                    _dlopenRefCounts(&alloc), _dynamicNeverUnloads(&alloc) {}

    void                        setMainLoader(const Loader*);
    void                        add(const Loader*);

    MainFunc                    mainFunc()                   { return _driverKitMain; }
    void                        setMainFunc(MainFunc func)   { _driverKitMain = func; }

    void                        setDyldLoader(const Loader* ldr);

    uint8_t*                    appState(uint16_t index);
    uint8_t*                    cachedDylibState(uint16_t index);
    const MachOLoaded*          appLoadAddress(uint16_t index);
    void                        setAppLoadAddress(uint16_t index, const MachOLoaded* ml);
    const MachOLoaded*          cachedDylibLoadAddress(uint16_t index);

    void                        log(const char* format, ...) const __attribute__((format(printf, 2, 3))) ;
    void                        vlog(const char* format, va_list list);

    void                        setObjCNotifiers(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped);
    void                        addNotifyAddFunc(const Loader* callbackLoader, NotifyFunc);
    void                        addNotifyRemoveFunc(const Loader* callbackLoader, NotifyFunc);
    void                        addNotifyLoadImage(const Loader* callbackLoader, LoadNotifyFunc);
    void                        addNotifyBulkLoadImage(const Loader* callbackLoader, BulkLoadNotifier);

    void                        notifyObjCInit(const Loader* ldr);
    void                        buildInterposingTables();

    void                        withNotifiersReadLock(void (^work)());
    void                        withNotifiersWriteLock(void (^work)());

    void                        addPermanentRanges(const Array<const Loader*>& neverUnloadLoaders);
    bool                        inPermanentRange(uintptr_t start, uintptr_t end, uint8_t* perms, const Loader** loader);

    void                        notifyLoad(const dyld3::Array<const Loader*>& newLoaders);
    void                        notifyUnload(const dyld3::Array<const Loader*>& removeLoaders);
    void                        notifyDebuggerLoad(const Loader* oneLoader);
    void                        notifyDebuggerLoad(const dyld3::Array<const Loader*>& newLoaders);
    void                        notifyDebuggerUnload(const dyld3::Array<const Loader*>& removingLoaders);
    void                        notifyDtrace(const dyld3::Array<const Loader*>& newLoaders);

    void                        incDlRefCount(const Loader* ldr);  // used by dlopen
    void                        decDlRefCount(const Loader* ldr);  // used by dlclose

    void                        setLaunchMissingDylib(const char* missingDylibPath, const char* clientUsingDylib);
    void                        setLaunchMissingSymbol(const char* missingSymbolName, const char* dylibThatShouldHaveSymbol, const char* clientUsingSymbol);

    void                        addMissingFlatLazySymbol(const Loader* ldr, const char* symbolName, uintptr_t* bindLoc);
    void                        rebindMissingFlatLazySymbols(const dyld3::Array<const Loader*>& newLoaders);
    void                        removeMissingFlatLazySymbols(const dyld3::Array<const Loader*>& removingLoaders);
    bool                        hasMissingFlatLazySymbols() const;

    void                        addDynamicReference(const Loader* from, const Loader* to);
    void                        removeDynamicDependencies(const Loader* removee);

    void                        setSavedPrebuiltLoaderSet()      { _wrotePrebuiltLoaderSet = true; }
    bool                        didSavePrebuiltLoaderSet() const { return _wrotePrebuiltLoaderSet; }

    void                        setVMAccountingSuspending(bool mode);
    bool                        hasOverriddenCachedDylib() { return _hasOverriddenCachedDylib; }
    void                        setHasOverriddenCachedDylib() { _hasOverriddenCachedDylib = true; }

    pthread_key_t               dlerrorPthreadKey() { return _dlerrorPthreadKey; }

    typedef void  (*TLV_TermFunc)(void* objAddr);

    void                        initialize();
    void                        setUpTLVs(const MachOAnalyzer* ma);
    void                        addTLVTerminationFunc(TLV_TermFunc func, void* objAddr);
    void                        exitTLV();

    void                        withLoadersReadLock(void (^work)());
    void                        withLoadersWriteLock(void (^work)());

    void                        incWritable();
    void                        decWritable();

    void                        initializeClosureMode();
    const PrebuiltLoaderSet*    processPrebuiltLoaderSet() const { return _processPrebuiltLoaderSet; }
    const PrebuiltLoaderSet*    cachedDylibsPrebuiltLoaderSet() const { return _cachedDylibsPrebuiltLoaderSet; }
    uint8_t*                    prebuiltStateArray(bool app) const { return (app ? _processDylibStateArray : _cachedDylibsStateArray); }

    const PrebuiltLoader*       findPrebuiltLoader(const char* loadPath) const;
    bool                        saveAppClosureFile() const { return _saveAppClosureFile; }
    bool                        failIfCouldBuildAppClosureFile() const { return _failIfCouldBuildAppClosureFile; }
    bool                        saveAppPrebuiltLoaderSet(const PrebuiltLoaderSet* pblset) const;
    bool                        inPrebuiltLoader(const void* p, size_t len) const;
#if !BUILDING_DYLD
    void                        resetCachedDylibs(const PrebuiltLoaderSet* dylibs, uint8_t* stateArray);
    void                        setProcessPrebuiltLoaderSet(const PrebuiltLoaderSet* appPBLS);
    void                        resetCachedDylibsArrays();
#endif

    // this need to be virtual to be callable from libdyld.dylb
    virtual void                _finalizeListTLV(void* l);
    virtual void*               _instantiateTLVs(pthread_key_t key);

protected:

    // Helpers to reset locks across fork()
    void                        takeLockBeforeFork();
    void                        releaseLockInForkParent();
    void                        resetLockInForkChild();
    void                        takeDlopenLockBeforeFork();
    void                        releaseDlopenLockInForkParent();
    void                        resetDlopenLockInForkChild();

private:
    //
    // The PermanentRanges structure is used to make dyld_is_memory_immutable()
    // fast and lock free. The table contains just ranges of memory that are in
    // images that will never be unloaded.  Dylibs in the dyld shared cache are
    // never in this table. A PermanentRanges struct is allocated at launch for
    // app and its non-cached dylibs, because they can never be unloaded. Later
    // if a dlopen() brings in non-cached dylibs which can never be unloaded,
    // another PermanentRanges is allocated with the ranges brought in by that
    // dlopen.  The PermanentRanges struct are chained together in a linked list
    // with state._permanentRanges pointing to the start of the list.
    // Because these structs never change, they can be read without taking a lock.
    // That makes finding immutable ranges lock-less.
    //
    class PermanentRanges
    {
    public:
        static PermanentRanges* make(RuntimeState& state, const Array<const Loader*>& neverUnloadLoaders);
        bool                    contains(uintptr_t start, uintptr_t end, uint8_t* perms, const Loader** loader) const;
        PermanentRanges*        next() const;
        void                    append(PermanentRanges*);

    private:
        void                addPermanentRange(uintptr_t start, uintptr_t end, bool immutable, const Loader* loader);
        void                add(const Loader*);

        struct Range
        {
            uintptr_t      start;
            uintptr_t      end;
            const Loader*  loader;
            uintptr_t      permissions;
        };

        // FIXME: we could pack this structure better to reduce memory usage
        std::atomic<PermanentRanges*>   _next       = nullptr;
        uintptr_t                       _rangeCount = 0;
        Range                           _ranges[1];
    };

    // keep dlopen counts in a side table because it is rarely used, so it would waste space for each Loader object to have its own count field
    friend class Reaper;
    friend class RecursiveAutoLock;
    struct DlopenCount {
        const Loader*  loader;
        uintptr_t      refCount;
    };

    // when a thread_local is first accessed on a thread, the thunk calls into dyld
    // to allocate the variables.  The pthread_key is the index used to find the
    // TLV_Info which then describes how much to allocate and how to initalize that memory.
    struct TLV_Info
    {
        const MachOAnalyzer*    ma;
        pthread_key_t           key;
        uint32_t                initialContentOffset;
        uint32_t                initialContentSize;
    };

    // used to record _tlv_atexit() entries to clean up on thread exit
    struct TLV_Terminator
    {
        TLV_TermFunc  termFunc;
        void*         objAddr;
    };

    struct TLV_TerminatorList {
        TLV_TerminatorList* next  = nullptr;
        uintptr_t           count = 0;
        TLV_Terminator      elements[7];

        void                reverseWalkChain(void (^work)(TLV_TerminatorList*));
    };

    struct RegisteredDOF
    {
        const Loader*   ldr;
        int             registrationID;
    };

    struct MissingFlatSymbol
    {
        const Loader*   ldr;
        const char*     symbolName;
        uintptr_t*      bindLoc;
    };

    struct DynamicReference
    {
        const Loader*   from;
        const Loader*   to;
    };

    struct HiddenCacheAddr { const void* cacheAddr; const void* replacementAddr; };

    enum { kMaxBootTokenSize = 128 };

    void                        appendInterposingTuples(const Loader* ldr, const uint8_t* dylibTuples, uint32_t tupleCount);
    void                        garbageCollectImages();
    void                        garbageCollectInner();
    void                        removeLoaders(const dyld3::Array<const Loader*>& loadersToRemove);
    void                        withTLVLock(void (^work)());
    void                        setUpLogging();
    void                        buildAppPrebuiltLoaderSetPath(bool createDirs);
    bool                        fileAlreadyHasBootToken(const char* path, const Array<uint8_t>& bootToken) const;
    bool                        buildBootToken(dyld3::Array<uint8_t>& bootToken) const;
    void                        loadAppPrebuiltLoaderSet();
    bool                        allowOsProgramsToSaveUpdatedClosures() const;
    bool                        allowNonOsProgramsToSaveUpdatedClosures() const;
    void                        allocateProcessArrays(uintptr_t count);
    void                        checkHiddenCacheAddr(const Loader* t, const void* targetAddr, const char* symbolName, dyld3::OverflowSafeArray<HiddenCacheAddr>& hiddenCacheAddrs) const;

    
    _dyld_objc_notify_mapped        _notifyObjCMapped       = nullptr;
    _dyld_objc_notify_init          _notifyObjCInit         = nullptr;
    _dyld_objc_notify_unmapped      _notifyObjCUnmapped     = nullptr;
    Vector<NotifyFunc>              _notifyAddImage;
    Vector<NotifyFunc>              _notifyRemoveImage;
    Vector<LoadNotifyFunc>          _notifyLoadImage;
    Vector<BulkLoadNotifier>        _notifyBulkLoadImage;
    Vector<TLV_Info>                _tlvInfos;
    Vector<RegisteredDOF>           _loadersNeedingDOFUnregistration;
    Vector<MissingFlatSymbol>       _missingFlatLazySymbols;
    Vector<DynamicReference>        _dynamicReferences;
    const PrebuiltLoaderSet*        _cachedDylibsPrebuiltLoaderSet  = nullptr;
    uint8_t*                        _cachedDylibsStateArray         = nullptr;
    const char*                     _processPrebuiltLoaderSetPath   = nullptr;
    const PrebuiltLoaderSet*        _processPrebuiltLoaderSet       = nullptr;
    uint8_t*                        _processDylibStateArray         = nullptr;
    const MachOLoaded**             _processLoadedAddressArray      = nullptr;
    bool                            _saveAppClosureFile;
    bool                            _failIfCouldBuildAppClosureFile;
    PermanentRanges*                _permanentRanges          = nullptr;
    MainFunc                        _driverKitMain            = nullptr;
    Vector<DlopenCount>             _dlopenRefCounts;
    Vector<const Loader*>           _dynamicNeverUnloads;
    std::atomic<int32_t>            _gcCount                  = 0;
    pthread_key_t                   _tlvTerminatorsKey        = 0;
    pthread_key_t                   _dlerrorPthreadKey        = 0;
    int                             _logDescriptor            = -1;
    bool                            _logToSyslog              = false;
    bool                            _logSetUp                 = false;
    bool                            _hasOverriddenCachedDylib = false;
    bool                            _wrotePrebuiltLoaderSet   = false;
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
    bool                            _vmAccountingSuspended    = false;
#endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
};
  • 加載程序的所有依賴項(xiàng)并將state綁定在一起:prepare(state, dyldMA);
  • 調(diào)用main(): appMain
2.prepare:加載程序的所有依賴項(xiàng)
  • gProcessInfo 是存儲(chǔ)dyld所有鏡像信息的結(jié)構(gòu)體:,保存著mach_header, dyld_uuid_info, dyldVersion等等信息蹦狂。

gProcessInfo的聲明:
struct dyld_all_image_infos* gProcessInfo = &dyld_all_image_infos;

struct dyld_all_image_infos {
    uint32_t                        version;        /* 1 in Mac OS X 10.4 and 10.5 */
    uint32_t                        infoArrayCount;
#if defined(__cplusplus) && (BUILDING_LIBDYLD || BUILDING_DYLD)
    std::atomic<const struct dyld_image_info*>  infoArray;
#else
    const struct dyld_image_info*    infoArray;
#endif
    dyld_image_notifier             notification;       
    bool                            processDetachedFromSharedRegion;
    /* the following fields are only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later */
    bool                            libSystemInitialized;
    const struct mach_header*       dyldImageLoadAddress;
    /* the following field is only in version 3 (Mac OS X 10.6, iPhoneOS 3.0) and later */
    void*                           jitInfo;
    /* the following fields are only in version 5 (Mac OS X 10.6, iPhoneOS 3.0) and later */
    const char*                     dyldVersion;
    const char*                     errorMessage;
    uintptr_t                       terminationFlags;
    /* the following field is only in version 6 (Mac OS X 10.6, iPhoneOS 3.1) and later */
    void*                           coreSymbolicationShmPage;
    /* the following field is only in version 7 (Mac OS X 10.6, iPhoneOS 3.1) and later */
    uintptr_t                       systemOrderFlag;
    /* the following field is only in version 8 (Mac OS X 10.7, iPhoneOS 3.1) and later */
    uintptr_t                       uuidArrayCount;
    const struct dyld_uuid_info*    uuidArray;      /* only images not in dyld shared cache */
    /* the following field is only in version 9 (Mac OS X 10.7, iOS 4.0) and later */
    struct dyld_all_image_infos*    dyldAllImageInfosAddress;
    /* the following field is only in version 10 (Mac OS X 10.7, iOS 4.2) and later */
    uintptr_t                       initialImageCount;
    /* the following field is only in version 11 (Mac OS X 10.7, iOS 4.2) and later */
    uintptr_t                       errorKind;
    const char*                     errorClientOfDylibPath;
    const char*                     errorTargetDylibPath;
    const char*                     errorSymbol;
    /* the following field is only in version 12 (Mac OS X 10.7, iOS 4.3) and later */
    uintptr_t                       sharedCacheSlide;
    /* the following field is only in version 13 (Mac OS X 10.9, iOS 7.0) and later */
    uint8_t                         sharedCacheUUID[16];
    /* the following field is only in version 15 (macOS 10.12, iOS 10.0) and later */
    uintptr_t                       sharedCacheBaseAddress;
#if defined(__cplusplus) && (BUILDING_LIBDYLD || BUILDING_DYLD)
    // We want this to be atomic in libdyld so that we can see updates when we map it shared
    std::atomic<uint64_t>           infoArrayChangeTimestamp;
#else
    uint64_t                        infoArrayChangeTimestamp;
#endif
    const char*                     dyldPath;
    mach_port_t                     notifyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
#if __LP64__
    uintptr_t                       reserved[11-(DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT/2)];
#else
    uintptr_t                       reserved[9-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
#endif
    // The following fields were added in version 18 (previously they were reserved padding fields)
    uint64_t                        sharedCacheFSID;
    uint64_t                        sharedCacheFSObjID;
    /* the following field is only in version 16 (macOS 10.13, iOS 11.0) and later */
    uintptr_t                       compact_dyld_image_info_addr;
    size_t                          compact_dyld_image_info_size;
    uint32_t                        platform; // FIXME: really a dyld_platform_t, but those aren't exposed here.

    /* the following field is only in version 17 (macOS 10.16) and later */
    uint32_t                          aotInfoCount;
    const struct dyld_aot_image_info* aotInfoArray;
    uint64_t                          aotInfoArrayChangeTimestamp;
    uintptr_t                         aotSharedCacheBaseAddress;
    uint8_t                           aotSharedCacheUUID[16];
};
  • 進(jìn)行pre-build, 創(chuàng)建 mainLoader鏡像裝載器
    ps : 如果熟悉dyld3的小伙伴知道, 舊版本是創(chuàng)建一個(gè)ImageLoader鏡像裝載器
mainLoader
  • 創(chuàng)建just-in-time
    dyld4一個(gè)新特性, dyld4在保留了dyld3mach-o 解析器基礎(chǔ)上誓篱,同時(shí)也引入了 just-in-time 的加載器來(lái)優(yōu)化朋贬。

dyld3 出于對(duì)啟動(dòng)速度的優(yōu)化的目的, 增加了預(yù)構(gòu)建(閉包)。App第一次啟動(dòng)或者App發(fā)生變化時(shí)會(huì)將部分啟動(dòng)數(shù)據(jù)創(chuàng)建為閉包存到本地窜骄,那么App下次啟動(dòng)將不再重新解析數(shù)據(jù)锦募,而是直接讀取閉包內(nèi)容。當(dāng)然前提是應(yīng)用程序和系統(tǒng)應(yīng)很少發(fā)生變化邻遏,但如果這兩者經(jīng)常變化等, 就會(huì)導(dǎo)閉包丟失或失效糠亩。

dyld4 采用了 pre-build + just-in-time 的雙解析模式,預(yù)構(gòu)建 pre-build 對(duì)應(yīng)的就是 dyld3 中的閉包准验,just-in-time 可以理解為實(shí)時(shí)解析削解。當(dāng)然just-in-time 也是可以利用 pre-build 的緩存的,所以性能可控沟娱。有了just-in-time, 目前應(yīng)用首次啟動(dòng)团搞、系統(tǒng)版本更新同蜻、普通啟動(dòng)逝变,dyld4 則可以根據(jù)緩存是否有效去選擇合適的模式進(jìn)行解析达皿。

  • 裝載uuid腾供、動(dòng)態(tài)庫(kù)疹启、可執(zhí)行文件

記錄插入信息, 遍歷所有dylibs, 一些記錄檢查操作繼續(xù)往下走

  • 插入緩存

Loader::applyInterposingToDyldCache的實(shí)現(xiàn):

void Loader::applyInterposingToDyldCache(RuntimeState& state)
{
    const DyldSharedCache* dyldCache = state.config.dyldCache.addr;
    if ( dyldCache == nullptr )
        return; // no dyld cache to interpose
    if ( state.interposingTuplesAll.empty() )
        return; // no interposing tuples

    // make the cache writable for this block
    DyldCacheDataConstScopedWriter patcher(state);

    state.setVMAccountingSuspending(true);
    for ( const InterposeTupleAll& tuple : state.interposingTuplesAll ) {
        uint32_t imageIndex;
        uintptr_t cacheOffsetOfReplacee = tuple.replacee - (uintptr_t)dyldCache;
        if ( !dyldCache->addressInText(cacheOffsetOfReplacee, &imageIndex) )
            continue;

        // Convert from a cache offset to an image offset
        uint64_t mTime;
        uint64_t inode;
        const dyld3::MachOAnalyzer* imageMA = (dyld3::MachOAnalyzer*)(dyldCache->getIndexedImageEntry(imageIndex, mTime, inode));
        if ( imageMA == nullptr )
            continue;

        uint32_t dylibOffsetOfReplacee = (uint32_t)((dyldCache->unslidLoadAddress() + cacheOffsetOfReplacee) - imageMA->preferredLoadAddress());

        dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t dylibVMOffsetOfImpl, const char* exportName) {
            // Skip patching anything other than this symbol
            if ( dylibVMOffsetOfImpl != dylibOffsetOfReplacee )
                return;
            uintptr_t newLoc = tuple.replacement;
            dyldCache->forEachPatchableUseOfExport(imageIndex, dylibVMOffsetOfImpl,
                                                   ^(uint64_t cacheVMOffset, MachOLoaded::PointerMetaData pmd, uint64_t addend) {
                uintptr_t* loc      = (uintptr_t*)((uintptr_t)dyldCache + cacheVMOffset);
                uintptr_t  newValue = newLoc + (uintptr_t)addend;
    #if __has_feature(ptrauth_calls)
                if ( pmd.authenticated ) {
                    newValue = dyld3::MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(newValue, loc, pmd.usesAddrDiversity, pmd.diversity, pmd.key);
                    *loc     = newValue;
                    if ( state.config.log.interposing )
                        state.log("interpose: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
                                  loc, (void*)newValue, pmd.diversity, pmd.usesAddrDiversity, MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::keyName(pmd.key));
                    return;
                }
    #endif
                if ( state.config.log.interposing )
                    state.log("interpose: *%p = 0x%0llX (dyld cache patch) to %s\n", loc, newLoc + addend, exportName);
                *loc = newValue;
            });
        });
    }
    state.setVMAccountingSuspending(false);
}

接下來(lái)的工作是一些其他通知和寫入操作磅网。

  • 運(yùn)行初始化方法
    // 運(yùn)行所有的初始化
    state.runAllInitializersForMain();
3.runAllInitializersForMain:運(yùn)行初始化方法
// 這是從dyldMain.cpp中提取出來(lái)的虱岂,以支持使用crt1.o的舊macOS應(yīng)用程序
void APIs::runAllInitializersForMain()
{
    // 首先運(yùn)行l(wèi)ibSystem的初始化器
    const_cast<Loader*>(this->libSystemLoader)->beginInitializers(*this);
    this->libSystemLoader->runInitializers(*this);
    gProcessInfo->libSystemInitialized = true;

    // 在運(yùn)行l(wèi)ibSystem的初始化器后唉铜,告訴objc在libSystem的子dylibs上運(yùn)行任何+load方法
    this->notifyObjCInit(this->libSystemLoader);
    // <rdar://problem/32209809> 調(diào)用'init'函數(shù)對(duì)所有已經(jīng)init'ed的圖像 (below libSystem)
    // 使用下標(biāo)進(jìn)行迭代台舱,以便在+加載dloopen時(shí)數(shù)組不會(huì)在我們下面增長(zhǎng)
    for ( uint32_t i = 0; i != this->loaded.size(); ++i ) {
        const Loader* ldr = this->loaded[i];
        if ( ldr->analyzer(*this)->isDylib() && (strncmp(ldr->analyzer(*this)->installName(), "/usr/lib/system/lib", 19) == 0) ) {
            // 檢查安裝名稱而不是路徑,以處理libsystem子dylibs的DYLD_LIBRARY_PATH覆蓋
            const_cast<Loader*>(ldr)->beginInitializers(*this);
            this->notifyObjCInit(ldr);
        }
    }

    // 自底向上運(yùn)行所有其他初始化器潭流,首先運(yùn)行插入的dylib初始化器
    // 使用下標(biāo)進(jìn)行迭代竞惋,以便在初始化式dloopen時(shí)數(shù)組不會(huì)在下面增長(zhǎng)
    for ( uint32_t i = 0; i != this->loaded.size(); ++i ) {
        const Loader* ldr = this->loaded[i];
        ldr->runInitializersBottomUpPlusUpwardLinks(*this);
        // stop as soon as we did main executable
        // normally this is first image, but if there are inserted N dylibs, it is Nth in the list
        if ( ldr->analyzer(*this)->isMainExecutable() )
            break;
    }
}
  • 運(yùn)行libSystem的初始化器
  • 告訴objclibSystem的子dylibs上運(yùn)行所有的+load方法
    (可執(zhí)行文件、動(dòng)態(tài)庫(kù)等等上面的load)

notifyObjCInit 這個(gè)工作在第4部分介紹

  • link 動(dòng)態(tài)庫(kù)和主程序: runInitializersBottomUpPlusUpwardLinks

runInitializersBottomUpPlusUpwardLinks 這個(gè)工作在第5部分介紹

4.notifyObjCInit

libSystem的初始化時(shí)灰嫉,在其子dylibs上需要通知objc運(yùn)行所有的+load方法

notifyObjCInit的定義是在RuntimeState類的聲明里:

_dyld_objc_notify_initsetObjCNotifiers被賦值的:

setObjCNotifiers是在_dyld_objc_notify_register被調(diào)用的

image.png

_dyld_objc_notify_register是在objc4源碼里的 objc_init()里被調(diào)用的

最后objc_init()在庫(kù)初始化時(shí)間之前由libSystem調(diào)用拆宛。

ps: load_images的內(nèi)容在下章節(jié),知道它是用來(lái)進(jìn)行加載和調(diào)用+load方法就夠了讼撒。

5.link 動(dòng)態(tài)庫(kù)和主程序: runInitializersBottomUpPlusUpwardLinks
void Loader::runInitializersBottomUpPlusUpwardLinks(RuntimeState& state) const
{
    //state.log("runInitializersBottomUpPlusUpwardLinks() %s\n", this->path());
    state.incWritable();

    // 遞歸地運(yùn)行所有初始化器 initializers
    STACK_ALLOC_ARRAY(const Loader*, danglingUpwards, state.loaded.size());
    this->runInitializersBottomUp(state, danglingUpwards);

    //state.log("runInitializersBottomUpPlusUpwardLinks(%s), found %d dangling upwards\n", this->path(), danglingUpwards.count());

    // 返回所有向上鏈接的image浑厚,并檢查它們是否已初始化 (might be danglers)
    STACK_ALLOC_ARRAY(const Loader*, extraDanglingUpwards, state.loaded.size());
    for ( const Loader* ldr : danglingUpwards ) {
        //state.log("running initializers for dangling upward link %s\n", ldr->path());
        ldr->runInitializersBottomUp(state, extraDanglingUpwards);
    }
    if ( !extraDanglingUpwards.empty() ) {
        // 如果有兩個(gè)向上懸空的image,請(qǐng)?jiān)俅螜z查初始化器initializers
        danglingUpwards.resize(0);
        for ( const Loader* ldr : extraDanglingUpwards ) {
            //state.log("running initializers for dangling upward link %s\n", ldr->path());
            ldr->runInitializersBottomUp(state, danglingUpwards);
        }
    }

    state.decWritable();
}

runInitializersBottomUp

void Loader::runInitializersBottomUp(RuntimeState& state, Array<const Loader*>& danglingUpwards) const
{
    // 如果已經(jīng)初始化器已經(jīng)運(yùn)行就什么都不做
    if ( (const_cast<Loader*>(this))->beginInitializers(state) )
        return;

    //state.log("runInitializersBottomUp(%s)\n", this->path());

    // 在運(yùn)行我的初始化器之前根盒,確保這個(gè)image下面的所有東西都已初始化
    const uint32_t depCount = this->dependentCount();
    for ( uint32_t i = 0; i < depCount; ++i ) {
        DependentKind childKind;
        if ( Loader* child = this->dependent(state, i, &childKind) ) {
            if ( childKind == DependentKind::upward ) {
                // 向上添加到列表稍后處理
                if ( !danglingUpwards.contains(child) )
                    danglingUpwards.push_back(child);
            }
            else {
                child->runInitializersBottomUp(state, danglingUpwards);
            }
        }
    }

    // tell objc to run any +load methods in this image (done before C++ initializers)
    state.notifyObjCInit(this);

    // run initializers for this image
    this->runInitializers(state);
}
6.調(diào)起主程序入口

prepare 函數(shù)調(diào)用最后返回的就是主程序入口 MainFunc

prepare最后返回主程序入口

start函數(shù)里調(diào)用了主程序入口

最后給個(gè)總結(jié)圖

dyld4應(yīng)用程序加載流程圖

dyld4應(yīng)用程序加載流程圖

dyld3應(yīng)用程序加載流程圖:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钳幅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子炎滞,更是在濱河造成了極大的恐慌敢艰,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厂榛,死亡現(xiàn)場(chǎng)離奇詭異盖矫,居然都是意外死亡丽惭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門辈双,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)责掏,“玉大人,你說我怎么就攤上這事湃望』怀模” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵证芭,是天一觀的道長(zhǎng)瞳浦。 經(jīng)常有香客問我,道長(zhǎng)废士,這世上最難降的妖魔是什么叫潦? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮官硝,結(jié)果婚禮上矗蕊,老公的妹妹穿的比我還像新娘。我一直安慰自己氢架,他們只是感情好傻咖,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著岖研,像睡著了一般卿操。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上孙援,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天害淤,我揣著相機(jī)與錄音,去河邊找鬼拓售。 笑死筝家,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的邻辉。 我是一名探鬼主播溪王,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼值骇!你這毒婦竟也來(lái)了莹菱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吱瘩,失蹤者是張志新(化名)和其女友劉穎道伟,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜜徽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年祝懂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拘鞋。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡砚蓬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盆色,到底是詐尸還是另有隱情灰蛙,我是刑警寧澤,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布隔躲,位于F島的核電站摩梧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏宣旱。R本人自食惡果不足惜仅父,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浑吟。 院中可真熱鬧驾霜,春花似錦、人聲如沸买置。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)忿项。三九已至,卻和暖如春城舞,著一層夾襖步出監(jiān)牢的瞬間轩触,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工家夺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脱柱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓拉馋,卻偏偏與公主長(zhǎng)得像榨为,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子煌茴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359