DYLD加载器(dynamic loader)动态链接器
首先用户点击应用,此时系统开启一个进程给DYLD,它就加载所有的库和可执行文件,也就是全部的MachO文件,MachO文件的结构可以使用MachOView查看
加载的流程主要是DYLD的_main函数,可以用一张图来概括
- 配置环境变量,有下面这些。重要参数: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;
- 加载共享缓存库(就是系统的动态库,为了节省内存使用和提高调用效率设计的)方法最后有注释说明// iOS cannot run without shared region
- 注册gdb notifier通知
- addDyldImageToUUIDList();// 意思是加载DYLD 到 UUID 表
- 加载任何插入的库也叫镜像文件(也叫image、MachO、framework文件),无论是工程本身的还是注入的库(详情看MachO结构里loadcommands的表)
- 链接主程序和加载的库
- 绑定并记录库绑定的时间,环境变量参数为DYLD_PRINT_STATISTICS
- 所有插入库链接完成后弱绑定(看MachO文件结构中的lazy symbol),猜测是未用到的符号绑定
- 重点步骤来了,初始化主程序,此处先初始化库再初始化主程序
- 初始化过程中会调用notifySingle,该函数会执行一个回掉,通过符号断点_dyld_objc_notify_register,知道是objc的初始化函数_objc_init调用了此回调
- 在_objc_init函数中调用_dyld_objc_notify_register,并在load_images函数参数中调用+load方法
- 当镜像image初始化完成后会调用doModInitFunctions,内部会调用带有__atrribute__((constructor))的c函数,理解为类似OC的load方法。因此+load方法调用位置已经清晰。
// mach-o has -init and static initializers
doImageInit(context);
doModInitFunctions(context);
- 最后返回主程序的入口函数,开始进入主程序main函数。
最后,DYLD的加载流程可以简单总结为加载(读)MachO的流程,而MachO的结构就像一本书,loadCommands作为其目录。