美文网首页
07--应用加载01--应用加载流程[_dyld]

07--应用加载01--应用加载流程[_dyld]

作者: 修_远 | 来源:发表于2020-07-21 22:34 被阅读0次
    TOC

    应用程序加载

    准备条件:dyld-732.8 源码

    编译过程

    源文件 -> 预编译 -> 编译 -> 汇编 -> 链接(.a/.lib.so) -> 可执行文件

    编译过程 编译过程

    静态库

    在链接阶段,会将汇编生成的目标与引用的库一起链接打包到可执行文件中

    静态库

    动态库

    程序编译并不会链接到目标代码中,而是在程序运行时才被载入

    动态库
    • 优势

      • 减少打包只有的APP的大小
      • 共享内存,节约资源
      • 通过更新动态库,达到更新程序的目的
    • 常见动态库

      • UIkit
      • libdispatch
      • libobjc.dyld

    APP加载流程

    APP加载流程
    1. APP启动
    2. 加载 libSystem
    3. Runtime 向 dyld 注册回调函数
    4. 加载新的 image(镜像文件)
    5. 执行 map_imagesload_images(执行这一步之后,会回到第4步之前,ImageLoader 加载 image)
    6. 调用 main 函数

    _dyld 分析

    • _dyld 是动态链接器
    • _dyld 将所有动态库链接到内存中
    • _dyld_start:链接开始

    程序加载的顺序

    1. load 方法
    load方法
    1. load 前还有 _dyld_start
    _dyld_start
    1. 找到 arm64 的流程
    image

    通过注释找到

    call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue)
    
    1. 找到 start 源码
    • 第一步:rebaseDyld(dyldsMachHeader);。调整dyld,在磁盘中,所有的dyld的 DATA 段都连在一起,所以需要修复成真正的指针,以保证程序运行
    • 第二步:__guard_setup(apple);。设置随机偏移值,这个是内存段的偏移,虚拟地址映射物理地址
    • 第三步:return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
    start 源码

    5. 找到 main 源码

    整个 main 函数在 5880-6365部分,接近500行代码

    • 第一部分:函数开始到下面这一段代码,前面都是环境判断、初始化操作;

      CRSetCrashLogMessage("dyld: launch started");
      setContext(mainExecutableMH, argc, argv, envp, apple);
      
    • 第二部分:将dyld添加到uuidArray中以启用堆栈符号。中间其他代码都是环境判断以及日志处理

      addDyldImageToUUIDList();
      
    • 第三部分:reloadAllImages:,这段代码之后才是真正的开始加载镜像的一个分水岭。

    • 第四部分:instantiateFromLoadedImage,为main执行文件初始化ImageLoader

    • 第五部分:sInsertedDylibCount = sAllImages.size()-1;,记录插入库的数量并描述插入库的顺序。

      1. 先插入库:inserted libraries
      2. 然后是主库:then main
      3. 然后是其他库:then others
    • 第六部分:gLinkContext 的处理

      1. 链接 main 执行文件:gLinkContext.linkingMainExecutable = true;
      2. 申请插入式缓存:ImageLoader::applyInterposingToDyldCache(gLinkContext);
      3. 通知dyld状态:gLinkContext.notifyBatch(dyld_image_state_bound, false);
      4. 断开 main 执行文件的链接:gLinkContext.linkingMainExecutable = false;
    • 第七部分:initializeMainExecutable();,初始化主执行文件,这里才是真正的初始化、绑定等操作

      1. 执行初始化器:runInitializers(const LinkContext& context, InitializerTimingList& timingInfo)

      2. 处理初始化器:processInitializers(context, thisThread, timingInfo, up);

      3. 递归初始化:对images列表中的所有映像调用recursive init,构建未初始化的向上依赖项的新列表。

        recursiveInitialization(const LinkContext& context,
                                    mach_port_t this_thread,
                                    const char* pathToInitialize,
                                    ImageLoader::InitializerTimingList&,
                                    ImageLoader::UninitedUpwards&);
        
    • 第八部分:notifyMonitoringDyldMain();,通知所有的监听对象(进程),当前进程即将进入 main()方法。这里通过 mach_msg 进行的通知

      1. sendMessage
      2. mach_msg
    main 源码
    1. reloadAllImages
    reloadAllImages
    1. instantiateFromLoadedImage
    instantiateFromLoadedImage
    1. sniffLoadCommands
    sniffLoadCommands

    dyld 流程:main函数分析

    应用程序的入口:dyld::_main uintptr_t start(...)

    1. 环境变量相关处理

    • 从环境变量中读取可执行文件 cdHash
    • checkEnvironmentVariables(envp);
    • defaultUninitializedFallbackPaths(envp);

    2. 加载共享缓存

    checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH,mainExecutableSlide);
    mapSharedCache();
    

    3. 将 dyld 本身添加到UUID列表

    addDyldImageToUUIDList();
    

    4. reloadAllImages:

    1. 实例化主程序:instantiateFromLoadedImage

      • ImageLoaderMachO::instantiateMainExecutable

        • sniffLoadCommands:确定此 mach-o 文件是否具有压缩的 LINKEDIT 以及 段数
        • addImage(image);
      • 内核会映射到主要可执行文件中。我们需要为已经映射到主可执行文件中的文件中的文件创建一个 ImageLoader *

    2. 加载任何插入的动态库

      loadInsertedDylib(*lib);
      
    3. 链接库

      • 遍历:sInsertedDylibCount
      • ImageLoader* image = sAllImages[i+1];
      • link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
      • this->recursiveApplyInterposing(context);:插入任何动态加载的镜像文件

    5. 运行所有初始化程序

    1. initializeMainExecutable();

    2. runInitializers

    3. 初始化准备:processInitializers(images.count)

    4. 递归一个个开始初始化:images.images[i]->recursiveInitialization

    5. notifySingle 单个镜像通知-开始行动

      1. 获取镜像文件真实地址:(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
      2. 初始化objc通知


        初始化objc通知
      3. 遍历初始化 this->doInitialization(context);
        1. doImageInit(context);

          Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide);
          libSystemInitialized; //libSystem 初始化必须提前
          
        2. C++函数处理

          doModInitFunctions(context);
          

    6. 通知监听 dyld 的main

    notifyMonitoringDyldMain();
    

    相关文章

      网友评论

          本文标题:07--应用加载01--应用加载流程[_dyld]

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