应用程序会依赖不少的库,包括系统的,如UIKit
、CoreFoundation
,还有第三方的。c++
什么是库?bootstrap
- 库是可执行的二进制文件,可以被操做系统加载到内存。
- 库又分为静态库和动态库。
在咱们使用断点断住程序的时候,xCode
左侧Thread1
最下面会调用start
函数这个函数来自libdyld.dylib
缓存
1.下载dyld
最新源码,目前最新是852
。dyld源码下载markdown
2.全局搜索_dyld_start
app
3.全局搜索c++
函数的命名空间dyldbootstrap
,在命名空间所在文件搜索start
函数函数
4.在start
函数中最后一步return
到dyld::_main
,进入main
函数。oop
能看到
main
函数占用了八百多行代码。这里不打算一开始就从上往下一行一行分析,而是使用反推法倒着分析,先了解主要流程。源码分析
1.函数的末尾返回了result
,查看result
在函数体内有哪些赋值操做。post
2.下面两处都是sMainExecutable
在调用函数的返回值测试
3.查看sMainExecutable
在 _main
函数中的出处
查看初始化函数instantiateFromLoadedImage
返回
machO
读取对象。
4.连接sMainExecutable
5.弱引用绑定主程序
6.运行全部已经初始化的东西
7.通知主程序进入的main()
函数
主线流程已经分析完,下面跟随主线流程看看细节
1.首先_main
函数前两百多行代码是条件准备,包括环境、平台、版本、路径、主机等信息的处理 加载插入的动态库
2.读取共享缓存
3.在连接主程序前面,读取插入的动态库
4.在连接主程序后面,连接插入的动态库
插入的动态库、主程序都调用了
runInitializers
函数
由于gLinkContext.notifySingle = ¬ifySingle;
全局搜索registerObjCNotifiers
的调用
发现是_dyld_objc_notify_register
调用的,并且这个函数是在objc
源码是函数_objc_init
调用的
接下来在
objc
源码中_objc_init
打上断点,打印调用栈
再次使用反推法:
由函数调用栈能看到
libobjc.A.dylib
调用了_objc_init
libdispatch.dylib
调用了_os_object_init
_os_object_init
实现:
_os_object_init
函数内部调用了_objc_init
libdispatch.dylib
调用了libdispatch_init
libdispatch_init
实现:
libdispatch_init
调用了_os_object_init
libSystem.B.dylib
调用了libSystem_initializer
libSystem_initializer
实现:libSystem_initializer
调用了libdispatch_init
dyld ImageLoaderMachO::doModInitFunctions
函数
doModInitFunctions
实现:
也就是说
doModInitFunctions
确保libSystem
初始化
dyld ImageLoaderMachO::doInitialization
doInitialization
实现:
doInitialization
调用了doModInitFunctions
dyld ImageLoader::recursiveInitialization
recursiveInitialization
实现: 这一块
initializeMainExecutable
分析流程也提到了
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
在_objc_init
中_dyld_objc_notify_register
有三个参数&map_images
、load_images
、unmap_image
, _dyld_objc_notify_register
把参数原封不动的传给了registerObjCNotifiers
在
registerObjCNotifiers
内部:
sNotifyObjCMapped
=mapped
sNotifyObjCInit
=init
sNotifyObjCUnmapped
=unmapped
map_images
调用就是sNotifyObjCMapped
的调用全局搜索sNotifyObjCMapped
在哪里调用?
全局搜索notifyBatchPartial
在哪里调用?
也就是map_images(sNotifyObjCMapped)
在registerObjCNotifiers
->notifyBatchPartial
函数内调用
load_images
调用就是sNotifyObjCInit
的调用全局搜索sNotifyObjCInit
在哪里调用?
全局搜索notifySingle
在哪里调用?
测试代码准备:
int main(int argc, char * argv[]) {
printf("main函数调用 %s \n",__func__);
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
__attribute__((constructor)) void cppFunc(){
printf("C++函数调用 : %s \n",__func__);
}
复制代码
@implementation ViewController
+ (void)load{
NSLog(@"\n %s 函数调用",__func__);
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
@end
复制代码
执行结果以下: 调用顺序依次为
load
、c++
函数、main
函数
在_objc_init
调用_dyld_objc_notify_register
,_dyld_objc_notify_register
第二个参数load_images
定义以下:
load
的方法:
load
方法 因此
load
的方法在_objc_init
即将结束时就调用了。
在c++
函数内打上断点,查看函数调用栈
在调用
doModInitFunctions
后调用了cppFunc
,doModInitFunctions
是读取machO
的,因此这个c++
函数是写在machO
中的
在dyld
源码中搜索_dyld_start
的汇编 在调用完
dyldbootstrap::start
后会调到main
函数
实操验证:
dyldbootstrap::start
register read
读取寄存器rax
就是main
函数