底层原理-12-dyld动态链接器加载流程

1.dyld介绍

dyld是苹果的动态连接器,是苹果操作系统的重要组成,在系统编译好后,交给dylb进行链接把依赖的库生成可执行文件。英文全称是:the dynamic link editor.简单介绍一下苹果的dyld的历史

  • dyld1 :dyld 1,并于1996年作为NeXTStep 3.3的一部分发货。在此之前,NeXT使用静态二进制文件。值得注意的是,这早于POSIX dlopen调用标准化。现在,dlopen确实存在于一些Unix上。它们是后来人们采用的专有扩展。NeXTStep有不同的专有扩展,因此人们在早期版本的macOS 10上编写第三方包装,以支持标准的Unix软件。问题是他们不太支持同式语义。有一些奇怪的边缘情况不起作用,最终它们有点慢。在发布 macOS 10.0、猎豹之前,我们还添加了另一个功能,即预绑定。预绑定是一种技术,我们将尝试为系统中的每个dylib和您的应用程序找到固定地址,动态加载程序将所有内容加载到这些地址,如果成功,它将编辑所有这些二进制文件,以包含这些预计算地址,然后下次它将它们放在相同的地址时,它不需要做任何额外的工作。
  • dyld2 :dyld 2是dyld的完全重写。它正确地支持C++初始化器语义,因此我们略微扩展了mach-o格式,并更新了dyld,以便获得高效的C++库支持。它还具有完整的原生dlopen和dlsym实现,语义正确,此时我们不建议使用Legacy API。它们仍在 macOS 上。他们从未在我们的任何其他平台上发货。它是为速度而设计的,由于它是为速度而设计的,所以它的理智检查有限。我们没有今天的恶意软件环境。最后,我们提高了性能,因为我们提高了性能,我们可以摆脱预绑定,代之以所谓的共享缓存。那么什么是共享缓存呢?嗯,它是在iOS 3.1和macOS Snow Leopard中引入的,它完全取代了预绑定。这是一个包含大多数系统dylibs的单个文件。由于我们将它们合并到一个文件中,我们可以进行某些类型的优化。我们可以重新排列他们所有的文本段和所有数据段,并重写他们的整个符号表,以减少大小,因此我们需要在每个进程中装载更少的区域。它还允许我们打包二进制段并节省大量内存。它实际上是dylibs的前链接器。
  • dyld3 :dyld 3 是一款全新的动态链接器,我们今天宣布。这是对我们如何进行动态链接的完全反思,本周种子中大多数macOS系统应用程序默认打开,2017年苹果操作系统平台上的所有系统应用程序默认打开安全。因此,正如我所说,我们将许多安全功能改装为dyld 2,但事后添加此类内容确实很难。我认为近年来我们做得很好,但真的很难做到。那么,我们是否可以进行更积极的安全检查,并预先设计安全?最后,可测试性和可靠性。能不能让dyld更容易测试?因此,苹果推出了大量很棒的测试框架,如XCTest,您应该使用,我们也应该使用,但它们依赖于动态链接器的低级功能将这些库插入到流程中,因此它们基本上不能用于测试现有的dyld代码,这也使我们更难测试安全性和性能功能。那么我们是怎么做到的呢?嗯,我们已经把大部分染料移到了流程之外。现在它基本上只是一个普通的守护进程,我们可以像其他人使用标准测试工具一样进行测试,这将使我们能够在未来更快地改进它。它还允许保持过程的dyld位尽可能小,并减少应用程序中的攻击表面。它还加快了启动速度,因为最快的代码是您从未编写过的代码,紧随其后的是您几乎从未执行的代码。dyld 3是一个进程外的mach-o解析器

只是摘要了下大概,具体大家可以看WWDC17:应用启动时间:过去、现在和未来

1.1 编译

在iOS开发中,我们要运行程序的话需要通过编译。否则会报错,无法运行,command +b 是否成功。 我们写的高级语言通常是对基本语言的封装,对于oc是对c/c++封装成面向对象的开发语言。通过封装了很多API提高了我们开发效率。
我们使用编译器把我们编程的语言转换为机器码,在CPU上运行,让我们的app运行效率更高。
编译器的组成:前段和后端

  • 前端:语法分析,此法分析,生成中间代码。
  • 后端:以中间代码为输入,根据不同架构生成不同机器代码。

iOS中使用的前端编译器是Clang,后端编译器是llvm

未命名文件.jpg

1.2 iOS中的静态库和动态库的介绍

  • 静态库:链接时,静态库会被完整的复制到可执行文件中,被多次使用就会有多次拷贝。通常以.a.framework的形式。
  • 动态库:链接时不复制,程序启动后使用dyld加载,然后在决议符号。运行时由系统加载到内存,供程序使用。多个程序可以动态链接到这个动态库上,节约内存。由于是动态链接的,可以升级动态库,达到更新程序。但是目前来说,不是官方的话无法通过更新动态库的形式,达到热更新的效果。通常以.dylib.framework的形式。

截屏2021-07-08 下午6.10.56.png

2.dyld 流程分析

我们开发中都优化过app启动速度,提升用户体验。其中有一点是减少+load()使用,因为它在main函数之前就会调用,可以使用+initialize()替代。我们写个+load()方法,打断点看下bt情况。

截屏2021-07-09 下午3.09.31.png 可以发现dyld 是_dyld_start开始的,我们下载一份最新的dyld代码进行查看。

选择最新的dyld-852dyld点击下载

打开dyld-852全局搜索_dyld_start,选择arm64架构,发现是由汇编实现。

截屏2021-07-09 下午3.23.08.png 其中注释告诉我们这个方法会进入dyldbootstrap::start,全局搜索dyldbootstrap找到后在搜索start得到:

截屏2021-07-09 下午4.08.28.png 这个方法的关注return dyld::_main,dyld调用了main函数,传入参数macho_headerMach-o(可执行文件)的头部文件。

第一步环境变量相关处理

截屏2021-07-09 下午5.53.34.png 截屏2021-07-09 下午5.59.29.png 主要是进行环境变量的处理,检查是否配置,没有的话使用默认值。

第二步加载共享缓存

截屏2021-07-09 下午6.11.56.png 检查共享缓存是否开启,以及是否映射到公共区域。

第三步dyld添加到UUID列表

截屏2021-07-09 下午6.21.27.png 截屏2021-07-09 下午6.20.41.png 把dyld添加到UUID列表

第四步实例化主程序

截屏2021-07-11 上午8.42.20.png 在映射可执行文件之前,需要制作一个ImageLoader*为已经映射在主可执行文件。

第五步 插入动态库

截屏2021-07-11 上午8.54.39.png for循环加载所有DYLD_INSERT_LIBRARIES的库,调用loadInsertedDylib方法,记录插入的数量,插入库之后main,然后是其他。

第六步 链接主程序

截屏2021-07-13 下午5.05.50.png

第七步 链接所有插入的库

截屏2021-07-13 下午5.07.35.png

第八步 弱符号绑定

截屏2021-07-12 上午11.17.15.png

第九步 运行所有初始化程序

截屏2021-07-12 上午11.18.43.png

第十步 找到主程序的入口

截屏2021-07-12 上午11.20.32.png

第四步实例化主程序分析

sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
// The kernel maps in main executable before dyld gets control.  We need to 
// make an ImageLoader* for the already mapped in main executable.内核映射在主可执行文件之前,dyld得到控制。我们需要制作一个ImageLoader*为已经映射在主可执行文件。
//
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";
}

复制代码

sMainExecutable表示主程序变量。通过instantiateFromLoadedImage方法初始化。在instantiateMainExecutable中返回一个ImageLoader类型的image

// create image for main executable创建镜像为主要可执行文件
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;
	sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);//sniffLoadCommands确定文件是否有压缩的LINKEDIT以及具有的段数
	// instantiate concrete class based on content of load commands 实例化具体类基于加载命令的内容
	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函数获取mach-0类型文件的 load command的相关信息,并进行校验。ImageLoader是一个抽象的基类,以支持加载特定的可执行文件。对于使用中的每一个可执行文件,一个ImageLoader被实例化,ImageLoader基类做链接镜像的工作。

第九步执行初始化方法initializeMainExecutable分析

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

	// run initialzers for any inserted dylibs为所有插入的动态库执行run initialzers方法
	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
	// 当程序退出的时候 注册 cxa_atexit() 处理所有加载过的镜像运行静态终止程序
	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]);
}
复制代码

for循环为所有插入的动态库执行run initialzers方法,注册cxa_atexit()处理程序,以便在此进程退出时在所有加载的镜像中运行静态终止程序

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

复制代码

processInitializersrunInitializers核心的调用

// <rdar://problem/14412057> upward dylib initializers can be run too soon
// To handle dangling dylibs which are upward linked but not downward, all upward linked dylibs
// have their initialization postponed until after the recursion through downward dylibs
// has completed.
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循环所有镜像列表,进行镜像实例化,建立一个向上的依赖关系列表
	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);
}
复制代码

对所有镜像文件for循环实例化,建立一个向上关系依赖列表核心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
			// 让objc 知道我们要初始化这个镜像
			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();//解锁
}
复制代码

主要实现了notifySingledoInitialization函数,先看下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
		// <rdar://problem/50432671> Include UUIDs for shared cache dylibs in all image info when using private mapped shared caches
		if (!image->inSharedCache()
			|| (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion)) {
			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);
	}
}

复制代码

notifySingle函数中核心代码如下:

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);
		}
	}
复制代码

(*sNotifyObjCInit)(image->getRealPath(), image->machHeader())notifySingle的核心。sNotifyObjCInit是:

static _dyld_objc_notify_init		sNotifyObjCInit;
复制代码

全局搜索得到

void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
	// record functions to call
	sNotifyObjCMapped	= mapped;
	sNotifyObjCInit		= init;
	sNotifyObjCUnmapped = unmapped;

	// call 'mapped' function with all images mapped so far
	try {
		notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
	}
	catch (const char* msg) {
		// ignore request to abort during registration
	}

	// <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
	for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
		ImageLoader* image = *it;
		if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
			dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
			(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
		}
	}
}

复制代码

在方法registerObjCNotifiers中进行了复制init继续搜索registerObjCNotifiers调用出发现调用出_dyld_objc_notify_register

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);
}
复制代码

这个方法:仅供objc运行时使用,注册处理程序,当objc镜像被映射、未映射和初始化时调用。我们在objc源码中搜索得到:

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

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}
复制代码

_objc_init中调用并传入了参数。所以在sNotifyObjCInit的赋值在objcload_images中调用,load_images会调用所有的+load方法。

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
        didInitialAttachCategories = true;
        loadAllCategories();
    }

    // Return without taking locks if there are no +load methods here.
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}

复制代码

会调用核心call_load_methods方法

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

    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();

    do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}

复制代码

执行dowhile循环。调用所有挂起的类和类+加载方法,Category +load方法直到父类的+load之后才被调用。

static void call_class_loads(void)
{
    int i;
    
    // Detach current loadable list.
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        load_method_t load_method = (load_method_t)classes[i].method;
        if (!cls) continue; 

        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
        }
        (*load_method)(cls, @selector(load));
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}
复制代码

断点验证下

截屏2021-07-12 下午5.25.36.png 可以得出load的加载流程。_dyld_start->dyldbootstrap::start->dyld::_main->dyld::initializeMainExecutable()->ImageLoader::runInitializers->ImageLoader::processInitializers->ImageLoader::recursiveInitialization->dyld::notifySingle->load_images ->+load

_objc_init探索

	// initialize this image
	bool hasInitializers = this->doInitialization(context);
复制代码

查看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);
}
复制代码

先看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:
					//mach-o方法平移得到方法函数
					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
						//libSystem初始化之前必须先运行
						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);
		}
	}
}
复制代码

进入doModInitFunctions源码实现,这个方法中加载了所有Cxx文件验证一下

截屏2021-07-12 下午5.47.00.pngdoModInitFunctions后调用类的。objc_init调用在哪?
使用符号断点,断住后打印堆栈信息:

截屏2021-07-12 下午5.57.06.png 会在libSystem库中调用libSystem_initializerlibSystem中搜索

截屏2021-07-12 下午6.08.22.png 会对这2个库进行初始化。继续在libdispatch 下载 中搜索libdispatch_init_os_object_init 截屏2021-07-12 下午6.17.12.png 其中有_os_object_init的 实现继续查看

截屏2021-07-12 下午6.18.45.png 所以object 实现_objc_init函数

截屏2021-07-13 上午11.01.14.png

综上所述:初始化_objc_init 会调用_dyld_objc_notify_register通知,第二个参数load_images,收到通知回调sNotifyObjcInit处理函数。
总结:_dyld_start-->dyld::dyldbootstrap -->dyld::_main-->dyld::initializeMainExecutable()-->ImageLoader::runInitializers-->ImageLoader::processInitializers-->ImageLoader::recursiveInitialization-->ImageLoaderMachO::doInitialization-->ImageLoaderMachO::doModInitFunctions-->libSystem_initializer(libSystem.B.dylib)-->libdispatch_init(libdispatch.dylib)-->_os_object_init(libdispatch.dylib)-->_objc_init(libobjc.A.dylib).

总结

我们在iOS运行程序时,我们要把我们写的代码转换为机器识别的代码。在这个过程中,我们会使用一些封装的三方库,或者引用系统依赖的库来实现某些功能。

  • 编译过程中有前端和后端的概念,iOS中前端时Clang编译器,主要进行词法分析,语法分析生成中间代码,后端通常是llvm编译器,后端编译器把中间代码根据不同架构进行编译生成机器可运行的代码类型为Mach-o类型。
  • 在编译过程我们需要加载一些三方的库和系统库。库通常分为动态库和静态库,动态库在内存中只存在一份,程序使用它的时候直接引用它的映射就可以了,而静态库则要加载到所需的程序,多个程序的话需要加载多份。
  • dyld是苹果的动态链接器。最早是dyld1采用预绑定技术,计算出对应的库的地址,后面直接加载。之后为了提高效率重写了dyld1,变成了dyld2主要采用了共享缓存策略,把公用的库打包到一个文件中,进行优化,重写符号表,节约内存。为了提高速度设计的。dyld3在dyld2的基础上增加了许多安全策略。
  • dyld加载过程dyld库start函数进入main函数后首先先处理环境变量,之后加载共享缓存,将dyld本身加入UDID列表。实例化主程序,加载任何插入的动态库,链接库。运行所有初始化程序,找到主程序入口。

大概流程

dyld加载流程.png

猜你喜欢

转载自juejin.im/post/6984333280086605837