dyld在app启动过程中的作用

在前面的文章中介绍过,app启动过程中,首先是操作系统内核进行一些处理,比如新建进程,分配内存等。在iOS/Mac OS系统中,操作系统内核是XNU。在XNU完成相关的工作后,会将控制权交给dyld。dyld,即动态链接器,用于加载动态库。dyld是运行在用户态的,从XNU到dyld,完成了一次内核态到用户态的切换。那么,后续dyld做了哪些事情呢?幸运的是,dyld是开源的,我们通过分析dyld的源码,来看一下dyld在app启动过程中做了哪些工作。

dyld入口

在之前的文章中介绍过,dyld入口函数是__dyld_start,我们看一下__dyld_start里面做了那些操作。dyld中的部分源码是汇编语言,__dyld_start源码就是汇编。__dyld_start部分代码如下:

__dyld_start:
    // 这里调用了dyldbootstrap::start()函数,此函数会完成动态库加载过程,并返回主程序main函数入口
    bl  __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm
    mov x16,x0                  // save entry point address in x16
    ldr     x1, [sp]
    cmp x1, #0

    // LC_MAIN case, set up stack for call to main()
Lnew:   mov lr, x1          // simulate return address into _start in libdyld.dylib
    ldr     x0, [x28, #8]       // main param1 = argc
    add     x1, x28, #16        // main param2 = argv
    add x2, x1, x0, lsl #3  
    add x2, x2, #8      // main param3 = &env[0]
    mov x3, x2

__dyld_start内部调用了dyldbootstrap::start()函数,看一下dyldbootstrap::start()内部的实现:

uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* argv[], 
                intptr_t slide, const struct macho_header* dyldsMachHeader,
                uintptr_t* startGlue)
{
    // if kernel had to slide dyld, we need to fix up load sensitive locations
    // we have to do this before using any global variables
    if ( slide != 0 ) {
        rebaseDyld(dyldsMachHeader, slide);
    }
    // 调用dyld中的_main()函数,_main()函数返回主程序的main函数入口,也就是我们App的main函数地址
    return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}

查找App main函数地址的操作主要是在_main函数中,_main函数中做了较多的操作,看一下_main()函数是如何实现的。

_main()函数

_main()函数中代码比较多,做的事情也比较多。主要完成了上下文的建立,主程序初始化成ImageLoader对象,加载共享的系统动态库,加载依赖的动态库,链接动态库,初始化主程序,返回主程序main()函数地址。接下来分别看一下每个功能的具体实现。

instantiateFromLoadedImage

instantiateFromLoadedImage()函数主要是将主程序Mach-O文件转变成了一个ImageLoader对象,用于后续的链接过程。ImageLoader是一个抽象类,和其相关的类有ImageLoaderMachO,ImageLoaderMachO是ImageLoader的子类,ImageLoaderMachO又有两个子类,分别是ImageLoaderMachOCompressed和ImageLoaderMachOClassic。这几个类之间的关系如下:

在app启动过程中,主程序和其相关的动态库,最后都被转化成了一个ImageLoader对象。看一下instantiateFromLoadedImage中做的操作。

static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path)
{
    // 检测mach-o header的cputype与cpusubtype是否与当前系统兼容
    if ( isCompatibleMachO((const uint8_t*)mh, path) ) {
        ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
        addImage(image);
        return (ImageLoaderMachO*)image;
    }
}

isCompatibleMachO主要是检测mach-o文件的cputype和cpusubtype是否与当前系统兼容,之后调用了instantiateMainExecutable()函数,看一下instantiateMainExecutable()函数的实现:

// 初始化ImageLoader
ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context)
{
    bool compressed;
    unsigned int segCount;
    unsigned int libCount;
    // sniffLoadCommands主要获取加载命令中compressed的值(压缩还是传统)以及segment的数量、libCount(需要加载的动态库的数量)
    sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
    if ( compressed ) 
        return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context);
    else
#if SUPPORT_CLASSIC_MACHO
        return ImageLoaderMachOClassic::instantiateMainExecutable(mh, slide, path, segCount, libCount, context);
#
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值