The entrance

We already know that map_images is called by dyLD in the registerObjCNotifiers, The specific implementation is also in libobJC source code, in objc_init register dyLD, _DYLD_OBJC_notify_register (&map_images, load_images, unmap_image);

Analysis of the

map_image

void
map_images(unsigned count, const char * const paths[],

           const struct mach_header * const mhdrs[])
{
    / / the mutex
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}
Copy the code
  • Parameter analysis
  • count = objcImageCount, the image of the Count
  • paths[] = imageFilePath, file path
  • mhdrs[] = imageLoadAddressAnd the address

map_images_nolock

void map_images_nolock(unsigned mhCount, const char * const mhPaths[],

                  const struct mach_header * const mhdrs[])
{
    // The main variable
    / / for the first time
    static bool firstTime = YES;
    / / hList array
    header_info *hList[mhCount];
    / / the number of header
    uint32_t hCount;
    //SEL count
    size_t selrefCount = 0;
// Main logic
if (firstTime) {
   // Handle shared cache
  preopt_init();
}


/ / MHDR address
// mhPaths
// Add to header_info single necklace table, FirstHeader LastHeader
uint32_t i = mhCount;
while (i--) {
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
// Add header_info to hList
hList[hCount++] = hi;
}


if (firstTime) {

// Initialize and register internal namedSelectors
sel_init(selrefCount);

// AutoreleasePoolPage initials. it is a bidirectional list and automatically releases the pool
// SideTableMap initialization -> weak_table_t Weak reference table RefcountMap reference table
// AssociationsManager initializes the AssociationsManager to store the lock, the lock hash, that is, the association list
 arr_init();
 
 
 if (hCount > 0) {
HList: header_info *[header_info *], with a pointer to header_info
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}

// Call all load methods in the image
for (auto func : loadImageFuncs) {
    for (uint32_t i = 0; i < mhCount; i++) { func(mhdrs[i]); }}}Copy the code
  • The first time, we deal with the shared cache
  • Traversal loop creationheader_infoLinked list, and willheader_infoAdded to thehListIn the
  • call_read_images
  • Call all files in the image fileloadmethods

read_images


void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses) {
    // Main logic
    // Only once
    if(! doneOnce) { doneOnce = YES;// Initialize the taggedPointer confounder
      initializeTaggedPointerObfuscator();
      
      int namedClassesSize = 
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;

        / / create NXMapTable
        // NXStrValueMapPrototype = _mapStrHash(string hash), whether strings are equal (_mapStrIsEqual)
        // All classes except the shared cache
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
    }
    
    // Fix the @selector reference during precompilation
    / / traverse hList
    for (EACH_HEADER) {
      // MachO read-only __DATA_CONST __objc_selrefs section retrives sels
      SEL *sels = _getObjc2SelectorRefs(hi, &count);
      // Walk through all sels
      (i = 0; i < count; i++) { 
          // Convert SEL to const char *, SEL pointer
          const char *name = sel_cname(sels[i]);
          
          // dyld check if name points to SEL, if so, return SEL directly
          // If not, insert a copy of name into nameSelctors
          SEL sel = sel_registerNameNoLock(name, isBundle);
          
          // The address of the registered SEL is different from the current address
          if(sels[i] ! = sel) { sels[i] = sel } } } ts.log("IMAGE TIMES: fix up selector references");
    
    // Find unknown class, fault tolerant, generally not go
    for (EACH_HEADER) { 
    // 1. The dyLD shared cache does not exist or expires
    // 2. Emulator runs
    // 3. The shared cache is overwritten
    // 4, image lacks weak superclasses
    // 5, there are unknown classes
    if (! mustReadClasses(hi, hasDyldRoots)) {
            // Image is sufficiently optimized that we need not call readClass()
            continue;
        }
        // Mach-o gets the class
        classref_t const *classlist = _getObjc2ClassList(hi, &count);
        for (i = 0; i < count; i++) { 
          / / read
          Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized)
          // Class changed, but not removed
          if(newCls ! = cls && newCls) { } } } 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");
    // mach-o : __objc_protolist section
    
    NXMapTable *protocol_map = protocols();
    protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
    for protolist {
    readProtocol(protolist[i], cls, protocol_map, 
                         isPreoptimized, isBundle);
    }
    
    
    ts.log("IMAGE TIMES: fix up @protocol references");
    for (EACH_HEADER) {
        if (launchTime && hi->isPreoptimized())
            continue;
        protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
        for (i = 0; i < count; i++) { remapProtocolRef(&protolist[i]); }}// If it is already load_image, load the classification
    if (didInitialAttachCategories) {
        for (EACH_HEADER) {
            load_categories_nolock(hi);
        }
    }
    ts.log("IMAGE TIMES: discover categories");
    
    
    // Implement classes that are not lazily loaded (load and statically initialized)
    for (EACH_HEADER) {
        classref_t const *classlist = hi->nlclslist(&count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if(! cls)continue;
            // Add classes and metaclasses to allocatedClasses
            addClassTableEntry(cls);
            if (cls->isSwiftStable()) {
            }
            realizeClassWithoutSwift(cls, nil);
        }
    }
    ts.log("IMAGE TIMES: realize non-lazy classes");
    

    ts.log("IMAGE TIMES: realize future classes");
}
Copy the code
  • You can use environment variablesOBJC_PRINT_IMAGE_TIMES = YESprint
  • EACH_HEADERtraversehList
  • The first time, the taggedPointer confounder is initialized, and then three-quarters of the load factor is opened up in memory and createdgdb_objc_realized_classes , gdb_objc_realized_classesA misnomer,All classes that are not actually shared in the cache, implemented or not
  1. ts.log("IMAGE TIMES: fix up selector references");
    • Repair the Mach – o@selectorTo traverse thehList
    • fromMach-ORemove thesels, the registeredselThat is inserted into thenameSelctiors
    • The currentselAddress is not equal to the registeredselAddress, then modified
  2. ts.log("IMAGE TIMES: discover classes");
    • Look for a class. If you don’t know a class, take it frommach-oTo re-read and load into memory
    • Traverse the read class,addNamedClassInserts the class intogdb_objc_realized_classesIn the
    • addClassTableEntry(cls), inserts classes and metaclasses intoallocatedClassesIn the
  3. ts.log("IMAGE TIMES: remap classes");
    • inremapped_class_mapTo find the
    • Remap classes that have not been loaded by the image file
    • Not usually.
  4. ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
    • Fix old message sending
    • The real thing doesn’t work
  5. ts.log("IMAGE TIMES: discover protocols");
    • Read the protocol in Mach-O into memory
    • frommach-oReads,protocollist
    • readProtocolTo createprotocol_tInserted into theprotocol_mapIn the
  6. ts.log("IMAGE TIMES: fix up @protocol references");
    • Fixed pointer address for protocol in Mach-O
    • remapProtocolRefRemap protocols in memory, reassign if they are different
  7. ts.log("IMAGE TIMES: discover categories");
    • didInitialAttachCategoriesThe default isfalse
    • Load the category if the image is already goneload_image
  8. ts.log("IMAGE TIMES: realize non-lazy classes");
    • Implement classes that are not lazily loaded
    • When a class implements+loadIs the non-lazy loading method
    • so+loadCauses the class to be implemented ahead of time
    • addClassTableEntry, add classes and metaclasses toallocatedClassesIn the
    • realizeClassWithoutSwiftThe implementation class, the point, is thatRo, rw, categoryThe processing of
  9. ts.log("IMAGE TIMES: realize future classes");
    • If there are unprocessed classes, new parsing unknown classes are implemented to prevent CF from modifying them
    • Not usually.

conclusion

  • map_imagesindyldRegistration is a call
  • Mainly is tomach-oMap to memory
  • Repair themach-oIn theselWith the memorynameSelctiorsmapping
  • willmach-oIn theprotocolRead into memoryprotocol_mapIn the
  • If the class implementsloadMethod will implement the class, not lazy loading