We all know that the entry function of an APP is main(), but what happens to the APP loading process before main() is called? Next, let’s analyze the APP loading process.

I. Preparation

Since load() is called earlier than main(), we create a project, write a load() function in the controller, and run it at a breakpoint, as shown below:

Dyld :notifySingle(); dyld:notifySingle(); dyld:notifySingle What is it doing?

Simply put, dyld is a dynamic linker that loads all libraries and executables. Next, we will trace what DyLD does by analyzing the dyld source code.

2. Analysis of dyLD loading process

1. Download firstDyld source.

2. Open the dyld source project, according to the figure abovedyldbootstrap::startSearch for the keyworddyldbootstrapIn the callstart(), as shown below:

3. Enter the dyldstartfunction

RebaseDyld () is analyzed as follows:

4. Enter the dyldmainfunction

Note: Because the dyld::main() function has a lot of code, the following sections will cover it, as well as the more important functions.

4.1 Kernel Detection

4.2 Obtaining the cdHash Cache of the Main execution file

4.3 Obtaining CPU Information

    // Obtain the CPU information
    getHostInfo(mainExecutableMH, mainExecutableSlide);
Copy the code

4.4 Setting MachHeader and Memory Offset

    // Set the MachHeader and the memory offset Slide
    uintptr_t result = 0;
    sMainExecutableMachHeader = mainExecutableMH;
    sMainExecutableSlide = mainExecutableSlide;
Copy the code

4.5 Setting context

    // Set the context and save the information
    setContext(mainExecutableMH, argc, argv, envp, apple);
Copy the code

4.6 Configuring Process Restriction

4.7 Checking Environment Variables

4.8 Printing Environment Configuration Information

    // Prints the environment configuration information
    if ( sEnv.DYLD_PRINT_OPTS )
        printOptions(argv);
    if ( sEnv.DYLD_PRINT_ENV ) 
        printEnvironmentVariables(envp);
Copy the code

Here you can define your own environment variable configuration, go back to the new project you created, Add DYLD_PRINT_OPTS and DYLD_PRINT_ENV to the Edit Scheme -> Run -> Arguments -> Environment Variables and set the test value as below:

Run the program, you can see the following printed information:

4.9 Loading shared cache (iOS will not run without shared cache)

The main function mapSharedCache() is as follows:

4.10 dyldconfiguration

(1) dyld3(Closure mode)

After iOS11, dyld3 ClosureMode (ClosureMode) was introduced: faster and more efficient loading.

Start executing closure mode

Determine whether closure mode is enabled

Start closure mode loading

The launchWithClosure() function encapsulates most of dyld2’s process in the launchWithClosure() function. I won’t talk about launchWithClosure in detail. This is because the whole process of dyLD loading is explained in detail in dyLD2 (non-closure), which is the launchWithClosure implementation.

(2) dyld2(Non-closure mode)

Start executing closure mode

Add dyld to the UUID list

    // Add dyld to UUID list
    addDyldImageToUUIDList();
Copy the code

Configuring the Cache Proxy

4.11 Create the Image of the main program

Start to create the Image of the main program, through the instantiateFromLoadedImage (), call instantiateMainExecutable (), instantiate the concrete Image, finally generated object, set in the gLinkContext.

4.12 Setting the Version of the Dynamic Library

    // After loading the shared cache, set the version of the dynamic library
    checkVersionedPaths();
Copy the code

4.13 Loading the Inserted dynamic library

    // Load the inserted dynamic library
    if( sEnv.DYLD_INSERT_LIBRARIES ! =NULL ) {
        for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib ! =NULL; ++lib) 
            loadInsertedDylib(*lib);
    }
Copy the code

4.14 Link the main program to the dynamic library

  • One of thelink()Function, function body calledimage->link(), the function is as follows:

  • Determine if all images need to be reloaded

4.15 Bind the main program to the dynamic library

4.16 Initialize the main program

According to the stack information in Demo, it is as follows:

Now that you’ve finally seen the familiar functions, the dyld loading process is almost over. Gets the function call hierarchy based on the stack information.

    // Initialize the main program
    initializeMainExecutable(); 
Copy the code
  • To find therunInitializers():dyld::initializeMainExecutable() -> ImageLoader::runInitializers()

  • To find theprocessInitializers():ImageLoader::runInitializers() -> ImageLoader::processInitializers()

  • To find therecursiveInitialization():ImageLoader::processInitializers() -> ImageLoader::recursiveInitialization()

  • To find thenotifySingle():ImageLoader::recursiveInitialization() -> dyld::notifySingle()

  • To find theload_images()In:dyld::notifySingle()Is not found inload_images()“, but found itsNotifyObjCInit(), this field is an objc function callback. indyld::notifySingle()We need to trace back to who registered the callback.
  • Global searchsNotifyObjCInit()Where the value is assigned. inregisterObjCNotifiers(), as follows:

  • Global searchregisterObjCNotifiersIn the_dyld_objc_notify_register()And the second argument is what we need. As follows:

  • Global search_dyld_objc_notify_register()Not found in the dyld source repository. In this case, you need to sign a breakpoint in the source project_dyld_objc_notify_register, recompile execution, you can see is_objc_init()The call. You can only look for itObjc source.
  • objcSource code analysis, inobjc-os.mmIn the file_objc_init()Function in which the_dyld_objc_notify_registerThe callback.

The second parameter is load_images(), and call_load_methods() is also found in load_images().

Had been completes initialization program at this time to go back to the previous ImageLoader: : recursiveInitialization () method.

  • performthis->doInitialization()function

  • Send a notification that the initialization main program is complete.

4.17 Enter the main program

    // Tell the process to enter the program main()
    notifyMonitoringDyldMain();
Copy the code

Start () -> main()

3. To summarize

  • Dyld (dynamic linker) : an important part of apple’s operating system that loads all libraries and executables.
  • Dyld loading process:
    • from_dyld_start()Start – >dyldbootstrap::start()
    • Enter the dyldmain()
    • Check the kernel and configure the redirection information:rebase_dyld()
    • Loading the shared cache
    • dyld2 / dyld3(Closure mode)
      • Instantiate the main program
      • Load dynamic link library (main program and dynamic library image will load allImage:loadAllImage, the main program in the0Location)
      • Link main programs, dynamic libraries, binding symbols (non-lazy loading, weak symbols), etc
      • The most critical: Indicates the initialization methodinitializeMainExecutable()
        • ImageLoader::runInitializers()
        • ImageLoader::processInitializers()
        • ImageLoader::processInitializers()
        • ImageLoader::recursiveInitialization()
        • dyld::notifySingle()
          • This function performs a callback_dyld_objc_notify_register()
          • Debug by breakpoint: This callback is_objc_init()A function assigned on initializationload_images(), which is implementedcall_load_methods()Function that loops through the methods of each class.
        • doModInitFunctions()Function: the constructor of the global C++ object is called internally__attribute__((constructor))The C function
      • Return to the main program entry and executemainfunction