本篇文章将探究app运用程序是怎么加载的? 咱们平时都认为main是程序的进口,可是实际上是这姿态的吗?不是的话,运用在冷发动main之前做了什么呢,咱们去探究下流程。

一、发动流程开端探究

新建iOS工程,在ViewController.m中添加一个load办法并打断点,程序main.m中添加一个C++函数kcFunc

  • ViewControler代码:
@interface ViewController ()
@end
@implementation ViewController
+ (void)load{
    NSLog(@"%s", __func__);
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    NSLog(@"viewDidLoad --- ");
}
@end
  • main.m代码:
// 在`main`函数之前,履行一些咱们的设置,比方状况打印
__attribute__((constructor)) void kcFunc(){
    printf("C... %s \n",__func__);
}
int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSLog(@"main ...");
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
  • llvm打印:
2023-02-02 14:21:51.278198+0800 DyldTest[17170:4322854] +[ViewController load]
C... kcFunc
2023-02-02 14:21:51.280613+0800 DyldTest[17170:4322854] main ...
2023-02-02 14:21:52.835821+0800 DyldTest[17170:4322854] viewDidLoad ---
  • 定论:
  1. __attribute__((constructor))是在main函数之前履行一个函数,便于咱们做一些准备工作。能够查阅了GNU的文档印证了我的主意。

  2. 运转程序,依据终端打印成果,可发现程序首要运转了load办法,然后调用了C++函数,终究才进入到main办法中。

在剖析发动流程之前,先学习一些概念。

二、运用程序编译进程

静态库

在链接阶段,会将汇编生成的方针程序引证的库一同链接打包到可履行文件傍边。此刻的静态库就会再改了,由于它是编译时被直接复制一份,复制到方针程序里的。例如:.a.lib

  • 长处:编译完结后,库文件实际上就没有作用了,方针程序没有外部依靠,直接就能够运转
  • 缺陷:由于静态库或许会有两份,所以会导致方针程序的体积增大,对内存、功能、速度消耗很大

动态库

程序编译时并不会链接到方针程序中,方针程序只会存储指向动态库的引证,在程序运转时才被载入。例如:.so.framwork.dll

  • 长处:削减打包之后app的巨细,共享内存,节省资源,更新动态库,到达更新程序
  • 缺陷:动态载入会带来一部分功能损失,运用动态库也会使得程序依靠于外部环境,假如环境缺少了动态库,或许库的版别不正确,就会导致程序无法运转。

OC底层原理(十一):应用程序的dyld4流程上

编译进程

程序的编译进程:预编译->编译->汇编->链接->可履行文件

OC底层原理(十一):应用程序的dyld4流程上

  • 源文件:载入.h、.m、.cpp等文件
  • 预处理:替换宏,删去注释,翻开头文件,产生.i文件
  • 编译:经过词法剖析、语法剖析、语义剖析后,将.i文件转换为汇编语言,产生.s文件
  • 汇编:将汇编文件转换为机器码文件,产生.o文件
  • 链接:对.o文件中引证其他库的当地进行引证,生成Mach-O格局的可履行文件 经过上述步骤,咱们开发的代码会生成一个Mach-O格局的可履行文件。概况可阅读这儿。

动态链接器dyld

dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作体系的重要组成部分,在app被编译打包成可履行文件格局的Mach-O文件后,交由dyld担任连接,并加载程序。

  • dyld的作用:加载各个库,也便是image镜像文件,由dyld从内存中读到表中,加载主程序,link链接各个动静态库,进行主程序初始化。

OC底层原理(十一):应用程序的dyld4流程上

Mach-O

Mach-O: Mach Object文件格局的缩写,它是一种用于可履行文件,方针文件.o,动态库,内核转储的文件格局。作为a.out格局的代替,Mach-O供给了更强的扩展性,并提升了符号表中信息的拜访速度。

Mach-O格局的文件:

  • Executable(产品为ipa包)
  • Dynamic Library(产品为动态库)
  • Bundle(产品为bundle文件)
  • Static Library(产品为静态库)
  • Relocatable Object File(重定向文件)

OC底层原理(十一):应用程序的dyld4流程上

  • 翻开ipa内容文件:

OC底层原理(十一):应用程序的dyld4流程上

  • 经过MachOView可查看可履行文件信息:

OC底层原理(十一):应用程序的dyld4流程上

  • Mach-O由四部分组成:Mach-O HeaderLoad CommandsSectionOther Data

典型的Mach-O文件包含三个区域:

  1. Header:保存Mach-O的一些基本信息,包括渠道、文件类型、指令数、指令总巨细,dyld符号Flags等等。

  2. Load Commands:紧跟Header,加载Mach-O文件时会运用这部分数据确认内存散布,对体系内核加载器和动态连接器起指导作用。

  3. Data:每个segment的具体数据保存在这儿,包含具体的代码、数据等等。概况可阅读这儿,了解这个部分是iOS逆向工程的根底。

App发动的进程:

当咱们点击iPhone上的运用图标后,整个iOS体系发动App阅历了:

  • 体系调用 exec() 分配内存、开辟进程
  • app对应的可履行文件加载到内存
  • dyld动态链接器加载到内存:load dyld
  • dyld动态链接器进行动态链接:rebase->bing->Objc->initalizers
  • 调起main()函数

发动时间的划分能够把main()函数作为要害点分割成两块

OC底层原理(十一):应用程序的dyld4流程上

三、dyld进口探究

Viewcontrollerload办法添加断点,运转程序。在控制台输入指令bt,查看运转的函数调用仓库信息。

  • 图:

OC底层原理(十一):应用程序的dyld4流程上

  • llvm打印:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
 * frame #0: 0x00000001047cde38 DyldTest`+[ViewController load](self=ViewController, _cmd="load") at ViewController.m:17:5
  frame #1: 0x00000001bed30a80 libobjc.A.dylib`load_images + 824
  frame #2: 0x00000001e40d83fc dyld`dyld4::RuntimeState::notifyObjCInit(dyld4::Loader const*) + 164
  frame #3: 0x00000001e40dbcc0 dyld`dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const + 204
  frame #4: 0x00000001e40e133c dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const + 328
  frame #5: 0x00000001e4115244 dyld`dyld4::APIs::runAllInitializersForMain() + 360
  frame #6: 0x00000001e40ea66c dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3388
  frame #7: 0x00000001e40e88d4 dyld`start + 2388
(lldb)
  • 定论:
  1. 从左侧的仓库信息能够找到了程序的进口是start,这个部分是iOS16后的发动情况。现在最新的iOS体系基本上都是dyld4流程,与网上大部分材料仍是iOS15曾经版别的dyld3流程是有所不同,可是这儿不进行详细翻开。

  2. 调用流程start -> dyld4::prepare -> dyld4::APIs::runAllInitializersForMain -> dyld4::Loader::runInitializersBottomUpPlusUpwardLinks -> dyld4::Loader::runInitializersBottomUp -> dyld4::RuntimeState::notifyObjCInit -> load_images -> [ViewController load]

四、探究dyld源码

截止现在(2023年2月)dyld现已更新至dyld4(1042.1)了,iOS15以上体系都是运用的dyld4。 下载dyld当时最新版别是dyld-1042.1 ,这部分源码是不能编译的,能够结合objc4源码剖析。

  1. 翻开dyld源码,虽然控制台是从start开端打印的,可是dyld4dyld3一样都是从__dyld_start。先在工程里全局查找__dyld_start,开端探究程序加载流程。终究在dyldStartup.s文件中查找到了伪汇编流程。
  • 见下图:

    OC底层原理(十一):应用程序的dyld4流程上

  • 定论:

    ①. 此汇编代码仅仅对齐仓库并跳转到C++代码dyld:: start

  1. 针对不同的环境供给了不同的完结方式,比方,咱们现在看到的便是针对arm64真机环境的完结。看注释发现,不论是何种环境,终究都会走到start流程中。这也和咱们前面bt看到的函数调用仓库持续!下面全局查找start
  • 见下图:
    OC底层原理(十一):应用程序的dyld4流程上

start

查看dyld-1042.1工程里找到的start函数。获取Mach-O解析器,解析Mach-O文件,还有获取一个随机数(ASLR)。

namespace dyld4 {
...
...
// dyld的进口点。内核加载dyld并跳转到__dyld_start,它设置一些寄存器并调用这个函数。
// 留意:这个函数永久不会回来,它调用exit()。
// 因而,仓库维护程序是无用的,由于永久不会履行epilog。
// 符号函数no-return禁用仓库维护。
// 仓库维护器也会导致armv7k代码生成的问题,由于它经过prolog中的GOT槽拜访随机值,但dyld还没有rebased。
void start(const KernelArgs* kernArgs, void* prevDyldMH) __attribute__ ((noreturn)) __asm("start");
void start(const KernelArgs* kernArgs, void* prevDyldMH)
{
    // 宣布kdebug盯梢点来指示dyld引导程序现已发动 <rdar://46878536>
    // 留意:这是在dyld rebased之前调用的,所以kdebug_trace_dyld_marker()不能运用任何全局变量
    dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);
    // 走一切的fixups chains 和 rebase dyld
    // 留意:withChainStarts()和fixupAllChainedFixups()不能运用任何静态数据指针,由于它们还没有rebased
  const MachOAnalyzer* dyldMA = getDyldMH(); //获取Mach-O解析器,用于解析Mach-O格局文件内容
  uintptr_t      slide = dyldMA->getSlide();// 会生成一个随机数(ASLR)
    if ( !dyldMA->inDyldCache() ) {
        assert(dyldMA->hasChainedFixups());
    __block Diagnostics diag;
    dyldMA->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
      dyldMA->fixupAllChainedFixups(diag, starts, slide, dyld3::Array<const void*>(), nullptr);
    });
    diag.assertNoError();
    // make __DATA_CONST read-only (kernel maps it r/w)
    dyldMA->forEachSegment(^(const MachOAnalyzer::SegmentInfo& segInfo, bool& stop) {
      if ( segInfo.readOnlyData ) {
        const uint8_t* start = (uint8_t*)(segInfo.vmAddr + slide);
        size_t     size = (size_t)segInfo.vmSize;
        sSyscallDelegate.mprotect((void*)start, size, PROT_READ);
      }
    });
    }
  
    // 现在,咱们能够调用运用DATA的函数
  mach_init();// mach音讯初始化
    // 为stack canary设置随机值
  __guard_setup(kernArgs->findApple()); //栈溢出维护
  // 设置以便open_with_subsystem()工作
  _subsystem_init(kernArgs->findApple());
  // 在__DATA_CONST中将ProcessConfig目标变为只读之前,运用placement new来结构它
  handleDyldInCache(dyldMA, kernArgs, (MachOFile*)prevDyldMH);
  bool useHWTPro = false;
    // 在自己的分配池中创立一个分配器Allocator
  Allocator& allocator = Allocator::persistentAllocator(useHWTPro);
    // 运用placement new在Allocator池中结构ProcessConfig目标
    // 构建进程装备
  ProcessConfig& config = *new (allocator.aligned_alloc(alignof(ProcessConfig), sizeof(ProcessConfig))) ProcessConfig(kernArgs, sSyscallDelegate, allocator);//构建进程装备
#if !SUPPPORT_PRE_LC_MAIN
  // 仓库分配RuntimeLocks。它们不能在通常为只读的Allocator池中
  RuntimeLocks sLocks;
#endif
  // 在分配器中创立API(也称为RuntimeState)目标
  APIs& state = *new (allocator.aligned_alloc(alignof(APIs), sizeof(APIs))) APIs(config, allocator, sLocks);
#if !TARGET_OS_SIMULATOR
  // FIXME:咱们应该更早地移动它,但现在咱们需求在设置紧缩信息之前将运转时状况初始化,
    // 直到咱们这样做之前,紧缩信息或许会错过某些前期的dyld崩溃
    auto processSnapshot = state.getCurrentProcessSnapshot();
  processSnapshot->setPlatform((uint64_t)state.config.process.platform);
  processSnapshot->setDyldState(dyld_process_state_dyld_initialized);
  FileRecord cacheFileRecord = state.fileManager.fileRecordForVolumeDevIDAndObjID(gProcessInfo->sharedCacheFSID, gProcessInfo->sharedCacheFSObjID);
    if (cacheFileRecord.exists()) {
    auto sharedCache = SharedCache(state.ephemeralAllocator, std::move(cacheFileRecord), processSnapshot->identityMapper(), (uint64_t)config.dyldCache.addr, gProcessInfo->processDetachedFromSharedRegion);
    processSnapshot->addSharedCache(std::move(sharedCache));
  }
    // 将dyld添加到紧缩信息
    if ( dyldMA->inDyldCache() && processSnapshot->sharedCache() ) {  } else {  }
    // 将主可履行文件添加到紧缩信息
    FileRecord mainExecutableFile;
  if (state.config.process.mainExecutableFSID && state.config.process.mainExecutableObjID) {  } else {  }
    auto mainExecutableImage = Image(state.ephemeralAllocator, std::move(mainExecutableFile), processSnapshot->identityMapper(), (const mach_header*)state.config.process.mainExecutable);
  processSnapshot->addImage(std::move(mainExecutableImage));
  processSnapshot->setInitialImageCount(state.initialImageCount());
  state.commitProcessSnapshot();
#endif
    // -------------要点进口-------------------
    // 加载一切的库与程序
  MainFunc appMain = prepare(state, dyldMA);
  // 现在将一切dyld分配的数据结构设置为只读
  state.decWritable();
    // 调用main(),假如它回来,调用exit()并回来成果
    // 留意:这是经过安排的,以便在程序的主线程中回溯时只在“main”下面显现“start”。
  int result = appMain(state.config.process.argc, state.config.process.argv, state.config.process.envp, state.config.process.apple);
    // 假如咱们到达这儿,main()回来(与程序调用exit()相反)
#if TARGET_OS_OSX
    // <rdar://74518676>libSystemHelpers没有为模拟器设置,因而直接调用_exit()
  if ( MachOFile::isSimulatorPlatform(state.config.process.platform) )
    _exit(result);
#endif
  state.libSystemHelpers->exit(result);
}
...
...
}// namespace
  • 定论:

    ①.dyld4dyld3不同便是,main没有了回来值,是经过exit结束!无法经过epilog退出代码,它释放仓库空间并回来到调用者。

    ②. start中读取mach-o文件中的符号地址都是虚拟地址,在程序发动的时候,体系会生成一个随机数(ASLR),运用虚拟地址加上ASLR才是物理地址,也便是程序真正调用的地址。咱们把从虚拟地址换算成物理地址的进程称之为rebase


prepare

//
// 加载任何相关的dylib并将它们绑定在一同。
// 回来main()在target中的地址。
//
__attribute__ ((noinline)) static MainFunc prepare(APIs& state, const MachOAnalyzer* dyldMH)
{
    // `gProcessInfo`是存储dyld一切镜像信息的结构体:,保存着mach_header,dyld_uuid_info,dyldVersion等等信息。
    // 装备dyld信息中中止标识符
    gProcessInfo->terminationFlags = 0; // 默许情况下,在崩溃日志中显现回溯
    // 装备dyld信息中渠道信息
  gProcessInfo->platform     = (uint32_t)state.config.process.platform;
    // 装备dyld信息中途径信息
  gProcessInfo->dyldPath     = state.config.process.dyldPath;
    uint64_t launchTraceID = 0;
  if ( dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE) ) {
    launchTraceID = dyld3::kdebug_trace_dyld_duration_start(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, (uint64_t)state.config.process.mainExecutable, 0, 0);
  }
  #if TARGET_OS_OSX //模拟环境,涉及Simulator和Sim(略过)
     const bool isSimulatorProgram = MachOFile::isSimulatorPlatform(state.config.process.platform);
  if ( const char* simPrefixPath = state.config.pathOverrides.simRootPath() ) {
#if __arm64e__
    if ( strcmp(state.config.process.mainExecutable->archName(), "arm64e") == 0 )
      halt("arm64e not supported for simulator programs");
#endif
    if ( isSimulatorProgram ) {
      char simDyldPath[PATH_MAX];
      strlcpy(simDyldPath, simPrefixPath, PATH_MAX);
      strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX);
      return prepareSim(state, simDyldPath);
    }
    halt("DYLD_ROOT_PATH only allowed with simulator programs");
  }
  else if ( isSimulatorProgram ) {
    halt("DYLD_ROOT_PATH not set for simulator program");
  }
#endif
#if 0
  // 查看主可履行文件是否有效
  Diagnostics diag;
  bool validMainExec = state.config.process.mainExecutable->isValidMainExecutable(diag, state.config.process.mainExecutablePath, -1, *(state.config.process.archs), state.config.process.platform);
  if ( !validMainExec && state.config.process.mainExecutable->enforceFormat(dyld3::MachOAnalyzer::Malformed::sdkOnOrAfter2021)) {
    state.log("%s in %s", diag.errorMessage(), state.config.process.mainExecutablePath);
    halt(diag.errorMessage());
  }
#endif
// 假如问询,log打印环境env变量
  if ( state.config.log.env ) {
    for (const char* const* p=state.config.process.envp; *p != nullptr; ++p) {
      state.log("%s\n", *p);
    }
  }
  // 查看预构建pre-built Loader
  state.initializeClosureMode();
  const PrebuiltLoaderSet* mainSet  = state.processPrebuiltLoaderSet();
  Loader*         mainLoader = nullptr;
  if ( mainSet != nullptr ) {
    mainLoader = (Loader*)mainSet->atIndex(0);
    state.loaded.reserve(state.initialImageCount()); // 协助避免Vector的从头定位
  }
  if ( mainLoader == nullptr ) {
    // 假如没有预构建的Loader,制作一个just-in-time
        // just-in-time是dyld4的新特性:`pre-build + just-in-time`预构建+实时解析的双解析形式
    state.loaded.reserve(512); // 猜测Vector巨细的起点
    Diagnostics buildDiag;
    mainLoader = JustInTimeLoader::makeLaunchLoader(buildDiag, state, state.config.process.mainExecutable,
                            state.config.process.mainExecutablePath, nullptr);
    if ( buildDiag.hasError() ) {
      state.log("%s in %s\n", buildDiag.errorMessage(), state.config.process.mainExecutablePath);
      halt(buildDiag.errorMessage());
    }
  }
  state.setMainLoader(mainLoader);
  // 首要将主可履行文件添加到mainLoader预构建的镜像列表中
  state.notifyDebuggerLoad(mainLoader);
    const bool needToWritePrebuiltLoaderSet = !mainLoader->isPrebuilt && (state.saveAppClosureFile() || state.failIfCouldBuildAppClosureFile());
  // <rdar://problem/10583252> Add dyld to uuidArray to enable symbolication of stackshots (unless dyld is in the cache)
    // 装载uuid相关信息
  if ( !dyldMH->inDyldCache() ) {
    dyld_uuid_info dyldInfo;
    dyldInfo.imageLoadAddress = dyldMH;
    dyldMH->getUuid(dyldInfo.imageUUID);
    addNonSharedCacheImageUUID(state.persistentAllocator, dyldInfo);
  }
  // 加载所需求刺进的动态库(dylibs)
  STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Loader*, topLevelLoaders, 16);
  topLevelLoaders.push_back(mainLoader);
  Loader::LoadChain  loadChainMain { nullptr, mainLoader };
  Loader::LoadOptions options;
  options.staticLinkage  = true;
  options.launching    = true;
  options.insertedDylib  = true;
  options.canBeDylib   = true;
  options.rpathStack   = &loadChainMain;
  state.config.pathOverrides.forEachInsertedDylib(^(const char* dylibPath, bool& stop) {
    Diagnostics insertDiag;
    if ( Loader* insertedDylib = (Loader*)Loader::getLoader(insertDiag, state, dylibPath, options) ) {
      topLevelLoaders.push_back(insertedDylib);
      state.notifyDebuggerLoad(insertedDylib);
      if ( insertedDylib->isPrebuilt )
        state.loaded.push_back(insertedDylib);
    }
        else if ( insertDiag.hasError() && !state.config.security.allowInsertFailures ) {
      state.log("terminating because inserted dylib '%s' could not be loaded: %s\n", dylibPath, insertDiag.errorMessageCStr());
      halt(insertDiag.errorMessage());
    }
  });
  // 将刺进的库移到处于状况state.loaded中的主可履行文件之前,以获得正确的平面命名空间查找
  if ( topLevelLoaders.count() != 1 ) {
    state.loaded.erase(state.loaded.begin());
    state.loaded.push_back(mainLoader);
  }
  // 用于录制一定丢掉的文件
  __block MissingPaths missingPaths;
  auto missingLogger = ^(const char* mustBeMissingPath) {
    missingPaths.addPath(mustBeMissingPath);
  };
    // 递归加载 主可履行文件 和 刺进的dylib所需的一切内容
    // 记载刺进信息, 遍历一切dylibs, 一些记载查看操作持续往下走
  Diagnostics depsDiag;
  options.insertedDylib = false;
  if ( needToWritePrebuiltLoaderSet )
    options.pathNotFoundHandler = missingLogger;
  for ( Loader* ldr : topLevelLoaders ) {
    ldr->loadDependents(depsDiag, state, options);
    if ( depsDiag.hasError() ) {
      //state.log("%s loading dependents of %s\n", depsDiag.errorMessage(), ldr->path());
      // 让debugger/crashreporter了解咱们能够加载的dylib
      uintptr_t topCount = topLevelLoaders.count();
      STACK_ALLOC_VECTOR(const Loader*, newLoaders, state.loaded.size() - topCount);
      for (size_t i = topCount; i != state.loaded.size(); ++i)
        newLoaders.push_back(state.loaded[i]);
      state.notifyDebuggerLoad(newLoaders);
      gProcessInfo->terminationFlags = 1; // 不要显现痕迹,由于没有什么风趣的
      halt(depsDiag.errorMessage());
    }
  }
  uintptr_t topCount = topLevelLoaders.count();
  {
    STACK_ALLOC_VECTOR(const Loader*, newLoaders, state.loaded.size());
    for (const Loader* ldr : state.loaded)
      newLoaders.push_back(ldr);
    // 在主可履行文件之后告诉调试器debuger一切加载的映像
    std::span<const Loader*> unnotifiedNewLoaders(&newLoaders[topCount], newLoaders.size() - topCount);
    state.notifyDebuggerLoad(unnotifiedNewLoaders);
    // 告诉kernel内核任何dtrace静态用户勘探
    state.notifyDtrace(newLoaders);
  }
    // <rdar://problem/7739489> 记载images的初始计数
  // 因而CrashReporter能够记载哪些images是动态加载的
  gProcessInfo->initialImageCount = state.loaded.size();
  // 添加到永久范围
  STACK_ALLOC_ARRAY(const Loader*, nonCacheNeverUnloadLoaders, state.loaded.size());
  for (const Loader* ldr : state.loaded) {
    if ( !ldr->dylibInDyldCache )
      nonCacheNeverUnloadLoaders.push_back(ldr);
  }
  state.addPermanentRanges(nonCacheNeverUnloadLoaders);
  // proactive weakDefMap means we build the weakDefMap before doing any binding
    // 主动weakDefMap意味着咱们在进行任何绑定之前构建weakDefMap
  if ( state.config.process.proactivelyUseWeakDefMap ) {
    state.weakDefMap = new (state.persistentAllocator.malloc(sizeof(WeakDefMap))) WeakDefMap();
    STACK_ALLOC_VECTOR(const Loader*, allLoaders, state.loaded.size());
    for (const Loader* ldr : state.loaded)
      allLoaders.push_back(ldr);
    Loader::addWeakDefsToMap(state, allLoaders);
  }
    // 在履行修正之前查看是否刺进元组
  state.buildInterposingTables();
  // do fixups做修补工作,这儿也是去处理了`rebase`
  {
    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_FIXUPS, 0, 0, 0);
    // 以防万一咱们需求修补这个案件
    DyldCacheDataConstLazyScopedWriter cacheDataConst(state);
    // C++标准表示,首要可履行文件能够界说非弱函数,这些函数掩盖dylib中的weak-defs
    // 关于在发动时绑定的任何目标,这都会主动产生,但dyld缓存是预绑定pre-bound的,因而咱们需求修补主可履行文件中被该非弱目标掩盖的任何绑定。
    // 留意,在macOS上,咱们还答应dylib具有weak-defs的non-weak掩盖
    if ( !mainLoader->isPrebuilt )
      JustInTimeLoader::handleStrongWeakDefOverrides(state, cacheDataConst);
    for ( const Loader* ldr : state.loaded ) {
      Diagnostics fixupDiag;
      ldr->applyFixups(fixupDiag, state, cacheDataConst, true);
      if ( fixupDiag.hasError() ) {
        halt(fixupDiag.errorMessage());
      }
            // 根目录需求修补缓存中未找到的GOT
      ldr->applyCachePatches(state, cacheDataConst);
    }
    // 假如咱们有单例修补
    state.doSingletonPatching();
  }
    // 假如存在刺进,则将刺进元组运用于dyld缓存
  if ( !state.interposingTuplesAll.empty() ) {
    Loader::applyInterposingToDyldCache(state);
  }
  // 假如mainLoader是pre-build,则或许会掩盖dyld缓存中的weak-defs
  if ( mainLoader->isPrebuilt ) {
    DyldCacheDataConstLazyScopedWriter dataConstWriter(state);
    DyldCacheDataConstLazyScopedWriter* dataConstWriterPtr = &dataConstWriter; // 在cacheWeakDefFixup中进行变通以使其可拜访
    state.processPrebuiltLoaderSet()->forEachCachePatch(^(const PrebuiltLoaderSet::CachePatch& patch) {
      uintptr_t newImpl = (uintptr_t)patch.patchTo.value(state);
      state.config.dyldCache.addr->forEachPatchableUseOfExport(patch.cacheDylibIndex, patch.cacheDylibVMOffset,
                                  ^(uint64_t cacheVMOffset,
                                   dyld3::MachOLoaded::PointerMetaData pmd, uint64_t addend) {
        uintptr_t* loc   = (uintptr_t*)(((uint8_t*)state.config.dyldCache.addr) + cacheVMOffset);
        uintptr_t newValue = newImpl + (uintptr_t)addend;
#if __has_feature(ptrauth_calls)
        if ( pmd.authenticated )
          newValue = MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(newValue, loc, pmd.usesAddrDiversity, pmd.diversity, pmd.key);
#endif
                // 忽略重复的修补程序条目
        if ( *loc != newValue ) {
          dataConstWriterPtr->makeWriteable();
          if ( state.config.log.fixups )
            state.log("cache patch: %p = 0x%0lX\n", loc, newValue);
          *loc = newValue;
        }
      });
    });
  }
  // call kdebug trace for each image
  if ( kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, DBG_DYLD_UUID_MAP_A)) ) {
    // dyld in the cache event was sent earlier when we unmapped the on-disk dyld
        // 缓存事件中的dyld是在咱们取消映射磁盘上的dyld时发送的
    if ( !dyldMH->inDyldCache() ) {
      // 为dyld本身添加盯梢
      uuid_t dyldUuid;
      dyldMH->getUuid(dyldUuid);
      struct stat    stat_buf;
      fsid_t       dyldFsid  = { { 0, 0 } };
      fsobj_id_t     dyldFfsobjid = { 0, 0 };
      if ( dyld3::stat(state.config.process.dyldPath, &stat_buf) == 0 ) {
        dyldFfsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
        dyldFsid   = { { stat_buf.st_dev, 0 } };
      }
      kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, state.config.process.dyldPath, &dyldUuid, dyldFfsobjid, dyldFsid, dyldMH);
    }
    // 为每个image的加载进程添加盯梢
    for ( const Loader* ldr : state.loaded ) {
      const MachOLoaded* ml = ldr->loadAddress(state);
      fsid_t       fsid  = { { 0, 0 } };
      fsobj_id_t     fsobjid = { 0, 0 };
      struct stat    stat_buf;
      if ( !ldr->dylibInDyldCache && (dyld3::stat(ldr->path(), &stat_buf) == 0) ) { //FIXME Loader knows inode
        fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
        fsid  = { { stat_buf.st_dev, 0 } };
      }
      uuid_t uuid;
      ml->getUuid(uuid);
      kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, ldr->path(), &uuid, fsobjid, fsid, ml);
    }
  }
  // 告诉任何其他查看此进程的进程
  // 告诉盯梢此进程中负载的任何进程
  STACK_ALLOC_ARRAY(const char*, pathsBuffer, state.loaded.size());
  STACK_ALLOC_ARRAY(const mach_header*, mhBuffer, state.loaded.size());
  for ( const Loader* ldr : state.loaded ) {
    pathsBuffer.push_back(ldr->path());
    mhBuffer.push_back(ldr->loadAddress(state));
  }
  notifyMonitoringDyld(false, (unsigned int)state.loaded.size(), &mhBuffer[0], &pathsBuffer[0]);
  // 将libdyld.dylib连接到dyld
  LibdyldDyld4Section* libdyld4Section = nullptr;
  if ( state.libdyldLoader != nullptr ) {
    const MachOLoaded* libdyldML = state.libdyldLoader->loadAddress(state);
    uint64_t      sectSize;
    libdyld4Section = (LibdyldDyld4Section*)libdyldML->findSectionContent("__DATA", "__dyld4", sectSize, true);
#if __has_feature(ptrauth_calls)
    if ( libdyld4Section == nullptr )
      libdyld4Section = (LibdyldDyld4Section*)libdyldML->findSectionContent("__AUTH", "__dyld4", sectSize, true);
#endif
    if ( libdyld4Section != nullptr ) {
      // 设置指向全局API目标的指针
      libdyld4Section->apis = &state;
      // 将指针设置为dyld_all_image_infosos
      libdyld4Section->allImageInfos = gProcessInfo;
      // 程序变量(例如environ)通常在libdyld.dylib中界说(但或许在旧macOS二进制文件的主可替换文件中界说)
      // 记住progams变量的方位,以便libc能够同步它们
      state.vars         = &libdyld4Section->defaultVars;
      state.vars->mh       = state.config.process.mainExecutable;
      *state.vars->NXArgcPtr   = state.config.process.argc;
      *state.vars->NXArgvPtr   = (const char**)state.config.process.argv;
      *state.vars->environPtr  = (const char**)state.config.process.envp;
      *state.vars->__prognamePtr = state.config.process.progname;
    }
    else {
      halt("compatible libdyld.dylib not found");
    }
  }
  else {
    halt("libdyld.dylib not found");
  }
  if ( state.libSystemLoader == nullptr)
    halt("program does not link with libSystem.B.dylib");
#if !TARGET_OS_SIMULATOR
  // 假如运用JustInTimeLoader发动,或许需求将其序列化
  if ( needToWritePrebuiltLoaderSet ) {
    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_BUILD_CLOSURE, 0, 0, 0);
    if ( state.config.log.loaders )
      state.log("building PrebuiltLoaderSet for main executable\n");
    Diagnostics       prebuiltDiag;
    const PrebuiltLoaderSet* prebuiltAppSet = PrebuiltLoaderSet::makeLaunchSet(prebuiltDiag, state, missingPaths);
    if ( (prebuiltAppSet != nullptr) && prebuiltDiag.noError() ) {
      if ( state.failIfCouldBuildAppClosureFile() )
        halt("dyld: PrebuiltLoaderSet expected but not found");
      // 将PrebuiltLoaderSet保存到磁盘以便下次发动时运用,持续运用JustInTimeLoaders运转
      if ( state.saveAppPrebuiltLoaderSet(prebuiltAppSet) )
        state.setSavedPrebuiltLoaderSet();
      prebuiltAppSet->deallocate();
      timer.setData4(dyld3::DyldTimingBuildClosure::LaunchClosure_Built);
    }
    else if ( state.config.log.loaders ) {
      state.log("could not build PrebuiltLoaderSet: %s\n", prebuiltDiag.errorMessage());
    }
  }
  // 假如运用程序发动pre-warm,请提前退出
  if ( state.config.process.environ("DYLD_JUST_BUILD_CLOSURE") != nullptr ) {
    return &fake_main;
  }
#endif
#if SUPPPORT_PRE_LC_MAIN
  uint32_t        progVarsOffset;
  dyld3::DyldLookFunc*  dyldLookupFuncAddr = nullptr;
  bool          crtRunsInitializers = false;
  if ( state.config.process.mainExecutable->hasProgramVars(progVarsOffset, crtRunsInitializers, dyldLookupFuncAddr) ) {
    // 这是一个旧的macOS运用程序,它有自己的NXArgv等全局变量。咱们需求运用它们。
    ProgramVars* varsInApp  = (ProgramVars*)(((uint8_t*)state.config.process.mainExecutable) + progVarsOffset);
    varsInApp->mh       = state.config.process.mainExecutable;
    *varsInApp->NXArgcPtr   = state.config.process.argc;
    *varsInApp->NXArgvPtr   = (const char**)state.config.process.argv;
    *varsInApp->environPtr  = (const char**)state.config.process.envp;
    *varsInApp->__prognamePtr = state.config.process.progname;
    state.vars        = varsInApp;
  }
  if ( dyldLookupFuncAddr ) {
    if ( libdyld4Section != nullptr ) {
      *dyldLookupFuncAddr = (dyld3::DyldLookFunc)libdyld4Section->dyldLookupFuncAddr;
    } else {
      halt("compatible libdyld.dylib not found");
    }
  }
  if ( !crtRunsInitializers )
    state.runAllInitializersForMain();
#else
    // 运转一切的初始化
  dyld4::notifyMonitoringDyldBeforeInitializers();
  state.runAllInitializersForMain();
    ...
    ...
    return result;
}
  • 定论:

    ①. 回来main函数地址,一切装备完后 _dyld_register_driverkit_main便是咱们自界说工程的main地址。

    ②. gProcessInfo是存储dyld一切images镜像信息的结构体,保存着mach_headerdyld_uuid_infodyldVersion等等信息。

    ③. 创立just-in-time,是dyld4一个新特性。dyld4在保留了dyld3mach-o 解析器根底上,同时也引入了 just-in-time 的加载器来优化。

    • dyld3 出于对发动速度的优化的目的, 增加了预构建(闭包)App第一次发动或许App产生改变时会将部分发动数据创立为闭包存到本地,那么App下次发动将不再从头解析数据,而是直接读取闭包内容。当然前提是运用程序和体系应很少产生改变,但假如这两者常常改变等, 就会导闭包丢掉或失效。

    • dyld4 采用了 pre-build + just-in-time双解析形式预构建pre-build 对应的便是 dyld3中的闭包,just-in-time能够理解为实时解析。当然just-in-time 也是能够利用 pre-build 的缓存的,所以功能可控。有了just-in-time, 现在运用首次发动、体系版别更新、一般发动,dyld4 则能够依据缓存是否有效去选择合适的形式进行解析。


runAllInitializersForMain

// 这是dyldMain.cpp中提取出来的一部分,以支持运用crt1.o的旧macOS运用程序
void APIs::runAllInitializersForMain()
{

    // 禁用page-in链接,不用于dlopen()加载images
  if ( !config.security.internalInstall || (config.process.pageInLinkingMode != 3) )
    config.syscall.disablePageInLinking();
    // 首要答应libSystem的初始化器
  const_cast<Loader*>(this->libSystemLoader)->beginInitializers(*this);
  this->libSystemLoader->runInitializers(*this);
  gProcessInfo->libSystemInitialized = true;
  // 运转libSystem的初始化程序后,告诉objc在libSystem子dylib上运转任何+load办法
  this->notifyObjCInit(this->libSystemLoader);
  // <rdar://problem/32209809> 调用一切现已images的'init' 函数 (below libSystem)
  // 运用下标索引进行迭代,以便在+load dloopen时数组不会在咱们下面增加
  for ( uint32_t i = 0; i != this->loaded.size(); ++i ) {
    const Loader* ldr = this->loaded[i];
    if ( (ldr->dylibInDyldCache || ldr->analyzer(*this)->isDylib()) && (strncmp(ldr->analyzer(*this)->installName(), "/usr/lib/system/lib", 19) == 0) ) {
      // 查看装置名称而不是途径,以处理libsystem子dylibs的DYLD_LIBRARY_PATH掩盖
      const_cast<Loader*>(ldr)->beginInitializers(*this);
      this->notifyObjCInit(ldr);
      const_cast<Loader*>(ldr)->runInitializers(*this);
    }
  }
#if TARGET_OS_OSX
  // If we're PID 1, scan for roots
  if ( (this->config.process.pid == 1) && (this->libSystemHelpers->version() >= 5) ) {
    this->libSystemHelpers->run_async(&ProcessConfig::scanForRoots, (void*)&this->config);
  }
#endif // TARGET_OS_OSX
    //自下而上运转一切其他初始化器,首要运转刺进的dylib初始化器
    //运用下标索引进行迭代,以便假如初始dloopen时数组不会向下增加
  for ( uint32_t i = 0; i != this->loaded.size(); ++i ) {
    const Loader* ldr = this->loaded[i];
    ldr->runInitializersBottomUpPlusUpwardLinks(*this);
    // stop as soon as we did main executable
    // normally this is first image, but if there are inserted N dylibs, it is Nth in the list
    if ( ldr->analyzer(*this)->isMainExecutable() )
      break;
  }
}
  • 定论:

    ①. 在libSystem的初始化时,用notifyObjCInit告诉libSystem依靠的dylibs运转一切的+load办法,这儿不是咱们自界说工程里+load

    ②. 处理完libSystem的初始化后,再加载后自下而上运转一切其他镜像的初始化器。


runInitializersBottomUpPlusUpwardLinks

void Loader::runInitializersBottomUpPlusUpwardLinks(RuntimeState& state) const
{
  //state.log("runInitializersBottomUpPlusUpwardLinks() %s\n", this->path());
  state.incWritable();
  // 递归运转一切初始化程序 initializers
  STACK_ALLOC_ARRAY(const Loader*, danglingUpwards, state.loaded.size());
  this->runInitializersBottomUp(state, danglingUpwards);
  //state.log("runInitializersBottomUpPlusUpwardLinks(%s), found %d dangling upwards\n", this->path(), danglingUpwards.count());
  // 回来到一切向上链接的images,并从头查看它们是否已初始化(或许是挂起的)
  STACK_ALLOC_ARRAY(const Loader*, extraDanglingUpwards, state.loaded.size());
  for ( const Loader* ldr : danglingUpwards ) {
    //state.log("running initializers for dangling upward link %s\n", ldr->path());
    ldr->runInitializersBottomUp(state, extraDanglingUpwards);
  }
  if ( !extraDanglingUpwards.empty() ) {
    // 假如有两个向上悬挂的images,请再次查看初始化器
    danglingUpwards.resize(0);
    for ( const Loader* ldr : extraDanglingUpwards ) {
      //state.log("running initializers for dangling upward link %s\n", ldr->path());
      ldr->runInitializersBottomUp(state, danglingUpwards);
    }
  }
  state.decWritable();
}
  • 定论:

    ①.递归运转一切初始化程序initializers


runInitializersBottomUp

void Loader::runInitializersBottomUp(RuntimeState& state, Array<const Loader*>& danglingUpwards) const
{
  // 假如已运转初始化程序,则不履行任何操作
  if ( (const_cast<Loader*>(this))->beginInitializers(state) )
    return;
  //state.log("runInitializersBottomUp(%s)\n", this->path());
  // 在运转初始化程序之前,请保证此image下面的一切内容都已初始化
  const uint32_t depCount = this->dependentCount();
  for ( uint32_t i = 0; i < depCount; ++i ) {
    DependentKind childKind;
    if ( Loader* child = this->dependent(state, i, &childKind) ) {
      if ( childKind == DependentKind::upward ) {
        // 向上添加到列表以稍后处理
        if ( !danglingUpwards.contains(child) )
          danglingUpwards.push_back(child);
      }
      else {
        child->runInitializersBottomUp(state, danglingUpwards);
      }
    }
  }
  // 告诉objc在此image映像中运转任何+load办法(在C++初始化器之前完结)
  state.notifyObjCInit(this);
  // 为此image映像运转初始化程序
  this->runInitializers(state);
}
  • 定论:

    ①. 终究经过notifyObjCInit告诉objc在此image映像中运转任何+load办法(在C++初始化器之前完结)。


notifyObjCInit

void RuntimeState::notifyObjCInit(const Loader* ldr)
{
  //this->log("objc-init-notifier checking mh=%p, path=%s, +load=%d, objcInit=%p\n", ldr->loadAddress(), ldr->path(), ldr->mayHavePlusLoad, _notifyObjCInit);
  if ( (_notifyObjCInit != nullptr) && ldr->mayHavePlusLoad ) {
    const MachOLoaded* ml = ldr->loadAddress(*this);
    const char*    pth = ldr->path();
    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)ml, 0, 0);
    if ( this->config.log.notifications )
      this->log("objc-init-notifier called with mh=%p, path=%s\n", ml, pth);
    _notifyObjCInit(pth, ml);
  }
}
  • 定论:

    ②. 首要是遍历各个库文件,经过_notifyObjCInit来加载+load办法。


探究_notifyObjCInit来历

  1. _notifyObjCInit的界说是在RuntimeState类的声明里:

OC底层原理(十一):应用程序的dyld4流程上

  1. _dyld_objc_notify_initsetObjCNotifiers被赋值的:
#if BUILDING_DYLD || BUILDING_UNIT_TESTS
void RuntimeState::setObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init,
                  _dyld_objc_notify_unmapped unmapped, _dyld_objc_notify_patch_class patchClass,
                  _dyld_objc_notify_mapped2 mapped2)
{
  _notifyObjCMapped    = mapped;
  _notifyObjCInit     = init;
  _notifyObjCUnmapped   = unmapped;
  _notifyObjCPatchClass  = patchClass;
  _notifyObjCMapped2   = mapped2;
  withLoadersReadLock(^{
    if ( (_notifyObjCPatchClass != nullptr) && !this->objcReplacementClasses.empty() ) {
      // 告诉Symbolication象征咱们正在修补类
      this->setDyldPatchedObjCClasses();
      for ( const ObjCClassReplacement& classReplacement : this->objcReplacementClasses )
        (*_notifyObjCPatchClass)(classReplacement.cacheMH, (void*)classReplacement.cacheImpl,
                    classReplacement.rootMH, (const void*)classReplacement.rootImpl);
      if ( this->config.log.notifications ) {
        this->log("objc-patch-class-notifier called with %ld patches:\n", this->objcReplacementClasses.size());
      }
      // 清除替换类。假如产生dlopen,咱们不想再次告诉它们
      this->objcReplacementClasses.clear();
    }
    // 回调关于已加载图像images
    size_t maxCount = this->loaded.size();
    STACK_ALLOC_ARRAY(const mach_header*, mhs, maxCount);
    STACK_ALLOC_ARRAY(const char*, paths, maxCount);
    STACK_ALLOC_ARRAY(_dyld_objc_notify_mapped_info, infos, maxCount);
    for ( const Loader* ldr : loaded ) {
      // 这儿不需求_mutex互斥锁,由于这是在进程仍然是单线程时调用的
      const MachOLoaded* ml = ldr->loadAddress(*this);
      if ( ldr->hasObjC ) {
        paths.push_back(ldr->path());
        mhs.push_back(ml);
        infos.push_back({ml, ldr->path(), ldr->dyldDoesObjCFixups(), 0});
        // 在map_images运转时内存read-write可读写
        if ( ldr->hasConstantSegmentsToProtect() && ldr->hasReadOnlyObjC )
          ldr->makeSegmentsReadWrite(*this);
      }
    }
    if ( !mhs.empty() ) {
      if ( _notifyObjCMapped != nullptr )
            // 经过指针_notifyObjCMapped指向运转map_images
        (*_notifyObjCMapped)((uint32_t)mhs.count(), &paths[0], &mhs[0]);
      if ( _notifyObjCMapped2 != nullptr )
        (*_notifyObjCMapped2)((uint32_t)mhs.count(), &infos[0]);
      if ( this->config.log.notifications ) {
        this->log("objc-mapped-notifier called with %ld images:\n", mhs.count());
        for ( uintptr_t i = 0; i < mhs.count(); ++i ) {
          this->log(" objc-mapped: %p %s\n", mhs[i], paths[i]);
        }
      }
      // 运转map_images后使内存为只读read-only
      for ( const Loader* ldr : loaded ) {
        if ( ldr->hasObjC && ldr->hasConstantSegmentsToProtect() && ldr->hasReadOnlyObjC )
          ldr->makeSegmentsReadOnly(*this);
      }
    }
  });
}
  1. setObjCNotifiers是在_dyld_objc_notify_register被调用的:
// FIXME: 一旦libobjc移动到_dyld_objc_register_callbacks(),请删去此项
// 这个是从前的注册办法
void APIs::_dyld_objc_notify_register(_dyld_objc_notify_mapped  mapped,
                   _dyld_objc_notify_init   init,
                   _dyld_objc_notify_unmapped unmapped)
{
  if ( config.log.apis )
    log("_dyld_objc_notify_register(%p, %p, %p)\n", mapped, init, unmapped);
  setObjCNotifiers(mapped, init, unmapped, nullptr, nullptr);
  // If we have prebuilt loaders, then the objc optimisations may hide duplicate classes from libobjc.
  // We need to print the same warnings libobjc would have.
  if ( const PrebuiltLoaderSet* mainSet = this->processPrebuiltLoaderSet() )
    mainSet->logDuplicateObjCClasses(*this);
}
// 最新的代码objc4的注册办法
void APIs::_dyld_objc_register_callbacks(const _dyld_objc_callbacks* callbacks)
{
  if ( config.log.apis ) {
    void** p = (void**)callbacks;
    log("_dyld_objc_register_callbacks(%lu, %p, %p, %p, %p)\n", callbacks->version, p[1], p[2], p[31], p[4]);
  }
  if ( callbacks->version == 1 ) {
    const _dyld_objc_callbacks_v1* v1 = (const _dyld_objc_callbacks_v1*)callbacks;
    setObjCNotifiers(v1->mapped, v1->init, v1->unmapped, v1->patches, nullptr);
  }
  else if ( callbacks->version == 2 ) {
    const _dyld_objc_callbacks_v2* v2 = (const _dyld_objc_callbacks_v2*)callbacks;
    setObjCNotifiers(nullptr, v2->init, v2->unmapped, v2->patches, v2->mapped);
  }
  else {
  }
  // 假如咱们有预构建的加载器,那么objc优化或许会从libobjc中躲藏重复的类。
  // 咱们需求打印libobjc将具有的相同警告。
  if ( const PrebuiltLoaderSet* mainSet = this->processPrebuiltLoaderSet() )
    mainSet->logDuplicateObjCClasses(*this);
}
  • 定论:

    ①. 从前dyld3的注册办法_dyld_objc_notify_register,最新dyld4_dyld_objc_register_callbacks

  1. _dyld_objc_register_callbacks是在objc4-866.9源码里的objc_init()里被调用的
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**************************************************************************/
void _objc_init(void)
{
  static bool initialized = false;
  if (initialized) return;
  initialized = true;
  // fixme defer initialization until an objc-using image is found?
  environ_init();
  static_init();
  runtime_init();
  exception_init();
  cache_t::init();
  _imp_implementationWithBlock_init();
  _dyld_objc_callbacks_v1 callbacks = {
    1, // version
    &map_images,
    load_images,
    unmap_image,
    _objc_patch_root_of_class
  };
  _dyld_objc_register_callbacks((_dyld_objc_callbacks*)&callbacks);
  didCallDyldNotifyRegister = true;
}
  • 定论:

    ①. 这儿解释了从前dyld进口探究,调用完notifyObjCInit后便是load_images

    ②.map_images映射镜像、load_images加载镜像、unmap_image免除镜像objc4源码初始化装备的要点,为了进一步了解dyld4关于images镜像们的办理,我要先去了解objc4的objc_init。


五、总结

  1. 剖析了map_imagesload_images注册load_images调用流程,可是未具体剖析objc4_objc_initmap_imagesload_images的源码与流程。

  2. 只剖析了dyld4的部分流程,而未剖析dyld3的流程。下面是两者对比:

  • dyld3:

    OC底层原理(十一):应用程序的dyld4流程上

  • dyld4:

    OC底层原理(十一):应用程序的dyld4流程上