dyld 简介
dyld(the dynamic link editor)是苹果的动态链接器,用来加载所有的库和可执行文件,是苹果操作系统一个重要组成部分,在系统内核做好程序准备工作之后,交由 dyld 负责余下的工作。而且它是开源的,任何人可以通过苹果官网下载它的源码来阅读理解它的运作方式,那下面我们就通过源码尝试看一下 dyld
的加载流程。
下面我们通过对控制器的 load
方法添加断点,看看 dyld
是从哪里开始执行的:
从函数的调用栈我们可以看到,dyld
是从 start
方法开始执行的,那我我们就打开源码找到 start
方法,来一步一步的分析 dyld
的调用过程。这里用到是832.7.3版本。
dyld 入口函数 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地址,把dyld加载进来(虚拟内存下,一个进程启动都需要做地址重定位)
rebaseDyld(dyldsMachHeader);
// kernel sets up env pointer to be just past end of agv array
const char** envp = &argv[argc+1];
// kernel sets up apple pointer to be just past end of envp array
const char** apple = envp;
while(*apple != NULL) { ++apple; }
++apple;
// 栈溢出的保护
__guard_setup(apple);
#if DYLD_INITIALIZER_SUPPORT
// run all C++ initializers inside dyld
runDyldInitializers(argc, argv, envp, apple);
#endif
// 初始化dyld
_subsystem_init(apple);
// now that we are done bootstrapping dyld, call dyld's main
uintptr_t appsSlide = appsMachHeader->getSlide();
// 调用main(最核心的代码在main函数里面,因为dyld几乎所有的逻辑都在main里面处理)
return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}
在 start
方法里面主要是做了一些准备工作,是开始启动阶段,最核心的代码在 main
函数里面,下面我们接着进入 main
函数。
dyld 的 main 函数
现在我们来到 main
函数
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
int argc, const char* argv[], const char* envp[], const char* apple[],
uintptr_t* startGlue)
{
// 1. 内核检测代码,这里省略......
// 2.主程序配置相关
uint8_t mainExecutableCDHashBuffer[20];
// 主程序的hash
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;
}
// mainExecutableMH :主程序的header,mainExecutableSlide :主程序的slide
// 设置cpu,cpu的类型等信息
getHostInfo(mainExecutableMH, mainExecutableSlide);
// 对header跟slide赋值
uintptr_t result = 0;
sMainExecutableMachHeader = mainExecutableMH;
sMainExecutableSlide = mainExecutableSlide;
// 3.设置上下文,把配置信息保存到gLinkContext这个变量里面,到这里都是做一些配置相关的操作并保存下来
setContext(mainExecutableMH, argc, argv, envp, apple);
// 配置进程是否受限,envp:环境变量
// AMFI相关(Apple Mobile File Integrity苹果移动文件保护)
configureProcessRestrictions(mainExecutableMH, envp);
// 判断是否要强制使用dyld3 (dyld3是iOS11之后新增的一种闭包模式,处理效率更高,加载速度更快)
if ( dyld3::internalInstall() ) {...}
// 4. 检测环境变量,并给环境变量设置默认值
checkEnvironmentVariables(envp);
defaultUninitializedFallbackPaths(envp);
// 打印相关的环境变量(在load之前打印)
if ( sEnv.DYLD_PRINT_OPTS )
printOptions(argv);
if ( sEnv.DYLD_PRINT_ENV )
printEnvironmentVariables(envp);
// 5. 加载共享缓存,UIKit,Foundtion等动态库 (在这之前还没有加载主程序,但是已经拿到主程序的header,开始读主程序了,根据主程序的header可以知道主程序的cup,架构,系统等信息)
// 共享缓存: 用来存储UIkit等系统库的,保证这些库只加载一份,因为iOS进程之间访问受限,所以要放到共享缓存里面
checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
// 6.加载各项我们所依赖的framework,还有三方库
// 7.开始判断是加载`dyld2`还是`dyld3`。
dyld3 的加载流程
// ClosureMode: 闭包模式,iOS11后引入的,iOS13开始动态库,三方库都用ClosureMode模式加载
// 判断是否是闭包模式,是的话就先开始创建闭包模式加载器
if ( sClosureMode == ClosureMode::Off ) {...} else {
// 给sLaunchModeUsed变量设置一个表示,表示用闭包模式去启动dyld
sLaunchModeUsed = DYLD_LAUNCH_MODE_USING_CLOSURE;
// 1. 配置主程序的信息:mainFileInfo,主程序的header:mainExecutableMH
const dyld3::closure::LaunchClosure* mainClosure = nullptr;
dyld3::closure::LoadedFileInfo mainFileInfo;
mainFileInfo.fileContent = mainExecutableMH;
// check for closure in cache first
if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
// 先去共享缓存中取查看 mainClosure 实例
mainClosure = sSharedCacheLoadInfo.loadAddress->findClosure(sExecPath);
// 如果实例对象为空就主动创建
if ( mainClosure == nullptr ) {
mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
}
// 2. 这里开始进入到启动
if ( mainClosure != nullptr ) {
// 开启启动并返回启动结果
bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
// 如果启动失败或者closureOutOfDate过期
if ( !launched && closureOutOfDate && allowClosureRebuilds ) {
// 再次创建一个mainClosure实例
mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
// 并再次启动
launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
}
}
// 如果启动成功,就保存变量,主程序加载成功
if ( launched ) {
gLinkContext.startedInitializingMainExecutable = true;
// 拿到主程序的main函数,并返回result
if (sSkipMain)
result = (uintptr_t)&fake_main;
return result;
}
// 如果启动失败,就报响应的错误信息
else {
if ( gLinkContext.verboseWarnings ) {
dyld::log("dyld: unable to use closure %p\n", mainClosure);
}
if ( !recoverable )
halt(diag.errorMessage());
}
dyld2 的加载流程
// 如果不是闭包模式 dyld2模式
sLaunchModeUsed = 0;
// 把 notifyGDB 跟 updateAllImages 回调放到 sBatchHandlers 数组
sBatchHandlers)->push_back(notifyGDB);
stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
// 分配一些初始化空间,尽量空间分配的大一点,保证后面够用
sImageRoots.reserve(16);
sAddImageCallbacks.reserve(4);
sRemoveImageCallbacks.reserve(4);
sAddLoadImageCallbacks.reserve(4);
sImageFilesNeedingTermination.reserve(16);
sImageFilesNeedingDOFUnregistration.reserve(8);
// 把dyld加入到UUIDList列表
addDyldImageToUUIDList();
// 开始加载主程序
// mainExcutableAlreadyRebased:主程序Rebased状态标识
bool mainExcutableAlreadyRebased = false;
if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) {
struct stat statBuf;
if ( dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR "no-dyld2-accelerator-tables", &statBuf) != 0 )
sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(&sSharedCacheLoadInfo.loadAddress->header, sSharedCacheLoadInfo.slide, mainExecutableMH, gLinkContext);
}
// 实例化主程序 image:可执行文件(dyld第一个加载的image是主程序)
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
gLinkContext.mainExecutable = sMainExecutable;
// 代码签名
gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
// 检测主程序是否属于当前系统,当前设备版本
{
if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH, sExecPath) ) {
throwf("program was built for a platform that is not supported by this runtime");
}
uint32_t mainMinOS = sMainExecutable->minOSVersion();
// dyld is always built for the current OS, so we can get the current OS version
// from the load command in dyld itself.
uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion((const mach_header*)&__dso_handle);
if ( mainMinOS > dyldMinOS ) {...}
// 相关配置
if (dyld::isTranslated()) {...}
// 设置动态库的版本
checkVersionedPaths();
// 先判断有没有DYLD_INSERT_LIBRARIES这个环境变量,有的话插入所有动态库
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
// 记录插入动态库
gLinkContext.linkingMainExecutable = true;
// 链接主程序,在此函数开始时记录开始时间,然后递归加载主程序依赖的库,加载完毕后会发送通知,修正ASLR,绑定NoLazy符号,绑定弱符号,递归应用插入的动态库,注册,最后记录结束时间
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
if ( sInsertedDylibCount > 0 ) {
// 循环遍历把动态库加入到allImage
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 ) {
// only INSERTED libraries can interpose
// register interposing info after all inserted libraries are bound so chaining works
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
image->registerInterposing(gLinkContext);
}
}
}
// 循环拿出image,绑定插入的动态库
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);
实例化主程序的过程
这里其实就是实例化一个iamge的过程,其他动态库跟三方库的实例化过程跟主程序的实例化过程一样。
// 实例化一个iamge的过程
static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path) {
// 加载主程序的MachO文件(header,LoadCommands等)
ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
// 把iamge加入到allImage数组
addImage(image);
return (ImageLoaderMachO*)image;
}
// 进入到instantiateMainExecutable函数
ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context)
{
//dyld::log("ImageLoader=%ld, ImageLoaderMachO=%ld, ImageLoaderMachOClassic=%ld, ImageLoaderMachOCompressed=%ld\n",
// sizeof(ImageLoader), sizeof(ImageLoaderMachO), sizeof(ImageLoaderMachOClassic), sizeof(ImageLoaderMachOCompressed));
bool compressed;
unsigned int segCount;
unsigned int libCount;
const linkedit_data_command* codeSigCmd;
const encryption_info_command* encryptCmd;
sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
// 根据compressed的值选择不同的子类来实例化image
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);
#else
throw "missing LC_DYLD_INFO load command";
#endif
}
进入到sniffLoadCommands函数
void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed,
unsigned int* segCount, unsigned int* libCount, const LinkContext& context,
const linkedit_data_command** codeSigCmd,
const encryption_info_command** encryptCmd)
{
*compressed = false;
*segCount = 0;
*libCount = 0;
*codeSigCmd = NULL; // 代码签名
*encryptCmd = NULL; // 代码加密
// segCount,libCount的数量限制
if ( *segCount > 255 )
dyld::throwf("malformed mach-o image: more than 255 segments in %s", path);
// fSegmentsArrayCount is only 8-bits
if ( *libCount > 4095 )
dyld::throwf("malformed mach-o image: more than 4095 dependent libraries in %s", path);
if ( needsAddedLibSystemDepency(*libCount, mh) )
*libCount = 1;
// dylibs that use LC_DYLD_CHAINED_FIXUPS have that load command removed when put in the dyld cache
if ( !*compressed && (mh->flags & MH_DYLIB_IN_CACHE) )
*compressed = true;
}
initializeMainExecutable 函数
在 initializeMainExecutable
函数之前都是做一些初始化与加载准备工作,这里才是开始真正进入到主程序。
- 进入到 initializeMainExecutable 函数
void initializeMainExecutable()
{
// record that we've reached this step
gLinkContext.startedInitializingMainExecutable = true;
// run initialzers for any inserted dylibs
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]);
}
}
// run initializers for main executable and everything it brings up
sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
// register cxa_atexit() handler to run static terminators in all loaded images when this process exits
if ( gLibSystemHelpers != NULL )
(*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL, NULL);
// dump info if requested
if ( sEnv.DYLD_PRINT_STATISTICS )
ImageLoader::printStatistics((unsigned int)allImagesCount(), initializerTimes[0]);
if ( sEnv.DYLD_PRINT_STATISTICS_DETAILS )
ImageLoaderMachO::printStatisticsDetails((unsigned int)allImagesCount(), initializerTimes[0]);
}
- 通过函数调用栈我们可以看到,initializeMainExecutable函数之后进入到
ImageLoader
函数,不管dyld2还是dyld3都会走到这
dyld函数调用栈.png
void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo)
{
uint64_t t1 = mach_absolute_time();
mach_port_t thisThread = mach_thread_self();
ImageLoader::UninitedUpwards up;
up.count = 1;
up.imagesAndPaths[0] = { this, this->getPath() };
processInitializers(context, thisThread, timingInfo, up);
context.notifyBatch(dyld_image_state_initialized, false);
mach_port_deallocate(mach_task_self(), thisThread);
uint64_t t2 = mach_absolute_time();
fgTotalInitTime += (t2 - t1);
}
- 同样根据函数调用栈来到
recursiveInitialization
函数,看一下最终是怎么调到load_images
函数的。
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
}
- 进到
notifySingle
函数, 但是在这里我们并没有找到load_images
函数的调用,因为load_images
是objc这个库里面的,那他们是怎么调用的呢?
static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
uint64_t t0 = mach_absolute_time();
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
uint64_t t1 = mach_absolute_time();
uint64_t t2 = mach_absolute_time();
uint64_t timeInObjC = t1-t0;
uint64_t emptyTime = (t2-t1)*100;
if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) {
timingInfo->addTime(image->getShortName(), timeInObjC);
}
}
}
- load_images 调用过程
在notifySingle函数的1019行我们可以看到这个地方有一个回调指针,在这里会有一个判断,sNotifyObjCInit不为空就会调用这个回调,那sNotifyObjCInit在哪里赋值的呢?
我们在当前文件搜索会在 registerObjCNotifiers
函数看到是在这里赋值的, 赋值为init,那我们来看一下是哪里调用了 registerObjCNotifiers
函数,并传了一个 init。我们全局搜索一下。
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
}
registerObjCNotifiers函数调用的地方.png
在这里我们看到registerObjCNotifiers在这被调用了,并传入了一个init。
接着再搜索 _dyld_objc_notify_register
会发现找不到了。那我们用符号断点的方式,看看谁调用了 _dyld_objc_notify_register
。
运行之后,通过打印堆栈信息我们可以看到 _dyld_objc_notify_register
函数是被libobjc文件里面的 _objc_init
函数调用了。这里我们需要看objc的源码,找到 _objc_init
函数,并看看这个地方给 _dyld_objc_notify_register
方法传了哪些值。
// _objc_init函数
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();
tls_init();
static_init();
runtime_init();
exception_init();
#if __OBJC2__
cache_t::init();
#endif
_imp_implementationWithBlock_init();
// 在这里我们看到了_dyld_objc_notify_register函数的调用,且第二个参数是load_images
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
在这里我们找到了 _dyld_objc_notify_register
的调用,并看到了第二个参数是 load_images
,也就是init是 load_images
,也就找到了在 notifySingle
函数的1019行调用的是 load_images
函数。这里就看到了从 start
到 load_images
的一个完整过程。
call_load_methods 函数
从 _objc_init
函数进的 load_images
函数,接着进入到 call_load_methods
函数,在这里我们可以看到353行 call_class_loads
,类的 load
方法在这个时候被调用了。
// 1.从 `_objc_init` 函数进的 `load_images` 函数
void
load_images(const char *path __unused, const struct mach_header *mh)
{
call_load_methods();
}
// 2.进入到 `call_load_methods` 函数
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. 类的load方法调用
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. 分类的load方法调用
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
C++全局构造函数的加载
看完了 load_images
函数的调用过程之后,我们来到Demo,试一个现象。
#import "AppDelegate.h"
#import "ViewController.h"
__attribute__((constructor)) void func1() {
printf("func1 执行了");
}
__attribute__((constructor)) void func2() {
printf("func2 执行了");
}
@interface AppDelegate ()
MachO文件结构.png
printf打印顺序.png
在 AppDelegate.m
文件添加 __attribute__((constructor)) void func1
与 __attribute__((constructor)) void func2
函数,这两个函数是全局C++构造函数,工程编译之后,把编译文件拖到MachOView应用之后,可以看到文件结构里面多了__mod_init_func文件。且工程运行的时候,通过 printf
打印顺序我们可以看到,func1
和 func12
在 load
之后 main
之前执行,那是哪块代码来控制 func1
和 func12
类型的函数的加载呢?
现在我们回到 dyld 源码,来的 recursiveInitialization
函数,并最终来到 ImageLoaderMachO
函数。在这里可以看到 doModInitFunctions
函数其实就是负责构造函数的加载。
// 1.进入到ImageLoaderMachO函数
bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
CRSetCrashLogMessage2(this->getPath());
// mach-o has -init and static initializers
doImageInit(context);
// 加载init构造方法
doModInitFunctions(context);
CRSetCrashLogMessage2(NULL);
return (fHasDashInit || fHasInitializers);
}
initializeMainExecutable函数之后
现在我们回到 dyld 的 main 函数,在 之后我们找到7114行,在这里调用了 (uintptr_t)gLibSystemHelpers->startGlueToCallExit
函数,获取主程序的 main
。把主程序的 main
函数的地址赋值给 result
,并判断 result
是否有值,最后在 dyld main函数的结尾返回 result
。
{
// find entry point for main executable
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
if ( result != 0 ) {
// main executable uses LC_MAIN, we need to use helper in libdyld to call into main()
if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
*startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
else
halt("libdyld.dylib support not present for LC_MAIN");
}
else {
// main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
*startGlue = 0;
}
}
}
// dyld main函数的结尾
return result;
最后总结
- DYLD : 动态链接器,加载所有的库和可执行文件。
- dyld 加载流程
- 程序执行从_dyld_start开始 -> dyld`dyldbootstrap::start
- 进入 dyld: main 函数
- 配置一些环境 : rebase_dyld
- 加载共享缓存
- 判断加载模式 DYLD2/DYLD3(闭包模式)
- 实例化主程序
- 加载动态库(首先是插入动态库) (主程序和动态库都会添加到 allImages 里面)
- 链接主程序、绑定符号(这里绑定的都非懒加载的、弱符号)等等
- 最关键的: 初始化方法 initializeMainExecutable (初始化主程序)
- dyld`ImageLoader::runInitializers
- dyld`ImageLoader::processInitializers:
- dyld`ImageLoader::recursiveInitialization:
-
dyld`dyld::notifySingle:函数
- 此函数执行一个回调
- 通过断点调试: 此函数是_objc_init初始化赋值的一个函数Load_images
- Load_images里面执行class_load_methods函数
- call_class_loads函数: 循环调用各个类的 load 方法
- Load_images里面执行class_load_methods函数
-
doModInitFunction 函数
- 内部会调动全局 C++对象的构造函数attribute((constructor))的 C 函数
-
- dyld`ImageLoader::recursiveInitialization:
- dyld`ImageLoader::processInitializers:
- dyld`ImageLoader::runInitializers
- 返回主程序的入口函数,开始进入主程序的 main 函数
不足之处,还请指正......
网友评论