文章目录
前言
@interface ViewController : UIViewController
@end
@implementation ViewController
+ (void)load
{
NSLog(@"load");
}
@end
int main(int argc, char * argv[]) {
NSLog(@"main");
NSString * appDelegateClassName;
@autoreleasepool {
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
//__attribute__((constructor))编译器constructor属性,会让函数在main()之前执行
__attribute__((constructor)) static void abc() {
printf("abc\n");
}
运行结果如下
2020-09-26 14:26:35.519913+0800 RuntimeLeaning[2704:1529828] load
abc
2020-09-26 14:26:35.521017+0800 RuntimeLeaning[2704:1529828] main
可以看到打印顺序为load->abc->main, 在load方法中打上断点,然后运行
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* frame #0: 0x0000000100bcded4 RuntimeLeaning`+[ViewController load](self=ViewController, _cmd="load") at ViewController.m:23:5
frame #1: 0x000000020b669ffc libobjc.A.dylib`call_load_methods + 184
frame #2: 0x000000020b66be54 libobjc.A.dylib`load_images + 180
frame #3: 0x0000000100fc2390 dyld`dyld::notifySingle(dyld_image_states, ImageLoader const*, ImageLoader::InitializerTimingList*) + 444
frame #4: 0x0000000100fd4380 dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 440
frame #5: 0x0000000100fd33dc dyld`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 136
frame #6: 0x0000000100fd3498 dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 84
frame #7: 0x0000000100fc26d8 dyld`dyld::initializeMainExecutable() + 220
frame #8: 0x0000000100fc72a0 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 4304
frame #9: 0x0000000100fc1044 dyld`_dyld_start + 68
(lldb)
从这个堆栈信息中可以知道在最开始先调用的是dyld的_dyld_start方法,dyld是苹果的动态链接器, 是苹果操作系统一个重要组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作。
dyld源码下载:dyld源码,下面使用750.6版本。
dyld源码分析
一、dyld汇编入口点:__dyld_start
在dyld源码中搜索_dyld_start,发现只有__dyld_start, 存在于dyld.xcconfig和dyldStartup.s中, dyld.xcconfig从后缀看是个配置文件, 里面有一段如下:
ENTRY[sdk=*simulator*] = -Wl,-e,_start_sim //模拟器
ENTRY[sdk=iphoneos*] = -Wl,-e,__dyld_start //真机
ENTRY[sdk=macosx*] = -Wl,-e,__dyld_start //mac
再看dyldStartup.s从后缀看是汇编文件,名字涵义就是dyld启动, 下面是其中arm64的部分代码
__dyld_start:
......
// call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
bl __ZN13dyldbootstrap5startEPKN5dyld311MachOLoadedEiPPKcS3_Pm
mov x16,x0 // save entry point address in x16
ldr x1, [sp]
cmp x1, #0
b.ne Lnew
// LC_MAIN case, set up stack for call to main()
Lnew: mov lr, x1 // simulate return address into _start in libdyld.dylib
......
#if __arm64e__ //用于A12芯片组,从2018年XS、XS MAX、XR开始
braaz x16 //带指针验证,转到寄存器x16
#else
br x16
#endif
从上面可以看到,先是调用了dyldbootstrap::start,后面如果x1不等于0就会进入程序的main()函数。
// This is code to bootstrap dyld. This work in normally done for a program by dyld and crt.
// In dyld we have to do this manually.
uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[],
const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue)
{
dyld3::kdebug_trace_dyld_marker