First, explore the dyLD loading process

Here’s a sneak peek at dyLD’s loading process:

_LD_start -> DYLD bootstrap -> DYld ::_main NotifySingle is called for single notification injection, load_images is called, and doInitialization is called. Dyld -> libSystem -> libdispatch -> objC _objc_init _objC_init in turn calls the _DYLD_OBJC_Notify_register callback implemented in DYLD, thus forming a cross-library closed loop.

Explore _objc_init

1. Environ_init method: initialize environment variables

The source code for the environ_init method is shown below, with the key code being the for loop

void environ_init(void) { //... Omit some logic if (PrintHelp | | PrintOptions) {/ /... For (size_t I = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) { const option_t *opt = &Settings[i]; if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help); if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env); }}}Copy the code

There are two ways to print all environment variables

  • 1. See the figure. Set the environment variable Product -> Scheme -> Edit Scheme. Take OBJC_PRINT_LOAD_METHODS as an example and set it to YES. Print all classes that implement the load method

    2. Run the terminal commandexport OBJC_hrlp = 1Print environment variablesSo,OBJC_PRINT_LOAD_METHODSYou can monitor all of them+loadMethod to handle boot optimization (the boot optimization method will be summarized later)

Tls_init: binds the thread key

mainlyLocal thread poolInitialization anddestructor, the source code is as follows

Static_init: runs system-level C++ static constructors

It runs primarily at the system levelC++Static constructor, liBC calls before DYLD calls our static constructor_objc_initMethods, i.e.,System-level C++ constructorsbeforeA custom C++ constructorrun

4. Runtime_init: runtime environment initialization

This is mainly runtime initialization, which is divided into two parts:Class initialization,Class to initialize the table(More on the corresponding functions later)

5. Exception_init: Initialize the exception handling system of LibobJC

isInitialize libobJC's exception handling system and register the callback for exception handling to monitor the handling of exceptions, the source code is as follows

Cache_init: cache initialization

_imp_implementationWithBlock_init: Enables the callback mechanism

This method mainly starts the callback mechanism, which usually doesn’t do much because all initialization is lazy, but for some processes we can’t wait to load libobjc-trampolines.dylib

7, _DYLD_OBJC_notify_register: dyLD register, crash library call

The meanings of the three parameters in the method are as follows:

Map_images: This function is triggered when dyld loads an image into memory

Load_image: this function is triggered when dyld initializes the image

Unmap_image: Triggered when dyld removes the image

This leads to the association between DYLD and Objc

Libobjc — call _dyLD_OBJC_notify_register (&map_images, load_images, unmap_image);

2, load_images === 3, unmapped = unmap_images Map_images is preceded by &, which is a reference type and changes when conditions change. Note that load_images is a value type and does not pass a value

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());
        }
    }
}
Copy the code

Objc passed the callbacks map_images, load_images, and unmap_image into dyLD’s sNotifyObjCMapped, sNotifyObjCInit, and sNotifyObjCUnmapped respectively SNotifyObjCMapped is called in the notifyBatchPartial method after a global search

static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification) { ... //: -- sNotifyObjCMapped (*sNotifyObjCMapped)(objcImageCount, paths, MHS); . }Copy the code

NotifyBatchPartial is called in the registerObjCNotifiers above. When _dyLD_OBJC_notify_register is called into dyLD in objC, map_images is called. To map an image file. This creates a cross-library closed loop that associates OBJC with DYLD.