Hi 👋

  • Wechat: RyukieW
  • 📦 Archive of technical articles
  • 🐙 making
My personal project Minesweeper Elic Endless Ladder Dream of books
type The game financial
AppStore Elic Umemi

This article is based on dyLD-832.7.3 and objC4-818.2 source code

  • Series of articles:
    • [iOS app startup (I)] Dyld and main function
    • [iOS App Startup (2)] Environment configuration and Runtime initialization
    • [iOS app Startup (3)] Image file reading and loading
    • The process of realizing and initialize a Class is illustrated
    • [iOS app launch (four)] classification of loading |

preface

In the last article, we mentioned _dyLD_OBJC_Notify_register. In this article, we will explain how to read map_images and load loadClass.

A, _dyld_objc_notify_register

_dyld_objc_notify_register(&map_images, load_images, unmap_image);
Copy the code

  • There are two important functions:
    • map_images
      • Manage all symbols in files and dynamic libraries:class protocol selector category
    • load_images
      • perform+loadmethods
  • The image file is read and loaded

There’s a little detail here

So what we see here is that we’re calling &map_images and we’re using the address of the function here, because this is very important, in order to keep the whole thing in sync.

Second, the map_images

/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}
Copy the code

2.1 map_images_nolock

void 
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{...if (hCount > 0) { _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses); }...// Call image load funcs after everything is set up.
    for (auto func : loadImageFuncs) {
        for (uint32_t i = 0; i < mhCount; i++) {
            func(mhdrs[i]); }}}Copy the code

2.2 _read_images

The source code here is very long, but Apple has nicely added a Log and we can get a clue from the Log

ts.log("IMAGE TIMES: fix up selector references");

ts.log("IMAGE TIMES: discover classes");

ts.log("IMAGE TIMES: remap classes");

ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");

ts.log("IMAGE TIMES: discover protocols");

ts.log("IMAGE TIMES: fix up @protocol references");

ts.log("IMAGE TIMES: discover categories");

ts.log("IMAGE TIMES: realize non-lazy classes");

ts.log("IMAGE TIMES: realize future classes");
Copy the code

Overall process combing

  • Condition control is loaded for the first time
  • Fixed precompile phase@selectormess
  • Wrong messy class handling
  • Fixed remapping some classes that were not added by the image file
  • Fix some messages
  • When there are protocols in the class,readProtocol
  • Fix protocols that were not loaded
  • Deal with classification
  • Class load related processing

Repair: mainly refers to the repair of the address, the address in MachO is repaired to the address after dyLD processing

2.3 Loop calls readClass to load the class

Add Log here and you can see that the custom class is loaded last:

.// A series of system class loads
readClass -- RYModel
readClass -- RYSubModel
Copy the code

Here if you want to filter the system can refer to the following code:

const char *myClassName = "RYModel";
const char *mangledName = cls->nonlazyMangledName(a);if (strcmp(mangledName, myClassName) == 0) {
    printf("%s -- %s\n", __func__, myClassName);
}
Copy the code

2.4 Holes in readClass source code

Do not be misled by the source code as shown in the following figure, which appears to perform Class ro RW operations but does not execute them when the breakpoint is run.

See the three breakpoints:

The break point in the middle is skipped

2.5 Finding processes related to classes

Finding possible places in the source code, add breakpoints:

Implement non-lazy-loaded classes (implement class +load method or static instance)

Realize non-lazy classes (for +load methods and static instances)

Realize newly-resolved future classes

After running, only output:

Realize non-lazy classes _read_images -- RYModel
Copy the code

In this process, we also found another important process: realizeClassWithoutSwift

This part is explored in the process of studying msgSend, which can be further understood in the article illustrating the realization and initialization process of Class.

Added: lazy loaded classes & non-lazy loaded classes

Non-lazily loaded classes were mentioned in 2.5, so what are non-lazily loaded classes and what are lazily loaded classes?

Lazy loading class

Data loading is delayed until the first message is received

Non-lazy-loaded classes

Map_image loads all class data.

Third, load_images

  • Here the process is simpler
    • Load classification
    • Find all the load methods
    • Call the load method
void
load_images(const char *path __unused, const struct mach_header *mh)
{
    if(! didInitialAttachCategories && didCallDyldNotifyRegister) { didInitialAttachCategories =true;
        // Load the classification
        loadAllCategories(a); }// 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
    // Find all load methods
    {
        mutex_locker_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    /// call the load method
    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods(a); }Copy the code

The flow chart