【iOS】应用程序加载

237 阅读2分钟

编译流程

image.png

应用程序加载

基础

  1. 库:可执行的二进制文件,能被操作系统加载到内存中。动态库和静态库的区别主要在于他们的链接方式。 image.png
  2. 基本流程 image.png

流程探索

Pre

我们知道+load()方法会在main函数之前执行,在AppDelegate中添加+load()方法并打上断点,既可以找到dyld调用入口了

image.png 打开dyld-852版本并进行探索。首先找到_dyld_start函数,可以看到是汇编层面的。

#if __arm64__ && !TARGET_OS_SIMULATOR
.text
.align 2
.globl __dyld_start
__dyld_start:
···
// call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
···
#endif

看不懂汇编没关系,在其注释中我们可以看到这么一句dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue),来到dyldbootstrap的命名空间下找到start函数。

uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, **int** argc, const char* argv[],const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue)
{
    ··· // 准备工作
return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}

可见会调用dyldmain函数,该函数首先做了这些事:条件准备:环境、平台、版本、路径、主机信息等;共享缓存的处理等。

【反推法】我们可以看到main的代码量是非常大的,探索源码时碰到这种无法入手的情况下,可以尝试从返回值入手。返回值result是在哪里定义并生成的呢?

主要流程

result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();

首先我们可以看到,调用sMainExecutablegetEntryFromLC_MAIN函数获取result。我们就可以从sMainExecutable入手。

  1. mapSharedCache共享缓存加载
mapSharedCache(mainExecutableSlide);
  1. 实例化主程序,sMainExecutable初始化
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);

// instantiateFromLoadedImage
static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path) {
ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
addImage(image); // 按Mach-o格式解析代码
return (ImageLoaderMachO*)image;
}
  1. link主程序
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, **true**, ImageLoader::RPathChain(NULL, NULL), -1);
  1. loadInsertDylib加载插入的动态库
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) 
loadInsertedDylib(*lib);

}
  1. link插入的动态库
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();
    }

    if ( gLinkContext.allowInterposing ) {
        for(**unsigned int i=0; i < sInsertedDylibCount; ++i) {
            ImageLoader* image = sAllImages[i+1];
            image->registerInterposing(gLinkContext);
        }
    }
}
  1. 弱引用绑定主程序
// 为主程序递归绑定、链接相关数据
sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
// 弱引用绑定主程序
sMainExecutable->weakBind(gLinkContext);
sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);
  1. initializeMainExecutable初始化【重点】
initializeMainExecutable();
  1. 通知dyld可以进main()函数了
notifyMonitoringDyldMain();
  1. 获取主程序入口
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
if ( result != 0 ) {
    if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
        *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
    else
        halt("libdyld.dylib support not present for LC_MAIN");
} else {
    result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
    *startGlue = 0;
}

重点流程initializeMainExecutable探索

  1. 为所有插入的动态库执行initialize
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]);
    }
}
  1. 执行主程序的initialize
sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);

runInitializers

void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo) {
    ···
    // 两个重点流程
    processInitializers(context, thisThread, timingInfo, up);
    context.notifyBatch(dyld_image_state_initialized, **false**);
    ···
}