iOS底层-dyld

前言

在平时的开发过程中,app的入口函数是main(),而在main()函数调用之前,系统是如何做的?具体都做了什么?下面具体分析app的加载流程。

准备工作

在分析app的加载流程之前,先了解几个概念。

  • 编译过程

编译过程:是由系统将源文件(.h .m等),通过预编译编译,生成对应的汇编代码,然后通过dyld加载库文件到内存,再将汇编库文件链接成可执行文件的过程。

  • 动态库和静态库

    • 动态库:动态链接。只会存在一份,在内存中共享。动态库减少了包的体积大小。例如系统的动态库:FoundationUIKit等。
    • 静态库:静态链接。静态库在加载的时候会重复,浪费了空间。
  • 下载 dyld源码

  • 下载 Libsystem源码

  • 下载 libdispatch源码

dyld

dyld(动态链接器):是苹果操作系统一个重要组成部分,加载所有的库和可执行文件。

由于load()main()调用更早,因此创建一个Project,在控制器中添加系统load()函数,并断点运行:

  • 通过堆栈分析:在执行_dyld_start之后,调用了dyldbootstrap::start
  • dyld源码中搜索_dyld_start,发现无论是什么架构下,都执行了dyldbootstrap::start

所以dyldbootstrap::start方法是研究dyld的整体流程的入口。

dyld加载流程分析

搜素dyldbootstrap,找到其start方法。( 工程选择dyld_executables )

start函数

uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, 
                int argc, 
                const char* argv[], 
                const dyld3::MachOLoaded* dyldsMachHeader, 
                uintptr_t* startGlue) 
{
    // 发出kdebug标记,dyld已启动
    dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);
    // 重定向dyld
    rebaseDyld(dyldsMachHeader);
    // 设置堆栈随机值,用于堆栈保护
    __guard_setup(apple);
#if DYLD_INITIALIZER_SUPPORT
    // 运行所有C++初始化器
    runDyldInitializers(argc, argv, envp, apple);
#endif
    // 初始化栈针apple
    _subsystem_init(apple);
    // 获取app头文件偏移量ASLR
    uintptr_t appsSlide = appsMachHeader->getSlide();
    // 调用dyld的main函数。
    return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}
复制代码

rebaseDyld (重定义dyld)

static void rebaseDyld(const dyld3::MachOLoaded* dyldMH)
{
    // 遍历所有的头文件,根据偏移量,重新修正dyld地址
    const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)dyldMH;
    assert(ma->hasChainedFixups());
    // 由于所有基于fixup链的映像的基址都为零,因此slide为加载地址
    uintptr_t slide = (long)ma;
    __block Diagnostics diag;
    ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
        ma->fixupAllChainedFixups(diag, starts, slide, dyld3::Array<const void*>(), nullptr);
    });
    diag.assertNoError();

    // 重定向完成,初始化mach
    mach_init();
    // 一旦修复完成,将dyld中的数据常数段标记为只读
    ma->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
        if ( info.readOnlyData ) {
            ::mprotect(((uint8_t*)(dyldMH))+info.vmAddr, (size_t)info.vmSize, VM_PROT_READ);
        }
    });
}
复制代码
  • 根据偏移量,修正dyld地址
  • 重定向完成,设置dyld常数为只读

main函数

main函数是dyldmain函数,并不是app程序常见的main函数。(由于main函数,只分析主要代码。

准备工作

// 获取主程序的cdHash
uint8_t mainExecutableCDHashBuffer[20];
const uint8_t* mainExecutableCDHash = nullptr;
if ( const char* mainExeCdHashStr = _simple_getenv(apple, "executable_cdhash") ) {
    unsigned bufferLenUsed;
    if ( hexStringToBytes(mainExeCdHashStr, mainExecutableCDHashBuffer, 
        sizeof(mainExecutableCDHashBuffer), bufferLenUsed) )
        mainExecutableCDHash = mainExecutableCDHashBuffer;
}

// 设置架构(CPU)信息
getHostInfo(mainExecutableMH, mainExecutableSlide);

// 设置主程序的头文件和内存偏移量
uintptr_t result = 0;
sMainExecutableMachHeader = mainExecutableMH;
sMainExecutableSlide = mainExecutableSlide;

// 设置上下文,将信息放入 gLinkContext 中( notifySingle函数 赋值在其中)
setContext(mainExecutableMH, argc, argv, envp, apple);

// 配置进程是否受限制
configureProcessRestrictions(mainExecutableMH, envp);

// 检查是否应该强制使用dyld3。AMFI相关(Apple Mobile File Integrity苹果移动文件保护)
if ( dyld3::internalInstall() ) {
    if (const char* useClosures = _simple_getenv(envp, "DYLD_USE_CLOSURES")) {
        if ( strcmp(useClosures, "0") == 0 ) {
            // 关闭闭包模式
            sClosureMode = ClosureMode::Off;
        } else if ( strcmp(useClosures, "1") == 0 ) {
#if !__i386__ // don't support dyld3 for 32-bit macOS
            // 非i386下,开启闭包模式
            sClosureMode = ClosureMode::On;
            sClosureKind = ClosureKind::full;
#endif
        } else if ( strcmp(useClosures, "2") == 0 ) {
            // 开启闭包模式
            sClosureMode = ClosureMode::On;
            sClosureKind = ClosureKind::minimal;
        }
    }
}

#if TARGET_OS_OSX
// 判断环境变量是否发生改变(此时并没有加载主程序)
if (!gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && 
    !gLinkContext.allowEnvVarsSharedCache ) {
    pruneEnvironmentVariables(envp, &apple);
    // 再次设置 (因为此时 envp和apple 可能已经更改或移动)
    setContext(mainExecutableMH, argc, argv, envp, apple);
}
else
#endif
{
    // 检查环境变量
    checkEnvironmentVariables(envp);
    // 设置默认路径
    defaultUninitializedFallbackPaths(envp);
}

// 加载共享缓存,加载UIKit、Foundation动态库。(此时并未加载主程序,但是已经读取到MachO头文件)
checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
#if TARGET_OS_SIMULATOR
if ( sSharedCacheOverrideDir)
    // 设置共享缓存偏移地址
    mapSharedCache(mainExecutableSlide);
#else
// 设置共享缓存偏移地址
mapSharedCache(mainExecutableSlide);
#endif
复制代码
  • 获取主程序的hash地址
  • 设置CPU架构信息、设置默认上下文
  • 判断是否是dyld3
  • 检查环境变量
  • 加载共享缓存

闭包模式判断

if ( sClosureMode == ClosureMode::Off ) {
    // 不使用闭包
} else {
    // 使用闭包。(具体是否真正使用还要判断其闭包地址。)
    sLaunchModeUsed = DYLD_LAUNCH_MODE_USING_CLOSURE;
    const dyld3::closure::LaunchClosure* mainClosure = nullptr;
    // 设置加载所需主要信息
    dyld3::closure::LoadedFileInfo mainFileInfo;
    mainFileInfo.fileContent = mainExecutableMH;
    mainFileInfo.path = sExecPath;
    mainFileInfo.sliceOffset = 0;
    mainFileInfo.sliceLen = -1;
    
    // 判断闭包缓存地址是否为空
    if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
        // 如果不为空,设置闭包 (从缓存加载)
        mainClosure = sSharedCacheLoadInfo.loadAddress->findClosure(sExecPath);
        if ( mainClosure != nullptr )
            // 如果闭包不为空,证明闭包模式
            sLaunchModeUsed |= DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
    }
   
    // sClosureMode 上面的判断是否是闭包模式
    if ( sClosureMode == ClosureMode::On ) {
        // 闭包模式
        allowClosureRebuilds = true;
    } else if ( (sClosureMode == ClosureMode::PreBuiltOnly) && (mainClosure != nullptr) ) {
        allowClosureRebuilds = true;
    }
    
    // 验证闭包
    if ( (mainClosure != nullptr) && !closureValid(mainClosure, mainFileInfo, 
        mainExecutableCDHash, true, envp) ) {
        // 如果没有验证通过,清空闭包地址,模式复位
        mainClosure = nullptr;
        sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
    }
    
    // 判断闭包地址是否为空,是否允许创建
    if ( (mainClosure == nullptr) && allowClosureRebuilds ) {
        if ( !sForceInvalidSharedCacheClosureFormat )
            // 缓存查找闭包
            mainClosure = findCachedLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
        if ( mainClosure == nullptr ) {
            // 创建一个新的闭包
            mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
            if ( mainClosure != nullptr )
                sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH;
        }
    }
}
复制代码
  • 判断是否是闭包模式
  • 判断缓存闭包地址,并查找。
  • 验证闭包
  • 判断闭包地址,如果没有,从缓存创建,如果还没有,则创建一个新的。

dyld3(闭包)加载

// 如果闭包地址存在,则使用dyld3加载
if ( mainClosure != nullptr ) {
    // 启动闭包
    bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress,
                                     (dyld3::MachOLoaded*)mainExecutableMH, 
                                     mainExecutableSlide, 
                                     argc, argv, envp, apple, diag, 
                                     &result, startGlue, &closureOutOfDate, 
                                     &recoverable);
    if ( !launched && closureOutOfDate && allowClosureRebuilds ) {
        // 如果启动闭包失败,且超时,也允许重建。则再次创建闭包
        mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
        if ( mainClosure != nullptr ) {
            // 如果闭包创建成功
            sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH;
            // 再次启动
            launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, 
                                        (dyld3::MachOLoaded*)mainExecutableMH, 
                                        mainExecutableSlide, 
                                        argc, argv, envp, apple, diag, 
                                        &result, startGlue, &closureOutOfDate, 
                                        &recoverable);
        }
    }
    // 如果启动成功
    if ( launched ) {
        // 更新主程序加载成功标识
        gLinkContext.startedInitializingMainExecutable = true;
        if (sSkipMain)
            // 拿到主程序地址,并返回。
            result = (uintptr_t)&fake_main;
        return result;
    } else {
        // 如果启动失败,保存错误信息
        if ( !recoverable )
            halt(diag.errorMessage());
    }
}
复制代码
  • 启动闭包
  • 如果启动失败,重建闭包,重建成功则再次启动。
  • 启动成功,获取主程序地址,并返回。
  • 启动失败,保存错误信息。

MainExecutable 实例化(dyld2)

// 实例化主程序
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
// 更新主程序
gLinkContext.mainExecutable = sMainExecutable;
// 更新代码签名状态
gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
复制代码

instantiateFromLoadedImage

static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, 
                                                    uintptr_t slide, 
                                                    const char* path)
{
    // 加载镜像文件
    ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path,
                                                                    gLinkContext);
    // 添加到主程序的allImages里面
    addImage(image);
    return (ImageLoaderMachO*)image;
}
复制代码

MainExecutable 链接(dyld2)

// 链接主程序
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);

// 链接动态库
if ( sInsertedDylibCount > 0 ) {
   for (unsigned int i = 0; i < sInsertedDylibCount; ++i) {
       // 此时共享缓存已经有了主程序,所以从1开始
       ImageLoader* image = sAllImages[i+1];
       // 链接镜像文件
       link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
       // 递归加载
       image->setNeverUnloadRecursive();
       }
   }
}
复制代码
  • 链接主程序
  • 链接动态库:加载动态库,从1开始。
  • 递归加载未加载的镜像文件

link

void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, 
          const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex)
{
    // 先把自身添加到allImages里面
    if ( image->isBundle() && !image->isLinked() )
        addImage(image);
    try {
        // 镜像地址
        const char* path = image->getPath();
        // 镜像文件的链接
        image->link(gLinkContext, forceLazysBound, false, neverUnload, loaderRPaths, path);
    }
    catch (const char* msg) {
        // 垃圾回收处理
        garbageCollectImages();
        throw;
    }
}
复制代码

image->link

void ImageLoader::link(const LinkContext& context, bool forceLazysBound, 
                       bool preflightOnly, bool neverUnload, 
                       const RPathChain& loaderRPaths, const char* imagePath)
{
    // 递归加载库,并实现通知(递归完成)
    this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath);
    context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly);
    // 清空所有的Images
    context.clearAllDepths();
    this->updateDepth(context.imageCount());
    {
        // 递归重定向ASLR,并实现通知(重定向完成)
        this->recursiveRebaseWithAccounting(context);
        context.notifyBatch(dyld_image_state_rebased, false);
        
        // 递归绑定懒加载符号
        if ( !context.linkingMainExecutable )
            this->recursiveBindWithAccounting(context, forceLazysBound, neverUnload);
        
        // 绑定弱引用符号
        if ( !context.linkingMainExecutable )
            this->weakBind(context);
        }
        // 递归插入
        if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0) ) {
            this->recursiveApplyInterposing(context);
    }
    // 递归设置只读
    if ( !context.linkingMainExecutable )
        this->recursiveMakeDataReadOnly(context);
    // 所有都完成之后,实现通知(绑定完成)
    if ( !context.linkingMainExecutable )
        context.notifyBatch(dyld_image_state_bound, false);
}
复制代码
  • 递归加载动态库
  • 递归重定向ASLR
  • 递归绑定懒加载符号
  • 绑定弱引用符号
  • 递归应用插入
  • 递归设置只读
  • 以上都完成,实现通知(状态:绑定完成)

reloadAllImages

// 是否重新加载镜像文件
if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) {
    // 取消所有的已加载的Image,主程序除外,所以从1开始
    for (long i = 1; i < sAllImages.size(); ++i) {
        ImageLoader* image = sAllImages[i];
        if ( image == sMainExecutable )
            continue;
        // 不加载
        image->setCanUnload();
        // 删除
        ImageLoader::deleteImage(image);
    }
    // 通知取消所有Images
    resetAllImages();
    // 重新加载
    goto reloadAllImages;
}
复制代码

MainExecutable 绑定(dyld2)

// 绑定主程序
sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
// 实现通知(绑定完成)
gLinkContext.notifyBatch(dyld_image_state_bound, false);

// 递归绑定已经插入的Images,默认从1开始
if ( sInsertedDylibCount > 0 ) {
    for(unsigned int i = 0; i < sInsertedDylibCount; ++i) {
        ImageLoader* image = sAllImages[i+1];
        image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true, nullptr);
    }
}

// 绑定主程序的弱引用符号
sMainExecutable->weakBind(gLinkContext);
// 设置主程序的只读数据
sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);
复制代码

MainExecutable 初始化(dyld2)

void initializeMainExecutable() {
    // 开始初始化主程序标记
    gLinkContext.startedInitializingMainExecutable = true;
    // 为所有的rootImages初始化
    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]);
        }
    }
    // 为主程序及所有的内容初始化加载器
    sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
}
复制代码

runInitializers

void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo) {
    ImageLoader::UninitedUpwards up;
    up.count = 1;
    up.imagesAndPaths[0] = { this, this->getPath() };
    // 进程初始化
    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)
{
    for (uintptr_t i=0; i < images.count; ++i) {
        // 递归初始化images列表中的所有images,构建一个新的依赖项。
        images.imagesAndPaths[i].first->recursiveInitialization(context, 
                                                                thisThread, 
                                                                images.imagesAndPaths[i].second, 
                                                                timingInfo, 
                                                                ups);
    }
}
复制代码

recursiveInitialization

// 初始化下级动态库(可理解为多维数组)
for (unsigned int i = 0; i < libraryCount(); ++i) {
    ImageLoader* dependentImage = libImage(i);
    if ( dependentImage != NULL ) {
        // 获取上一级lib
        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);
        }
    }
}

// 发送通知(所有的附属Image都加载完成)
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);

// 初始化整体上下文
bool hasInitializers = this->doInitialization(context);

// 发送通知(初始化完成)
context.notifySingle(dyld_image_state_initialized, this, NULL);
复制代码

doInitialization

bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
    // 初始化image
    doImageInit(context);
    // 初始化全局C++对象的构造函数,内部进行了libSystem库的初始化
    doModInitFunctions(context);
}
复制代码
notifySingle(发送通知)

recursiveInitialization中发送通知,状态为dyld_image_state_dependents_initialized,因此实现sNotifyObjCInit回调。

static void notifySingle(dyld_image_states state, 
                        const ImageLoader* image, 
                        ImageLoader::InitializerTimingList* timingInfo)
{
    // 判断状态是 dyld_image_state_dependents_initialized 且 
    // sNotifyObjCInit回调存在 且 
    // 加载image标记存在
    if ( (state == dyld_image_state_dependents_initialized) && 
        (sNotifyObjCInit != NULL) && 
        image->notifyObjC() ) 
    {
        // 实现sNotifyObjCInit通知回调
        (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
}
复制代码
registerObjCNotifiers(注册通知)
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, 
                           _dyld_objc_notify_init init, 
                           _dyld_objc_notify_unmapped unmapped)
{
    // 注册map_images
    sNotifyObjCMapped = mapped;
    // 注册load_images
    sNotifyObjCInit = init;
    // 注册unmap_images
    sNotifyObjCUnmapped = unmapped;
}
复制代码
_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方法并不在dyld源码库中。

稍后利用反推法梳理其流程。

notifyMonitoringDyldMain

// 通知监视器进入dyld的main()函数
notifyMonitoringDyldMain();

// 获取主程序入口的地址
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
复制代码

反推法

通过符号断点_dyld_objc_notify_register查看堆栈信息:

_objc_init

_objc_init属于libobjc.A.dylib(objc源码)

_objc_init源码:

_os_object_init

_os_object_init属于libdispatch.dylib(libdispatch源码)

_os_object_init源码:

libdispatch_init

libdispatch_init属于libdispatch.dylib(libdispatch源码)

libdispatch_init源码:

libSystem_initializer

libSystem_initializer属于libSystem.B.dylib(libSystem源码)

libSystem_initializer源码:

doModInitFunctions

已经在上面分析了,doModInitFunctions源码中有关于libSystem库的初始化

小结

通过分析,当主程序初始化完成后,先注册通知,再执行初始化。其具体流程为:

  • doModInitFunctions(libSystem加载) → libSystem_initializerlibdispatch_init_os_object_init_objc_init_dyld_objc_notify_register

dyld加载流程图

补充

map_images

通过_dyld_objc_notify_register注册完的第一个参数map_images调用流程:

void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, 
                           _dyld_objc_notify_init init, 
                           _dyld_objc_notify_unmapped unmapped)
{
    // 注册map_images
    sNotifyObjCMapped = mapped;
    // 注册load_images
    sNotifyObjCInit = init;
    // 注册unmap_images
    sNotifyObjCUnmapped = unmapped;
    
    try {
        // 调用 mapped 函数,并映射所有图像
        notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
    } catch (const char* msg) {
        // 忽略请求
    }
}
复制代码

通过全文查找sNotifyObjCMapped,反推导出notifyBatchPartial方法:

notifyBatchPartial是在registerObjCNotifiers注册完之后调用的。

结论

map_images调用顺序为:_dyld_objc_notify_registerregisterObjCNotifiersnotifyBatchPartialsNotifyObjCMappedmap_images)实现。

dyld2和dyld3

dyld2

  • dyld2dyld1的完全重写版本。
  • 正确支持c++初始化器,扩展了mach-o格式并且更新了dyld
  • 具有完整的dlopendlsym实现,弃用了旧版API(旧版API仍然仅位于macOS中)。
  • dyld2的设计目标是提高速度,因此仅进行有限的健全性检查。
  • dyld1有一些安全性问题,dyld2对一些功能性改进,提高了安全性。
  • 速度大幅提升性能,可以减少预绑定工作量。不同于dyld1编译你的程序数据,dyld2仅编译系统库。可以仅在软件更新时做这些事情。因此在软件更新时可能会看到优化系统性能之类的文字,这时就是在更新预绑定。

dyld3

dyld3是全新的动态连接器,iOS11以上的系统程序都默认使用dyld3,在iOS13完全取代dyld2dyld3主要做了以下三方面的改进:

  • 性能。提高启动速度。dyld3可以帮助我们获得更快的程序启动和运行速度。
  • 安全性。dyld2增加的安全性很难跟随现实情形增强安全性。
  • 可测试性和可靠性。

dyld2和dyld3加载对比

dyld2流程:

  • Parse mach-o headers & Find dependencies:分析macho headers,确认需要哪些库。递归分析依赖的库直到获得所有的dylib库。普通iOS程序需要3-600dylib,数据庞大需要进行大量处理。
  • Map mach-o files:映射所有macho文件将他们放入地址空间(映射进内存)。
  • Perform symbol lookups:执行符号查找。比如使用printf函数,将会查找printf是否在库系统中,然后找到它的地址,将它复制给应用程序中的函数指针。
  • Bind and rebase:绑定和重定向。复制这些指针,所有指针必须使用基地址(ASLR的存在)。
  • Run initializers:运行所有初始化器。这之后就开始准备执行main函数。

dyld3流程:

  • dyld3是一个进程外macho分析器和编译器

    • 解析所有搜索路径、rpaths、环境变量。
    • 分析macho二进制数据。
    • 执行所有符号查找。
    • 创建闭包。
    • 可以进行正常测试。
    • 大多数程序启动会使用缓存,始终不需要调用进程外macho分析器或编译器。
    • 启动闭包比macho更简单,它们是内存映射文件,不需要用复杂的方式进行分析,可以简单的验证它们,作用是为了提高速度。
  • dyld3也是一个进程内引擎

    • 检查闭包是否正确。
    • 使用闭包映射所有dylibs
    • 绑定和重定向。
    • 运行所有初始化器,然后跳转主程序main()
  • 启动闭包缓存服务

    • 系统app闭包模式构建在共享缓存中。
    • 第三方应用在安装时构建,在软件更新时重新构建。
    • macOS上后台进程引擎可以在后台进程被调用,在其它平台上不需要这么做。

dyld3不需要分析macho Headers或者执行符号查找。在App启动时没有这个过程,因此极大的提升了程序的启动速度。

具体详细情况参考 官方视频

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享