例:
在main函数中打上断点和viewController中手写+(void)load 方法,在应用启动的时候,是viewController 中的load先断到
bt 指令是查看函数调用栈的
up 指令在函数调用栈中往上走
可以看到 dyldbootstrap::start 函数
查看 DYLD 源码
全局搜索 dyldbootstrap ,然后在 dyldbootstrap 中搜索start 函数,可以找到在468行,可以通过 command + shift + j 定位到这个文件
const struct macho_header* appsMachHeader, (参数是machO header,是和宏定义,64位就是64位的header)
int argc,
const char* argv[],
intptr_t slide,(ASLR,随机值,machO加载到内存中是,随机加一个变量)
const struct macho_header* dyldsMachHeader,
uintptr_t* startGlue
配置环境变量
当我们配置了环境变量的值,可以看到打印信息,在load调用前就已经打印了环境变量的信息
配置DYLD_PRINT_OPTS 后可以看到打印的是machO 的地址
加载共享缓存
//检查共享缓存是否为禁用状态 但是最后一句话告诉我们iOS设备不能禁用共享缓存
主程序的实例化
sniffLoadCommands 实例化的是抽象类,需要其他子类实力化为machO 的image
加载动态库
连接三方库
自己添加的库,会根据添加的顺序加载对应的库,+(void)load方法的调用顺序也是如此。
在这里进行weak 的绑定
run主程序
有个回调方法,sNotifyObjCInit
这个时候 (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); 就会调用objc 中的方法load_images
在 _dyld_objc_notify_register 方法中调用 registerObjCNotifiers
可以下一个符号断点来调试,可以发现在这个方法之前framework 的load 方法都还没有调用,所以之前的配置环境变量、加载共享缓存、实例化主程序、加载动态库、链接三方库 操作应该在 all Images 中。
可以通过寄存器查看 参数
可以查看objc 源码,可以看到第二个参数就是 load_images
可以查找load_images 方法,(函数名称就是函数指针)
最后到 call_load_methods ,然后循环调用每一个类的loads
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo); 执行完成后所有framework的load方法都已经执行完毕了,然后开始加载image
doModInitFunctions 函数会执行 c++的构造函数 (module inti function ),framework代码注入都在这个构造函数之前
__attribute__((constructor))voidfunc3(){ printf("func3来了 \n"); } //固定写法
如果多个framework 中都有load 和 __attribute__ 构造方法,那么会根据添加的顺序,执行完每一个framework 的 load 和 __attribute__ 构造方法 才会执行下一个 framework 的 load 和 __attribute__ 构造方法
可以通过machO view 看到, _mod_init_func 中有所有的构造函数
初始化函数执行完后,这时 main 函数还没有执行
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN(); 这时才开始调用main 函数,最终将result 返回
load -> 构造函数 - > main
image 的加载过程 :加载共享缓存 -> 加载插入的库 -> 加载三方库 -> 加载主程序
DYLD:
- dyld加载所有的库和可执行文件
- dyld加载流程
- 程序执行从_dyld_start 开始
- 进入 dyld:main 函数
- 配置一些环境变量
- 加载共享缓存库,一开始就判断是否禁用,iOS无法被禁用
- 实例化主程序
- 加载动态库
- 链接主程序
- 最关键的地方:初始化方法
- 经过一些列初始化到 调用 notifySingle 函数
- 该函数会执行一个回调
- 通过断点调试:该回调是 _objc_init初始化的时候赋值的一个函数load_Images
- load_images 里面执行的是 call_load_methods函数
- call_load_methods 函数循环调用各个类的load方法
- doModInitFunctions 函数
- 内部会调用带 __attribute__((constructor)) 的c函数
- 返回主程序的入口函数,开始进入主程序的main函数