OC dyld750调用分析

一、准备工作

  • 工欲善其事,必先利其器。想要分析dyld调用原理、首先需要获取苹果源码。

     1.获取苹果dyld-750.6源码调试备用、(经过多次编译试错、缺失部分头文件、苹果并未开源、去除dyld编译之外部分及修改部分不影响主体源码)
     2.准备objc4-781可调式版本源码

二、项目调试阶段

  1.  创建个空工程DYLDExe:在ViewController中调用load函数并打断点。OC dyld750调用分析_第1张图片
  2. 开始阶段为第12条:_dyld_start、查看调用汇编信息、OC dyld750调用分析_第2张图片

  3.根据函数信息走第11步dyldbootstrap:start

dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*)
  • 在对应的dyld源码中,定位到dyldInitialization.cpp C++文件中的引导dyld进入主函数的函数

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

    //发出 kdebug 跟踪点以指示 dyld 引导程序已启动
    dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);

	// 如果内核必须滑动 dyld,我们需要修复负载敏感位置,我们必须在使用任何全局变量之前执行此操作
    rebaseDyld(dyldsMachHeader);
	// 内核将 env 指针设置为刚好经过 agv 数组的末尾
	const char** envp = &argv[argc+1];
	// 内核将苹果指针设置为刚好越过 envp 数组的末尾
	const char** apple = envp;
	while(*apple != NULL) { ++apple; }
	++apple;
	// 为堆栈设置随机值
	__guard_setup(apple);
#if DYLD_INITIALIZER_SUPPORT
	// 在 dyld 中运行所有 C++ 初始值设定项
	runDyldInitializers(argc, argv, envp, apple);
#endif
	// 现在我们已经完成了 dyld 的引导,调用 dyld 的 main函数
	uintptr_t appsSlide = appsMachHeader->getSlide();
	return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}

4.第11条为进入dyld2.cpp 主函数阶段、该阶段为过程概要为:

  • 配置相关环境变量 mainExecutableCDHashBuffer
  • 第10条进入、第9条执行判断是否正在运行iOS模拟器:useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue, &result);
  • 第8条模拟器判断 start_sim 、之后配置完信息后回到 _main
  • 设置上下文信息 setContext(mainExecutableMH, argc, argv, envp, apple);
  • 配置进程是否受限 configureProcessRestrictions
  • 检测环境变量:加载三方库环境变量判断、如果环境变量设置为受限状态、那么就不会去加载其他第三方库、从而其他人就不能注入 Framework、dylib库文件、用于防护手段 checkEnvironmentVariables(envp);
  • 配置相应的环境变量输出相应的值在main函数中、load之前打印
    • DYLD_PRINT_OPTS 配置后会打印Mach-O文件地址
    • DYLD_PRINT_ENV 一些环境变量信息
  • 配置步骤为: Edit Schemes --> Run --> Arguments --> Environment Variables -> +
  • 获得所有主程序架构:getHostInfo(mainExecutableMH, mainExecutableSlide);
  • 加载共享缓存、 首先检查共享缓存是否处于禁用状态。对于iOS来说必须要有共享缓存库checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
  • 加载所有镜像 reloadAllImages:
  • 实例化主程序 :instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
      1. 判断MachO文件兼容性
      2. 创建主程序镜像:ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
        1. 加载实例化子程序过程 sniffLoadCommands
      3. 添加镜像:addImage(image);
  • 插入动态库处理 sEnv.DYLD_INSERT_LIBRARIES != NULL,有值时处理、越狱时可使用
  • 链接主程序 link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
  • 加载依赖库(LC_LOAD_DYLIB):recursiveLoadLibraries
  • 绑定weak的第三方库、weak后加载、非weak先加载。sMainExecutable->weakBind(gLinkContext);

到此时符号绑定完成

  • Framework、dylib中的load 执行先后顺序取决于 Build Phases --> Link Binary With Libraries文件上下顺序
  • 工程中.m 文件 load执行先后顺序取决于、Build Phases--> Compile Sources文件上下顺序
  • Link Binary With Libraries 先于 Compile Sources执行
  •  
  • 开始执行主程序 总结在下文 initializeMainExecutable
//dyld 的入口点。 
//内核加载 dyld 并跳转到 __dyld_start,它设置一些寄存器并调用此函数。 
//返回 __dyld_start 跳转到的目标程序中 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)
{getHostInfo(
	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);
	}
	//检查并查看是否有任何内核标志
	dyld3::BootArgs::setFlags(hexToUInt64(_simple_getenv(apple, "dyld_flags"), nullptr));
	//配置相关环境的操作 
	uint8_t mainExecutableCDHashBuffer[20];
	const uint8_t* mainExecutableCDHash = nullptr;
	if ( hexToBytes(_simple_getenv(apple, "executable_cdhash"), 40, mainExecutableCDHashBuffer) )
		mainExecutableCDHash = mainExecutableCDHashBuffer;

#if !TARGET_OS_SIMULATOR
	// 跟踪 dyld 的加载
	notifyKernelAboutImage((macho_header*)&__dso_handle, _simple_getenv(apple, "dyld_file"));
	// 跟踪主要可执行文件的加载
	notifyKernelAboutImage(mainExecutableMH, _simple_getenv(apple, "executable_file"));
#endif

	uintptr_t result = 0;
    //主程序的MachO的头Header
	sMainExecutableMachHeader = mainExecutableMH;
    //主程序的MachO的ASLR值。
	sMainExecutableSlide = mainExecutableSlide;

	// 在所有镜像信息中设置平台 ID,以便调试器可以判断进程类型
	if (gProcessInfo->version >= 16) {
		__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) {
		//在二进制文件中没有找到平台。 对于备用工具链和旧二进制文件,这可能会在 macOS 上发生。
		//它永远不会发生在我们的任何嵌入式平台上。
#if __MAC_OS_X_VERSION_MIN_REQUIRED
			gProcessInfo->platform = (uint32_t)dyld3::Platform::macOS;
#else
			halt("MH_EXECUTE binaries must specify a minimum supported OS version");
#endif
		}
	}

#if __MAC_OS_X_VERSION_MIN_REQUIRED
	// 检查我们是否需要重写平台信息
	const char* forcedPlatform = _simple_getenv(envp, "DYLD_FORCE_PLATFORM");
	if (forcedPlatform) {
		if (strncmp(forcedPlatform, "6", 1) != 0) {
			halt("DYLD_FORCE_PLATFORM is only supported for platform 6");
		}
		const dyld3::MachOFile* mf = (dyld3::MachOFile*)sMainExecutableMachHeader;
		if (mf->allowsAlternatePlatform()) {
			gProcessInfo->platform = PLATFORM_IOSMAC;
		}
	}

	// 如果这是主机 dyld,请检查是否正在运行 iOS 模拟器
	const char* rootPath = _simple_getenv(envp, "DYLD_ROOT_PATH");
	if ( (rootPath != NULL) ) {
		// 看看模拟器有没有自己的dyld
		char simDyldPath[PATH_MAX]; 
		strlcpy(simDyldPath, rootPath, PATH_MAX);
		strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX);
		int fd = my_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);

	// 拾取指向 可执行文件程序 路径的指针。
	sExecPath = _simple_getenv(apple, "executable_path");

	if (!sExecPath) sExecPath = apple[0];

#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
	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] != '/' ) {
		// 有相对路径,使用 cwd 做绝对路径
		char cwdbuff[MAXPATHLEN];
	    if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) {
			// 也许使用静态缓冲区来避免这么早调用 malloc ......
			char* s = new char[strlen(cwdbuff) + strlen(sExecPath) + 2];
			strcpy(s, cwdbuff);
			strcat(s, "/");
			strcat(s, sExecPath);
			sExecPath = s;
		}
	}

	// 记住进程的短名称以便以后记录
	sExecShortName = ::strrchr(sExecPath, '/');
	if ( sExecShortName != NULL )
		++sExecShortName;
	else
		sExecShortName = sExecPath;
	//配置进程是否受限
    configureProcessRestrictions(mainExecutableMH, envp);

	// 检查我们是否应该强制 dyld3。 请注意,由于 AMFI,我们必须在常规 env 解析之外执行此操作
	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 __MAC_OS_X_VERSION_MIN_REQUIRED

#if __i386__
				// don't support dyld3 for 32-bit macOS
#else
				// Also don't support dyld3 for iOSMac right now
				if ( gProcessInfo->platform != PLATFORM_IOSMAC ) {
					sClosureMode = ClosureMode::On;
				}
#endif // __i386__

#else
				sClosureMode = ClosureMode::On;
#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
			} else {
				dyld::warn("unknown option to DYLD_USE_CLOSURES.  Valid options are: 0 and 1\n");
			}

		}
	}

#if __MAC_OS_X_VERSION_MIN_REQUIRED
    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
	{       //检测环境变量:加载三方库环境变量判断、如果环境变量设置为受限状态、那么就不会去加载其他第三方库、从而其他人就不能注入 Framework、dylib库文件、用于防护手段
		checkEnvironmentVariables(envp);
		defaultUninitializedFallbackPaths(envp);
	}
#if __MAC_OS_X_VERSION_MIN_REQUIRED
	if ( gProcessInfo->platform == PLATFORM_IOSMAC ) {
		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;
	}
	else if ( ((dyld3::MachOFile*)mainExecutableMH)->supportsPlatform(dyld3::Platform::driverKit) ) {
		gLinkContext.driverKit = true;
		gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
	}
#endif //配置相应的环境变量就能打出相应的值、在Main函数中、load之前打印
	if ( sEnv.DYLD_PRINT_OPTS )// DYLD_PRINT_OPTS 配置后会打印Mach-O文件地址
		printOptions(argv);
	if ( sEnv.DYLD_PRINT_ENV )//一些环境变量信息
		printEnvironmentVariables(envp);

	// 在常规逻辑之外解析这个环境变量,因为我们希望在没有权限的情况下在二进制文件上接受它
#if !TARGET_OS_SIMULATOR
	if ( _simple_getenv(envp, "DYLD_JUST_BUILD_CLOSURE") != nullptr ) {
#if TARGET_OS_IPHONE
		const char* tempDir = getTempDir(envp);
		if ( (tempDir != nullptr) && (geteuid() != 0) ) {
			// 使用 realpath 来防止像 TMPRIR=/tmp/../usr/bin 这样的东西
			char realPath[PATH_MAX];
			if ( realpath(tempDir, realPath) != NULL )
				tempDir = realPath;
			if (strncmp(tempDir, "/private/var/mobile/Containers/", strlen("/private/var/mobile/Containers/")) == 0) {
				sJustBuildClosure = true;
			}
		}
#endif
		// 如果我们不喜欢 TMPDIR 的格式,就退出。 我们不想启动应用程序,因为这会显示 UI
		if (!sJustBuildClosure) {
			_exit(EXIT_SUCCESS);
		}
	}
#endif

	if ( sJustBuildClosure )
		sClosureMode = ClosureMode::On;
	getHostInfo(mainExecutableMH, mainExecutableSlide);//到此时 已获得所有主程序架构

	//加载共享缓存 load shared cache、 首先检查共享缓存是否处于禁用状态。对于iOS来说必须要有共享缓存库、
	checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
	if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
#if TARGET_OS_SIMULATOR
		if ( sSharedCacheOverrideDir)
			mapSharedCache();
#else
		mapSharedCache();
#endif
	}

	// 如果我们还没有关闭模式,那么检查环境和缓存类型
	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 closure because of DYLD_USE_CLOSURES or -force_dyld2=1 override\n");
	} else {
		const dyld3::closure::LaunchClosure* mainClosure = nullptr;
		dyld3::closure::LoadedFileInfo mainFileInfo;
		mainFileInfo.fileContent = mainExecutableMH;
		mainFileInfo.path = sExecPath;
		mainFileInfo.sliceOffset = 0;
		mainFileInfo.sliceLen = -1;
		struct stat mainExeStatBuf;
		if ( ::stat(sExecPath, &mainExeStatBuf) == 0 ) {
			mainFileInfo.inode = mainExeStatBuf.st_ino;
			mainFileInfo.mtime = mainExeStatBuf.st_mtime;
		}
		// 首先检查缓存中的关闭
		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());
		}

		// 我们只想在运行时尝试构建一个闭包,如果它是 iOS 第三方二进制文件,或者来自共享缓存的 macOS 二进制文件
		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;

		//如果我们没有找到有效的缓存闭包,则尝试构建一个新的缓存闭包
		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);
			if ( mainClosure == nullptr ) {
				// if  no cached closure found, build new one
				mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
			}
		}
		// 关闭构建后退出dyld,不运行程序
		if ( sJustBuildClosure )
			_exit(EXIT_SUCCESS);

		// try using launch closure
		if ( mainClosure != nullptr ) {
			CRSetCrashLogMessage("dyld3: launch started");
			bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
											  mainExecutableSlide, argc, argv, envp, apple, &result, startGlue);
			if ( !launched && allowClosureRebuilds ) {
				// closure is out of date, build new one
				mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
				if ( mainClosure != nullptr ) {
					launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
												 mainExecutableSlide, argc, argv, envp, apple, &result, startGlue);
				}
			}
			if ( launched ) {
				gLinkContext.startedInitializingMainExecutable = true;
#if __has_feature(ptrauth_calls)
				// start() calls the result pointer as a function pointer so we need to sign it.
				result = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)result, 0, 0);
#endif
				if (sSkipMain)
					result = (uintptr_t)&fake_main;
				return result;
			}
			else {
				if ( gLinkContext.verboseWarnings ) {
					dyld::log("dyld: unable to use closure %p\n", mainClosure);
				}
			}
		}
	}
#endif // TARGET_OS_SIMULATOR
	// could not use closure info, launch old way

	// install gdb notifier
	stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB);
	stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
	// 使初始分配足够大,不太可能需要重新分配
	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
	
	WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld::gProcessInfo->systemOrderFlag);
#endif
#endif

	try {
		// 将 dyld 本身添加到 UUID 列表
		addDyldImageToUUIDList();

#if SUPPORT_ACCELERATE_TABLES
#if __arm64e__
		// 当我们线程化 rebase/bind 时禁用加速器表,这只是 arm64e 可执行文件现在。
		if (sMainExecutableMachHeader->cpusubtype == CPU_SUBTYPE_ARM64E)
			sDisableAcceleratorTables = true;
#endif
		bool mainExcutableAlreadyRebased = false;
		if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) {
			struct stat statBuf;
			if ( ::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 __MAC_OS_X_VERSION_MIN_REQUIRED
		gLinkContext.strictMachORequired = false;
        ((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
		gLinkContext.strictMachORequired = true;
	#endif
		CRSetCrashLogMessage(sLoadingCrashMessage);
		// 此处实例化主程序
		sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
		gLinkContext.mainExecutable = sMainExecutable;
		gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);

#if TARGET_OS_SIMULATOR
		// 检查主可执行文件对于这个操作系统来说不是太新
		{
			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 总是为当前操作系统构建的,因此我们可以获得当前的操作系统版本
			//  来自 dyld 本身的 load 命令。
			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

		// 现在共享缓存已加载,设置版本化的 dylib 重写
	#if SUPPORT_VERSIONED_PATHS
		checkVersionedPaths();
	#endif

		// dyld_all_image_infos 镜像列表不包含dyld;
        //在模拟器的dyld_all_image_infos中将其添加为dyldPath字段,dyld_sim在镜像列表中,需要添加主机dyld
#if TARGET_OS_SIMULATOR
		// 从主机 dyld 中的系统调用向量表中获取主机 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);
		}
		//插入的动态库、有值时处理、越狱时可使用 DYLD_INSERT_LIBRARIES
		if	( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
			for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) 
				loadInsertedDylib(*lib);
		}loadInsertedDylib
		 // 插入库的记录数量,使得一个平坦的搜索将着眼于插入库,然后主程序,然后其他。
          //记录动态库
		sInsertedDylibCount = sAllImages.size()-1;
		// 链接主程序
		gLinkContext.linkingMainExecutable = true;
#if SUPPORT_ACCELERATE_TABLES
		if ( mainExcutableAlreadyRebased ) {
			// 主可执行文件上的前一个链接()已经为 ASLR 调整了其内部指针
			// 通过反数量变基来解决这个问题
			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;
		}

		// 链接任何插入的库
		// 在链接主可执行文件后执行此操作,以便插入的 dylib(例如 libSystem)引入的任何 dylib 不会在程序使用的 dylib 前面
		if ( sInsertedDylibCount > 0 ) {
			for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
				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
				// 在绑定所有插入的库后注册插入信息,以便链接工作
				for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
					ImageLoader* image = sAllImages[i+1];
					image->registerInterposing(gLinkContext);
				}
			}
		}

		if ( gLinkContext.allowInterposing ) {
			for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) {
				ImageLoader* image = sAllImages[i];
				if ( image->inSharedCache() )
					continue;
				image->registerInterposing(gLinkContext);
			}
		}
	#if SUPPORT_ACCELERATE_TABLES
		if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) {
			// 加速器表不能与隐式插入一起使用,因此在禁用加速器表的情况下重新启动
			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);
			}
	//我们不需要担心插入的镜像,因为如果设置了DYLD_INSERT_LIBRARIES我们就不会使用加速器表
			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

		// 将插入应用于初始镜像集
		for(int i=0; i < sImageRoots.size(); ++i) {
			sImageRoots[i]->applyInterposing(gLinkContext);
		}
		ImageLoader::applyInterposingToDyldCache(gLinkContext);

		// 绑定并通知主可执行文件,因为已经注册了插入的镜像
		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);

		// 现在插入的镜像的绑定和通知已经注册
		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);
			}
		}
		
		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
		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 __MAC_OS_X_VERSION_MIN_REQUIRED
		if ( gLinkContext.driverKit ) {
			result = (uintptr_t)sEntryOveride;
			if ( result == 0 )
				halt("no entry point registered");
			*startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
		}
		else
#endif
		{
			// find entry point for main executable
			result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
			if ( result != 0 ) {
			// main可执行文件使用 LC_MAIN,我们需要使用 libdyld 中的 helper 来调用 main()
				if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
					*startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
				else
					halt("libdyld.dylib support not present for LC_MAIN");
			}
			else {
			// 主可执行文件使用 LC_UNIXTHREAD,dyld 需要在为 main() 设置的程序中让“开始”
				result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
				*startGlue = 0;
			}
		}
#if __has_feature(ptrauth_calls)
		// start() calls the result pointer as a function pointer so we need to sign it.
		result = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)result, 0, 0);
#endif
	}
	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;
	}
	
	return result;
}
  • 共享缓存禁用状态判断
static void checkSharedRegionDisable(const dyld3::MachOLoaded* mainExecutableMH, uintptr_t mainExecutableSlide)
{
#if __MAC_OS_X_VERSION_MIN_REQUIRED
	// if main executable has segments that overlap the shared region,
	// then disable using the shared region
	if ( mainExecutableMH->intersectsRange(SHARED_REGION_BASE, SHARED_REGION_SIZE) ) {
		gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
		if ( gLinkContext.verboseMapping )
			dyld::warn("disabling shared region because main executable overlaps\n");
	}
#if __i386__
	if ( !gLinkContext.allowEnvVarsPath ) {
		//  use private or no shared region for suid processes
		gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
	}
#endif
#endif
	// iOS cannot run without shared region
}
  • 共享缓存库mapSharedCache()
static void mapSharedCache()
{
	dyld3::SharedCacheOptions opts;
	opts.cacheDirOverride	= sSharedCacheOverrideDir;
	opts.forcePrivate		= (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion);


#if __x86_64__ && !TARGET_OS_SIMULATOR
	opts.useHaswell			= sHaswell;
#else
	opts.useHaswell			= false;
#endif
	opts.verbose			= gLinkContext.verboseMapping;
	loadDyldCache(opts, &sSharedCacheLoadInfo);

	// update global state
	if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
		gLinkContext.dyldCache 								= sSharedCacheLoadInfo.loadAddress;
		dyld::gProcessInfo->processDetachedFromSharedRegion = opts.forcePrivate;
		dyld::gProcessInfo->sharedCacheSlide                = sSharedCacheLoadInfo.slide;
		dyld::gProcessInfo->sharedCacheBaseAddress          = (unsigned long)sSharedCacheLoadInfo.loadAddress;
		sSharedCacheLoadInfo.loadAddress->getUUID(dyld::gProcessInfo->sharedCacheUUID);
		dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_SHARED_CACHE_A, sSharedCacheLoadInfo.path, (const uuid_t *)&dyld::gProcessInfo->sharedCacheUUID[0], {0,0}, {
    { 0, 0 }}, (const mach_header *)sSharedCacheLoadInfo.loadAddress);
	}

}
  • 加载共享缓存loadDyldCache
bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
{
    results->loadAddress        = 0;
    results->slide              = 0;
    results->errorMessage       = nullptr;

#if TARGET_OS_SIMULATOR
    // simulator only supports mmap()ing cache privately into process
    return mapCachePrivate(options, results);
#else
    if ( options.forcePrivate ) {
        // mmap cache into this process only 仅加载到当前进程
        return mapCachePrivate(options, results);
    }
    else {
        // fast path: when cache is already mapped into shared region
        bool hasError = false;//已经加载后 不做任何处理 
        if ( reuseExistingCache(options, results) ) {
            hasError = (results->errorMessage != nullptr);
        } else {//首次记载时、调用下面函数
            // slow path: this is first process to load cache
            hasError = mapCacheSystemWide(options, results);
        }
        return hasError;
    }
#endif
}
  • 首次加载共享缓存时、调用mapCacheSystemWide
static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
{
    CacheInfo info;
    if ( !preflightCacheFile(options, results, &info) )
        return false;

    const dyld_cache_slide_info2* slideInfo = nullptr;
    if ( info.slideInfoSize != 0 ) {
        results->slide = pickCacheASLR(info);
        slideInfo = (dyld_cache_slide_info2*)(info.slideInfoAddressUnslid + results->slide);
    }

    int result = __shared_region_map_and_slide_np(info.fd, 3, info.mappings, results->slide, slideInfo, info.slideInfoSize);
    ::close(info.fd);
    if ( result == 0 ) {
        results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address);
    }
    else {
        if ( reuseExistingCache(options, results) )
            return true;
        // 如果缓存不存在,那么确实是一个错误
        if ( results->errorMessage == nullptr )
            results->errorMessage = "syscall to map cache into shared region failed";
        return false;
    }

    if ( options.verbose ) {
        dyld::log("mapped dyld cache file system wide: %s\n", results->path);
        verboseSharedCacheMappings(info.mappings);
    }
    return true;
}
  • 实例化主程序 instantiateFromLoadedImage
// The kernel maps in main executable before dyld gets control.  We need to 
// make an ImageLoader* for the already mapped in main executable.
static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path)
{
	// try mach-o loader
	if ( isCompatibleMachO((const uint8_t*)mh, path) ) {
             //创建主程序镜像
		ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
            //添加镜像
		addImage(image);
		return (ImageLoaderMachO*)image;
	}
	
	throw "main executable not a known format";
}

// 判断MachO兼容性是否满足
// This is used to validate if a non-fat (aka thin or raw) mach-o file can be used
// on the current processor. //
bool isCompatibleMachO(const uint8_t* firstPage, const char* path)
{
#if CPU_SUBTYPES_SUPPORTED
	// It is deemed compatible if any of the following are true:
	//  1) mach_header subtype is in list of compatible subtypes for running processor
	//  2) mach_header subtype is same as running processor subtype
	//  3) mach_header subtype runs on all processor variants
	const mach_header* mh = (mach_header*)firstPage;//第一页数据、
	if ( mh->magic == sMainExecutableMachHeader->magic ) {//魔数:32位还是64位
		if ( mh->cputype == sMainExecutableMachHeader->cputype ) {
			if ( mh->cputype == sHostCPU ) {
				// 获取本机可以使用的子类型的优先排序列表
				const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(mh->cputype, sHostCPUsubtype);
				if ( subTypePreferenceList != NULL ) {
					// 如果镜像的子类型在列表中,则它是兼容的
					for (const cpu_subtype_t* p = subTypePreferenceList; *p != CPU_SUBTYPE_END_OF_LIST; ++p) {
						if ( *p == mh->cpusubtype )
							return true;
					}
					// have list and not in list, so not compatible
					throwf("incompatible cpu-subtype: 0x%08X in %s", mh->cpusubtype, path);
				}
				// 未知 cpu 子类型,但如果与当前子类型完全匹配,则可以使用
				if ( mh->cpusubtype == sHostCPUsubtype ) 
					return true;
			}
			
			// cpu 类型没有子类型的有序列表
			switch (mh->cputype) {
				case CPU_TYPE_I386:
				case CPU_TYPE_X86_64:
					// subtypes are not used or these architectures
					return true;
			}
		}
	}
#else
	// 对于不支持 cpu-sub-types 的架构
    //这只是检查cpu类型
	const mach_header* mh = (mach_header*)firstPage;
	if ( mh->magic == sMainExecutableMachHeader->magic ) {
		if ( mh->cputype == sMainExecutableMachHeader->cputype ) {
			return true;
		}
	}
#endif
	return false;
}
  • 是否设置LoadCommand签名信息
static bool hasCodeSignatureLoadCommand(const macho_header* mh)
{
	const uint32_t cmd_count = mh->ncmds;
	const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
	const struct load_command* cmd = cmds;
	for (uint32_t i = 0; i < cmd_count; ++i) {
		if (cmd->cmd == LC_CODE_SIGNATURE) 
			return true;
		cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
	}
	return false;
}
  • 创建主程序镜像
// 创建主程序镜像
ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context)
{
	//dyld::log("ImageLoader=%ld, ImageLoaderMachO=%ld, ImageLoaderMachOClassic=%ld, ImageLoaderMachOCompressed=%ld\n",
	//	sizeof(ImageLoader), sizeof(ImageLoaderMachO), sizeof(ImageLoaderMachOClassic), sizeof(ImageLoaderMachOCompressed));
	bool compressed;
	unsigned int segCount;
	unsigned int libCount;
	const linkedit_data_command* codeSigCmd;
	const encryption_info_command* encryptCmd;
  //确定此 mach-o 文件是否具有经典或压缩的 LINKEDIT 以及它具有的段数
	sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
	// 根据加载命令的内容实例化具体类
	if ( compressed ) 
		return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context);
	else
#if SUPPORT_CLASSIC_MACHO
		return ImageLoaderMachOClassic::instantiateMainExecutable(mh, slide, path, segCount, libCount, context);
#else
		throw "missing LC_DYLD_INFO load command";
#endif
}
  • 加载实例化子程序过程 sniffLoadCommands
// determine if this mach-o file has classic or compressed LINKEDIT and number of segments it has
void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed,
											unsigned int* segCount, unsigned int* libCount, const LinkContext& context,
											const linkedit_data_command** codeSigCmd,
											const encryption_info_command** encryptCmd)
{
	*compressed = false;//LC_DYLD_INFO_ONLY 加载取值
	*segCount = 0;      //Load Commands 中列出的LC_SEGMENT(__PAGEZERO、__TEXT、__DATA、__LINKEDIT)等的个数 不超过255个
	*libCount = 0;      //Load Commands 中第三方库文件数量LC_LOAD_DYLIB        不超过4095个
	*codeSigCmd = NULL; //代码签名
	*encryptCmd = NULL;//加密信息

。。。。。。。。。。

	// fSegmentsArrayCount is only 8-bits
	if ( *segCount > 255 )
		dyld::throwf("malformed mach-o image: more than 255 segments in %s", path);

	// fSegmentsArrayCount is only 8-bits
	if ( *libCount > 4095 )
		dyld::throwf("malformed mach-o image: more than 4095 dependent libraries in %s", path);

	if ( needsAddedLibSystemDepency(*libCount, mh) )
		*libCount = 1;

	//使用 LC_DYLD_CHAINED_FIXUPS 的 dylib 在放入 dyld 缓存时会删除该加载命令
	if ( !*compressed && (mh->flags & MH_DYLIB_IN_CACHE) )
		*compressed = true;
}
  • 链接主程序过程、加载依赖库
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);
 
。。。。。。
	
	// done with initial dylib loads
	fgNextPIEDylibAddress = 0;
}
  • 开始执行主程序
    • initializeMainExecutable (第6条)-->
    • ImageLoader::runInitializers (第5条)-->
    • ImageLoader::processInitializers (第4条) -->
    • ImageLoader::recursiveInitialization (第3条)--->>>context.notifySingle (第2条) -->执行回调函数 (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); sNotifyObjCInit函数的赋值在 registerObjCNotifiers函数 --->
    • dyld::registerObjCNotifiers(mapped, init, unmapped);下符号断点、在_dyld_objc_notify_register上、溯源得到 由 _objc_init调用。
    • 在objc4-781中搜索 _objc_init 得到 objc-os.mm文件中的调用信息。
    • 或者_dyld_objc_notify_register断点处、控制台调试、register read 得到、对比得到 第二个参数为load_images、
       rdx = 0x00007fff20180dd7  libobjc.A.dylib`unmap_image
       rdi = 0x00007fff2017fff1  libobjc.A.dylib`map_images
       rsi = 0x00007fff20180041  libobjc.A.dylib`load_images
  • load_images (第1条)--> call_load_methods 加载所有load方法
void initializeMainExecutable()
{
	// 记录我们已经到了加载主程序的这一步
	gLinkContext.startedInitializingMainExecutable = true;

	// 为任何插入的 dylib 运行初始值设定项
	ImageLoader::InitializerTimingList initializerTimes[allImagesCount()];
	initializerTimes[0].count = 0;
	const size_t rootCount = sImageRoots.size();
	if ( rootCount > 1 ) {
		for(size_t i=1; i < rootCount; ++i) {
			sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]);
		}
	}
	// 为主程序可执行文件及其带来的镜像运行初始化
	sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
	
	//注册 cxa_atexit() 处理程序以在此进程退出时在所有加载的镜像中运行静态终止符
	if ( gLibSystemHelpers != NULL ) 
		(*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL, NULL);

	// 如果需要,输出加载信息、包含各个加载过程的耗时
	if ( sEnv.DYLD_PRINT_STATISTICS )
		ImageLoader::printStatistics((unsigned int)allImagesCount(), initializerTimes[0]);
	if ( sEnv.DYLD_PRINT_STATISTICS_DETAILS )
		ImageLoaderMachO::printStatisticsDetails((unsigned int)allImagesCount(), initializerTimes[0]);
}

void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo)
{
	uint64_t t1 = mach_absolute_time();
	mach_port_t thisThread = mach_thread_self();
	ImageLoader::UninitedUpwards up;
	up.count = 1;
	up.imagesAndPaths[0] = { this, this->getPath() };
	processInitializers(context, thisThread, timingInfo, up);
	context.notifyBatch(dyld_image_state_initialized, false);
	mach_port_deallocate(mach_task_self(), thisThread);
	uint64_t t2 = mach_absolute_time();
	fgTotalInitTime += (t2 - t1);
}

// 为了处理向上链接而不是向下链接的悬空 dylib,
// 所有向上链接的 dylib 都将其初始化推迟到通过向下 dylib 的递归完成之后。
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread,
									 InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
{
	uint32_t maxImageCount = context.imageCount()+2;
	ImageLoader::UninitedUpwards upsBuffer[maxImageCount];
	ImageLoader::UninitedUpwards& ups = upsBuffer[0];
	ups.count = 0;
	// 对图像列表中的所有镜像调用递归初始化,构建一个新的未初始化向上依赖列表。
	for (uintptr_t i=0; i < images.count; ++i) {
		images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups);
	}
	// 如果任何向上的依赖仍然存在,初始化它们。
	if ( ups.count > 0 )
		processInitializers(context, thisThread, timingInfo, ups);
}

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 {
			// 首先初始化较低级别的库
			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;
			context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
			
			// initialize this image
			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();
}

static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
	//dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath());
	std::vector* handlers = stateToHandlers(state, sSingleHandlers);
	if ( handlers != NULL ) {
		dyld_image_info info;
		info.imageLoadAddress	= image->machHeader();
		info.imageFilePath		= image->getRealPath();
		info.imageFileModDate	= image->lastModified();
		for (std::vector::iterator it = handlers->begin(); it != handlers->end(); ++it) {
			const char* result = (*it)(state, 1, &info);
			if ( (result != NULL) && (state == dyld_image_state_mapped) ) {
				//fprintf(stderr, "  image rejected by handler=%p\n", *it);
				// make copy of thrown string so that later catch clauses can free it
				const char* str = strdup(result);
				throw str;
			}
		}
	}
	if ( state == dyld_image_state_mapped ) {
		//  Save load addr + UUID for images from outside the shared cache
		if ( !image->inSharedCache() ) {
			dyld_uuid_info info;
			if ( image->getUUID(info.imageUUID) ) {
				info.imageLoadAddress = image->machHeader();
				addNonSharedCacheImageUUID(info);
			}
		}
	}
	if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
		uint64_t t0 = mach_absolute_time();
		dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
		(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
		uint64_t t1 = mach_absolute_time();
		uint64_t t2 = mach_absolute_time();
		uint64_t timeInObjC = t1-t0;
		uint64_t emptyTime = (t2-t1)*100;
		if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) {
			timingInfo->addTime(image->getShortName(), timeInObjC);
		}
	}
    // mach message csdlc about dynamically unloaded images
	if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) {
		notifyKernel(*image, false);
		const struct mach_header* loadAddress[] = { image->machHeader() };
		const char* loadPath[] = { image->getPath() };
		notifyMonitoringDyld(true, 1, loadAddress, loadPath);
	}
}
//dyldAPIs.cpp中
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);
}

三、objc4-781源码部分

  • _objc_init --> load_images --> call_load_methods 加载所有load方法
/***********************************************************************
* _objc_init
* 引导初始化。 使用 dyld 注册我们的图像通知程序。
* 在库初始化时间之前由 libSystem 调用
**********************************************************************/

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    environ_init();
    tls_init();
    static_init();
    runtime_init();
    exception_init();
    cache_init();
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

void 
load_images(const char *path __unused, const struct mach_header *mh)
{
    if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
        didInitialAttachCategories = true;
        loadAllCategories();
    }
    // 如果这里没有 +load 方法,则不带锁返回。
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);
    // 查找+load 加载方法
    {
        mutex_locker_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }
    // 调用 +load 方法(没有 runtimeLock - 可重入)
    call_load_methods();
}

**********************************************************************/
void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;

    loadMethodLock.assertLocked();

    // 重入调用什么都不做; 最外层的调用将完成工作。
    if (loading) return;
    loading = YES;
    void *pool = objc_autoreleasePoolPush();
    do {
        // 1. 重复调用 class +loads 直到不再有
        while (loadable_classes_used > 0) {
            call_class_loads();
        }
        // 2. 调用分类的 + load 一次
        more_categories = call_category_loads();

        // 3. 如果有类或更多未尝试的分类,则运行更多 + load方法
    } while (loadable_classes_used > 0  ||  more_categories);
    objc_autoreleasePoolPop(pool);
    loading = NO;
}

 

 

 

 

 

 

你可能感兴趣的:(#,iOS深入浅出,dyld加载流程,load_images,main,dyld调用分析)