案例
新建一个demo, 打印一下输出顺序.
@interface ViewController ()
@end
__attribute__((constructor)) void cFunc(){
printf("constructor... %s \n",__func__);
}
@implementation ViewController
+ (void)load{
NSLog(@"%s", __func__);
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"%s", __func__);
}
@end
上图可只, 打印顺序为
+[ViewController load]
→constructor函数
→main函数
→-[ViewController viewDidLoad]
.为什么? 不应该是main是程序入口, mian之前怎么会有函数调用. 今天来探索一下程序启动流程
程序编译阶段
稍微详细点的, 大家可以看这篇文章 iOS同学需要了解的基本编译原理
静态库
静态库是编译阶段, 会直接复制一份进入程序中. 后缀一般为.a, .lib, .framework
等等.
一般静态库里面可以含有单一架构, 也可以含有多种架构, 正常使用的OC静态库, 一般都是含有
arm64
和armv7
两种架构lipo
可以拆分架构,libtool
可以从库中输出.o文件. 拆分之后修改文件在进行合并, 在有冲突的情况下, 可以进行一些处理.
-
优点
: 程序没有外部依赖, 可以直接运行 -
缺点
: 会使目标程序体积变大.
动态库
程序编译时, 不会连接进入目标程序中, 运行时会被载入内存. .so, .dll, .framework
-
优点
: 减少打包后app的体积, 共享内容, 解约资源, 动态更新 -
缺点
iOS上动态库是在运行时加载的, 会有一定的性能消耗, 会依赖于一定环境运行等.
编译
.h、.m、.cpp等源文件→预编译(输出.i)→编译(输出.s)→汇编(.o)→链接→可执行文件。(那一坨黑不溜秋的东西就是可执行文件)
核心就是每个阶段的输出产物, 就是下个阶段的输入.
还有一些AST语法树什么的, 我也是大致看过了解一下, 记不清..
程序员的自我修养
就是讲这些的, 很多人推荐, 有兴趣的可以自己读.
动态链接器 dyld
dyld
主要作用是, 在app编译之后生成Mach-O可执行文件, 对其进行链接, 加载程序, 修复符号等操作.
探索
在constructor
内部打下断点, 打印堆栈, 可以看到堆栈的流程.
dyld _dyld_start
→
dyld dyldbootstrap::start
→
dyld dyld::_main
→
dyld dyld::useSimulatorDyld
→
dyld dyld::_main
→
dyld dyld::initializeMainExecutable()
→
dyld ImageLoader::runInitializers
→
dyld ImageLoader::processInitializers
→
dyld ImageLoader::recursiveInitialization
→
dyld ImageLoaderMachO::doInitialization
→
dyld ImageLoaderMachO::doModInitFunctions
→
从上面流程可知, 从_dyld_start
开始, 依次往下运行, 走到我们了我们的类中的cxx
加载, 然后到mian
函数.
dyld源码跟踪
源码
可以直接查询最新的下载完毕之后, 全局搜索_dyld_start
我这里截取了arm64架构下的走向, 不需要都看懂, 图03-04-05我们可以看到整个流程是这样的.
-
_dyld_start
→ -
dyldbootstrap::start
→ ...
-
mian() jump to main(argc,argv,env,apple)
也就是说, 我们现在还未确认的流程就是上面的...
流程, 那我们继续看看dyldbootstrap::start
下面做了什么吧, 这个是C++语法, 不需要会, 只需要知道::
前面是命名空间, 相当于一个类, 后面是调用的方法. 所以我们全局先搜索dyldbootstrap
命名空间
上面看了也没啥用, 直接看注释就行和主流程关系不大但是在这里已经使用到整个macho了, 直接看最下面main函数.
900行代码, 说真的有点头大... , 摘录下来:
//1.检查运行环境变量
checkEnvironmentVariables(envp);
defaultUninitializedFallbackPaths(envp);
//2.加载共享缓存, 检查是否开启,以及共享缓存是否映射到共享区域
mapSharedCache(mainExecutableSlide);
// 3.实例化ImageLoader对象
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
gLinkContext.mainExecutable = sMainExecutable;
gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
// 4.加载插入动态库, 环境变量为DYLD_INSERT_LIBRARIES
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
// 5.链接主程序
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
sMainExecutable->setNeverUnloadRecursive();
// 6.链接动态库
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
image->setNeverUnloadRecursive();
}
}
// 7.初始化主程序, 运行所有initializer
initializeMainExecutable();
// 8. 主程序入口 LC_MAIN代表的就是main()
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
if ( result != 0 ) {
// main executable uses LC_MAIN, we need to use helper in libdyld to call into main()
if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
*startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
else
halt("libdyld.dylib support not present for LC_MAIN");
}
else {
// main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
*startGlue = 0;
}
整个流程就是上面 , 最后也找到了LC_MAIN.
注释说到了, LC_MAIN case, 会调用 mian().
接下来看一下主程序的实例化:
主程序的实例化,
sniffLoadCommands
这个里面加载mh的各种命令, 里面有很多报错, machO的格式什么的都在里面可以看到, 加载逻辑, 各种case
, 建议有需求的可以翻一翻.
根据上面堆栈, 我们接下来该寻找initializeMainExecutable(). 在我们上面注释的7流程.
简要的看一下
void initializeMainExecutable()
{
// record that we've reached this step
// 记录已经到了初始化程序
gLinkContext.startedInitializingMainExecutable = true;
// run initialzers for any inserted dylibs
// 初始化所有插入的动态库
ImageLoader::InitializerTimingList initializerTimes[allImagesCount()];
initializerTimes[0].count = 0;
const size_t rootCount = sImageRoots.size();
if ( rootCount > 1 ) {
for(size_t i=1; i < rootCount; ++i) {
sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]);
}
}
// run initializers for main executable and everything it brings up
// 为主可执行文件及内部的一切运行初始化程序
sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
// register cxa_atexit() handler to run static terminators in all loaded
// 注册退出函数
// dump info if requested
// 如果需要,转储信息
}
接下来看看runInitializers
内部
void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo)
{
//上面的流程一样
processInitializers(context, thisThread, timingInfo, up);
context.notifyBatch(dyld_image_state_initialized, false);
}
接下来看看processInitializers
内部
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread,
InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
{
uint32_t maxImageCount = context.imageCount()+2;
ImageLoader::UninitedUpwards upsBuffer[maxImageCount];
ImageLoader::UninitedUpwards& ups = upsBuffer[0];
ups.count = 0;
// Calling recursive init on all images in images list, building a new list of
// uninitialized upward dependencies.
// 对镜像列表中的所有镜像递归初始化,构建一个新的未初始化向上依赖列表。
for (uintptr_t i=0; i < images.count; ++i) {
images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups);
}
// If any upward dependencies remain, init them.
// 如果任何向上的依赖仍然存在,初始化它们。
if ( ups.count > 0 )
processInitializers(context, thisThread, timingInfo, ups);
}
在这里我们找到了recursiveInitialization
, 这个方法应该是全局最重要的方法了, 下面会具体查看.
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
recursive_lock lock_info(this_thread);
recursiveSpinLock(lock_info);
if ( fState < dyld_image_state_dependents_initialized-1 ) {
uint8_t oldState = fState;
// break cycles
fState = dyld_image_state_dependents_initialized-1;
try {
// initialize lower level libraries first
// record termination order
// let objc know we are about to initialize this image
// initialize this image
bool hasInitializers = this->doInitialization(context);
// let anyone know we finished initializing this image
}
catch (const char* msg) {
// this image is not initialized
throw;
}
}
recursiveSpinUnLock();
}
在上面我们找到了, doInitialization
bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
CRSetCrashLogMessage2(this->getPath());
// mach-o has -init and static initializers
// mach-o 镜像初始化
doImageInit(context);
doModInitFunctions(context);
return (fHasDashInit || fHasInitializers);
}
我们找到了doModInitFunctions
, 至此我们找到了上面堆栈的整个流程.
核心
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
recursive_lock lock_info(this_thread);
recursiveSpinLock(lock_info);
if ( fState < dyld_image_state_dependents_initialized-1 ) {
uint8_t oldState = fState;
// break cycles
fState = dyld_image_state_dependents_initialized-1;
try {
// initialize lower level libraries first
// 首先初始化优先级低的库(就是被依赖的库)
for(unsigned int i=0; i < libraryCount(); ++i) {
ImageLoader* dependentImage = libImage(i);
if ( dependentImage != NULL ) {
// don't try to initialize stuff "above" me yet
if ( libIsUpward(i) ) {
uninitUps.imagesAndPaths[uninitUps.count] = { dependentImage, libPath(i) };
uninitUps.count++;
}
else if ( dependentImage->fDepth >= fDepth ) {
dependentImage->recursiveInitialization(context, this_thread, libPath(i), timingInfo, uninitUps);
}
}
}
// record termination order
// 记录终止命令
if ( this->needsTermination() )
context.terminationRecorder(this);
/************ 核心 ↓ ***********/
// let objc know we are about to initialize this image
// 让objc知道, 我们要初始化这个镜像
uint64_t t1 = mach_absolute_time();
fState = dyld_image_state_dependents_initialized;
oldState = fState;
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
// initialize this image
// 初始化image
bool hasInitializers = this->doInitialization(context);
// let anyone know we finished initializing this image
// 让所有人知道我们完成了这个初始化
fState = dyld_image_state_initialized;
oldState = fState;
context.notifySingle(dyld_image_state_initialized, this, NULL);
/************ 核心 ↑***********/
if ( hasInitializers ) {
uint64_t t2 = mach_absolute_time();
timingInfo.addTime(this->getShortName(), t2-t1);
}
}
catch (const char* msg) {
// this image is not initialized
// 这里的镜像没有初始化
fState = oldState;
recursiveSpinUnLock();
throw;
}
}
recursiveSpinUnLock();
}
关注上面的核心部分
1. 依赖库 notifySingle
2. 初始化镜像 doInitialization
3. 完成初始化 notifySingle
然后我们看notifySingle
//dyld_image_state_dependents_initialized, sNotifyObjCInit!=null, notifyObjC
static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
uint64_t t0 = mach_absolute_time();
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
/** 重点 入参image路径, macho的header, 要根据head去解析 **/
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
uint64_t t1 = mach_absolute_time();
uint64_t t2 = mach_absolute_time();
uint64_t timeInObjC = t1-t0;
uint64_t emptyTime = (t2-t1)*100;
if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) {
timingInfo->addTime(image->getShortName(), timeInObjC);
}
}
}
接下来继续查找可知
// _dyld_objc_notify_init
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
//赋值
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
// call 'mapped' function with all images mapped so far
try {
notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
}
catch (const char* msg) {
// ignore request to abort during registration
}
// <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
}
}
}
继续搜索registerObjCNotifiers
// _dyld_objc_notify_register
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped)
{
dyld::registerObjCNotifiers(mapped, init, unmapped);
}
所以这一条线就是
-
_dyld_objc_notify_register
调用 -
registerObjCNotifiers
给sNotifyObjCInit
赋值` -
notifySingle
中调用了sNotifyObjCInit
- 在
recursiveInitialization
中调用了notifySingle
.
最后就等于是在recursiveInitialization
中调用了sNotifyObjCInit
.
接下来我们关注_dyld_objc_notify_register
是在何时被赋值的就可以了.
其实这个回调函数还是挺出名的. 相当于dyld
在libobjc
中下的句柄`:
也就是说我们现在要找到_objc_init
在什么时候调用, 才能知道, 调试源码打印堆栈:
也就是说, 我们在堆栈看到了调用顺序, 我们需要在dyld
的doModInitFunctions
中找到看看是否调用了libSystem_initializer
.
我删除掉大部分代码, 只留下核心部分.
void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
{
if ( fHasInitializers ) {
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
const uint8_t type = sect->flags & SECTION_TYPE;
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
// <rdar://problem/17973316> libSystem initializer must run first
/*****libSystem initializer must run first*****/
const char* installPath = getInstallPath();
if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) )
dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath());
}
else if ( type == S_INIT_FUNC_OFFSETS ) {
//与上面一样
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
}
此致我们就找到了dyld的调用, 结合上面的流程.
_dyld_start
dyldbootstrap::start
dyld::_main
dyld::initializeMainExecutable
ImageLoader::runInitializers
ImageLoader::processInitializers
ImageLoader::recursiveInitialization
ImageLoaderMachO::doInitialization
ImageLoaderMachO::doModInitFunctions
-
dyld::gProcessInfo->libSystemInitialized
这里调用的libSystem
框架
-
-
libdispatch_init
这里是libdispatch.dylib
框架
-
-
_os_object_init
这里是libdispatch.dylib
框架
-
-
_objc_init
这里是libobjc
框架
-
-
_dyld_objc_notify_register
调用, 赋值, 通知dyld
回调函数是_dyld_objc_notify_register
, 前面分析过sNotifyObjCInit
的调用时机.
-
在
recursiveInitialization
中doInitialization
整个流程执行完毕了之后, 才调用了notifySingle.
(上面的8-14的流程都是doInitialization内部的流程)
notifySingle
中调用了sNotifyObjCInit
_dyld_objc_notify_register
调用registerObjCNotifiers
registerObjCNotifiers
内部给sNotifyObjCInit
赋值()
实际上如下:
sNotifyObjCMapped
=&map_images
sNotifyObjCInit
=load_images
最后就等于是在recursiveInitialization
中调用了sNotifyObjCInit
.
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
// call 'mapped' function with all images mapped so far
try {
//调用了sNotifyObjCMapped
notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
}
catch (const char* msg) {
// ignore request to abort during registration
}
// <rdar://problem/32209809>
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
//调用了sNotifyObjCInit, 这里注释为系统库下的init
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
}
}
}
notifyBatchPartial
中调用了notifyBatchPartial
, 下面的循环中调用了sNotifyObjCInit, 所以看的出来mapimage比loadimage先调用了.
整个流程是个递归流程.
总结
流程太长了, 有点乱, 其实只需要看最后面的1-14, 然后看回调函数的注册和调用的地方就可以了.