当前位置:   article > 正文

iOS底层探索(十) 应用程序加载_ios__attribute__懒加载路由mach-o

ios__attribute__懒加载路由mach-o

应用程序加载

load C++ main 加载顺序

在了解应用程序加载顺序之前,我们需要先了解一下runtime中比较常用的方法load以及C++方法、main函数的执行顺序

  • ViewController.m文件中添加load方法,并在方法中打断点
+ (void)load {
    NSLog(@"%s",__func__);
}
  • 1
  • 2
  • 3
  • main.m文件中增加C++方法,并且在方法中打断点
__attribute__((constructor)) void kcFunc() {
    printf("来了 : %s \n",__func__);
}
  • 1
  • 2
  • 3
  • main函数中打断点
int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    NSLog(@"1223333");
    @autoreleasepool {
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 运行查看结果
    在这里插入图片描述

由打印结果可知,这三个函数的加载顺序为:load -> C++ -> main,可仍然会有疑问
思考:1. 按照之前的理解,main是程序的第一个入口,但load比mian先执行,那么入口到底是什么?2. load函数是谁在调用

编译过程及库

在分析App自动之前,我们需要先了解iOSApp代码的编译过程以及动/静态库

编译过程

其中编译过程如下所示,主要分为以下几个步骤

  • 源文件:载入.h.m.cpp等文件
  • 预处理:替换宏,删除注释,展开头文件,产生.i文件
  • 编译:将.i文件转换为汇编语言,产生.s文件
  • 汇编:将汇编文件转换为机器码文件,产生.o文件
  • 链接:对.o文件中引用其他库的地方进行引用,生成可执行文件
    在这里插入图片描述

动态库与静态库

  • 静态库.a.framework文件,在链接时,静态库会被完整的复制到可执行文件中,被多次使用就有多份冗余拷贝
  • 动态库.dylib.framework文件,链接时动态库不复制,在程序运行时由系统动态的加载到内存,供程序调用,系统只加载一次,多个程序共用节省内存
    在这里插入图片描述
    想了解更多内容,请查看文档iOS 静态库

load 函数之前,系统做了什么

带着上述疑问,我们在load函数内加断点,并使用bt命令查看堆栈信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DyDU50K1-1601274637506)(media/16011714502276/16011748928324.jpg)]

使用bt命令打印堆栈信息,可知:_dyld_start为整个项目的第一个函数入口,该函数在dyld框架内,从dyld源码地址下载源码,改源码无法运行起来,只能打开查看
如果读bt命令打印出的堆栈信息,可以直接查看左侧的堆栈目录也是一样的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-guifKhu0-1601274637507)(media/16011714502276/16011764196871.jpg)]

_dyld_start方法

dyld源码中全局搜索_dyld_start方法,在dyld底层,该方法是做了底层环境区分的,虽然每个环境的编译大致相同,但还是建议查看arm64的源码

#if __arm64__
	.text
	.align 2
	.globl __dyld_start
__dyld_start:
	mov 	x28, sp
	and     sp, x28, #~15		// force 16-byte alignment of stack
	mov	x0, #0
	mov	x1, #0
	stp	x1, x0, [sp, #-16]!	// make aligned terminating frame
	mov	fp, sp			// set up fp to point to terminating frame
	sub	sp, sp, #16             // make room for local variables
	ldr     x0, [x28]               // get app's mh into x0
	ldr     x1, [x28, #8]           // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
	add     x2, x28, #16            // get argv into x2
	adrp	x3,___dso_handle@page
	add 	x3,x3,___dso_handle@pageoff // get dyld's mh in to x4
	mov	x4,sp                   // x5 has &startGlue

	// call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
	// 调用 dyldbootstrap::start方法
	bl	__ZN13dyldbootstrap5startEPKN5dyld311MachOLoadedEiPPKcS3_Pm
	mov	x16,x0                  // save entry point address in x16

	ldr     x1, [sp]
	
	cmp	x1, #0
	b.ne	Lnew

	// LC_UNIXTHREAD way, clean up stack and jump to result

	add	sp, x28, #8             // restore unaligned stack pointer without app mh


	braaz   x16                     // jump to the program's entry point


	// LC_MAIN case, set up stack for call to main()
Lnew:	mov	lr, x1		    // simulate return address into _start in libdyld.dylib
#if __LP64__
	ldr	x0, [x28, #8]       // main param1 = argc
	add	x1, x28, #16        // main param2 = argv
	add	x2, x1, x0, lsl #3
	add	x2, x2, #8          // main param3 = &env[0]
	mov	x3, x2
Lapple:	ldr	x4, [x3]
	add	x3, x3, #8
	
#endif
	cmp	x4, #0
	b.ne	Lapple		    // main param4 = apple
#if __arm64e__
	braaz   x16

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

源码有点长,我对代码做了一些删减,删减部分主要是if-else部分,可暂时不去查看,但是apple的注释写的还是不错的,英文好的可以直接查看,不好的用翻译软件也是可以的。在源码中可以看到call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)注释,调用 dyldbootstrap::start方法,根据堆栈信息可知该方法为_dyld_start方法执行后的第二个方法,全局搜索,找到该方法

dyldbootstrap::start()

全局搜索start方法,最终我们在dyldbootstrap.cpp文件中找到该函数。
仍然查看源码以及注释,源码如下

// 这是引导dyld的代码。这项工作通常是有dyld和crt完成的。在dyld中我们必须手动执行此操作
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 <rdar://46878536>
    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
    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

	// 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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

解释

  • 该函数是dyld的引导,这部分代码需要手动执行,也就是在上一个位置_dyld_start中进行调用
  • 如果没有运行C++初始化程序时,dyld调用runDyldInitializers(argc, argv, envp, apple);方法进行所有C++初始化程序的运行。
  • 完成引导程序后,调用dyld::_main方法进行下一步操作
  • 这里的堆栈信息有些许不正确是因为,我在调试时使用的是Simulator模拟器,而查看的信息为arm64真机环境。

dyld::_main方法

该函数的的源码有点长,而且基本删减不了什么,需要耐心查看,源码如下

// 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)
{
	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));

    // 从环境中获取 main 可执行文件的 cdHash
	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"));
	// 通知 main 可执行文件的加载
	notifyKernelAboutImage(mainExecutableMH, _simple_getenv(apple, "executable_file"));
#endif

	uintptr_t result = 0;
	sMainExecutableMachHeader = mainExecutableMH;
	sMainExecutableSlide = mainExecutableSlide;


	// Set the platform ID in the all image infos so debuggers can tell the process type
	// 给所有的image设置平台ID,以便调试器可以告诉进行类型
	// FIXME: This can all be removed once we make the kernel handle it in rdar://43369446
	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) {
			// There were no platforms found in the binary. This may occur on macOS for alternate toolchains and old binaries.
			// 它永远不会再我们任何嵌入式平台上发生,只能发生于macOS上
		}
	}
	
	CRSetCrashLogMessage("dyld: launch started");

// 获取上下文 -- 这个函数中是对 gLinkContent 进行一系列的配置
	setContext(mainExecutableMH, argc, argv, envp, apple);

	// Pickup the pointer to the exec path.
	// 获取执行exec路径的指针,exec就是编译后的可执行文件。
	sExecPath = _simple_getenv(apple, "executable_path");

	// <rdar://problem/13868260> Remove interim apple[0] transition code from dyld
	// 从 dyld 移除临时 apple[0] 过渡代码
	if (!sExecPath) sExecPath = apple[0];

#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
	// <rdar://54095622> kernel is not passing a real path for main executable
	// 内核没有为 main 可执行文件传递真实路径
	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] != '/' ) {
		// 有相对路径,需要使用 cmd 获取绝对路径
		char cwdbuff[MAXPATHLEN];
	    if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) {
			// maybe use static buffer to avoid calling malloc so early...
			// 可用静态缓冲区内容来避免过早的调用 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;

// 配置主流程 -- 内容仍然是配置 gLinkContext 内容
    configureProcessRestrictions(mainExecutableMH, envp);

	// Check if we should force dyld3.  Note we have to do this outside of the regular env parsing due to AMFI
	// 检查是否应强制使用dyld3
	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 ) {
        // 只有macOS能够设置
			} else {
			// 报警告
				dyld::warn("unknown option to DYLD_USE_CLOSURES.  Valid options are: 0 and 1\n");
			}
		}
	}
    
    // 检查环境变量
	checkEnvironmentVariables(envp);
	// 默认未初始化的后背路径
	defaultUninitializedFallbackPaths(envp);
	
	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
		const char* tempDir = getTempDir(envp);
		if ( (tempDir != nullptr) && (geteuid() != 0) ) {
			// 使用真实路径,防止出现类似 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
		// If we didn't like the format of TMPDIR, just exit.  We don't want to launch the app as that would bring up the UI
		// 路径不正确时直接退出
		if (!sJustBuildClosure) {
			_exit(EXIT_SUCCESS);
		}
	}
#endif

	if ( sJustBuildClosure )
	// 模式设置:on意味着我们设置DYLD_USE_CLOUSURES = 1,有env变量或iOS上的客户缓存
		sClosureMode = ClosureMode::On;
		// 获取Host信息
	getHostInfo(mainExecutableMH, mainExecutableSlide);

	// 加载共享缓存,iOS无法在没有共享缓存的情况下运行
	checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
	if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
#if TARGET_OS_SIMULATOR
		if ( sSharedCacheOverrideDir)
			mapSharedCache();
#else
    // 共享缓存映射
		mapSharedCache();
#endif
	}

	// 如果没有设置ClosureMode,需要检查环境和缓存类型
	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;
		// 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 ( ::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());
		}

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

		// 如果没有找到有效的closure缓存,将尝试构建一个新的closure
		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");
			// 构建所有已知的 image 列表
			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
				// chosure超时,创建新的
				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.
				// start() 将结果指针作为函数指针调用,因此我们需要对其进行签名。
				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 
	// 无法使用closure信息,以旧方式启动
	
	// 安装gdb通知程序
	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
	// <rdar://problem/6849505> Add gating mechanism to dyld support system order file generation process
	WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld::gProcessInfo->systemOrderFlag);
#endif
#endif


	try {
		// 将dyld本身添加到UUID列表
		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_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

		// simulators, iOS, tvOS, watchOS, are always strict
		gLinkContext.strictMachORequired = true;

		CRSetCrashLogMessage(sLoadingCrashMessage);
		// 为main可执行文件实例化ImageLoader
		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

		// 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
		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();
			}
			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 ) {
			// <rdar://problem/19315404> 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
		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);
			}
		}
		
		// <rdar://problem/12186933> 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
		// 运行所有初始化程序
		initializeMainExecutable(); 
	#endif

		// 通知dyld可以进入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);
		
		{
			// find entry point for main executable
			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;
			}
		}
#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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566

大致流程:

  • 获取main可执行文件的cdHash
  • 通知dyld的加载
  • 通知main可执行文件的加载
  • 设置平台ID
  • 设置LinkContext
  • 开启ClosureMode
  • 加载共享缓存mapSharedCache
  • 安装gdb通知stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB)
  • 实例化主程序instantiateFromLoadedImage
  • 加载插入的动态库loadInsertedDylib
  • 链接主程序link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1)
  • 链接插入的动态库link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1)
  • 弱引用绑定主程序weakBind
  • 初始化主程序initializeMainExecutable
  • 通知dyld可以进main()函数了notifyMonitoringDyldMain
  • 执行load方法
    到这里,我们基本了解了load之前的加载,继续探讨

initializeMainExecutable 加载main可执行文件

根据_main方法源码以及LLDB的堆栈信息可知,该方法是_main内部执行的,源码如下

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

	// run initialzers for any inserted dylibs
	// 加载并插入所有的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]);
		}
	}
	
	// run initializers for main executable and everything it brings up 
	// 运行main可执行文件
	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
	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]);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

大致流程:

  • 运行并插入所有的dylib文件
  • 运行mian可执行文件
  • 加载ImageLoader

ImageLoader::runInitializers 运行main可执行文件

查看runInitializers源码,源码如下

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() };
	// 递归加载dylib
	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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

processInitializers 加载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;
	// Calling recursive init on all images in images list, building a new list of
	// uninitialized upward dependencies.
	for (uintptr_t i=0; i < images.count; ++i) {
	// 递归初始化
		images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups);
	}
	// If any upward dependencies remain, init them.
	if ( ups.count > 0 )
		processInitializers(context, thisThread, timingInfo, ups);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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;
			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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 执行doInitialization方法

doInitialization 初始化

源码如下

bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
	CRSetCrashLogMessage2(this->getPath());

	// mach-o has -init and static initializers
	doImageInit(context);
	doModInitFunctions(context);
	
	CRSetCrashLogMessage2(NULL);
	
	return (fHasDashInit || fHasInitializers);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 执行doImageInitimage初始化方法
  • 执行doModInitFunctions

doImageInit 初始化方法

源码如下:

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
					// <rdar://problem/8543820&9228031> 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 ) {
						// <rdar://problem/17973316> 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);
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 循环加载image,但libSystem.dylib必须先运行

notifySingle 通知表

源码如下:

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<dyld_image_state_change_handler>* 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<dyld_image_state_change_handler>::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 ) {
		// <rdar://problem/7008875> 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);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

整体流程如下

  1. dyld_startdyld入口
  2. dyldbootstrap::start
  3. dyld::_maindyld的main入口
  4. mapSharedCache共享缓存加载
  5. instantiateFromLoadedImage 实例化主程序
  6. loadInsertedDylib加载插入的动态库
  7. link主程序
  8. link插入的动态库
  9. weakBind弱引用绑定主程序
  10. initializeMainExecutable初始化主程序
  11. notifyMonitoringDyldMain通知dyld可以进mian()函数
    在这里插入图片描述
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/403238
推荐阅读
相关标签
  

闽ICP备14008679号