美文网首页
iOS底层-dyld加载分析

iOS底层-dyld加载分析

作者: 似水流年_9ebe | 来源:发表于2021-07-12 07:31 被阅读0次

引言:

众所周知,我们的iOS应用是通过Dyld进行加载的,那么Dyld是如何加载我们的应用的,它的流程是怎样的,下面我们把dyld的加载分为几个步骤做个简短的分析。

1 dyld的start启动

首先我们创建一个Demo工程,在我们的AppDelegate.m文件里加入+(load)方法并断点,如下图所示:

1

运行Demo App后,可以得到所下图

2

从图2中我们可以看到,我们的App是从_dyld_start开始的,我们点击dyld_start,看到汇编的第18行代码,这里调用了dyldbootstrap::start(macho_header const, int, char const, long, macho_header const, unsigned long*)这行代码,我们再使用bt命令看下详细的堆栈,如下图

3

从这里可以看出一切的开始是从_dyld_start开始的,这个dyld_start在可以在dyld的源码里找(注:这里使用的dyld的源码的版本是dyld-832.7.3),下面我们打开dyld的源码进行分析

我们在dyld的源码里找 dyldbootstrap这个命名空间,按住shift+comand+j进入文件,所下图所示

4

从这里可以看到是在dyldInitialization.cpp文件里,然后我们在文件里搜索start,找到uintptr_tstart(constdyld3::MachOLoaded* appsMachHeader,intargc,constchar* argv[],

constdyld3::MachOLoaded* dyldsMachHeader,uintptr_t* startGlue)函数,如图所示:

image

我们从start函数逐步往下分析整个流程,我们先看下参数,appsMachHeader是我们App的MachHeader,dyldsMachHeader是dyld的MachHeader。

第121行代码是告诉我们的degbugServer,我的dyld开始起动了。

第125行代码rebaseDyld(dyldsMachHeader) 重定位我们的dyld。

第136,143行是栈溢出保护和初始化dyld,之后就是调用dyld的main函数(这是最核心的),我们着重分析下dyld的main函数流程。

5

main函数的前几行代码都是代码检测相关的,不是核心内容

我们下面来看主程序的配置相关,如图

6

这些是配置主程序的MachHeader(就是Macho的头),主程序的Slide(就是主程序的ASLR的偏移值,每次启动都是不一样的)

下面调用setContext(mainExecutableMH, argc, argv, envp, apple);保存我们配置的信息

7

然后通过configureProcessRestrictions(mainExecutableMH, envp)这个函数配置进程受限制(AMFI相关(Apple Mobile File Integrity苹果移动文件保护)),下图都是进程受限相关的配置,比如是否强制使用dyld3(dyld是在iOS11推出来的,加载高效)

8

下图打印我们的环境变量,这个环境变理可通过 Environment Variables配置

9

以下都是dyld的启动,配置,以及主程序的相关配置和一些代码检测的流程,下面我们来分析共享缓存

2 dyld加载共享缓存

10

点击进去checkSharedRegionDisable发现有一个“iOS cannot run without shared region”说明,这是表明我们的iOS是一定有共享缓存的。

接着调用mapSharedCache传进去主程序的Slide,这个函数调用了loadDyldCache加载我们的dyld缓存,如下图所示

11

满足options.forcePrivate 这个条件的话,只加载当前进程

reuseExistingCache如果缓存已经加载不再处理,如果第一次加载执行mapCacheSystemWide这个函数

通过以上分析,可以得出结论,动态库的共享缓存是最先被加载的(我们自己开发的动态库不可以)。从iOS11引入了dyld3的ClosureMode(闭包模式加载更快),下面我们来分析一下

3 dyld3的闭包模式

12

这里先判断闭包模式是否打开,如果没有的话将会走dyld2的流程,打开走dyld3的流程(dyld2,dyld3的加载流程一致),下面我们来分析dyld3的闭包模式

13

先从共享缓存中查找这个实例,如果拿到就先验证

14

这里判断是否查找成功,并且验证闭包的有效性,如果失效,sLaunchModeUsed设置为NULL

15

这里如果没找到,再去缓存中查一次,如果mainClosure为空,这里就调用buildLaunchClosure创建闭包实例

最终拿到这个mainClosure实例启动这个实例,如下图所示

16

如果启动失败或者闭包过期,这里就再重新调用buildLaunchClosure创建并调用launchWithClosure重新启动一次。

启动成功后设置gLinkContext.startedInitializingMainExecutable = true;这个主程序加载成功了。

同时返回结果result(即主程序的main),如下图所示:

17

接着就会实例化我们的主程序了,下面我们来分析是如何加载的。

4 dyld加载主程序

接下看下怎么实例化主程序的,如下图所示

18

第6862行代码会调用instantiateFromLoadedImage函数实例化我们的主程序,我们来看下instantiateFromLoadedImage这个函数,下图所示:

19

这里通过ImageLoaderMachO这个函数传image的macho_header,ASLR的偏移值,路径生成ImageLoader对象,然后调用addImage这个函数加入我们的镜像文件,同时返回ImageLoader这个对象。(通过dyld加载的第一个镜像是我们的主程序),我们来看下instantiateMainExecutable的这个函数的流程,如图所示:

20

这里调用sniffLoadCommands获取loadCommands,如图:

21

compressed是根据Macho中的LG_DYLD_INFO_ONLY和LG_LOAD_DYLINKER来获取的。

segCount是SEGMENT的数量,最大不能超过255。

libCount是LC_LOAD_DYLIB加载动态库的个数,最大不能超过4095。

*codeSigCmd是代码签名。

*encryptCmd是代码加密信息。

ImageLoaderMachO这个函数调用sniffLoadCommands这个之后会根据compressed这个变量判断调用ImageLoaderMachOCompressed或者ImageLoaderMachOClassic这两个函数实例化。

实例化完毕之后添加到AllImage中。

22

接着检测当前主程否是当前设备的,如上图所示,到这里我们的主程序实例化结束,接着我们来分析动态库的加载。

5 dyld加载动态库

23

这里先检查动态库的版本和路径,接着加载动态库,如图所示:

24

这里先根据环境变量判断动态插入库不为空,接着遍历loadInsertedDylib

25

这里调用load插入动态库,接着开始链接主程序,

26

先配置gLinkContext.linkingMainExecutable = true;这个变量为true.

接着调用link函数进行链接,我们来看看是如何链接的:

27 28 29

这里先记录起始时间,在最后在记录结束时间,把加载时间记录下来,这个就是dyld加载应用的时长。

这里链接插入动态库完成了。

30

之后把这些实例化的镜像文件加入到AllImages中(这里是从i+1开始的,因为主程序已经先加载了),之后再调用link进行链接,这里跟主程序的链接是一样的。

31

这里的条件不满足的话,将会持续的调用reloadAllImage,这里执行之后,就开始绑定动态库了,如图所示:

32

这里遍历AllImages绑定插入动态库,之后进行弱符号绑定。

接着调用initializeMainExecutable初始化主程序的Main方法,如图所示:

33

下面我们来分析主程序Main方法加载的流程。

6 load方法与初始化方法的加载

我们先进入initializeMainExecutable()这个函数,看下它的实现

34

这里有一个runInitializers函数,我们再进去

35

这里会调用processInitializers这个函数,我们再跟进去看看

36

接着我们再跟下recursiveInitialization这个函数

37 38

这里调用了notifySingle这个函数,我们需要再跟进去一下,

39 40

而这里没有找到loadImge的函数调用,这里到底是怎么回事,我们通过汇编可以看到load_image是在libobjc.dylib中,也就是说在objc的源码中,那它是怎么调用的,我们来看下代码。

在1019行中 (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()) 这个回调进行关联的。

这里首先判断sNotifyObjCInit这个是否为空,我们在这文件里搜下,发现是在registerObjCNotifiers这里调用的时候赋值的,如下图:

41

我们搜下registerObjCNotifiers这个函数发现是在dyldAPIS.cpp中的

42

这个函数调用的,这里有传递init进来,那么又是谁调用的_dyld_objc_notify_register这个函数呢,搜了之后,发现dyld里没用调用的,那么怎么办呢,我们可以在Demo工程中下一个符号断点_dyld_objc_notify_register,结果发现是在libobjc.dylib中的_objc_init调用的,下面打开objc的的源代码,按信shift+command+o找到定义,再按住shift+command+j找到源文件是在objc-os.mm文件中,如图所示:

43

这里可以看到_dyld_objc_notify_register这个函数是在_objc_init调用的,这里有一个load_images,我们再看下

44

这里面有一个call_load_methods方法,点进去看下

45

这里会do while调用call_class_loads方法来加载所有类的+(void)load方法,load方法加载完成后调用了call_category_loads这个方法,加载类加的loads方法,这也是为什么类别的方法与原类的方法重名后,会覆盖原类的方法。

我们回到dyld的源代码找到ImageLoader.cpp文件中的recursiveInitialization函数中调用notifySingle这里走到了objc中,objc把所有的load加载完成后,会调用doInitialization这个函数,进去看下

46

这里doModInitFunctions调用这个函数,这个函数的作用是什么,我们来看下,

47

这里就是在加载我们的构造函数,我们在Demo的main.m上面加入构造函数

attribute((constructor)) void test1() {

printf("test调用了");

}

经过调试,它比main函数先调用。

我们再回到dyld的main函数,找到这里,如图

48

这里通过LC_MAIN找到程序入口给result,最后返回主程序的main地址。dyld的加载就结束了。

下面是dyld的initializeMainExecutable初始化主程序的思维导图

49

结语:到这里dyld的流程分析就结束了,有所遗漏或者错误的地方,请指正,大家可以相互学习交流,共同进步!

补充 objc与dyld是如何关联?

接上文,我们知道notifySingle是在libobjc.dyld动态库中找到了,那么它在dyld源码中是怎么赋值的呢,经过我们的一系列的搜索查找,终于找到了,如图:

50
第4803行代码中,我们看到了notifySingle的函数指针给了gLinkContext.notifySingle,我们贴一下这个函数的代码:
static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
    //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath());
    std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sSingleHandlers);
    if ( handlers != NULL ) {
        dyld_image_info info;
        info.imageLoadAddress   = image->machHeader();
        info.imageFilePath      = image->getRealPath();
        info.imageFileModDate   = image->lastModified();
        for (std::vector<dyld_image_state_change_handler>::iterator it = handlers->begin(); it != handlers->end(); ++it) {
            const char* result = (*it)(state, 1, &info);
            if ( (result != NULL) && (state == dyld_image_state_mapped) ) {
                //fprintf(stderr, "  image rejected by handler=%p\n", *it);
                // make copy of thrown string so that later catch clauses can free it
                const char* str = strdup(result);
                throw str;
            }
        }
    }
    if ( state == dyld_image_state_mapped ) {
        // <rdar://problem/7008875> Save load addr + UUID for images from outside the shared cache
        // <rdar://problem/50432671> Include UUIDs for shared cache dylibs in all image info when using private mapped shared caches
        if (!image->inSharedCache()
            || (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion)) {
            dyld_uuid_info info;
            if ( image->getUUID(info.imageUUID) ) {
                info.imageLoadAddress = image->machHeader();
                addNonSharedCacheImageUUID(info);
            }
        }
    }
    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);
        (*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);
        }
    }
    // mach message csdlc about dynamically unloaded images
    if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) {
        notifyKernel(*image, false);
        const struct mach_header* loadAddress[] = { image->machHeader() };
        const char* loadPath[] = { image->getPath() };
        notifyMonitoringDyld(true, 1, loadAddress, loadPath);
    }
}

其中(sNotifyObjCInit)(image->getRealPath(), image->machHeader());*
这行代码有一个sNotifyObjCInit这个类型,我们全局搜索一下,如图:

51
发现它是一个_dyld_objc_notify_init这个类型 ,我们接着往下搜索,如图:
52

发现sNotifyObjCInit是在这里通过函数的参数进行的初始化。
我们再搜索一下registerObjCNotifiers这个函数是在什么时候调用的,如图所示:

53
发现是在_dyld_objc_notify_register这个函数调用的,我们再搜索下这个函数,没有找到它的定义,只有调用,这是怎么回事?
从这里找不到的时候,我们通过汇编可以看到是在libObjc.dylib中,我们打开objc的源码,搜索,看到如下图的代码:
54
在这里我们看到了_dyld_objc_notify_register这个函数的调用,我们断点调试一下,看下堆栈,如图:
55

我们经过一系列的分析,frame13到fame7的流程我们是知道的,而从frame0到fame6这个流程是怎么回事,我们还不得而知,我们接着分析,从堆栈我们可以看到_objc_init的是由_os_object_init发起的,是在libdispatch.dylib这个动态库中,我们打开libdispatch.dylib这个库的源码,通过搜索_os_object_init,如图:

56
这里有调用_objc_init这个函数通过搜索发现是引入objc库中的_objc_init,如图:
57
通过之前的堆栈可以看出libdispatch_init调用了_os_object_init,我们再搜下libdispatch_init这个函数,找到如下代码:
void
libdispatch_init(void)
{
    dispatch_assert(sizeof(struct dispatch_apply_s) <=
            DISPATCH_CONTINUATION_SIZE);

    if (_dispatch_getenv_bool("LIBDISPATCH_STRICT", false)) {
        _dispatch_mode |= DISPATCH_MODE_STRICT;
    }


#if DISPATCH_DEBUG || DISPATCH_PROFILE
#if DISPATCH_USE_KEVENT_WORKQUEUE
    if (getenv("LIBDISPATCH_DISABLE_KEVENT_WQ")) {
        _dispatch_kevent_workqueue_enabled = false;
    }
#endif
#endif

#if HAVE_PTHREAD_WORKQUEUE_QOS
    dispatch_qos_t qos = _dispatch_qos_from_qos_class(qos_class_main());
    _dispatch_main_q.dq_priority = _dispatch_priority_make(qos, 0);
#if DISPATCH_DEBUG
    if (!getenv("LIBDISPATCH_DISABLE_SET_QOS")) {
        _dispatch_set_qos_class_enabled = 1;
    }
#endif
#endif

#if DISPATCH_USE_THREAD_LOCAL_STORAGE
    _dispatch_thread_key_create(&__dispatch_tsd_key, _libdispatch_tsd_cleanup);
#else
    _dispatch_thread_key_create(&dispatch_priority_key, NULL);
    _dispatch_thread_key_create(&dispatch_r2k_key, NULL);
    _dispatch_thread_key_create(&dispatch_queue_key, _dispatch_queue_cleanup);
    _dispatch_thread_key_create(&dispatch_frame_key, _dispatch_frame_cleanup);
    _dispatch_thread_key_create(&dispatch_cache_key, _dispatch_cache_cleanup);
    _dispatch_thread_key_create(&dispatch_context_key, _dispatch_context_cleanup);
    _dispatch_thread_key_create(&dispatch_pthread_root_queue_observer_hooks_key,
            NULL);
    _dispatch_thread_key_create(&dispatch_basepri_key, NULL);
#if DISPATCH_INTROSPECTION
    _dispatch_thread_key_create(&dispatch_introspection_key , NULL);
#elif DISPATCH_PERF_MON
    _dispatch_thread_key_create(&dispatch_bcounter_key, NULL);
#endif
    _dispatch_thread_key_create(&dispatch_wlh_key, _dispatch_wlh_cleanup);
    _dispatch_thread_key_create(&dispatch_voucher_key, _voucher_thread_cleanup);
    _dispatch_thread_key_create(&dispatch_deferred_items_key,
            _dispatch_deferred_items_cleanup);
#endif
    pthread_key_create(&_os_workgroup_key, _os_workgroup_tsd_cleanup);
#if DISPATCH_USE_RESOLVERS // rdar://problem/8541707
    _dispatch_main_q.do_targetq = _dispatch_get_default_queue(true);
#endif

    _dispatch_queue_set_current(&_dispatch_main_q);
    _dispatch_queue_set_bound_thread(&_dispatch_main_q);

#if DISPATCH_USE_PTHREAD_ATFORK
    (void)dispatch_assume_zero(pthread_atfork(dispatch_atfork_prepare,
            dispatch_atfork_parent, dispatch_atfork_child));
#endif
    _dispatch_hw_config_init();
    _dispatch_time_init();
    _dispatch_vtable_init();
    _os_object_init();
    _voucher_init();
    _dispatch_introspection_init();
}

这里有调用_os_object_init这个函数。
libdispatch_init又由谁调起的,我们通过堆栈分析发现,是在libSystem.B.dylib这个库中调用,我们打开这个库的源码,搜索发现,如下图代码:

58
这里有调用libdispatch_init这个函数,这个流程是通的,那么libSystem_initializer是由谁调起的呢,我们接着分析。
从通过之前的堆栈分析,下面就来到了dylddoModInitFunctions这个函数,我们先贴下这个函数的代码:
void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
{
    if ( fHasInitializers ) {
        const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
        const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
        const struct load_command* cmd = cmds;
        for (uint32_t i = 0; i < cmd_count; ++i) {
            if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
                const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
                const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
                const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
                for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
                    const uint8_t type = sect->flags & SECTION_TYPE;
                    if ( type == S_MOD_INIT_FUNC_POINTERS ) {
                        Initializer* inits = (Initializer*)(sect->addr + fSlide);
                        const size_t count = sect->size / sizeof(uintptr_t);
                        // <rdar://problem/23929217> Ensure __mod_init_func section is within segment
                        if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
                            dyld::throwf("__mod_init_funcs section has malformed address range for %s\n", this->getPath());
                        for (size_t j=0; j < count; ++j) {
                            Initializer func = inits[j];
                            // <rdar://problem/8543820&9228031> verify initializers are in image
                            if ( ! this->containsAddress(stripPointer((void*)func)) ) {
                                dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
                            }
                            if ( ! dyld::gProcessInfo->libSystemInitialized ) {
                                // <rdar://problem/17973316> 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());
                            }
                            if ( context.verboseInit )
                                dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
                            bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
                            {
                                dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
                                func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
                            }
                            bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
                            if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
                                // now safe to use malloc() and other calls in libSystem.dylib
                                dyld::gProcessInfo->libSystemInitialized = true;
                            }
                        }
                    }
                    else if ( type == S_INIT_FUNC_OFFSETS ) {
                        const uint32_t* inits = (uint32_t*)(sect->addr + fSlide);
                        const size_t count = sect->size / sizeof(uint32_t);
                        // Ensure section is within segment
                        if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
                            dyld::throwf("__init_offsets section has malformed address range for %s\n", this->getPath());
                        if ( seg->initprot & VM_PROT_WRITE )
                            dyld::throwf("__init_offsets section is not in read-only segment %s\n", this->getPath());
                        for (size_t j=0; j < count; ++j) {
                            uint32_t funcOffset = inits[j];
                            // verify initializers are in image
                            if ( ! this->containsAddress((uint8_t*)this->machHeader() + funcOffset) ) {
                                dyld::throwf("initializer function offset 0x%08X not in mapped image for %s\n", funcOffset, this->getPath());
                            }
                            if ( ! dyld::gProcessInfo->libSystemInitialized ) {
                                // <rdar://problem/17973316> 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());
                            }
                            Initializer func = (Initializer)((uint8_t*)this->machHeader() + funcOffset);
                            if ( context.verboseInit )
                                dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
#if __has_feature(ptrauth_calls)
                            func = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
#endif
                            bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
                            {
                                dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
                                func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
                            }
                            bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
                            if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
                                // now safe to use malloc() and other calls in libSystem.dylib
                                dyld::gProcessInfo->libSystemInitialized = true;
                            }
                        }
                    }
                }
            }
            cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
    }
}

其中有一行if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) )这行代码就是所有文件加载之前,必须把libSystem加载进来,它的优先级最高,这行也就是libSystem.B.dylib这个库的加载,Initializer func = (Initializer)((uint8_t)this->machHeader() + funcOffset);这行代码就是libSystem_initializer这个函数的加载。
我们接着再搜索下
doModInitFunctions这个函数,找到它的调用,如图:

59
是在
doInitialization*这个函数调用的,我们再搜下这个函数,找到如下代码:
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
            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
            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();
}

发现这行代码调用了bool hasInitializers = this->doInitialization(context);
doInitialization函数,这行就是初始化我们镜像文件的。
notifySingledoInitialization是怎么关联的呢,我们接着分析。
我们知道notifySingle会间接调用_dyld_objc_notify_register这个函数,
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;

会初始化这些参数。
doInitialization必然会走到_objc_init这个函数,而_objc_init又必然走到_dyld_objc_notify_register这个函数,它的一个参数map_images,那么map_images(sNotifyObjCMapped这个是dyld的叫法,其实是一回事)这个函数又是什么时候调用的,我们继续往下分析。
我们在dyld的源码中搜下sNotifyObjCMapped这个,如图所示:

60
是在notifyBatchPartial这个函数调起的,我们再搜索notifyBatchPartial这个函数,如图所示: 61
是在registerObjCNotifiers这个函数发起的调用,经过我们的再次分析sNotifyObjCInit这个函数是在notifySingle发起的调用,而它又是在recursiveInitialization被调用的(recursiveInitialization是递归调用)。
doInitialization一旦初始化,就会对map_images初始化,同时load也会初始化,紧接着就会对map_images的调用,接着对就走notifySingle,notifySingle就会对load调用

以上就是个人的一些见解,请多多指证。

相关文章

  • iOS底层原理探索--dyld加载流程分析

    iOS底层原理探索--dyld加载流程分析 参考文章:https://juejin.im/post/5e12ce8...

  • iOS底层-dyld加载分析

    引言: 众所周知,我们的iOS应用是通过Dyld进行加载的,那么Dyld是如何加载我们的应用的,它的流程是怎样的,...

  • iOS底层-dyld加载流程分析

    一、dyld简介 在iOS系统中,几乎所有的程序都会用到动态库,静态库等,而这些库在加载的时候都需要用到dyld程...

  • iOS 应用加载`dyld`篇

    iOS 应用加载dyld篇 前言 我们探索了iOS底层对象以及类的原理,对其有了大概的了解。那么iOS应用倒地是如...

  • iOS-底层原理14:dyld与objc的关联

    在上一篇文章iOS-底层原理13:dyld加载流程[https://www.jianshu.com/p/030cf...

  • iOS底层之类的加载

    在iOS底层中,关于类的加载,在应用程序开始加载时,首先通过dyld链接到动态库objc,从objc中的init方...

  • iOS底层原理17:dyld与objc的关联

    本文主要的目的是理解 dyld与objc是如何关联的 在上一篇文章iOS底层原理16:dyld源码分析[https...

  • iOS dyld加载流程

    dyld加载的详细流程可以参考文章 iOS dyld加载流程[https://www.jianshu.com/p...

  • dyld加载流程

    本文主要是分析main函数之前,底层做了什么 -- dyld的加载流程 例子 新建一个项目,在ViewContro...

  • iOS底层探索 --- dyld加载流程

    dyld加载流程图 建议大家在阅读文章的时候,结合流程图阅读。这样方便理解这个流程,可以将图片下载到本地,一边阅读...

网友评论

      本文标题:iOS底层-dyld加载分析

      本文链接:https://www.haomeiwen.com/subject/npfrpltx.html