回顾
前面整个dyld加载流程,我们通过_dyld_start ->dyldbootstrap::start->dyld::_main ->dyld::initializeMainExecutable()->ImageLoader::runInitializers->ImageLoader::processInitializers->ImageLoader::processInitializers->ImageLoader::recursiveInitialization->dyld::notifySingle
最终notifySingle调用了sNotifyObjCInit,全局搜索sNotifyObjCInit找到函数registerObjCNotifiers,registerObjCNotifiers函数的调用是在_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(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
可以看到_dyld_objc_notify_register函数有三个参数,&map_images,load_images,unmap_image,他们分别在什么时候调用了。
map_images和load_images调用流程
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;
sNotifyObjCUnmapped = unmapped;
// call 'mapped' function with all images mapped so far
try {
notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
}
catch (const char* msg) {
// ignore request to abort during registration
}
// <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
}
}
}
可以看到sNotifyObjCMapped = mapped,全局搜索sNotifyObjCMapped,找到函数notifyBatchPartial中调用(*sNotifyObjCMapped)(objcImageCount, paths, mhs);,搜索notifyBatchPartial,我们发现又回到了registerObjCNotifiers函数,
// call 'mapped' function with all images mapped so far
try {
notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
}
原来,只要赋值了map_images就会立即调用。
sNotifyObjCInit = init,全局搜索sNotifyObjCInit,发现调用完map_images之后,load_images也会被调用
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); //load_images调用
}
}
sNotifyObjCUnmapped全局搜索,找到removeImage函数,发现在程序关闭时才会调用unmap_image
if ( sNotifyObjCUnmapped != NULL && image->notifyObjC() )
(*sNotifyObjCUnmapped)(image->getRealPath(), image->machHeader());
load函数调用流程
前面我们在load函数添加断点,得到load函数流程
- dyld`_dyld_start ->
- dyld`dyldbootstrap::start ->
- dyld`dyld::_main ->
- dyld`dyld::initializeMainExecutable() ->
- dyld`ImageLoader::runInitializers ->
- dyld`ImageLoader::processInitializers
- dyld`ImageLoader::recursiveInitialization ->
- dyld`dyld::notifySingle ->
- libobjc.A.dylib
load_images 最终我们是走到load_images函数,然后调用load方法,查看load_images`函数,
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
loadAllCategories();
}
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh); //准备load函数
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods(); //调用load函数
}
可以看到先调用prepare_load_methods函数,递归将包含load方法的类和方法,添加到add_class_to_loadable_list表或者add_category_to_loadable_list分类表里面,然后直接调用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. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads(); //调用类的load方法
}
// 2. Call category +loads ONCE
more_categories = call_category_loads(); //调用分类的load方法
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
递归分别调用类的load方法和分类的load方法。
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
(*load_method)(cls, @selector(load)); //给类发送消息load
}
// Destroy the detached list.
if (classes) free(classes);
}
然后给对应的类或者分类发送消息(*load_method)(cls, @selector(load))。
cxx函数调用流程
在main文件添加下面cxx函数,打上断点。
__attribute__ ((constructor)) void LKTeacher(){
printf("______%s_____",__func__);
}
bt打印堆栈信息
- dyld`_dyld_start ->
- dyld`dyldbootstrap::start ->
- dyld`dyld::_main ->
- dyld`dyld::initializeMainExecutable() ->
- dyld`ImageLoader::runInitializers ->
- dyld`ImageLoader::processInitializers
- dyld`ImageLoader::recursiveInitialization ->
- dyld`ImageLoaderMachO::doInitialization ->
- dyld
ImageLoaderMachO::doModInitFunctions 搜索doModInitFunctions`函数
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);
}
在doInitialization中调用doModInitFunctions(context)方法,搜索doInitialization
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);
// 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);
//中间代码省略
}
回到recursiveInitialization,我们发现notifySingle->sNotifyObjCInit->registerObjCNotifiers->_dyld_objc_notify_register即调用完load方法之后,然后调用
doInitialization(context)方法,接着又会再次调用notifySingle方法。
分析整个加载流程是一个递归的流程
//let objc know we are about to initialize this image
// initialize this image
// let anyone know we finished initializing this image
第一次调用时,image并没有被初始化,所以load方法并不会调用,然后调用doInitialization方法,初始化image,完成image初始化之后,再次调用load方法。
验证在任一objc文件中添加cxx函数
__attribute__ ((constructor)) void objcFunc(){
printf("______%s_____\n",__func__);
}
打印结果
______objcFunc_____
2021-07-16 16:33:36.482889+0800 KCObjcBuild[8825:231456] ______+[LKPerson load]______
______LKTeacher_____
可以看到,先调用本镜像文件的cxx函数,然后调用load函数,最后调用程序的cxx函数。
main函数调用流程
在load函数处打下断点,打开汇编,一步步往下走
读取寄存器信息
可以看到,
x16 = 0x0000000100003e60 KCObjcBuild main at main.m:31 ,main函数地址存在x16中,0x100015078 <+120>: braaz x16可知,当执行完_dyld_start之后,会跳转至main函数。
总结
引用自KC课件
![]()
load,cxx,main函数加载顺序
先加载镜像文件cxx函数,然后加载load函数,再加载主程序cxx函数,最后执行main函数