dyld加载流程(笔记)

最近在阅读iOS应用逆向与安全从中学习到了很多 ,这里感谢大神的分享,这里只做笔记,方便以后回忆学习。大家可以从苹果官网下载开源代码(433.5版本)

直接到dyld.cpp的main函数开始分析,dyld加载流程主要包括如下9个步骤:

  1. 设置上下文信息,配置进程是否受限  
  2. 配置环境变量,获取当前运行架构   
  3. 加载可执行文件,生成一个ImageLoader实例对象
  4. 检查共享缓存是否映射到了共享区域
  5. 加载所有插入的库
  6. 链接主程序 
  7. 链接所有插入的库,执行符号替换
  8. 执行初始化方法
  9. 寻找主程序入口
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, 
		int argc, const char* argv[], const char* envp[], const char* apple[], 
		uintptr_t* startGlue)
{
	uintptr_t result = 0;
	//保存执行文件头部,后续可以根据头部访问其它信息
	sMainExecutableMachHeader = mainExecutableMH;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
	// 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 ) {
		// Add dyld to the kernel image info before we jump to the sim
		notifyKernelAboutDyld();

		// 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 = 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;
		}
	}
#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 ( 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;
	
	//配置进程是否受限
    configureProcessRestrictions(mainExecutableMH);

#if __MAC_OS_X_VERSION_MIN_REQUIRED
    if ( gLinkContext.processIsRestricted ) {
		//去掉DYLD_* and LD_LIBRARY_PATH环境变量
		pruneEnvironmentVariables(envp, &apple);
		// set again because envp and apple may have changed or moved
		//重新设置上下文
		setContext(mainExecutableMH, argc, argv, envp, apple);
	}
	else
#endif
	{
		//检查设置环境变量
		checkEnvironmentVariables(envp);
		//如果DYLD_FALLBACK为nil,设置为默认的
		defaultUninitializedFallbackPaths(envp);
	}
	//如果设置了DYLD_PRINT_OPTS环境变量打印参数
	if ( sEnv.DYLD_PRINT_OPTS )
		printOptions(argv);
	//如果设置了DYLD_PRINT_ENV环境变量打印环境变量
	if ( sEnv.DYLD_PRINT_ENV ) 
		printEnvironmentVariables(envp);
	//获取当前运行架构信息
	getHostInfo(mainExecutableMH, mainExecutableSlide);
	// install gdb notifier
	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);
	sImageFilesNeedingTermination.reserve(16);
	sImageFilesNeedingDOFUnregistration.reserve(8);

#if !TARGET_IPHONE_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
		//增加自身到UUID列表
		addDyldImageToUUIDList();
		//通知内核
		notifyKernelAboutDyld();

#if SUPPORT_ACCELERATE_TABLES
		bool mainExcutableAlreadyRebased = false;

reloadAllImages:
#endif

		CRSetCrashLogMessage(sLoadingCrashMessage);
		// instantiate ImageLoader for main executable
		//加载可执行文件并生成一个ImageLoader实例对象
		sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
		gLinkContext.mainExecutable = sMainExecutable;
		gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);

#if TARGET_IPHONE_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 __MAC_OS_X_VERSION_MIN_REQUIRED
		//  be less strict about old mach-o binaries
		uint32_t mainSDK = sMainExecutable->sdkVersion();
		gLinkContext.strictMachORequired = (mainSDK >= DYLD_MACOSX_VERSION_10_12) || gLinkContext.processUsingLibraryValidation;
	#else
		// simulators, iOS, tvOS, and watchOS are always strict
		gLinkContext.strictMachORequired = true;
	#endif

		// load shared cache
		//检查共享缓存是否开启,ios必须开启
		checkSharedRegionDisable();
	#if DYLD_SHARED_CACHE_SUPPORT
		if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
			//检查共享缓存是否映射到共享区域
			mapSharedCache();
		} else {
			dyld_kernel_image_info_t kernelCacheInfo;
			bzero(&kernelCacheInfo.uuid[0], sizeof(uuid_t));
			kernelCacheInfo.load_addr = 0;
			kernelCacheInfo.fsobjid.fid_objno = 0;
			kernelCacheInfo.fsobjid.fid_generation = 0;
			kernelCacheInfo.fsid.val[0] = 0;
			kernelCacheInfo.fsid.val[0] = 0;
			task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, true, false);
		}
	#endif

	#if SUPPORT_ACCELERATE_TABLES
		sAllImages.reserve((sAllCacheImagesProxy != NULL) ? 16 : INITIAL_IMAGE_COUNT);
	#else
		sAllImages.reserve(INITIAL_IMAGE_COUNT);
	#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_IPHONE_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);
		}
		
		//加载所有DYLD_INSERT_LIBRARIES指定的库
		// load any inserted libraries
		if	( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
			for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) 
				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) {
				ImageLoader* image = sAllImages[i+1];
				link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
				image->setNeverUnloadRecursive();
			}
			// 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();
			}
		}

		//  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();
		}
	#if SUPPORT_ACCELERATE_TABLES
		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();
			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);
		}
		gLinkContext.linkingMainExecutable = false;
		
		//  do weak binding only after all inserted images linked
		//弱符号绑定
		sMainExecutable->weakBind(gLinkContext);

	#if DYLD_SHARED_CACHE_SUPPORT
		// If cache has branch island dylibs, tell debugger about them
		if ( (sSharedCache != NULL) && (sSharedCache->mappingOffset >= 0x78) && (sSharedCache->branchPoolsOffset != 0) ) {
			uint32_t count = sSharedCache->branchPoolsCount;
			dyld_image_info info[count];
			const uint64_t* poolAddress = (uint64_t*)((char*)sSharedCache + sSharedCache->branchPoolsOffset);
			//  empty branch pools can be in development cache
			if ( ((mach_header*)poolAddress)->magic == sMainExecutableMachHeader->magic ) {
				for (int poolIndex=0; poolIndex < count; ++poolIndex) {
					uint64_t poolAddr = poolAddress[poolIndex] + sSharedCacheSlide;
					info[poolIndex].imageLoadAddress = (mach_header*)(long)poolAddr;
					info[poolIndex].imageFilePath = "dyld_shared_cache_branch_islands";
					info[poolIndex].imageFileModDate = 0;
				}
				// add to all_images list
				addImagesToAllImages(count, info);
				// tell gdb about new branch island images
				gProcessInfo->notification(dyld_image_adding, count, info);
			}
		}
	#endif

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

		// find entry point for main executable
		//LC_MAIN
		result = (uintptr_t)sMainExecutable->getThreadPC();
		if ( result != 0 ) {
			// main executable uses LC_MAIN, needs to return to glue in libdyld.dylib
			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()
			//LC_UNIXTHREAD
			result = (uintptr_t)sMainExecutable->getMain();
			*startGlue = 0;
		}
	}
	catch(const char* message) {
		syncAllImages();
		halt(message);
	}
	catch(...) {
		dyld::log("dyld: launch failed\n");
	}

	CRSetCrashLogMessage(NULL);
	
	return result;
}
  • 设置上下文信息,配置进程是否受限  

 首先调用setContext ,设置上下文信息,然后,调用configureProcessRestrictions设置进程是否受限,其中sEnvMode设置不同的Mode,默认为envNone(受限模式,忽略环境变量)。在设置get_task_allow权限和和开发内核时会设置为envAll,但只要设置了uid和gid,就会变成受限模式,相关代码如下:

    sEnvMode = envNone;
	gLinkContext.requireCodeSignature = true;
	if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
		//代码签名
		if ( flags & CS_ENFORCEMENT ) {
			//get_task_allow权限
			if ( flags & CS_GET_TASK_ALLOW ) {
				// Xcode built app for Debug allowed to use DYLD_* variables
				sEnvMode = envAll;
			}
			else {
				// Development kernel can use DYLD_PRINT_* variables on any FairPlay encrypted app
				uint32_t secureValue = 0;
				size_t   secureValueSize = sizeof(secureValue);
				if ( (sysctlbyname("kern.secure_kernel", &secureValue, &secureValueSize, NULL, 0) == 0) && (secureValue == 0) && isFairPlayEncrypted(mainExecutableMH) ) {
					sEnvMode = envPrintOnly;
				}
			}
		}
		else {
			// Development kernel can run unsigned code
			sEnvMode = envAll;
			gLinkContext.requireCodeSignature = false;
		}
	}
	//调用setuid或setgid受限
	if ( issetugid() ) {
		sEnvMode = envNone;
	}
  • 配置环境变量,获取当前运行架构     

调用checkEnvironmentVariables, 根据环境变量设置相应的值,如果sEnvMode为envNone就直接跳过,否则调用processDyldEnvironmentVariable处理并设置环境变量,代码如下:


static void checkEnvironmentVariables(const char* envp[])
{
	if ( sEnvMode == envNone )
		return;
	const char** p;
	for(p = envp; *p != NULL; p++) {
		const char* keyEqualsValue = *p;
	    if ( strncmp(keyEqualsValue, "DYLD_", 5) == 0 ) {
			const char* equals = strchr(keyEqualsValue, '=');
			if ( equals != NULL ) {
				strlcat(sLoadingCrashMessage, "\n", sizeof(sLoadingCrashMessage));
				strlcat(sLoadingCrashMessage, keyEqualsValue, sizeof(sLoadingCrashMessage));
				const char* value = &equals[1];
				const size_t keyLen = equals-keyEqualsValue;
				char key[keyLen+1];
				strncpy(key, keyEqualsValue, keyLen);
				key[keyLen] = '\0';
				if ( (sEnvMode == envPrintOnly) && (strncmp(key, "DYLD_PRINT_", 11) != 0) )
					continue;
				processDyldEnvironmentVariable(key, value, NULL);
			}
		}
		else if ( strncmp(keyEqualsValue, "LD_LIBRARY_PATH=", 16) == 0 ) {
			const char* path = &keyEqualsValue[16];
			sEnv.LD_LIBRARY_PATH = parseColonList(path, NULL);
		}
	}

#if SUPPORT_LC_DYLD_ENVIRONMENT
	checkLoadCommandEnvironmentVariables();
#endif // SUPPORT_LC_DYLD_ENVIRONMENT	
	
#if SUPPORT_ROOT_PATH
	//  DYLD_IMAGE_SUFFIX and DYLD_ROOT_PATH cannot be used together
	if ( (gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) {
		dyld::warn("Ignoring DYLD_IMAGE_SUFFIX because DYLD_ROOT_PATH is used.\n");
		gLinkContext.imageSuffix = NULL;
	}
#endif
}

调用getHostInfo,获取获取当前运行架构信息,

  • 加载可执行文件,生成一个ImageLoader实例对象

调用instantiateFromLoadedImage函数实例化一个ImageLoader对象,利用isCompatibleMachO函数来比较文件架构和当前架构是否兼容,然后调用ImageLoaderMachO::instantiateMainExecutable来加载文件生成实例,并将image增加到全局sAllImages中,更新mapped range ,

instantiateMainExecutable 函数首先调用sniffLoadCommands获取load command的相关信息并且进行验证,根据是否存在LC_DYLD_INFO和LC_DYLD_INFO_ONLY 来判断当前macho是否压缩,然后根据当前macho是否压缩,使用不同的ImageLoaderMachO子类进行初始化,代码如下:

//从可执行文件创建image
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;
	//获取load command相关信息,并对其进行各种校验
	sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
	// instantiate concrete class based on content of load commands
	//根据文件类型调用不同的ImageLoader初始化
	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
}

ImageLoaderMachOCompressed::instantiateMainExecutable 先用instantiateStart进行实例化,然后调用instantiateFinish来处理其他Load Command。代码如下:

// create image for main executable
ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, 
																		unsigned int segCount, unsigned int libCount, const LinkContext& context)
{
	//实例化image
	ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount);

	// set slide for PIE programs
	image->setSlide(slide);

	// for PIE record end of program, to know where to start loading dylibs
	if ( slide != 0 )
		fgNextPIEDylibAddress = (uintptr_t)image->getEnd();

	image->disableCoverageCheck();
	//处理其它load command
	image->instantiateFinish(context);
	image->setMapped(context);
	
	//如果设置了DYLD_PRINT_SEGMENTS会打印segment详细信息
	if ( context.verboseMapping ) {
		dyld::log("dyld: Main executable mapped %s\n", path);
		for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) {
			const char* name = image->segName(i);
			if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0)  )
				dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i));
			else
				dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i));
		}
	}

	return image;
}
  • 检查共享缓存是否映射到了共享区域

首先调用checkSharedRegionDisable 检查是否开启了共享缓存(在ios中必须开启),然后调用mapSharedCache函数将共享缓存映射到共享区域,mapSharedCache函数先通过_shared_region_check_np检查缓存是否已经映射,如果已经在共享区域中,则更新sSharedCacheSlide和sharedCacheUUID,否则调用openSharedCacheFile打开共享缓存文件,检验缓存文件,最后使用_shared_region_map_and_slide_np完成映射。调用checkVersionedPaths();检查是否有库的版本是否有更新,如果有则覆盖原有的

  • 加载所有插入的库

遍历DYLD_INSERT_LIBRARIES环境变量,然后调用loadInsertedDylib加载,代码如下:

//加载所有DYLD_INSERT_LIBRARIES指定的库
// load any inserted libraries
if	( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
		for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) 
			loadInsertedDylib(*lib);
}

loadInsertedDylib函数代码如下:

//调用load去加载
static void loadInsertedDylib(const char* path)
{
	ImageLoader* image = NULL;
	unsigned cacheIndex;
	try {
		LoadContext context;
		context.useSearchPaths		= false;
		context.useFallbackPaths	= false;
		context.useLdLibraryPath	= false;
		context.implicitRPath		= false;
		context.matchByInstallName	= false;
		context.dontLoad			= false;
		context.mustBeBundle		= false;
		context.mustBeDylib			= true;
		context.canBePIE			= false;
		context.origin				= NULL;	// can't use @loader_path with DYLD_INSERT_LIBRARIES
		context.rpath				= NULL;
		image = load(path, context, cacheIndex);
	}
	catch (const char* msg) {
#if TARGET_IPHONE_SIMULATOR
		dyld::log("dyld: warning: could not load inserted library '%s' because %s\n", path, msg);
#else
#if __MAC_OS_X_VERSION_MIN_REQUIRED
		if ( gLinkContext.processUsingLibraryValidation )
			dyld::log("dyld: warning: could not load inserted library '%s' into library validated process because %s\n", path, msg);
		else
#endif
			halt(dyld::mkstringf("could not load inserted library '%s' because %s\n", path, msg));
#endif
	}
	catch (...) {
		halt(dyld::mkstringf("could not load inserted library '%s'\n", path));
	}
}
  •  链接主程序 

调用link链接主程序,代码如下:

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();
	//对image进行排序
	this->recursiveUpdateDepth(context.imageCount());

	uint64_t t2 = mach_absolute_time();
	//递归rebase
 	this->recursiveRebase(context);
	context.notifyBatch(dyld_image_state_rebased, false);
	
	uint64_t t3 = mach_absolute_time();
	//递归绑定符号表
 	this->recursiveBind(context, forceLazysBound, neverUnload);

	uint64_t t4 = mach_absolute_time();
	if ( !context.linkingMainExecutable )
		this->weakBind(context);
	uint64_t t5 = mach_absolute_time();	

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

	std::vector dofs;
	
	//注册DOF节区
	this->recursiveGetDOFSections(context, dofs);
	context.registerDOFs(dofs);
	uint64_t t7 = mach_absolute_time();	

	// interpose any dynamically loaded images
	if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0) ) {
		this->recursiveApplyInterposing(context);
	}
	
	// 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;
}

 调用recursiveLoadLibraries递归加载动态库,加载动态库后,对动态库进行排序,被依赖的排在前面,然后调用recursiveRebase进行rebase操作,因为模块被加载的内存基地址不同,所以需要rebase,然后递归绑定符号表。

  • 链接所有插入的库,执行符号替换

对sAllImages(除第一项主程序)中的库调用link进行链接,然后调用registerInterposing注册符号替换,代码如下:

//链接插入的动态库
// 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) {
		ImageLoader* image = sAllImages[i+1];
		link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
		image->setNeverUnloadRecursive();
	}
	// 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();
	}
  •  执行初始化方法

调用initializeMainExecutable执行初始化方法,代码如下:

void initializeMainExecutable()
{
	// record that we've reached this step
	gLinkContext.startedInitializingMainExecutable = true;

	// run initialzers for any inserted dylibs
	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]);
		}
	}
	
	// run initializers for main executable and everything it brings up 
	sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
	
	// register cxa_atexit() handler to run static terminators in all loaded images when this process exits
	if ( gLibSystemHelpers != NULL ) 
		(*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL, NULL);

	// dump info if requested
	//DYLD_PRINT_STATISTICS
	if ( sEnv.DYLD_PRINT_STATISTICS )
		ImageLoader::printStatistics((unsigned int)allImagesCount(), initializerTimes[0]);
	//DYLD_PRINT_STATISTICS_DETAILS
	if ( sEnv.DYLD_PRINT_STATISTICS_DETAILS )
		ImageLoaderMachO::printStatisticsDetails((unsigned int)allImagesCount(), initializerTimes[0]);
}
  •  寻找主程序入口

调用getThreadPC,从Load Command读取LC_MAIN入口,如果没有 LC_MAIN就读取LC_UNIXTHREAD,然后跳到入口处执行,这样就来到了main函数处

 

你可能感兴趣的:(Obj-C)