从应用程序的加载上,我们了解了main函数之前的dyld整个链接过程
- _dyld_start
- dyldbootstrap::start
- dyld::_main
- dyld::initializeMainExecutable()
- ImageLoader::runInitializers
- ImageLoader::processInitializers
- ImageLoader::recursiveInitialization
- ImageLoaderMachO::doInitialization
- libSystem_initializer
- libdispatch_init
- _os_object_init
- _objc_init
在
_objc_init中我们看到调用了_dyld_objc_notify_register(&map_images, load_images, unmap_image);❓map_images()是什么时候调用的,load_images()是什么时候调用的。 ❓dyld是怎么到main函数的 ❓cxx 和load()和main函数的加载顺序是什么
_dyld_objc_notify_register
dyld源码
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);
}
我们看下 registerObjCNotifiers
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;对map_images的赋值,我们看下sNotifyObjCMapped调用的地方,全局搜索sNotifyObjCMapped是在notifyBatchPartial函数中调用,在registerObjCNotifiers中赋值之后就调起了
load_images
我们看下load_images里面做了什么
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);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
看上面的代码知道,load_images是对load方法的加载,prepare_load_methods中准备了类的load方法和分类的load方法
void prepare_load_methods(**const** headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls, **nil**);
ASSERT(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
schedule_class_load是一个递归查找父类load方法,并加入loadable_classes中
static void schedule_class_load(Class cls)
{
if (!cls) return;
ASSERT(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->getSuperclass());
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
我们看下add_class_to_loadable_list
加入loadable_classes操作
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
我们看下
IMP
objc_class::getLoadMethod()
{
runtimeLock.assertLocked();
const method_list_t *mlist;
ASSERT(isRealized());
ASSERT(ISA()->isRealized());
ASSERT(!isMetaClass());
ASSERT(ISA()->isMetaClass());
mlist = ISA()->data()->ro()->baseMethods();
if (mlist) {
for (const auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
if (0 == strcmp(name, "load")) {
return meth.imp(false);
}
}
}
return nil;
}
分类的load方法也是类似搜集的,就不列出来了。在prepare_load_methods之后就调用了 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();
}
// 2. Call category +loads ONCE
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**;
}
分别在call_class_loads和call_category_loads调用了对应的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));
}
// Destroy the detached list.
if (classes) free(classes);
}
(*load_method)(cls, **@selector**(load));load方法的调用
call_category_loads中(*load_method)(cls, @selector(load));对分类方法的load方法进行了调用
static bool call_category_loads(void)
{
int i, shift;
bool new_categories_added = NO;
struct loadable_category *cats = loadable_categories;
int used = loadable_categories_used;
int allocated = loadable_categories_allocated;
loadable_categories = **nil**;
loadable_categories_allocated = 0;
loadable_categories_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) **continue**;
cls = _category_getClass(cat);
if (cls && cls->isLoadable()) {
if (PrintLoading) {
_objc_inform("LOAD: +[%s(%s) load]\n",
cls->nameForLogging(),
_category_getName(cat));
}
(*load_method)(cls, @selector(load));
cats[i].cat = nil;
}
...
}
综上,我们知道了load_images就是对load方法的调用,我们看下在dyld是什么时候调用的,全局搜索sNotifyObjCInit是在notifySingle中调起的。搜索notifySingle在ImageLoader::recursiveInitialization中调用的。
c++,load(), main()的加载顺序
我们看下ImageLoader::recursiveInitializationrecursiveInitialization是一个递归的函数
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
...
// 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);
...
}
context.notifySingle是load_images()的调用时刻,即打印load方法的时刻,
示例代码:
在objc_init前面添加一个c++方法
__attribute__ ((constructor)) void objcFunc(){
printf("来了 : %s \n", __func__ );
}
在mian函数中
//Person.m
+ (void)load{
NSLog(@"load");
}
- (void)say1{
NSLog(@"Person say : %s", __func__);
}
//main
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [Person alloc];
[p say1];
NSLog(@"Hello, World!");
}
return 0;
}
__attribute__ ((constructor)) void cFunc(void){
printf("来了 : %s \n", __func__);
}
打印如下
来了 : objcFunc
load
来了 : cFunc
Person say : -[Person say1]
Hello, World!
打印顺序:写在objc库中的静态方法->load方法->c++方法
刚开始,进入到recursiveInitialization函数中的时候,如果有依赖库,先加载底层依赖库,会先走bool hasInitializers = this->doInitialization(context);经过一系列调用调起objc_init,objc_init中有一个static_init()会加载静态方法,所以先打印objcFunc。
在下面context.notifySingle(dyld_image_state_initialized, this, NULL);(doInitialization方法后面的context.notifySingle)中打印出load方法。
加载完系统依赖库,本次运行的工程的c++方法再加载,最后在是main函数。
我们在main函数中的c++方法中加一个断点
看到它是
doModInitFunctions里面调起的。
从dyld到main
dyld的源码中我们可以看到dyld到main的一个跳转。
验证:
在main中静态函数中打一个断点,然后看下汇编:
0x100015060 <+96>: jmpq *%rax 使用register read读取寄存器。
rax就是main函数