接下来,我们开始探索应用的加载流程,应用程序在启动的时候系统究竟做了什么事情, 当我们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
查看当前镜像信息
我们可以看到非常镜像图片怎么弄多的框架信息,这些都是属ios下载于image镜像文件。
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源码中进行断点调试
我ios是什么意思们发现是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