接下来,我们开始探索应用的加载流程,应用程序在启动的时候系统究竟做了什么事情, 当我们command+r运行项目时,就会生成一个mach-o
这样的可执行文件。
Mach-O文件
mach-o
是一种用于可执行文件、目标文件,静态库,动态库的文件格式。
属于Mach-O
格式的常见文件:
- 目标文件 .o
- 库文件
- .a
- .dylib
- Framework架构师
- 可执行文件
- dyld ( 动态链接器 )
- .dsym ( 符号表 )
我ios16们的工程在生成可执行文件之前都需要经过很多的过程:
- 预编译:主要去处理源代码中
#
开头的预编译镜像命令,删除注释,替换宏定义,将#import倒入的文件放在知道位置等操作。 - 编译:进行词法分析,语法分析,语义分析,生成相应的汇编代码文件
- 汇编:加编译完的汇编代码文件翻译成机器指令,生成.o文件
- 链接:静态链接 和 动态链接
静态库的本质是一堆.o文件的集合,当我们的代码经过静态链接后我们的程序中就不会再存在静态库里;架构工程师动态库的本质是已经链接完全的镜像im架构师工资age(表示任意一种类型的可执行文件),镜像架构师证书image可以看成三种类型可执行文件
、dylib
、源码编辑器bundle
,我们在项工作流程怎么写目中断点,用lldb输入image list
查看当前镜像信息

dyld
我们app启动镜像的时候,真正的加载过程是从一个exec()
的函数开始,这个函数为我们的程序分配内存,创建进程,然后将app对应的可执行文件加载进内存,再将dyld加载到内存,dyld进行动态链接。
dyld具体的工作流程:
- 1.找到可执行文架构工程师件中所依赖的动态库并加载到内存,这是一个递归加载的过程,因为可以会依赖其他动态库
- 2.rebase和binding,此处涉及到架构师工资虚拟内存,略过
- 3.调起
main()
所以,想要研究dyld,我们需要在main之前进行断点,我们重写一个+lo架构师和程序员的区别ad()方法,查看其堆栈信息,因为+load方法肯定是在main之前执行的

_dyld_start
我们先来看一下,入口函数_dy源码编辑器ld_start
,在dyld的源码中搜索到dyldStarios14.4.1更新了什么tup.s
中我们工作流是什么意思找到其汇编实现,我们找一下arm64架构的源码
#if __arm64__ && !TARGET_OS_SIMULATOR .text .align 2 .globl __dyld_start __dyld_start: mov x28, sp and sp, x28, #~15 // force 16-byte alignment of stack mov x0, #0 mov x1, #0 stp x1, x0, [sp, #-16]! // make aligned terminating frame mov fp, sp // set up fp to point to terminating frame sub sp, sp, #16 // make room for local variables ...省略... // call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue) bl __ZN13dyldbootstrap5startEPKN5dyld311MachOLoadedEiPPKcS3_Pm ...省略... // LC_MAIN case, set up stack for call to main() Lnew: mov lr, x1 // simulate return address into _start in libdyld.dylib #if __LP64__ 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 Lapple: ldr x4, [x3] add x3, x3, #8
我看不懂工作流程图汇编代码,可是堆栈信息我ios15们知道,他会调用dyldbootstrap::start
dyldbootstrap::start
dyldboots镜像图片怎么弄trap::start
就是指dyldbootstrap
这个命名空架构间作用域里的start
函数。来到源码中 ,搜索dyldbootst镜像rap
,然后找到start
函数 .
uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[], const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue) { // Emit kdebug tracepoint to indicate dyld bootstrap has started <rdar://46878536> dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0); // if kernel had to slide dyld, we need to fix up load sensitive locations // we have to do this before using any global variables 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; // set up random value for stack canary __guard_setup(apple); #if DYLD_INITIALIZER_SUPPORT // run all C++ initializers inside dyld runDyldInitializers(argc, argv, envp, apple); #endif _subsystem_init(apple); // now that we are done bootstrapping dyld, call dyld's main uintptr_t appsSlide = appsMachHeader->getSlide(); return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); }
其作用就是初始化,为main函数做好一系列的准备工作,然后调用dyld
的main
函数 dyld::_main
。
dyld::_main
直接点击跳转到dyld
–main
函数中 . 该函数是加载ap架构图p
的主要函数.
uintptr_t _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[], uintptr_t* startGlue) { 将近1000行代码,省略 }
其ios下载最下面是有个返回值镜像result
,我们通过操作resu架构师工资lt
的代码,来阅读其内部的代码工作流程,先将这1000行代码复制到一个空白文档中,全局搜索result
,有1ios是苹果还是安卓6个。
// if this is host dyld, check to see if iOS simulator is being run const char* rootPath = _simple_getenv(envp, "DYLD_ROOT_PATH"); if ( (rootPath != NULL) ) { // look to see if simulator has its own dyld char simDyldPath[PATH_MAX]; strlcpy(simDyldPath, rootPath, PATH_MAX); strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX); int fd = dyld3::open(simDyldPath, O_RDONLY, 0); if ( fd != -1 ) { const char* errMessage = useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue, &result); if ( errMessage != NULL ) halt(errMessage); return result; } } // try using launch closure if ( mainClosure != nullptr ) { CRSetCrashLogMessage("dyld3: launch started"); if ( mainClosure->topImage()->fixupsNotEncoded() ) sLaunchModeUsed |= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE; Diagnostics diag; bool closureOutOfDate; bool recoverable; bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable); if ( !launched && closureOutOfDate && allowClosureRebuilds ) { // closure is out of date, build new one mainClosure = buildLaunchClosure(canUseClosureFromDisk, mainExecutableCDHash, mainFileInfo, envp, bootToken); if ( mainClosure != nullptr ) { diag.clearError(); sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH; if ( mainClosure->topImage()->fixupsNotEncoded() ) sLaunchModeUsed |= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE; else sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_MINIMAL_CLOSURE; 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; } { // 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; } } if (sSkipMain) { notifyMonitoringDyldMain(); if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) { dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2); } ARIADNEDBG_CODE(220, 1); result = (uintptr_t)&fake_main; *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit; }
我们找工作流程图制作方法到了四镜像文件处操作了result的地方,仔细查看,我们能够看出操作result的地方只有result = (uintptr_t)sMainExecutable->
和result = (uintptr_t)&fake_main镜像翻转怎么弄
,
我们先来看fake_main做了什么事情, int fake_main() { re源码网站turn 0; }
,没有任何操作,只是返回0
,所以源码时代我们重点来看一下s源码编辑器下载MainExecutable
具体都做了什么工作
sMainExecutable
我们先来查看其初始化的地方
// 为工程的可执行文件初始化一个 imageLoader,镜像文件的加载器 sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
其下面的代码才是我们关系的加架构师证书载过程,我们还是将他们复制到一个空白文档中,方便源码之家我们阅读。
我们在下面找到了一个函数initializeMainEx源码时代ecutable
,其注释的含义就是进行所有初始化操作,我们到源码中搜索然后点击跳转查看其实现
void initializeMainExecutable() { // record that we've reached this step gLinkContext.startedInitializingMainExecutable = true; // 为所有动态库进行初始化 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]); // 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]); }
初始化主要是使用runInitializers
进行的,我们再次查看其实现
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); }
代码量很少架构工程师,我们重点研究一下processInitializers
,查看其内部实现
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread, InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images) { uint32_t maxImageCount = context.imageCount()+2; ImageLoader::UninitedUpwards upsBuffer[maxImageCount]; ImageLoader::UninitedUpwards& ups = upsBuffer[0]; ups.count = 0; // Calling recursive init on all images in images list, building a new list of // uninitialized upward dependencies. for (uintptr_t i=0; i < images.count; ++i) { images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups); } // If any upward dependencies remain, init them. if ( ups.count > 0 ) processInitializers(context, thisThread, timingInfo, ups); }
我们看到最后的注释,其如果有向上的依赖源码之家就进行递归调用初始化他们。内部其实是通过recursiveIn工作流程模板itialization
进行初始化库
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize, InitializerTimingList& timingInfo, UninitedUpwards& uninitUps) { recursive_lock lock_info(this_thread); recursiveSpinLock(lock_info); if ( fState < dyld_image_state_dependents_initialized-1 ) { uint8_t oldState = fState; // break cycles fState = dyld_image_state_dependents_initialized-1; try { // initialize lower level libraries first for(unsigned int i=0; i < libraryCount(); ++i) { ImageLoader* dependentImage = libImage(i); if ( dependentImage != NULL ) { // don't try to initialize stuff "above" me yet 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); } } } // record termination order if ( this->needsTermination() ) context.terminationRecorder(this); // let objc know we are about to initialize this image uint64_t t1 = mach_absolute_time(); fState = dyld_image_state_dependents_initialized; oldState = fState; context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo); // initialize this image bool hasInitializers = this->doInitialization(context); // let anyone know we finished initializing this image fState = dyld_image_state_initialized; oldState = fState; context.notifySingle(dyld_image_state_initialized, this, NULL); if ( hasInitializers ) { uint64_t t2 = mach_absolute_time(); timingInfo.addTime(this->getShortName(), t2-t1); } } catch (const char* msg) { // this image is not initialized fState = oldState; recursiveSpinUnLock(); throw; } } recursiveSpinUnLock(); }
经过前面的流程,我们终于找到真正初始化的地方了,其内部使用mach_absolute_time
让ob架构师jc知道初始化im架构age这个可执行文件,因为工作流是什么意思我们所有初始化架构图可执行文件都需要ios是苹果还是安卓依赖ObjC
;context.notifySingle
进行单个通知工作, ,doInitialization
调用init方法,dyld_image_state_initiali工作流程zed
让所有人知道已完成image初始化工作。
notiios模拟器fySingle
static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo){ …省略部分… if ( state == dyld_image_state_mapped ) { if (!image->inSharedCache() || (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion)) { dyld_uuid_info info; if ( image->getUUID(info.imageUUID) ) { info.imageLoadAddress = image->machHeader(); addNonSharedCacheImageUUID(info); } } } 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); } } …省略部分… }
我们看到在初始化image时,将state
传递的是dyld_image_state_dependents_initialized
,notifySingle
中会调用sNotifyObj工作流程组织CIniios是苹果还是安卓t
这个函数,我们源码中工作流程图全局搜索,发现其赋值的位ios模拟器置在registerObjCNotifiers
,再次全局搜ios下载索re镜像画面什么梗gisterObjCNotifiers
的调用位置,找到了_dyld_objc_notify_register
,继续搜索_dyld_objc_notify_register
,镜像找到不调用的地方,说工作流程怎么写明该函数不是在dyld
中进行的调用的,我们打开objc的源码进行搜索,发现其是在objc_init
中进行的调用,所以说我们所有初始化镜像翻转怎么弄可执行文件都需要依赖ObjC
/*********************************************************************** * _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(); tls_init(); static_init(); runtime_init(); exception_init(); #if __OBJC2__ cache_t::init(); #endif _imp_implementationWithBlock_init(); _dyld_objc_notify_register(&map_images, load_images, unmap_image); #if __OBJC2__ didCallDyldNotifyRegister = true; #endif }
_objc_i镜像翻转怎么弄nit
现在我们来探究一下_objc_init
,首先来看一下谁对他进行的调用,objc源码中进行断点调试

libdispatch.dylib _os_object_init
,说明是在GCD的源码中进行的调用,下面的调用栈最终指向的是libSystem.B.dylib libSystem_initiios系统alizer
。不再进行更深入的查找了,我们主要还是来看架构图_dyld_源码交易平台objc_notify_register
调用是传递了三个参数 , 这三个分别代表:
-
map_images
:dyld
将image
加载进内存时 , 会触发该函数 -
load_images
:dyld
初始化image
会触发该方法. ( 我们所熟知的load
方法也是在此处架构师和程序员的区别调用 ) -
unmap_源码中的图片image
:dyld
将image
移除时 , 会触发该函数
和dyld源码对比,我们发现load_images
对应的就是registerObjCNotifiers
中的sNotifyObjCInit
,notifySingle
中的(*sNotifyObjCInit)(image->getRealPa架构师th(), image->machHeader());
,相当于就是源码执行了_objc_init
中源码交易平台的load_images
这个函数。为什么+load
会在main
函数之前执行,其实就是因为load_images
在此执ios16行会加载所有的+load
方法。
doI源码1688nitialization
调用完load_images
后,下面将继续执行doInitialization
,从注释我们了解到该函数是真正进行初始化,我们查看其内部实现
bool ImageLoaderMachO::doInitialization(const LinkContext& context) { CRSetCrashLogMessage2(this->getPath()); // mach-o has -init and static initializers doImageInit(context); doModInitFunctions(context); CRSetCrashLogMessage2(NULL); return (fHasDashInit || fHasInitializers); }
doImageIn镜像翻转怎么弄it
、doModInitFunctions
两个函数中,都有一句报错信息,... that does not link w架构是什么意思ith libSystem.dylib
,说明libSystem.dylib
这个动态库必须在第一个初始化,否则其他无法初始化,因为libSystem架构图模板.源码交易平台dylib
中的方法才能初始化_objc_init
,其他的动态库需要依赖_objc_init
才可以进行初始化。
上面我们知道load_imagios是苹果还是安卓es
的调用位置,那ma源码之家p_images
是在哪里调架构是什么意思用的呢,我们全局搜索后发现notifyBatchPartial
方法中进行的调用, 继续查找其调用位置notifyBatch
、registerImageStateBatchChangeHandler
、registerObjCNotifiers
,我们再次查找notifyBatch
,在之前调用递归processInitializers
的方法下面,找到了其调用ImageLoader::runInitializers
,也就是说dyld
在初始化动态库的时候就会调用map_images
和load_images
这两个函数。 map_images
和load_images
这两个函数具体功能,再下篇镜像投屏文章中再来了解。
总结
dyld – 加载动态库和可执行文件的初始化操作:
- 当
dyldios系统
加载到开始链接主程序的时候, 递归调用recursiveInitialization
函数 - 这个函数第一次执行 , 进行
libs镜像是什么意思ystem
的初架构师始化, 会走到doInitialization
->doModInitFunctions
->libSystemInitialized
-
libsystem
的初始化,它会调用起libdispatch_init
,libdispatch
的架构图模板init
会调用_os_object_init
,这个函数里面调用了_objc_init
-
_objc_工作流程init
中注册并保存了map_images
、load_images
、unmap_image
函数工作流程怎么写地址 - 注册完毕继续回到
recursiveInitialization
递归下一次调用,例如libobjc
, 当libobjc
来到recursiveInitialization
调用时,会触发notifySingle
调用 里注册源码好的sNotifyObjCInit
回调函数。也就是libobjc
中的load_images
评论(0)