iOS底层探索12-应用程序加载dyld

500 阅读6分钟

[TOC]

本文所用源码 opensource.apple.com/tarballs/
dyld-852、libdispatch-1271.120.2、Libsystem-1292.120.1

应用程序加载

1.代码 -> 内存 代码如何写入内存
2.加载到内存 dyld 动静态库如何加载到内存
3.objc_init -> objc

应用程序加载原理
图片.png 图片.png

编译过程:源文件 -> 预编译 -> 编译 -> 汇编 -> 链接 -> 可执行文件
库:可执行二进制文件 -> 能被操作系统加载到内
分类
静态库:链接时,静态库会被完整地复制到可执行文件中,被多次使用就有多份冗余拷贝 (.a .lib...) 在链接阶段,会将汇编生成的目标与引用的库一起链接打包到可执行文件当中

系统动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存 (.so .dll .dylib...) 常见动态库:UIKit、libdispatch、libobjc.dyld
程序编译并不会链接到目标代码中,而是在程序运行时才被载入
动态库特点: 减少了包的体积,共享了内存, 可以做更新 热更新等
优势

  • 1.减少打包之后的app的大小
  • 2.共享内容,节约资源
  • 3.通过更新动态库,达到更新程序的目的

动态库和静态库 -> 链接
image 库
库加载 映射一份到内存 images

链接过程图示:
图片.png 图片.png

加载流程图:
图片.png

通过 image list 命令可以查看 加载的库
图片.png

dyld引出

dyld 动态链接库
图片.png 通过在 + load 方法添加断点 得到main函数之前应用程序做的操作
bt 命令打印调用堆栈信息
可以看到 最初是_dyld_start dyld做了一些事情
下面通过dyld源码查看dyld的流程

dyld流程上: 从_dyld_start 到 _main函数

从_dyld_start 到 _main函数
__dyld_start 汇编 -> dyldbootstrap -> start函数 (return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);) -> _main函数

图片.png 图片.png

dyld流程中的main函数主流程

_main函数要做的事情探索

1.条件准备:环境,平台,版本,路径,主机信息 mapSharedCache(mainExecutableSlide);

图片.png 由于_main函数过于长 采取反推法
根据最后的返回结果猜测
result -> sMainExecutable
result -> 有可能是其他
图片.png

搜索 sMainExecutable
目标是找到 images 相关加载 link bind load等来验证猜测是否正确

2.instantiateFromLoadedImage() 实例化主程序

图片.png

3.link主程序

图片.png

4.recursiveBind、sMainExecutable->weakBind(gLinkContext) 递归绑定 弱引用绑定

5.initializeMainExecutable() 初始化主程序

6.notifyMonitoringDyldMain() 通知dyld可以main()函数

图片.png 由以上探索可知:可以找到 images 相关加载 link bind load等 猜测正确

对于 instantiateFromLoadedImage 方法探索 图片.png 图片.png macho_header
图片.png 搜索 sniffLoadCommands(const 图片.png _main函数流程小结:

  1. 条件准备:环境,平台,版本,路径,主机信息 mapSharedCache(mainExecutableSlide);
  2. instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);//实例化主程序
  3. link主程序
  4. sMainExecutable->weakBind(gLinkContext);//所有镜像文件链接完毕之后才进行弱引用绑定
  5. initializeMainExecutable(); //初始化 主程序run之后要做的事情
  6. notifyMonitoringDyldMain();//通知dyld可以进main()函数了

dyld流程-主程序运行

下面就主程序运行之后要做的事情进行探索
5.initializeMainExecutable(); //初始化 主程序run之后要做的事情
图片.png 图片.png

拿到镜像文件个数 进行runInitializers

runInitializers 包括
1.images初始化
2.主程序初始化

图片.png

processInitializers 初始化准备

图片.png

recursiveInitialization(const 递归初始化

图片.png

图片.png

context.notifySingle( 单个通知注入

对于
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
单个通知注入
图片.png 发现notifySingle是函数类型
搜索 notifySingle(
图片.png

sNotifyObjCInit

图片.png 搜索sNotifyObjCInit 发现 sNotifyObjCInit是 _dyld_objc_notify_init 类型
图片.png sNotifyObjCInit的赋值过程在registerObjCNotifiers( 方法 中进行

registerObjCNotifiers( -> sNotifyObjCInit

至于registerObjCNotifiers( 方法

图片.png _dyld_objc_notify_register 调用的 registerObjCNotifiers( 方法

_dyld_objc_notify_register -> registerObjCNotifiers(

同时 _dyld_objc_notify_register 也是在_objc_init方法调用的

_objc_init -> _dyld_objc_notify_register

图片.png _objc_init -> _dyld_objc_notify_register -> registerObjCNotifiers( -> sNotifyObjCInit

至此 initializeMainExecutable(); 初始化 主程序run之后要做的事情 可总结如下

initializeMainExecutable();-> runInitializers ( 1.images初始化 -> processInitializers 初始化准备 -> recursiveInitialization(const 递归初始化 -> context.notifySingle( -> sNotifyObjCInit

->runInitializers ( 2.主程序初始化

图片.png

dyld流程-images初始化流程

图片.png

通过之前的探索 _dyld_start ---> recursiveInitialization已经完成
继续探索 doModInitFunctions 不好探索,则 反过来 从上往下探索
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x00000001002d568a libobjc.A.dylib`_objc_init at objc-os.mm:925:9 [opt]
    frame #1: 0x00000001003a70bc libdispatch.dylib`_os_object_init + 13
    frame #2: 0x00000001003b7afc libdispatch.dylib`libdispatch_init + 282
    frame #3: 0x00007fff68cc3791 libSystem.B.dylib`libSystem_initializer + 220
    
    frame #4: 0x000000010002f1d3 dyld`ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 535
    
frame #5: 0x000000010002f5de dyld`ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 40
    frame #6: 0x0000000100029ffb dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 493
    frame #7: 0x0000000100029f66 dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 344
    frame #8: 0x00000001000280b4 dyld`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 188
    frame #9: 0x0000000100028154 dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 82
    frame #10: 0x0000000100016662 dyld`dyld::initializeMainExecutable() + 129
    frame #11: 0x000000010001bbba dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 6667
    frame #12: 0x0000000100015227 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 453
    frame #13: 0x0000000100015025 dyld`_dyld_start + 37

则从上往下探索

dyld`ImageLoader::recursiveInitialization // 调用 doInitialization
dyld`ImageLoaderMachO::doInitialization // 调用 doModInitFunctions

dyld`ImageLoaderMachO::doModInitFunctions // 调用libSystem_initializer  必须先调用libSystem_initializer初始化
-> libSystem.B.dylib`libSystem_initializer // 查阅libSystem源码验证 libSystem_initializer方法会调用 libdispatch_init方法
-> libdispatch.dylib`libdispatch_init // 查阅libdispatch源码验证 libdispatch_init 会调用 _os_object_init 方法
-> libdispatch.dylib`_os_object_init  // 查阅libdispatch源码验证 _os_object_init 会调用 _objc_init 方法
-> libobjc.A.dylib`_objc_init 

dyld-852、libdispatch-1271.120.2、Libsystem-1292.120.1 opensource.apple.com/tarballs/

_os_object_init -> _objc_init

查阅libdispatch源码验证 _os_object_init 会调用 _objc_init 方法
图片.png

libdispatch_init -> _os_object_init

查阅libdispatch源码验证 libdispatch_init 会调用 _os_object_init 方法
图片.png

libSystem_initializer -> libdispatch_init

查阅libSystem源码验证 libSystem_initializer方法会调用 libdispatch_init方法
图片.png

doModInitFunctions -> libSystem_initializer

dyldImageLoaderMachO::doModInitFunctions dyld中查阅 doModInitFunctions 函数发现 libSystem initializer必须先调用执行 :doModInitFunctions调用了libSystem_initializer

图片.png

doInitialization -> doModInitFunctions

doModInitFunctions是由 doInitialization 调用的

图片.png

recursiveInitialization -> doInitialization

doInitialization 又回到了最初的 recursiveInitialization 调用的那里

图片.png

反推过程:
_os_object_init -> _objc_init
libdispatch_init -> _os_object_init
libSystem_initializer -> libdispatch_init
doModInitFunctions -> libSystem_initializer
doInitialization -> doModInitFunctions
recursiveInitialization -> doInitialization

总结:
recursiveInitialization -> doInitialization -> doModInitFunctions -> libSystem_initializer -> libdispatch_init -> _os_object_init -> _objc_init

dyld全流程: 图片.png

dyld和image流程来回

由以上的分析总结
initializeMainExecutable(); 初始化 主程序run之后要做的事情 可总结如下

initializeMainExecutable();-> runInitializers ( 1.images初始化 -> processInitializers 初始化准备 -> recursiveInitialization(const 递归初始化 -> context.notifySingle( -> sNotifyObjCInit ->runInitializers ( 2.主程序初始化

_objc_init -> _dyld_objc_notify_register -> registerObjCNotifiers( -> sNotifyObjCInit

recursiveInitialization -> doInitialization -> doModInitFunctions -> libSystem_initializer -> libdispatch_init -> _os_object_init -> _objc_init

目前的流程已经从dyld到了 一个一个的库,库有反向回调通知,反向调用通知还不知道 需要探索
dyld如何连接 镜像文件和反向回调
_dyld_objc_notify_register

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

// _dyld_objc_notify_init
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());
		}
	}
}
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);
    // map_images()
    // load_images()
#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

_dyld_objc_notify_register(&map_images, load_images, unmap_image);
map_images映射文件的 数据加载 sel class message protocol nonlazy bits等类的信息处理