iOS逆向-14:dyld

dyld

动态链接器:加载所有的库和可执行文件

这里主要分析一下最新的dyld-832.7.3版本
首先看一下+load之前都调用了哪些

image.png

image.png

start内部bl dyldbootstrap:start ,我们在源码中搜一下

uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[],
                const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue)
{

    // Emit kdebug tracepoint to indicate dyld bootstrap has started 
    //告诉debugserver开始启动
    dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);

    // if kernel had to slide dyld, we need to fix up load sensitive locations
    // we have to do this before using any global variables
    // 重定位dyld,在进程空间里面,虚拟内存都要重定位
    rebaseDyld(dyldsMachHeader);

    // kernel sets up env pointer to be just past end of agv array
    const char** envp = &argv[argc+1];
    
    // kernel sets up apple pointer to be just past end of envp array
    const char** apple = envp;
    while(*apple != NULL) { ++apple; }
    ++apple;

    // set up random value for stack canary
    //栈溢出保护
    __guard_setup(apple);

#if DYLD_INITIALIZER_SUPPORT
    // run all C++ initializers inside dyld
    runDyldInitializers(argc, argv, envp, apple);
#endif
//初始化dyld
    _subsystem_init(apple);

    // now that we are done bootstrapping dyld, call dyld's main
    uintptr_t appsSlide = appsMachHeader->getSlide();
    //核心
    return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);

看看main里面做了什么,内容比较多,我这里挑重点来看

uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, 
        int argc, const char* argv[], const char* envp[], const char* apple[], 
        uintptr_t* startGlue)
{
    //内核检测代码
    if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
        launchTraceID = dyld3::kdebug_trace_dyld_duration_start(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, (uint64_t)mainExecutableMH, 0, 0);
    }

    //Check and see if there are any kernel flags
    dyld3::BootArgs::setFlags(hexToUInt64(_simple_getenv(apple, "dyld_flags"), nullptr));

#if __has_feature(ptrauth_calls)
    // Check and see if kernel disabled JOP pointer signing (which lets us load plain arm64 binaries)
    if ( const char* disableStr = _simple_getenv(apple, "ptrauth_disabled") ) {
        if ( strcmp(disableStr, "1") == 0 )
            sKeysDisabled = true;
    }
    else {
        // needed until kernel passes ptrauth_disabled for arm64 main executables
        if ( (mainExecutableMH->cpusubtype == CPU_SUBTYPE_ARM64_V8) || (mainExecutableMH->cpusubtype == CPU_SUBTYPE_ARM64_ALL) )
            sKeysDisabled = true;
    }
#endif
    
    // Grab the cdHash of the main executable from the environment
    //主程序的一些配置信息
    uint8_t mainExecutableCDHashBuffer[20];
    const uint8_t* mainExecutableCDHash = nullptr;
    if ( const char* mainExeCdHashStr = _simple_getenv(apple, "executable_cdhash") ) {
        unsigned bufferLenUsed;
        if ( hexStringToBytes(mainExeCdHashStr, mainExecutableCDHashBuffer, sizeof(mainExecutableCDHashBuffer), bufferLenUsed) )
            mainExecutableCDHash = mainExecutableCDHashBuffer;
    }
//配置一些信息
    getHostInfo(mainExecutableMH, mainExecutableSlide);

#if !TARGET_OS_SIMULATOR
    // Trace dyld's load
    notifyKernelAboutImage((macho_header*)&__dso_handle, _simple_getenv(apple, "dyld_file"));
    // Trace the main executable's load
    notifyKernelAboutImage(mainExecutableMH, _simple_getenv(apple, "executable_file"));
#endif

    uintptr_t result = 0;
    //主程序的header , ALSR
    sMainExecutableMachHeader = mainExecutableMH;
    sMainExecutableSlide = mainExecutableSlide;


    // Set the platform ID in the all image infos so debuggers can tell the process type
    // FIXME: This can all be removed once we make the kernel handle it in rdar://43369446
    // The host may not have the platform field in its struct, but there's space for it in the padding, so always set it
    {
        __block bool platformFound = false;
        ((dyld3::MachOFile*)mainExecutableMH)->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
            if (platformFound) {
                halt("MH_EXECUTE binaries may only specify one platform");
            }
            gProcessInfo->platform = (uint32_t)platform;
            platformFound = true;
        });
        if (gProcessInfo->platform == (uint32_t)dyld3::Platform::unknown) {
            // There were no platforms found in the binary. This may occur on macOS for alternate toolchains and old binaries.
            // It should never occur on any of our embedded platforms.
#if TARGET_OS_OSX
            gProcessInfo->platform = (uint32_t)dyld3::Platform::macOS;
#else
            halt("MH_EXECUTE binaries must specify a minimum supported OS version");
#endif
        }
    }

#if TARGET_OS_OSX
    // Check to see if we need to override the platform
    const char* forcedPlatform = _simple_getenv(envp, "DYLD_FORCE_PLATFORM");
    if (forcedPlatform) {
        dyld_platform_t forcedPlatformType = 0;
        if (strncmp(forcedPlatform, "6", 1) == 0) {
            forcedPlatformType = PLATFORM_MACCATALYST;
        } else if (strncmp(forcedPlatform, "2", 1) == 0) {
            forcedPlatformType = PLATFORM_IOS;
        } else  {
            halt("DYLD_FORCE_PLATFORM is only supported for platform 2 or 6.");
        }
        const dyld3::MachOFile* mf = (dyld3::MachOFile*)sMainExecutableMachHeader;
        if (mf->allowsAlternatePlatform()) {
            gProcessInfo->platform = forcedPlatformType;
        }
    }

    // if this is host dyld, check to see if iOS simulator is being run
    const char* rootPath = _simple_getenv(envp, "DYLD_ROOT_PATH");
    if ( (rootPath != NULL) ) {
        // look to see if simulator has its own dyld
        char simDyldPath[PATH_MAX]; 
        strlcpy(simDyldPath, rootPath, PATH_MAX);
        strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX);
        int fd = dyld3::open(simDyldPath, O_RDONLY, 0);
        if ( fd != -1 ) {
            const char* errMessage = useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue, &result);
            if ( errMessage != NULL )
                halt(errMessage);
            return result;
        }
    }
    else {
        ((dyld3::MachOFile*)mainExecutableMH)->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
            if ( dyld3::MachOFile::isSimulatorPlatform(platform) )
                halt("attempt to run simulator program outside simulator (DYLD_ROOT_PATH not set)");
        });
    }
#endif

    CRSetCrashLogMessage("dyld: launch started");

    //设置上下文,配置信息并保存起来
    setContext(mainExecutableMH, argc, argv, envp, apple);

    // Pickup the pointer to the exec path.
    sExecPath = _simple_getenv(apple, "executable_path");

    //  Remove interim apple[0] transition code from dyld
    if (!sExecPath) sExecPath = apple[0];

#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
    //  kernel is not passing a real path for main executable
    if ( strncmp(sExecPath, "/var/containers/Bundle/Application/", 35) == 0 ) {
        if ( char* newPath = (char*)malloc(strlen(sExecPath)+10) ) {
            strcpy(newPath, "/private");
            strcat(newPath, sExecPath);
            sExecPath = newPath;
        }
    }
#endif

    if ( sExecPath[0] != '/' ) {
        // have relative path, use cwd to make absolute
        char cwdbuff[MAXPATHLEN];
        if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) {
            // maybe use static buffer to avoid calling malloc so early...
            char* s = new char[strlen(cwdbuff) + strlen(sExecPath) + 2];
            strcpy(s, cwdbuff);
            strcat(s, "/");
            strcat(s, sExecPath);
            sExecPath = s;
        }
    }

    // Remember short name of process for later logging
    sExecShortName = ::strrchr(sExecPath, '/');
    if ( sExecShortName != NULL )
        ++sExecShortName;
    else
        sExecShortName = sExecPath;

#if TARGET_OS_OSX && __has_feature(ptrauth_calls)
    // on Apple Silicon macOS, only Apple signed ("platform binary") arm64e can be loaded
    sOnlyPlatformArm64e = true;

    // internal builds, or if boot-arg is set, then non-platform-binary arm64e slices can be run
    if ( const char* abiMode = _simple_getenv(apple, "arm64e_abi") ) {
        if ( strcmp(abiMode, "all") == 0 )
            sOnlyPlatformArm64e = false;
    }
#endif
    //配置进程是否受限,AMFI相关(Apple Mobile File Integrity苹果移动文件保护)
    configureProcessRestrictions(mainExecutableMH, envp);
    
    // Check if we should force dyld3.  Note we have to do this outside of the regular env parsing due to AMFI
    if ( dyld3::internalInstall() ) {
        if (const char* useClosures = _simple_getenv(envp, "DYLD_USE_CLOSURES")) {
            if ( strcmp(useClosures, "0") == 0 ) {
                sClosureMode = ClosureMode::Off;
            } else if ( strcmp(useClosures, "1") == 0 ) {
    #if !__i386__ // don't support dyld3 for 32-bit macOS
                sClosureMode = ClosureMode::On;
                sClosureKind = ClosureKind::full;
    #endif
            } else if ( strcmp(useClosures, "2") == 0 ) {
                sClosureMode = ClosureMode::On;
                sClosureKind = ClosureKind::minimal;
            } else {
                dyld::warn("unknown option to DYLD_USE_CLOSURES.  Valid options are: 0 and 1\n");
            }

        }
    }

#if TARGET_OS_OSX
    if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) {
        pruneEnvironmentVariables(envp, &apple);
        // set again because envp and apple may have changed or moved
        setContext(mainExecutableMH, argc, argv, envp, apple);
    }
    else
#endif
    {
        checkEnvironmentVariables(envp);
        defaultUninitializedFallbackPaths(envp);
    }
#if TARGET_OS_OSX
    switch (gProcessInfo->platform) {
#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
        case PLATFORM_IOS:
            sClosureMode = ClosureMode::On; //  Run iOS apps on macOS in dyld3 mode
            [[clang::fallthrough]];
#endif
        case PLATFORM_MACCATALYST:
            gLinkContext.rootPaths = parseColonList("/System/iOSSupport", NULL);
            gLinkContext.iOSonMac = true;
            if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == sLibraryFallbackPaths )
                sEnv.DYLD_FALLBACK_LIBRARY_PATH = sRestrictedLibraryFallbackPaths;
            if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == sFrameworkFallbackPaths )
                sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths;
            break;
        case PLATFORM_DRIVERKIT:
            gLinkContext.driverKit = true;
            gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
            break;
    }
#endif
    //环境变量,可以在xcode中设置
    if ( sEnv.DYLD_PRINT_OPTS )
        printOptions(argv);
    if ( sEnv.DYLD_PRINT_ENV ) 
        printEnvironmentVariables(envp);

    // Parse this envirionment variable outside of the regular logic as we want to accept
    // this on binaries without an entitelment
#if !TARGET_OS_SIMULATOR
    if ( _simple_getenv(envp, "DYLD_JUST_BUILD_CLOSURE") != nullptr ) {
#if TARGET_OS_IPHONE
        char tempClosurePath[PATH_MAX];
        if ( dyld3::closure::LaunchClosure::buildClosureCachePath(sExecPath, envp, false, tempClosurePath) )
            sJustBuildClosure = true;
#endif
        // If the env vars for the data contain look wrong, don't want to launch the app as that would bring up the UI
        if (!sJustBuildClosure) {
            _exit(EXIT_SUCCESS);
        }
    }
#endif

    if ( sJustBuildClosure )
        sClosureMode = ClosureMode::On;

    // load shared cache
    //加载共享缓存,比如UIKit Foundation等,点进去可以看到iOS是必须有共享缓存的
    checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
    if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
#if TARGET_OS_SIMULATOR
        if ( sSharedCacheOverrideDir)
            mapSharedCache(mainExecutableSlide);
#else
        //主程序的ALSR -> loadDyldCache,1.加载当前进程, 2. 已经加载,不处理 3, 第一次加载三种情况
        mapSharedCache(mainExecutableSlide);
#endif
    }

#if !TARGET_OS_SIMULATOR
    //插入的动态库 iOS11之后引入使用dyld3的闭包模式,使用回调,加载速度更快,效率更高,iOS13之后动态库,三方库也都使用这种加载模式
    if ( getpid() == 1 ) {
        // Get the value as set by the boot-args
        uint64_t commPageValue = 0;
        size_t commPageValueSize = sizeof(commPageValue);
        if ( sysctlbyname("kern.dyld_flags", &commPageValue, &commPageValueSize, nullptr, 0) != 0 ) {
            // Try again with the old name
            // TODO: Remove this when we are always on new enough kernels
            sysctlbyname("kern.dyld_system_flags", &commPageValue, &commPageValueSize, nullptr, 0);
        }

        commPageValue &= CommPageBootArgMask;
        // logToConsole("dyld: got comm page flags 0x%llx\n", commPageValue);

        // If we are PID 1 (launchd) and on macOS, then we should check if the simulator support dylibs
        // are roots or not.
        // If they are not roots at launchd time, and the file system is read-only, then we know for sure
        // they will not be roots later
#if DYLD_SIMULATOR_ROOTS_SUPPORT
        bool fileSystemIsWritable = true;

        // logToConsole("dyld: in launchd\n");
        struct statfs statBuffer;
        int statResult = statfs("/", &statBuffer);
        if ( statResult == 0 ) {
            if ( !strcmp(statBuffer.f_fstypename, "apfs") ) {
                if ( (statBuffer.f_flags & (MNT_RDONLY | MNT_SNAPSHOT)) == (MNT_RDONLY | MNT_SNAPSHOT) ) {
                    // logToConsole("dyld: got statfs flags 0x%llx\n", statBuffer.f_flags);
                    fileSystemIsWritable = false;
                }
            }
        } else {
            int error = errno;
            logToConsole("dyld: could not stat '/', errno = %d\n", error);
        }

        // If the file system is read-only, then we can check now whether any of the simulator support
        // dylibs are roots
        bool libsystemKernelIsRoot      = false;
        bool libsystemPlatformIsRoot    = false;
        bool libsystemPThreadIsRoot     = false;
        if ( !fileSystemIsWritable && (sSharedCacheLoadInfo.loadAddress != nullptr)) {
            dyld3::closure::FileSystemPhysical fileSystem;
            libsystemKernelIsRoot   = !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_kernel.dylib",
                                                                                   &fileSystem, sSharedCacheLoadInfo.loadAddress);
            libsystemPlatformIsRoot = !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_platform.dylib",
                                                                                   &fileSystem, sSharedCacheLoadInfo.loadAddress);
            libsystemPThreadIsRoot  = !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_pthread.dylib",
                                                                                   &fileSystem, sSharedCacheLoadInfo.loadAddress);
        }
        commPageValue |= (fileSystemIsWritable ? CommPageFlags::fileSystemCanBeModified : CommPageFlags::None);
        commPageValue |= (libsystemKernelIsRoot ? CommPageFlags::libsystemKernelIsRoot : CommPageFlags::None);
        commPageValue |= (libsystemPlatformIsRoot ? CommPageFlags::libsystemPlatformIsRoot : CommPageFlags::None);
        commPageValue |= (libsystemPThreadIsRoot ? CommPageFlags::libsystemPThreadIsRoot : CommPageFlags::None);
#endif // DYLD_SIMULATOR_ROOTS_SUPPORT

        logToConsole("dyld: setting comm page to 0x%llx\n", commPageValue);
        if ( sysctlbyname("kern.dyld_flags", nullptr, 0, &commPageValue, sizeof(commPageValue)) != 0 ) {
            // Try again with the old name
            // TODO: Remove this when we are always on new enough kernels
            sysctlbyname("kern.dyld_system_flags", nullptr, 0, &commPageValue, sizeof(commPageValue));
        }
    }

#if DYLD_SIMULATOR_ROOTS_SUPPORT
    // Set the roots checker to the state from the comm page
    {
        uint64_t dyldFlags = *((uint64_t*)_COMM_PAGE_DYLD_SYSTEM_FLAGS);
        bool fileSystemCanBeModified = dyldFlags & CommPageFlags::fileSystemCanBeModified;
        bool libsystemKernelIsRoot = dyldFlags & CommPageFlags::libsystemKernelIsRoot;
        bool libsystemPlatformIsRoot = dyldFlags & CommPageFlags::libsystemPlatformIsRoot;
        bool libsystemPThreadIsRoot = dyldFlags & CommPageFlags::libsystemPThreadIsRoot;
        sRootsChecker.setFileSystemCanBeModified(fileSystemCanBeModified);
        sRootsChecker.setLibsystemKernelIsRoot(libsystemKernelIsRoot);
        sRootsChecker.setLibsystemPlatformIsRoot(libsystemPlatformIsRoot);
        sRootsChecker.setLibsystemPThreadIsRoot(libsystemPThreadIsRoot);
    }
#endif // DYLD_SIMULATOR_ROOTS_SUPPORT

#endif // !TARGET_OS_SIMULATOR

    // If we haven't got a closure mode yet, then check the environment and cache type
    // 如果是闭包模式,执行
    if ( sClosureMode == ClosureMode::Unset ) {
        // First test to see if we forced in dyld2 via a kernel boot-arg
        if ( dyld3::BootArgs::forceDyld2() ) {
            sClosureMode = ClosureMode::Off;
        } else if ( inDenyList(sExecPath) ) {
            sClosureMode = ClosureMode::Off;
        } else if ( sEnv.hasOverride ) {
            sClosureMode = ClosureMode::Off;
        } else if ( dyld3::BootArgs::forceDyld3() ) {
            sClosureMode = ClosureMode::On;
        } else {
            sClosureMode = getPlatformDefaultClosureMode();
        }
    }

#if !TARGET_OS_SIMULATOR
    if ( sClosureMode == ClosureMode::Off ) {
        if ( gLinkContext.verboseWarnings )
            dyld::log("dyld: not using closures\n");
    } else {
        sLaunchModeUsed = DYLD_LAUNCH_MODE_USING_CLOSURE;
        const dyld3::closure::LaunchClosure* mainClosure = nullptr;
        //主程序
        dyld3::closure::LoadedFileInfo mainFileInfo;
        mainFileInfo.fileContent = mainExecutableMH;
        mainFileInfo.path = sExecPath;
        // FIXME: If we are saving this closure, this slice offset/length is probably wrong in the case of FAT files.
        mainFileInfo.sliceOffset = 0;
        mainFileInfo.sliceLen = -1;
        struct stat mainExeStatBuf;
        if ( dyld3::stat(sExecPath, &mainExeStatBuf) == 0 ) {
            mainFileInfo.inode = mainExeStatBuf.st_ino;
            mainFileInfo.mtime = mainExeStatBuf.st_mtime;
        }
        // check for closure in cache first
        if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
            mainClosure = sSharedCacheLoadInfo.loadAddress->findClosure(sExecPath);
            if ( gLinkContext.verboseWarnings && (mainClosure != nullptr) )
                dyld::log("dyld: found closure %p (size=%lu) in dyld shared cache\n", mainClosure, mainClosure->size());
            if ( mainClosure != nullptr )
                sLaunchModeUsed |= DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
        }

        // We only want to try build a closure at runtime if its an iOS third party binary, or a macOS binary from the shared cache
        bool allowClosureRebuilds = false;
        if ( sClosureMode == ClosureMode::On ) {
            allowClosureRebuilds = true;
        } else if ( (sClosureMode == ClosureMode::PreBuiltOnly) && (mainClosure != nullptr) ) {
            allowClosureRebuilds = true;
        }
        //缓存中查找, 验证闭包,在从缓存中找,没有找到又创建啊,目的是一定拿到这个闭包实例对象,后面通过他来加载主程序
        if ( (mainClosure != nullptr) && !closureValid(mainClosure, mainFileInfo, mainExecutableCDHash, true, envp) ) {
            mainClosure = nullptr;
            sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
        }

        //  bootToken is a concat of boot-hash kernel passes down for app and dyld's uuid
        uint8_t bootTokenBufer[128];
        unsigned bootTokenBufferLen = 0;
        if ( const char* bootHashStr = _simple_getenv(apple, "executable_boothash") ) {
            if ( hexStringToBytes(bootHashStr, bootTokenBufer, sizeof(bootTokenBufer), bootTokenBufferLen) ) {
                if ( ((dyld3::MachOFile*)&__dso_handle)->getUuid(&bootTokenBufer[bootTokenBufferLen]) )
                    bootTokenBufferLen += sizeof(uuid_t);
            }
        }
        dyld3::Array bootToken(bootTokenBufer, bootTokenBufferLen, bootTokenBufferLen);

        // If we didn't find a valid cache closure then try build a new one
        if ( (mainClosure == nullptr) && allowClosureRebuilds ) {
            // if forcing closures, and no closure in cache, or it is invalid, check for cached closure
            if ( !sForceInvalidSharedCacheClosureFormat )
                mainClosure = findCachedLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
            if ( mainClosure == nullptr ) {
                // if  no cached closure found, build new one
                mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
                if ( mainClosure != nullptr )
                    sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH;
            }
        }

        // exit dyld after closure is built, without running program
        if ( sJustBuildClosure )
            _exit(EXIT_SUCCESS);

        // try using launch closure
        if ( mainClosure != nullptr ) {
            CRSetCrashLogMessage("dyld3: launch started");
            if ( mainClosure->topImage()->fixupsNotEncoded() )
                sLaunchModeUsed |= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE;
            Diagnostics diag;
            bool closureOutOfDate;
            bool recoverable;
            //启动主程序,返回一个结果,
            bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
                                              mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
            //启动失败或者过期,有创建一个closure,再给一次启动机会
            if ( !launched && closureOutOfDate && allowClosureRebuilds ) {
                // closure is out of date, build new one
                mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
                if ( mainClosure != nullptr ) {
                    diag.clearError();
                    sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH;
                    if ( mainClosure->topImage()->fixupsNotEncoded() )
                        sLaunchModeUsed |= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE;
                    else
                        sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_MINIMAL_CLOSURE;
                    launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
                                                 mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
                }
            }
            //拿到主程序的main函数 返回主程序的main函数的地址
            if ( launched ) {
                gLinkContext.startedInitializingMainExecutable = true;
                if (sSkipMain)
                    result = (uintptr_t)&fake_main;
                return result;
            }
            else {
                if ( gLinkContext.verboseWarnings ) {
                    dyld::log("dyld: unable to use closure %p\n", mainClosure);
                }
                if ( !recoverable )
                    halt(diag.errorMessage());
            }
        }
    }
#endif // TARGET_OS_SIMULATOR
    // could not use closure info, launch old way
    //dyld2模式
    sLaunchModeUsed = 0;


    // install gdb notifier
    //这是两个回到放到hannlers的数组中
    stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB);
    stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
    // make initial allocations large enough that it is unlikely to need to be re-alloced
    //尽可能分配大一点,保证够用
    sImageRoots.reserve(16);
    sAddImageCallbacks.reserve(4);
    sRemoveImageCallbacks.reserve(4);
    sAddLoadImageCallbacks.reserve(4);
    sImageFilesNeedingTermination.reserve(16);
    sImageFilesNeedingDOFUnregistration.reserve(8);

#if !TARGET_OS_SIMULATOR
#ifdef WAIT_FOR_SYSTEM_ORDER_HANDSHAKE
    //  Add gating mechanism to dyld support system order file generation process
    WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld::gProcessInfo->systemOrderFlag);
#endif
#endif


    try {
        // add dyld itself to UUID list
//
        addDyldImageToUUIDList();

#if SUPPORT_ACCELERATE_TABLES
#if __arm64e__
        // Disable accelerator tables when we have threaded rebase/bind, which is arm64e executables only for now.
        if ((sMainExecutableMachHeader->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E)
            sDisableAcceleratorTables = true;
#endif
//准备加载主程序
        bool mainExcutableAlreadyRebased = false;
        if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) {
            struct stat statBuf;
            if ( dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR "no-dyld2-accelerator-tables", &statBuf) != 0 )
                sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(&sSharedCacheLoadInfo.loadAddress->header, sSharedCacheLoadInfo.slide, mainExecutableMH, gLinkContext);
        }
//
reloadAllImages:
#endif


#if TARGET_OS_OSX
        gLinkContext.strictMachORequired = false;
        //  be less strict about old macOS mach-o binaries
        ((dyld3::MachOFile*)mainExecutableMH)->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
            if ( (platform == dyld3::Platform::macOS) && (sdk >= DYLD_PACKED_VERSION(10,15,0)) ) {
                gLinkContext.strictMachORequired = true;
            }
        });
        if ( gLinkContext.iOSonMac )
            gLinkContext.strictMachORequired = true;
    #else
        // simulators, iOS, tvOS, watchOS, are always strict
        gLinkContext.strictMachORequired = true;
    #endif


        CRSetCrashLogMessage(sLoadingCrashMessage);
        // instantiate ImageLoader for main executable
//      实例化主程序
        sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
        gLinkContext.mainExecutable = sMainExecutable;
        //代码签名
        gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);

#if TARGET_OS_SIMULATOR
        // check main executable is not too new for this OS
//      检测当前设备版本等
        {
            if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH, sExecPath) ) {
                throwf("program was built for a platform that is not supported by this runtime");
            }
            uint32_t mainMinOS = sMainExecutable->minOSVersion();

            // dyld is always built for the current OS, so we can get the current OS version
            // from the load command in dyld itself.
            uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion((const mach_header*)&__dso_handle);
            if ( mainMinOS > dyldMinOS ) {
    #if TARGET_OS_WATCH
                throwf("app was built for watchOS %d.%d which is newer than this simulator %d.%d",
                        mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF),
                        dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF));
    #elif TARGET_OS_TV
                throwf("app was built for tvOS %d.%d which is newer than this simulator %d.%d",
                        mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF),
                        dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF));
    #else
                throwf("app was built for iOS %d.%d which is newer than this simulator %d.%d",
                        mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF),
                        dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF));
    #endif
            }
        }
#endif

        
    #if SUPPORT_ACCELERATE_TABLES
        sAllImages.reserve((sAllCacheImagesProxy != NULL) ? 16 : INITIAL_IMAGE_COUNT);
    #else
        sAllImages.reserve(INITIAL_IMAGE_COUNT);
    #endif

#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
        
        if (dyld::isTranslated()) {
            struct dyld_all_runtime_info {
                uint32_t image_count;
                dyld_image_info* images;
                uint32_t uuid_count;
                dyld_uuid_info* uuids;
                uint32_t aot_image_count;
                dyld_aot_image_info* aots;
                dyld_aot_shared_cache_info aot_cache_info;
            };

            dyld_all_runtime_info* runtime_info;
            int ret = syscall(0x7000004, &runtime_info);
            if (ret == 0) {
                for (int i = 0; i < runtime_info->uuid_count; i++) {
                    dyld_image_info image_info = runtime_info->images[i];
                    dyld_uuid_info uuid_info = runtime_info->uuids[i];

                    // add the arm64 cambria runtime to uuid info
                    addNonSharedCacheImageUUID(uuid_info);

                    struct stat sb;
                    if (stat(image_info.imageFilePath, &sb) == 0) {
                        fsid_t fsid = {{0, 0}};
                        fsobj_id_t fsobj = {0};
                        ino_t inode = sb.st_ino;
                        fsobj.fid_objno = (uint32_t)inode;
                        fsobj.fid_generation = (uint32_t)(inode>>32);
                        fsid.val[0] = sb.st_dev;

                        dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, image_info.imageFilePath, &(uuid_info.imageUUID), fsobj, fsid, image_info.imageLoadAddress);
                    }
                }

                // add aot images to dyld_all_image_info
                addAotImagesToAllAotImages(runtime_info->aot_image_count, runtime_info->aots);

                // add the arm64 cambria runtime to dyld_all_image_info
                addImagesToAllImages(runtime_info->image_count, runtime_info->images);

                // set the aot shared cache info in dyld_all_image_info
                dyld::gProcessInfo->aotSharedCacheBaseAddress = runtime_info->aot_cache_info.cacheBaseAddress;
                memcpy(dyld::gProcessInfo->aotSharedCacheUUID, runtime_info->aot_cache_info.cacheUUID, sizeof(uuid_t));
            }
        }
#endif

        // Now that shared cache is loaded, setup an versioned dylib overrides
        
    #if SUPPORT_VERSIONED_PATHS
        //设置加载动态库的版本
        checkVersionedPaths();
    #endif


        // dyld_all_image_infos image list does not contain dyld
        // add it as dyldPath field in dyld_all_image_infos
        // for simulator, dyld_sim is in image list, need host dyld added
#if TARGET_OS_SIMULATOR
        // get path of host dyld from table of syscall vectors in host dyld
        void* addressInDyld = gSyscallHelpers;
#else
        // get path of dyld itself
        void*  addressInDyld = (void*)&__dso_handle;
#endif
        char dyldPathBuffer[MAXPATHLEN+1];
        int len = proc_regionfilename(getpid(), (uint64_t)(long)addressInDyld, dyldPathBuffer, MAXPATHLEN);
        if ( len > 0 ) {
            dyldPathBuffer[len] = '\0'; // proc_regionfilename() does not zero terminate returned string
            if ( strcmp(dyldPathBuffer, gProcessInfo->dyldPath) != 0 )
                gProcessInfo->dyldPath = strdup(dyldPathBuffer);
        }

        // load any inserted libraries
        // 环境变量,注意,这里是插入动态库,root越狱环境经常使用
        if  ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
            for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
            //load(path, context, cacheIndex);
                loadInsertedDylib(*lib);
        }
        // record count of inserted libraries so that a flat search will look at 
        // inserted libraries, then main, then others.
        sInsertedDylibCount = sAllImages.size()-1;

        // link main executable
        gLinkContext.linkingMainExecutable = true;
#if SUPPORT_ACCELERATE_TABLES
        if ( mainExcutableAlreadyRebased ) {
            // previous link() on main executable has already adjusted its internal pointers for ASLR
            // work around that by rebasing by inverse amount
            sMainExecutable->rebase(gLinkContext, -mainExecutableSlide);
        }
#endif
        //链接主程序及插入动态库
        link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
        sMainExecutable->setNeverUnloadRecursive();
        if ( sMainExecutable->forceFlat() ) {
            gLinkContext.bindFlat = true;
            gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
        }

        // link any inserted libraries
        // do this after linking main executable so that any dylibs pulled in by inserted 
        // dylibs (e.g. libSystem) will not be in front of dylibs the program uses
        if ( sInsertedDylibCount > 0 ) {
            for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
//              i+1插入动态库是在主程序之后,进行链接
                ImageLoader* image = sAllImages[i+1];
                link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
                image->setNeverUnloadRecursive();
            }
            if ( gLinkContext.allowInterposing ) {
                // only INSERTED libraries can interpose
                // register interposing info after all inserted libraries are bound so chaining works
                for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
                    ImageLoader* image = sAllImages[i+1];
                    image->registerInterposing(gLinkContext);
                }
            }
        }
        
        if ( gLinkContext.allowInterposing ) {
            //  dyld should support interposition even without DYLD_INSERT_LIBRARIES
            for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) {
                ImageLoader* image = sAllImages[i];
                if ( image->inSharedCache() )
                    continue;
                image->registerInterposing(gLinkContext);
            }
        }
    #if SUPPORT_ACCELERATE_TABLES
        //如果没有加载成功 goto reloadAllImages,重新加载
        if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) {
            // Accelerator tables cannot be used with implicit interposing, so relaunch with accelerator tables disabled
            ImageLoader::clearInterposingTuples();
            // unmap all loaded dylibs (but not main executable)
            for (long i=1; i < sAllImages.size(); ++i) {
                ImageLoader* image = sAllImages[i];
                if ( image == sMainExecutable )
                    continue;
                if ( image == sAllCacheImagesProxy )
                    continue;
                image->setCanUnload();
                ImageLoader::deleteImage(image);
            }
            // note: we don't need to worry about inserted images because if DYLD_INSERT_LIBRARIES was set we would not be using the accelerator table
            sAllImages.clear();
            sImageRoots.clear();
            sImageFilesNeedingTermination.clear();
            sImageFilesNeedingDOFUnregistration.clear();
            sAddImageCallbacks.clear();
            sRemoveImageCallbacks.clear();
            sAddLoadImageCallbacks.clear();
            sAddBulkLoadImageCallbacks.clear();
            sDisableAcceleratorTables = true;
            sAllCacheImagesProxy = NULL;
            sMappedRangesStart = NULL;
            mainExcutableAlreadyRebased = true;
            gLinkContext.linkingMainExecutable = false;
            resetAllImages();
            goto reloadAllImages;
        }
    #endif

        // apply interposing to initial set of images
        for(int i=0; i < sImageRoots.size(); ++i) {
            sImageRoots[i]->applyInterposing(gLinkContext);
        }
        ImageLoader::applyInterposingToDyldCache(gLinkContext);

        // Bind and notify for the main executable now that interposing has been registered
        uint64_t bindMainExecutableStartTime = mach_absolute_time();
        sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
        uint64_t bindMainExecutableEndTime = mach_absolute_time();
        ImageLoaderMachO::fgTotalBindTime += bindMainExecutableEndTime - bindMainExecutableStartTime;
        gLinkContext.notifyBatch(dyld_image_state_bound, false);

        // Bind and notify for the inserted images now interposing has been registered
        if ( sInsertedDylibCount > 0 ) {
            for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
                ImageLoader* image = sAllImages[i+1];
                //绑定插入的动态库!
                image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true, nullptr);
            }
        }
        
        //  do weak binding only after all inserted images linked
        //弱符号绑定
        sMainExecutable->weakBind(gLinkContext);
        gLinkContext.linkingMainExecutable = false;

        sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);

        CRSetCrashLogMessage("dyld: launch, running initializers");
    #if SUPPORT_OLD_CRT_INITIALIZATION
        // Old way is to run initializers via a callback from crt1.o
        if ( ! gRunInitializersOldWay ) 
            initializeMainExecutable(); 
    #else
        // run all initializers
        //初始化main方法,执行程序里的代码
        initializeMainExecutable(); 
    #endif

        // notify any montoring proccesses that this process is about to enter main()
        notifyMonitoringDyldMain();
        if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
            dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2);
        }
        ARIADNEDBG_CODE(220, 1);

#if TARGET_OS_OSX
        if ( gLinkContext.driverKit ) {
            result = (uintptr_t)sEntryOverride;
            if ( result == 0 )
                halt("no entry point registered");
            *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
        }
        else
#endif
        {
            // find entry point for main executable
            //找到main函数入口
            result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
            if ( result != 0 ) {
                // main executable uses LC_MAIN, we need to use helper in libdyld to call into main()
                if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
                    *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
                else
                    halt("libdyld.dylib support not present for LC_MAIN");
            }
            else {
                // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
                result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
                *startGlue = 0;
            }
        }
    }
    catch(const char* message) {
        syncAllImages();
        halt(message);
    }
    catch(...) {
        dyld::log("dyld: launch failed\n");
    }

    CRSetCrashLogMessage("dyld2 mode");
#if !TARGET_OS_SIMULATOR
    if (sLogClosureFailure) {
        // We failed to launch in dyld3, but dyld2 can handle it. synthesize a crash report for analytics
        dyld3::syntheticBacktrace("Could not generate launchClosure, falling back to dyld2", true);
    }
#endif

    if (sSkipMain) {
        notifyMonitoringDyldMain();
        if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
            dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2);
        }
        ARIADNEDBG_CODE(220, 1);
        result = (uintptr_t)&fake_main;
        *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
    }
    //start结束之后执行main
    return result;
}

代码比较多,主要看我用中文标注的部分

static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path)
{
    // try mach-o loader
//  if ( isCompatibleMachO((const uint8_t*)mh, path) ) {
//  实例化主程序 加入到allimage里面,也就是imagelist的第一个
        ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
        addImage(image);
        return (ImageLoaderMachO*)image;
//  }
    
//  throw "main executable not a known format";
}

void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath)
{
    //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", imagePath, fDlopenReferenceCount, fNeverUnload);
    
    // clear error strings
    (*context.setErrorStrings)(0, NULL, NULL, NULL);
    // 起始时间。用于记录时间间隔
    uint64_t t0 = mach_absolute_time();
    //递归加载主程序依赖的库.完成之后发通知。
    this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath);
    context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly);

    // we only do the loading step for preflights
    if ( preflightOnly )
        return;

    uint64_t t1 = mach_absolute_time();
    context.clearAllDepths();
    this->updateDepth(context.imageCount());

    __block uint64_t t2, t3, t4, t5;
    {
        dyld3::ScopedTimer(DBG_DYLD_TIMING_APPLY_FIXUPS, 0, 0, 0);
        t2 = mach_absolute_time();
        //Rebase修正ASLR!
        this->recursiveRebaseWithAccounting(context);
        context.notifyBatch(dyld_image_state_rebased, false);

        t3 = mach_absolute_time();
        if ( !context.linkingMainExecutable )
            //绑定NoLazy符号
            this->recursiveBindWithAccounting(context, forceLazysBound, neverUnload);

        t4 = mach_absolute_time();
        if ( !context.linkingMainExecutable )
            //绑定弱符号!
            this->weakBind(context);
        t5 = mach_absolute_time();
    }

    // interpose any dynamically loaded images
    if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0) ) {
        dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_INTERPOSING, 0, 0, 0);
        //递归应用插入的动态库
        this->recursiveApplyInterposing(context);
    }

    // now that all fixups are done, make __DATA_CONST segments read-only
    if ( !context.linkingMainExecutable )
        this->recursiveMakeDataReadOnly(context);

    if ( !context.linkingMainExecutable )
        context.notifyBatch(dyld_image_state_bound, false);
    uint64_t t6 = mach_absolute_time();

    if ( context.registerDOFs != NULL ) {
        std::vector dofs;
        this->recursiveGetDOFSections(context, dofs);
        //注册
        context.registerDOFs(dofs);
    }
    //计算结束时间.
    uint64_t t7 = mach_absolute_time();

    // clear error strings
    (*context.setErrorStrings)(0, NULL, NULL, NULL);

    fgTotalLoadLibrariesTime += t1 - t0;
    fgTotalRebaseTime += t3 - t2;
    fgTotalBindTime += t4 - t3;
    fgTotalWeakBindTime += t5 - t4;
    fgTotalDOF += t7 - t6;
    
    // done with initial dylib loads
    fgNextPIEDylibAddress = 0;
}

通过汇编调用流程以及comand shift o等 找到 ImageLoader::recursiveInitialization方法

void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
                                          InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
    recursive_lock lock_info(this_thread);
    recursiveSpinLock(lock_info);

    if ( fState < dyld_image_state_dependents_initialized-1 ) {
        uint8_t oldState = fState;
        // break cycles
        fState = dyld_image_state_dependents_initialized-1;
        try {
            // initialize lower level libraries first
            for(unsigned int i=0; i < libraryCount(); ++i) {
                ImageLoader* dependentImage = libImage(i);
                if ( dependentImage != NULL ) {
                    // don't try to initialize stuff "above" me yet
                    if ( libIsUpward(i) ) {
                        uninitUps.imagesAndPaths[uninitUps.count] = { dependentImage, libPath(i) };
                        uninitUps.count++;
                    }
                    else if ( dependentImage->fDepth >= fDepth ) {
                        dependentImage->recursiveInitialization(context, this_thread, libPath(i), timingInfo, uninitUps);
                    }
                }
            }
            
            // record termination order
            if ( this->needsTermination() )
                context.terminationRecorder(this);

            // let objc know we are about to initialize this image
            uint64_t t1 = mach_absolute_time();
            fState = dyld_image_state_dependents_initialized;
            oldState = fState;
            //调用objc loadimage +load方法调用
            context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
            
            // initialize this image
            // c++构造函数调用,
            bool hasInitializers = this->doInitialization(context);

            // let anyone know we finished initializing this image
            fState = dyld_image_state_initialized;
            oldState = fState;
            context.notifySingle(dyld_image_state_initialized, this, NULL);
            
            if ( hasInitializers ) {
                uint64_t t2 = mach_absolute_time();
                timingInfo.addTime(this->getShortName(), t2-t1);
            }
        }
        catch (const char* msg) {
            // this image is not initialized
            fState = oldState;
            recursiveSpinUnLock();
            throw;
        }
    }
    
    recursiveSpinUnLock();
}

comand shift o 找notifySingle

static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
...
        //回调指针
        (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
...
}
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
    // record functions to call
    sNotifyObjCMapped   = mapped;
    //赋值了
    sNotifyObjCInit     = init;
}

看哪里调用了

//这里调用了
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped)
{
    dyld::registerObjCNotifiers(mapped, init, unmapped);
}

_dyld_objc_notify_register 函数并没有其他地方调用,所以我们看汇编,发现在objc init里面

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    static_init();
    runtime_init();
    exception_init();
#if __OBJC2__
    cache_t::init();
#endif
    _imp_implementationWithBlock_init();

    //这里调用了
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

问题是objc库中的_objc_init是在哪里调用的呢

void ImageLoaderMachO::doImageInit(const LinkContext& context)
{
    if ( fHasDashInit ) {
        const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
        const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
        const struct load_command* cmd = cmds;
        for (uint32_t i = 0; i < cmd_count; ++i) {
            switch (cmd->cmd) {
                case LC_ROUTINES_COMMAND:
                    Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide);
#if __has_feature(ptrauth_calls)
                    func = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
#endif
                    //  verify initializers are in image
                    if ( ! this->containsAddress(stripPointer((void*)func)) ) {
                        dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
                    }
                    if ( ! dyld::gProcessInfo->libSystemInitialized ) {
                        //  libSystem initializer must run first
                        dyld::throwf("-init function in image (%s) that does not link with libSystem.dylib\n", this->getPath());
                    }
                    if ( context.verboseInit )
                        dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath());
                    {
                        dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
                        func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
                    }
                    break;
            }
            cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
    }
}

可以看到就是在这里调用libSystem.dylib init ->libdispatch ->os_object_init->objc_init

总结

dyld加载流程:

  1. 程序执行从_dyld_start开始 ‑》 dyld`dyldbootstrap::start
  2. 进入dyld:main函数
  3. 配置一些环境 :rebase_dyld
  4. 加载共享缓存
  5. DYLD2 / DYLD3(闭包模式)
    1. 实例化主程序
    2. 加载动态库(首先是插入的动态库)(主程序和动态库都会添加到allImages里面: loadAllImages)
    3. 链接主程序、绑定符号(非懒加载、弱符号)等等
    4. 最关键的:初始化方法initializeMainExecutable
      1. dyld`ImageLoader::runInitializers
      2. dyld`ImageLoader::processInitializers:
      3. dyld`ImageLoader::recursiveInitialization:
      4. dyld`dyld::notifySingle: 函数 此函数执行一个回调
        • 通过断点调试:此回调是_objc_init初始化时赋值的一个函数
          • Load_images Load_images里面执行call_load_methods函数
            • call_class_loads函数:循环调用各个类的load方法
          • doModInitFunction函数
            • 内部会调用全局C++对象的构造函数 attribute((constructor)) 的C函数
  6. 返回主程序的入口函数。开始进入主程序的main函数!

你可能感兴趣的:(iOS逆向-14:dyld)