前言
在平时的开发过程中,app
的入口函数是main()
,而在main()
函数调用之前,系统是如何做的?具体都做了什么?下面具体分析app
的加载流程。
准备工作
在分析app
的加载流程之前,先了解几个概念。
- 编译过程
编译过程
:是由系统将源文件(.h .m等)
,通过预编译
、编译
,生成对应的汇编
代码,然后通过dyld
加载库文件到内存
,再将汇编
和库文件
链接成可执行文件
的过程。
-
动态库和静态库
动态库
:动态链接。只会存在一份,在内存中共享。动态库
减少了包的体积大小。例如系统的动态库:Foundation
、UIKit
等。静态库
:静态链接。静态库
在加载的时候会重复,浪费了空间。
-
下载 dyld源码
-
下载 Libsystem源码
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
函数是dyld
的main
函数,并不是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_initializer
→libdispatch_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_register
→ registerObjCNotifiers
→ notifyBatchPartial
→ sNotifyObjCMapped
(map_images
)实现。
dyld2和dyld3
dyld2
dyld2
是dyld1
的完全重写版本。- 正确支持
c++
初始化器,扩展了mach-o
格式并且更新了dyld
。 - 具有完整的
dlopen
和dlsym
实现,弃用了旧版API
(旧版API
仍然仅位于macOS
中)。 dyld2
的设计目标是提高速度,因此仅进行有限的健全性检查。dyld1
有一些安全性问题,dyld2
对一些功能性改进,提高了安全性。- 速度大幅提升性能,可以减少预绑定工作量。不同于
dyld1
编译你的程序数据,dyld2
仅编译系统库。可以仅在软件更新时做这些事情。因此在软件更新时可能会看到优化系统性能
之类的文字,这时就是在更新预绑定。
dyld3
dyld3
是全新的动态连接器,iOS11
以上的系统程序都默认使用dyld3
,在iOS13
完全取代dyld2
。dyld3
主要做了以下三方面的改进:
- 性能。提高启动速度。
dyld3
可以帮助我们获得更快的程序启动和运行速度。 - 安全性。
dyld2
增加的安全性很难跟随现实情形增强安全性。 - 可测试性和可靠性。
dyld2和dyld3加载对比
dyld2流程:
Parse mach-o headers & Find dependencies
:分析macho headers
,确认需要哪些库。递归分析依赖的库直到获得所有的dylib
库。普通iOS
程序需要3-600
个dylib
,数据庞大需要进行大量处理。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
启动时没有这个过程,因此极大的提升了程序的启动速度。
具体详细情况参考 官方视频。