DYLD加载流程和+load方法何时调用简单记录

546 阅读2分钟

DYLD加载器(dynamic loader)动态链接器

首先用户点击应用,此时系统开启一个进程给DYLD,它就加载所有的库和可执行文件,也就是全部的MachO文件,MachO文件的结构可以使用MachOView查看

加载的流程主要是DYLD的_main函数,可以用一张图来概括

DYLD入口是start启动进入DYLD的_main函数,_main函数过程主要是:

  1. 配置环境变量,有下面这些。重要参数:DYLD_INSERT_LIBRARIES,越狱注入会用到。其次参数DYLD_PRINT_STATISTICS,可在项目,Edit scheme -> Run -> Auguments 将环境变量 DYLD_PRINT_STATISTICS 设为 1。打印动态库加载和链接时间,二进制重排优化app启动时间会用到此参数。
   const char* const *			DYLD_FRAMEWORK_PATH;
   const char* const *			DYLD_FALLBACK_FRAMEWORK_PATH;
   const char* const *			DYLD_LIBRARY_PATH;
   const char* const *			DYLD_FALLBACK_LIBRARY_PATH;
   const char* const *			DYLD_INSERT_LIBRARIES;
   const char* const *			LD_LIBRARY_PATH;			// for unix conformance
   const char* const *			DYLD_VERSIONED_LIBRARY_PATH;
   const char* const *			DYLD_VERSIONED_FRAMEWORK_PATH;
   bool				        DYLD_PRINT_LIBRARIES_POST_LAUNCH;
   bool					DYLD_BIND_AT_LAUNCH;
   bool					DYLD_PRINT_STATISTICS;
   bool					DYLD_PRINT_STATISTICS_DETAILS;
   bool					DYLD_PRINT_OPTS;
   bool					DYLD_PRINT_ENV;
   bool					DYLD_DISABLE_DOFS;
  1. 加载共享缓存库(就是系统的动态库,为了节省内存使用和提高调用效率设计的)方法最后有注释说明// iOS cannot run without shared region
  2. 注册gdb notifier通知
  3. addDyldImageToUUIDList();// 意思是加载DYLD 到 UUID 表
  4. 加载任何插入的库也叫镜像文件(也叫image、MachO、framework文件),无论是工程本身的还是注入的库(详情看MachO结构里loadcommands的表)
  5. 链接主程序和加载的库
  6. 绑定并记录库绑定的时间,环境变量参数为DYLD_PRINT_STATISTICS
  7. 所有插入库链接完成后弱绑定(看MachO文件结构中的lazy symbol),猜测是未用到的符号绑定
  8. 重点步骤来了,初始化主程序,此处先初始化库再初始化主程序
  9. 初始化过程中会调用notifySingle,该函数会执行一个回掉,通过符号断点_dyld_objc_notify_register,知道是objc的初始化函数_objc_init调用了此回调
  10. 在_objc_init函数中调用_dyld_objc_notify_register,并在load_images函数参数中调用+load方法
  11. 当镜像image初始化完成后会调用doModInitFunctions,内部会调用带有__atrribute__((constructor))的c函数,理解为类似OC的load方法。因此+load方法调用位置已经清晰。
// mach-o has -init and static initializers
	doImageInit(context);
	doModInitFunctions(context);
  1. 最后返回主程序的入口函数,开始进入主程序main函数。

最后,DYLD的加载流程可以简单总结为加载(读)MachO的流程,而MachO的结构就像一本书,loadCommands作为其目录。