iOS 分析dyld工作过程 dyld-941.5源码

dyld源码
苹果官方资源opensource
objc4-838可编译联调源码

一、了解相关概念

1.静态库、动态库

通常程序都会依赖系统一些库, 库是什么呢? 其实库就是一些可执行的二进制文件, 能被操作系统加载到内存里面中。库分为两种:静态库 / 动态库

  • 静态库:是一堆.o文件的集合。格式.a, .lib等。链接阶段时静态库会被完整地复制, 一起打包在可执行文件中,被多次使用就有多份冗余拷贝。
    • 优点: 编译完成之后, 链接到目标程序中, 同时打包到可执行文件里面, 不会有外部依赖。
    • 缺点: 静态库会有两份, 所以会导致目标程序体积增大, 对内存, 性能, 速度消耗很大。并且相同静态库每个app中都会拷贝一份。
  • 动态库:是一个已经完全链接后的镜像。格式.framework等。程序编译时并不会链接到目标程序中,目标程序只会存储指向动态库的引用,在程序运行时才被载入。苹果大部分都是动态库。
    • 优点: 不需要拷贝到目标程序, 减少App包的体积;多个App可以使用同一个动态库, 共享内存, 节约资源;由于运行时才会去加载, 那么可以在App不使用时随时对库进行替换或更新, 更新灵活。
    • 缺点: 动态载入会带来一部分性能损失, 同时动态库也会使得程序依赖于外部环境。一旦动态库没有或消失, 程序会出现问题。
1.Xcode编译command + B产生可执行文件的过程:
  • a.预编译
    处理源代码文件中以#开头的预编译的命令(#import/#include等等);把包含的文件插入到指定的位置;替换代码中的宏定义;删除代码里的注释等等。产生.i文件。
  • b.编译
    词法分析;语法分析;语义分析;生成相应的汇编代码文件。 产生.s文件(汇编文件)
  • c.汇编
    将汇编代码文件翻译成机器指令,机器指令会生成.o文件。
  • d.链接
    把之前所有操作的文件链接到程序里面来, 对.o文件中引用其他库的地方进行引用, 生成最后的 可执行文件。动态库与静态库区别其实就是链接的区别。
    • 静态链接: 链接器对.o文件进行符号收集、解析和重定位;把所有的.o粘合在一起从而形成可执行文件。(在静态链接之后就不会再有静态库了)
    • 动态链接: dyld动态链接(下面有介绍)
2.点击App启动的过程:
  • a.系统调用 exec() 分配内存、开辟进程
  • b.app对应的可执行文件加载到内存
  • c.dyld动态链接器加载到内存:load dyld
  • d.dyld动态链接器进行动态链接:rebase->bing->Objc->initalizers
  • e.调起main()函数

启动时间的划分可以把main()函数作为关键点分割成两块

main()函数启动分块.png

t1阶段main()之前的处理所需时间,称为pre-main

t1阶段.png

t2阶段main()main()之后处理所需时间
t2阶段耗时的主要是业务代码

3.Mach-O是什么?

Mach-O: Mach Object文件格式的缩写,它是一种用于可执行文件,目标文件.o,动态库,内核转储的文件格式。作为a.out格式的替代,Mach-O提供了更强的扩展性,并提升了符号表中信息的访问速度。

三种Mach-O格式的文件:可执行文件dylib动态库Bundle资源文件包

Mach-O由四部分组成:Mach-O头部Load CommandsectionOther Data,可以通过MachOView可查看可执行文件信息。

二、什么是dyld

本文主要介绍dyld源码执行流程。
app启动加载过程中类和分类加载都不可避免的触及dyld,所以了解dyld源码可以让我们更好的理解app的工作原理。

什么是dyld?
dyld(the dynamic link editor 动态链接器):是苹果操作系统的一个重要的组成部分。在iOS/Mac OSX系统中,仅有很少量的进程只需要内核就能完成加载,基本上所有的进程都是动态链接的,所以Mach-O镜像文件中会有很多对外部的库和符号的引用,但是这些引用并不能直接用,在启动时还必须要通过这些引用进行内容的填补,这个填补工作就是由动态链接器dyld来完成的,也就是符号绑定。

在app被编译打包成可执行文件格式的Mach-O文件后,在程序启动时交由dyld负责链接加载程序。

dyld是开源的,可以通过苹果官网下载它的dyld源码,本文的对应的版本是941.5。dyld4是iOS15发布的。

  • dyld3: 相比dyld2新增预构建/闭包, 目的是将一些启动数据创建为闭包存到本地,下次启动将不再重新解析数据,而是直接读取闭包内容
  • dyld4: 采用pre-build + just-in-time 预构建/闭包+实时解析的双解析模式, 将根据缓存有效与否选择合适的模式进行解析, 同时也处理了闭包失效时候需要重建闭包的性能问题。

三、dyld工作流程

dyld流程

新建一个工程,在ViewController.m里重写+(void)load方法,在load方法上打个断点调试,运行app后在断点处输出其函数调用的堆栈:

dyld4模拟器 入口
dyld4真机 入口

新版本dyld: start开始接下来走 dyld4中prepare方法, 源码入口需要在start中开始探索。
(旧版本dyld: _dyld_start开始, 接下来走dyldbootstrap, 源码入口需要在dyld_start开始。)

dyld start汇编
1.start函数是dyld的开始
// dyld的入口点。内核加载dyld并跳转到__dyld_start,它设置一些寄存器并调用这个函数。
// 注意:这个函数永远不会返回,它调用exit()。因此,堆栈保护程序是无用的,因为永远不会执行epilog。标记函数no-return禁用堆栈保护。堆栈保护器也会导致armv7k代码生成的问题,因为它通过prolog中的GOT槽访问随机值,但dyld还没有rebased。
void start(const KernelArgs* kernArgs) __attribute__((noreturn)) __asm("start");
void start(const KernelArgs* kernArgs)
{
    // 发出kdebug跟踪点来指示dyld引导程序已经启动 <rdar://46878536>
    // 注意:这是在dyld rebased之前调用的,所以kdebug_trace_dyld_marker()不能使用任何全局变量
    dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);

    // 走所有的fixups chains 和 rebase dyld
    // 注意:withChainStarts()和fixupAllChainedFixups()不能使用任何静态数据指针,因为它们还没有rebased
    const MachOAnalyzer* dyldMA = getDyldMH(); // 获取Mach-O分析器
    assert(dyldMA->hasChainedFixups());
    uintptr_t           slide = (long)dyldMA; // 所有基于修复链的图像的基址为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();

    // 现在,我们可以调用使用DATA的函数
    mach_init(); // mach消息初始化

    // 为stack canary设置随机值
    __guard_setup(kernArgs->findApple()); // 栈溢出保护

    // 设置以便open_with_subsystem()工作
    _subsystem_init(kernArgs->findApple());

    // 在__DATA_CONST中将ProcessConfig对象变为只读之前,使用placement new来构造它
    ProcessConfig& config = *new ((ProcessConfig*)sConfigBuffer) ProcessConfig(kernArgs, sSyscallDelegate);

    // 使__DATA_CONST只读(内核映射为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

    // 创建Allocator和APIs/RuntimeState对象
    APIs& state = APIs::bootstrap(config, sLocks);

    // 加载程序的所有依赖项并将它们绑定在一起
    MainFunc appMain = prepare(state, dyldMA);

    // 现在使所有的dyld分配的数据结构只读
    state.decWritable();

    // 调用main(),如果它返回,调用exit()并返回结果
    // 注意:这是经过组织的,以便在程序的主线程中回溯时只在“main”下面显示“start”。
    int result = appMain(state.config.process.argc, state.config.process.argv, state.config.process.envp, state.config.process.apple);

    // 如果我们到达这里,main()返回(而不是程序调用exit())
#if TARGET_OS_OSX
    // <rdar://74518676> libSystemHelpers不是为模拟器设置的,因此直接调用_exit()
    if ( MachOFile::isSimulatorPlatform(state.config.process.platform) )
        _exit(result);
#endif
    state.libSystemHelpers->exit(result);
}
  • 走所有的 fixups chainsrebase dyld
  • mach消息初始化:mach_init()
  • 栈溢出保护:__guard_setup(kernArgs->findApple());
  • 构建进程配置: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;
};
  • 创建Allocator和APIs/RuntimeState对象: 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
};
  • 加载程序的所有依赖项并将state绑定在一起:prepare(state, dyldMA);
  • 调用main(): appMain
2.prepare:加载程序的所有依赖项
  • gProcessInfo 是存储dyld所有镜像信息的结构体:,保存着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];
};
  • 进行pre-build, 创建 mainLoader镜像装载器
    ps : 如果熟悉dyld3的小伙伴知道, 旧版本是创建一个ImageLoader镜像装载器
mainLoader
  • 创建just-in-time
    dyld4一个新特性, dyld4在保留了dyld3mach-o 解析器基础上,同时也引入了 just-in-time 的加载器来优化。

dyld3 出于对启动速度的优化的目的, 增加了预构建(闭包)。App第一次启动或者App发生变化时会将部分启动数据创建为闭包存到本地,那么App下次启动将不再重新解析数据,而是直接读取闭包内容。当然前提是应用程序和系统应很少发生变化,但如果这两者经常变化等, 就会导闭包丢失或失效。

dyld4 采用了 pre-build + just-in-time 的双解析模式,预构建 pre-build 对应的就是 dyld3 中的闭包just-in-time 可以理解为实时解析。当然just-in-time 也是可以利用 pre-build 的缓存的,所以性能可控。有了just-in-time, 目前应用首次启动、系统版本更新、普通启动,dyld4 则可以根据缓存是否有效去选择合适的模式进行解析。

  • 装载uuid、动态库、可执行文件

记录插入信息, 遍历所有dylibs, 一些记录检查操作继续往下走

  • 插入缓存

Loader::applyInterposingToDyldCache的实现:

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

接下来的工作是一些其他通知和写入操作。

  • 运行初始化方法
    // 运行所有的初始化
    state.runAllInitializersForMain();
3.runAllInitializersForMain:运行初始化方法
// 这是从dyldMain.cpp中提取出来的,以支持使用crt1.o的旧macOS应用程序
void APIs::runAllInitializersForMain()
{
    // 首先运行libSystem的初始化器
    const_cast<Loader*>(this->libSystemLoader)->beginInitializers(*this);
    this->libSystemLoader->runInitializers(*this);
    gProcessInfo->libSystemInitialized = true;

    // 在运行libSystem的初始化器后,告诉objc在libSystem的子dylibs上运行任何+load方法
    this->notifyObjCInit(this->libSystemLoader);
    // <rdar://problem/32209809> 调用'init'函数对所有已经init'ed的图像 (below libSystem)
    // 使用下标进行迭代,以便在+加载dloopen时数组不会在我们下面增长
    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);
        }
    }

    // 自底向上运行所有其他初始化器,首先运行插入的dylib初始化器
    // 使用下标进行迭代,以便在初始化式dloopen时数组不会在下面增长
    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;
    }
}
  • 运行libSystem的初始化器
  • 告诉objclibSystem的子dylibs上运行所有的+load方法
    (可执行文件、动态库等等上面的load)

notifyObjCInit 这个工作在第4部分介绍

  • link 动态库和主程序: runInitializersBottomUpPlusUpwardLinks

runInitializersBottomUpPlusUpwardLinks 这个工作在第5部分介绍

4.notifyObjCInit

libSystem的初始化时,在其子dylibs上需要通知objc运行所有的+load方法

notifyObjCInit的定义是在RuntimeState类的声明里:

_dyld_objc_notify_initsetObjCNotifiers被赋值的:

setObjCNotifiers是在_dyld_objc_notify_register被调用的

image.png

_dyld_objc_notify_register是在objc4源码里的 objc_init()里被调用的

最后objc_init()在库初始化时间之前由libSystem调用。

ps: load_images的内容在下章节,知道它是用来进行加载和调用+load方法就够了。

5.link 动态库和主程序: runInitializersBottomUpPlusUpwardLinks
void Loader::runInitializersBottomUpPlusUpwardLinks(RuntimeState& state) const
{
    //state.log("runInitializersBottomUpPlusUpwardLinks() %s\n", this->path());
    state.incWritable();

    // 递归地运行所有初始化器 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() ) {
        // 如果有两个向上悬空的image,请再次检查初始化器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
{
    // 如果已经初始化器已经运行就什么都不做
    if ( (const_cast<Loader*>(this))->beginInitializers(state) )
        return;

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

    // 在运行我的初始化器之前,确保这个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.调起主程序入口

prepare 函数调用最后返回的就是主程序入口 MainFunc

prepare最后返回主程序入口

start函数里调用了主程序入口

最后给个总结图

dyld4应用程序加载流程图

dyld4应用程序加载流程图

dyld3应用程序加载流程图:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271