Dyld的原理
dynamic load: 动态加载.它主要是用的实现库之间的动态链接,库不用被直接编译到可以执行文件中,而是在执行的时候才会去link,达到动态加载的效果.共享动态库就是利用这个原理进行的。
- 如下通过
otool -L 可执行文件
可以看到它包含了如下信息,其中@rpath/xxx
和/usr/lib/
就标示它的动态链接路径.shell xxx@xxx wwdcc % otool -L App
## dyld环境变量速查
App:
@rpath/App.framework/App (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.0.0) 通过
man dyld
可以查看他的帮助命令
|Command|Description|
|---|---|
|DYLD_FRAMEWORK_PATH|动态库加载路径的集合,系统会按照这几个文件夹路径搜索动态库,XXX.framework/Versions/A/XXX or XXX.framework/XXX,根据所安装的库名字去查找|
|DYLD_FALLBACK_FRAMEWORK_PATH|从默认的文件路径查找 /Library/Frameworks:/System/Library/Frameworks|
|DYLD_VERSIONED_FRAMEWORK_PATH|优先使用新版本的framework|
|DYLD_LIBRARY_PATH|搜索dylib库的文件路径集合|
|DYLD_FALLBACK_LIBRARY_PATH|默认dylib库,如/usr/local/lib:/usr/lib.|
|DYLD_VERSIONED_LIBRARY_PATH|优先使用新版本的dylib|
|DYLD_PRINT_TO_FILE|日志输出的文件路径,通常用来记录错误|
|DYLD_SHARED_REGION|avoid不使用共享缓存,private,删除共享区域的地址空间和mmap,拷贝到私有范围的dyld共享缓存区域|
|DYLD_INSERT_LIBRARIES|在程序加载前动态的插入新的库|
|DYLD_FORCE_FLAT_NAMESPACE|防止其他的共享库名字冲突覆盖,强制指定当前这个库|
|DYLD_IMAGE_SUFFIX|为动态库设置统一后缀|
|DYLD_PRINT_OPTS|设置打印的文件描述符为2,标准错误输出|
|DYLD_PRINT_ENV|环境变量输出|
|DYLD_PRINT_LIBRARIES|打印加载的库|
|DYLD_BIND_AT_LAUNCH|在启动时绑定所有应用程序需要的符号,一般建议还是使用默认的懒加载|
|DYLD_PRINT_STATISTICS|打印动态库的主要方法的时间,分析启动时间非常有用|
|DYLD_DISABLE_DOFSDYLD_DISABLE_DOFS|不像kernel
注册静态检测|
|DYLD_PRINT_INITIALIZERS|打印构造初始化函数, C++ statically allocated objects, 标记为 attribute((constructor))的函数, and -init functions.|
|DYLD_PRINT_APIS|打印 dyld api的调用|
|DYLD_PRINT_SEGMENTS|打印dyld 加载时所map的mach-o segment|
|DYLD_PRINT_BINDINGS|打印binding的符号信息|
|DYLD_PRINT_DOFS|使dyld打印出关于已向内核注册的dtrace静态探测的信息。|
|DYLD_PRINT_RPATHS|打印@rpath路径|
|DYLD_SHARED_CACHE_DIR|打印共享文件路径,通常与DYLD_SHARED_REGION=private and DYLD_SHARED_CACHE_DONT_VALIDATE 结合使用|
|DYLD_SHARED_CACHE_DONT_VALIDATE|不关系在不在,直接去加载它|
| DYNAMIC LIBRARY LOADING|设置动态库加载路径,drawin使用完整路径加载,通过
@executable_path/
,@loader_path/
,@rpath/
来指定它的库文件加载前缀|关于
DYNAMIC LIBRARY LOADING
在Xcode
中会经常遇到- @executable_path: 这个变量表示可执行程序所在的目录,总是被解析为同一个路径(可执行程序所在目录)
- @loader_path: 这个变量表示每一个被加载的 binary (包括可执行程序, dylib, framework 等) 所在的目录. 它INSTALL_PATH 可以设置为 @loader_path/../dylib, 这样设置的话, 不论这个库放到什么位置它都能正确加载
- @rpath: 它只是一个保存着一个或多个路径的变量,增加查找的路径
App启动时间统计
- 就App而言主要分为三种加载形式
- 冷启动(Coding launch): App在第一次安装启动
- 热启动(Warming launch): App关闭后立即启动(有共享缓存,暂时没销毁)
- 正常启动: App相关的部分共享缓存释放
- 在Xcode中通过设置环境变量
DYLD_PRINT_STATISTICS_DETAILS=1
可以看到dylib在linker的过程中所有关键阶段的时间dtext total time: 4.0 seconds (100.0%)
total images loaded: 691 (636 from dyld shared cache)
total segments mapped: 178, into 23263 pages
total images loading time: 3.0 seconds (74.5%)
total load time in ObjC: 39.29 milliseconds (0.9%)
total debugger pause time: 1.5 seconds (37.7%)
total dtrace DOF registration time: 0.00 milliseconds (0.0%)
total rebase fixups: 502,572
total rebase fixups time: 70.65 milliseconds (1.7%)
total binding fixups: 45,451
total binding fixups time: 56.66 milliseconds (1.3%)
total weak binding fixups time: 33.12 milliseconds (0.8%)
total redo shared cached bindings time: 30.67 milliseconds (0.7%)
total bindings lazily fixed up: 0 of 0
total time in initializers and ObjC +load: 835.45 milliseconds (20.5%)
libSystem.B.dylib : 9.45 milliseconds (0.2%)
libBacktraceRecording.dylib : 6.27 milliseconds (0.1%)
libMainThreadChecker.dylib : 39.86 milliseconds (0.9%)
libglInterpose.dylib : 508.29 milliseconds (12.5%)
libMTLCapture.dylib : 20.72 milliseconds (0.5%)
RxSwift : 6.73 milliseconds (0.1%)
RxCocoa : 8.46 milliseconds (0.2%)
Flutter : 5.00 milliseconds (0.1%)
Realm : 11.78 milliseconds (0.2%)
SwiftProtobuf : 4.72 milliseconds (0.1%)
xx_plugin : 9.50 milliseconds (0.2%)
xxx3d : 12.38 milliseconds (0.3%)
xxxApp : 313.05 milliseconds (7.6%)
total symbol trie searches: 269548
total symbol table binary searches: 0
total images defining weak symbols: 71
total images using weak symbols: 162
dyld主要功能
docs->dyld.codes
包括了dyld的主要功能大纲codes 0x1f070000 dyld.static_intializer
## dyld执行过程,以arm64为例
0x1f070004 dyld.launch_executable
0x1f070008 dyld.map_file
0x1f07000c dyld.apply_fixups
0x1f070010 dyld.attach_codesignature
0x1f070014 dyld.build_closure
0x1f070018 dyld.add_image_callback
0x1f07001c dyld.remove_image_callback
0x1f070020 dyld.objc_image_init
0x1f070024 dyld.objc_images_map
0x1f070028 dyld.apply_interposing
0x1f07002c dyld.gdb_image_notifier
0x1f070030 dyld.remote_image_notifier
0x1f080000 dyld.dlopen
0x1f080004 dyld.dlopen_preflight
0x1f080008 dyld.dlclose
0x1f08000c dyld.dlsym
0x1f080010 dyld.dladdr
0x1f090000 dyld.DEBUG.vm_remap
0x1f090004 dyld.DEBUG.vm_dealloc
0x1f090008 dyld.DEBUG.map_loop
0x1f09000c dyld.DEBUG.marker- kernel内核做好相关的初始化工作之后,从汇编文件
dyldStartup.s -> __dyld_start
`` #if arm64 ...
__dyld_start: ...
// call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
// LC_MAIN case, set up stack for call to main() ...
b.ne Lapple // main param4 = apple
...
endif // arm64
2. dyldInitialization.cpp -> dyldbootstrap::start
-> rebaseDyld, 修改二进制位置,安全策略考虑
-> runDyldInitializers //初始化C++的函数,
extern const Initializer inits_start __asm("section$start$__DATA$__mod_init_func");
extern const Initializer inits_end __asm("section$end$__DATA$__mod_init_func");
-> dyld::_main
1. dyld2.cpp
-> _main
## TODO: