Loading iOS Classes by hand

In the first chapter of iOS bottom 18 — DYld and libObjc those things, mainly analyzed how dyLD and libObjc associated call, this purpose to understand the class information, such as methods, attributes, classification information is how to load into memory, The focus is on the map_images and load_images functions.

Map_images: Manages all symbols in files and dynamic libraries, i.e. classes, protocols, selectors, categories, etc

Load_images: Loads perform the load method

The code is compiled into a Mach-O executable, read into the Mach-O executable, and read the class information into memory from mach-O.

0x00 – map_imagesloadingimageTo the memory

_dyld_objc_notify_register(&map_images, load_images, unmap_image);

Before we look at the source code, we need to explain why map_images has & and load_images does not

  • map_imagesisReference typesThe outside world changes and changes with it.
  • load_imagesisValue types, do not pass values
/***********************************************************************
* 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
**********************************************************************/
// Process the given image mapped by dyLD
// After an ABI-specific lock is acquired, abI-independent code is called.
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
  • The key code ismap_images_nolock
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{
    / /... omit

    // Find all images with Objective-C metadata. Find all images with Objective-C metadata
    hCount = 0;

    // Count classes. Size various table based on the total. Count the number of classes
    int totalClasses = 0;
    int unoptimizedTotalClasses = 0;
    // Code block: scope, local processing, that is, local processing of some events
    {
        / /... omit
    }
    
    / /... omit

    if (hCount > 0) {
        // Load the image file
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }

    firstTime = NO;
    
    // Call image load funcs after everything is set up. When everything is set up, the image load function is called.
    for (auto func : loadImageFuncs) {
        for (uint32_t i = 0; i < mhCount; i++) {
            func(mhdrs[i]); }}}Copy the code

The internal code of map_images_NOLock is very lengthy. After analysis, the previous work is basically the extraction and statistics of image file information, so we can locate the last _read_images:

The condition for entering _read_images here is hCount greater than 0, which is the number of headers in Mach -o

Enter our main characters, _read_images and lookupImpOrForward, which we have explored in previous chapters in Runtime and iOS. The rest of the _read_images we can’t leave behind.

0x01 — _read_imagesThe source code to achieve

In the _read_iamges method, these are the main things you do

  • 1, condition control for a load
  • 2. Fix @selector confusion during precompilation
  • 3. Bad, messy class handling
  • 4, fix remapping some classes that were not loaded by the image file
  • 5. Fix some messages
  • 6, When there is a protocol in the class: readProtocol Reads the protocol
  • 7, fix the protocol that was not loaded
  • 8. Classification
  • 9. Class loading processing
  • 10. Optimize the classes that are violated without being processed

1. Conditional control for a load

if(! doneOnce) {/ /... omit
    initializeTaggedPointerObfuscator(a);/ / small objects
    // namedClasses
    // Preoptimized classes don't go in this table.
    // 4/3 is NXMapTable's load factor
    int namedClassesSize = 
        (isPreoptimized()? unoptimizedTotalClasses : totalClasses) *4 / 3;
// Create table key-value (hash table key-value) for quick lookup
    gdb_objc_realized_classes =
        NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);

    ts.log("IMAGE TIMES: first time tasks");
}
Copy the code
// This is a misnomer: gdb_objc_realized_classes is actually a list of 
// named classes not in the dyld shared cache, whether realized or not.
NXMapTable *gdb_objc_realized_classes;  // exported for debuggers in objc-gdb.h
uintptr_t objc_debug_realized_class_generation_count;
Copy the code

Gdb_objc_realized_classes is of NXMapTalbe type. This hash table is used to store named classes that are not in the shared cache, regardless of whether the class is implemented or not, and has a capacity of 3/4 of the class number

2. Fix @selector confusion during precompilation

// Fix up@selector References Fixes @selector references
//sel is not a simple string, but a string with an address
static size_t UnfixedSelectors;
{
    mutex_locker_t lock(selLock);
    for (EACH_HEADER) {
        if (hi->hasPreoptimizedSelectors()) continue;

        bool isBundle = hi->isBundle(a);_getObjc2SelectorRefs = _getObjc2SelectorRefs = _getObjc2SelectorRefs
        SEL *sels = _getObjc2SelectorRefs(hi, &count);
        UnfixedSelectors += count;
        for (i = 0; i < count; i++) { // List traversal
            const char *name = sel_cname(sels[i]);
            // Register sel operation to add SEL to
            SEL sel = sel_registerNameNoLock(name, isBundle);
            if(sels[i] ! = sel) {// When sel and sels[I] address are inconsistent, adjust them to be consistentsels[i] = sel; }}}}Copy the code

_getObjc2SelectorRefs to get the mach-O static section data __objc_selrefs, and then get the Mach-O static section data starting with _getObjc2

// function name content type section name
GETSECT(_getObjc2SelectorRefs,        SEL,             "__objc_selrefs"); 
GETSECT(_getObjc2MessageRefs,         message_ref_t."__objc_msgrefs"); 
GETSECT(_getObjc2ClassRefs,           Class,           "__objc_classrefs");
GETSECT(_getObjc2SuperRefs,           Class,           "__objc_superrefs");
GETSECT(_getObjc2ClassList,           classref_t const."__objc_classlist");
GETSECT(_getObjc2NonlazyClassList,    classref_t const."__objc_nlclslist");
GETSECT(_getObjc2CategoryList,        category_t * const."__objc_catlist");
GETSECT(_getObjc2CategoryList2,       category_t * const."__objc_catlist2");
GETSECT(_getObjc2NonlazyCategoryList, category_t * const."__objc_nlcatlist");
GETSECT(_getObjc2ProtocolList,        protocol_t * const."__objc_protolist");
GETSECT(_getObjc2ProtocolRefs,        protocol_t *,    "__objc_protorefs");
GETSECT(getLibobjcInitializers,       UnsignedInitializer, "__objc_init_func");
Copy the code

From Mach-O you can see the corresponding data segments.

Sel_registerNameNoLock –> __sel_registerName is as follows

SEL sel_registerNameNoLock(const char *name, bool copy) {
    return __sel_registerName(name, 0, copy);  // NO lock, maybe copy
}

static SEL __sel_registerName(const char *name, bool shouldLock, bool copy) 
{
    SEL result = 0;

    if (shouldLock) selLock.assertUnlocked();
    else selLock.assertLocked();

    if(! name)return (SEL)0;

    result = search_builtins(name);
    if (result) return result;
    
    conditional_mutex_locker_t lock(selLock, shouldLock);
	auto it = namedSelectors.get().insert(name);
	if (it.second) {
		// No match. Insert.
		*it.first = (const char *)sel_alloc(name, copy);
	}
	return (SEL)*it.first;
}
Copy the code

The key code is auto it = namedSelectors. Get ().insert(name); , insert SEL into the namedSelectors hash table

SEL is not a simple string, but a string with an address. Sels [I] has the same name as SEL string, but the address is different, so it needs to be fixed up

3. Bad, messy class handling

//3, error messy class handling
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden(a);// readClass: readClass
for (EACH_HEADER) {
    if (! mustReadClasses(hi, hasDyldRoots)) {
        // Image is sufficiently optimized that we need not call readClass()
        continue;
    }
    // Fetch all classes from the compiled classlist, i.e., get the static segment __objc_classlist from Mach-o, which is a pointer to classref_t type
    classref_t const *classlist = _getObjc2ClassList(hi, &count);

    bool headerIsBundle = hi->isBundle(a);bool headerIsPreoptimized = hi->hasPreoptimizedClasses(a);for (i = 0; i < count; i++) {
        Class cls = (Class)classlist[i];// The CLS is just an address
        Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized); // Read the class. After this step, the value CLS gets is a name
        // After debugging, the process in if is not executed
        // Initialize the memory required by all lazily loaded classes, but the data of lazily loaded classes is not loaded, even the class is not initialized
        if(newCls ! = cls && newCls) {// Class was moved but not deleted. Currently this occurs 
            // only when the new class resolved a future class.
            // Non-lazily realize the class below.
            // Add lazy-loaded classes to the array
            resolvedFutureClasses = (Class *)
                realloc(resolvedFutureClasses, 
                        (resolvedFutureClassCount+1) * sizeof(Class));
            resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
        }
    }
}
ts.log("IMAGE TIMES: discover classes");
Copy the code

The main thing is to get a list of classes from Mach-O and iterate through it. Through debugging, CLS just gets an address before readClass and a name after readClass

Perform before

After performing

So readClass is a method that needs to be researched and then explored. So at this point, the class’s information is currently only stored in the address + name

4, fix remapping some classes that were not loaded by the image file

// fix remapping some classes that were not loaded by the image file
// Fix up remapped classes
// Class list and nonlazy class list remain unremapped. The class list and non-lazy class list remain unmapped
// Class refs and super refs are remapped for message dispatching. Class references and super references are remapped for message distribution
// After debugging, the process in if is not executed
// Remap unmapped classes and Super classes. The remapped classes are lazy-loaded classes
if (!noClassesRemapped()) {
    for (EACH_HEADER) {
        Class *classrefs = _getObjc2ClassRefs(hi, &count);// Mach-o static segment __objc_classrefs
        for (i = 0; i < count; i++) {
            remapClassRef(&classrefs[i]);
        }
        // fixme why doesn't test future1 catch the absence of this?
        classrefs = _getObjc2SuperRefs(hi, &count);__objc_superrefs in Mach_O
        for (i = 0; i < count; i++) {
            remapClassRef(&classrefs[i]);
        }
    }
}

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

The unmapped Class and Super Class are remapped, where

  • _getObjc2ClassRefsIs to obtainMach-OStatic segment in__objc_classrefsnamelyThe references to classes
  • _getObjc2SuperRefsIs to obtainMach-OStatic segment in__objc_superrefsnamelyA reference to a parent class
  • It can be learned from the comments that byremapClassRefThe classes areLazy-loaded classes, so this part of the code was not executed when it was initially debugged

5. Fix some messages

#if SUPPORT_FIXUP
//5, fix some messages
    // Fix up old objc_msgSend_fixup call sites
    for (EACH_HEADER) {
        // _getObjc2MessageRefs Gets the static segment __objc_msgrefs of Mach-O
        message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
        if (count == 0) continue;

        if (PrintVtables) {
            _objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
                         "call sites in %s", count, hi->fname());
        }
        // After debugging, the process in for is not executed
        // The traversal registers the function pointer and fixes the new function pointer
        for (i = 0; i < count; i++) {
            fixupMessageRef(refs+i);
        }
    }

    ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif
Copy the code

The static segment __objc_MSgrefs of Mach-O is obtained by _getObjc2MessageRefs, and the function pointer is registered through fixupMessageRef and fixed as the new function pointer

When a protocol exists in a class:readProtocolRead the agreement

//6, when there is a protocol in the class: readProtocol readProtocol
// Discover protocols. Fix up protocol refs. Discover the protocol. Amendment agreement Reference
// Iterate over all Protocol lists and load the Protocol list into the Protocol hash table
for (EACH_HEADER) {
    extern objc_class OBJC_CLASS_$_Protocol;
    // CLS = Protocol. All protocols and objects have similar structures. Isa corresponds to Protocol
    Class cls = (Class)&OBJC_CLASS_$_Protocol;
    ASSERT(cls);
    // Get the protocol hash table -- protocol_map
    NXMapTable *protocol_map = protocols(a);bool isPreoptimized = hi->hasPreoptimizedProtocols(a);// Skip reading protocols if this is an image from the shared cache
    // and we support roots
    // Note, after launch we do need to walk the protocol as the protocol
    // in the shared cache is marked with isCanonical() and that may not
    // be true if some non-shared cache binary was chosen as the canonical
    // definition
    if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
        if (PrintProtocols) {
            _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
                         hi->fname());
        }
        continue;
    }

    bool isBundle = hi->isBundle(a);_getObjc2ProtocolList = _getObjc2ProtocolList;
    // The protocol is read from the compiler and initialized
    protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
    for (i = 0; i < count; i++) {
        // Add protocol to the PROTOCOL_map hash table
        readProtocol(protolist[i], cls, protocol_map, 
                     isPreoptimized, isBundle);
    }
}

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

NXMapTable *protocol_map = protocols(); Get the protocol PROTOCOL_map hash table,

/*********************************************************************** * protocols * Returns the protocol name => protocol map for protocols. * Locking: runtimeLock must read- or write-locked by the caller * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
static NXMapTable *protocols(void)
{
    static NXMapTable *protocol_map = nil;
    
    runtimeLock.assertLocked(a);INIT_ONCE_PTR(protocol_map, 
                  NXCreateMapTable(NXStrValueMapPrototype, 16), 
                  NXFreeMapTable(v) );

    return protocol_map;
}

Copy the code

_getObjc2ProtocolList reads the list of protocols in Mach-O,

protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);

Traversal is added to PROTOCOL_map

readProtocol(protolist[i], cls, protocol_map, isPreoptimized, isBundle);

7, fix the protocol that was not loaded

// Fix the protocol that was not loaded
// Fix up @protocol references
// Preoptimized images may have the right 
// answer already but we don't know for sure.
for (EACH_HEADER) {
    // At launch time, we know preoptimized image refs are pointing at the
    // shared cache definition of a protocol. We can skip the check on
    // launch, but have to visit @protocol refs for shared cache images
    // loaded later.
    if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
        continue;
    //_getObjc2ProtocolRefs Gets the static section __objc_protorefs of Mach-o
    protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
    for (i = 0; i < count; i++) {/ / traverse
        // Compare the current protocol with the same memory address in the protocol list. If not, replace it
        remapProtocolRef(&protolist[i]);// Not executed after code debugging
    }
}

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

Get the static section __objc_protorefs (not the same thing as __objc_protolist in 6) from Mach-O and loop over the protocol that needs to be fixed. The remapProtocolRef command is used to compare the current protocol with the same memory address in the protocol list. If different protocols are used, the protocol is replaced

RemapProtocolRef:

/*********************************************************************** * remapProtocolRef * Fix up a protocol ref, in case the protocol referenced has been reallocated. * Locking: runtimeLock must be read- or write-locked by the caller * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
static size_t UnfixedProtocolReferences;
static void remapProtocolRef(protocol_t **protoref)
{
    runtimeLock.assertLocked(a);// Get the protocol for the uniform memory address in the protocol list
    protocol_t *newproto = remapProtocol((protocol_ref_t)*protoref);
    if(*protoref ! = newproto) {// If the current protocol is different from the same memory address protocol, replace it*protoref = newproto; UnfixedProtocolReferences++; }}Copy the code

8. Classification

//8
// Discover categories. Only do this after the initial category
// attachment has been done. For categories present at startup,
// discovery is deferred until the first load_images call after
// the call to _dyld_objc_notify_register completes. rdar://problem/53119145
if (didInitialAttachCategories) {
    for (EACH_HEADER) {
        load_categories_nolock(hi);
    }
}

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

It deals primarily with classifications that need to be initialized and loaded into the class, and for classifications that occur at runtime, the discovery of classifications is deferred until the first load_images call after the call to _dyLD_OBJC_Notify_register is complete

9. Class loading processing

// Realize non-lazy classes (for +load methods and static Instances) initializes non-lazy classes and performs rW, RO and other operations: realizeClassWithoutSwift
    // Lazy loading class - others do not move me, I do not move
    // Implement non-lazy-loaded classes, for load methods and static instance variables
    for (EACH_HEADER) {
        _getObjc2NonlazyClassList GetMach-o static segment __objc_nlclslist non-lazy loaded class list
        classref_t const *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            
            const char *mangledName  = cls->mangledName(a);const char *LGPersonName = "LGPerson";
            
             if (strcmp(mangledName, LGPersonName) == 0) {
                 auto kc_ro = (const class_ro_t *)cls->data(a);printf("_getObjc2NonlazyClassList: this is I want to study % s \ n",LGPersonName);
             }
            
            if(! cls)continue;

            addClassTableEntry(cls);// Insert the table, but it has already been inserted before, so it will not be re-inserted

            if (cls->isSwiftStable()) {
                if (cls->swiftMetadataInitializer()) {
                    _objc_fatal("Swift class %s with a metadata initializer "
                                "is not allowed to be non-lazy",
                                cls->nameForLogging());
                }
                // fixme also disallow relocatable classes
                // We can't disallow all Swift classes because of
                // classes like Swift.__EmptyArrayStorage
            }
            // Implement the current class, because the previous readClass read only the address + name, the class data is not loaded
            // Implement all non-lazy-loaded classes (instantiate some information about the class object, such as RW)
            realizeClassWithoutSwift(cls, nil);
        }
    }

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

The implementation of class loading processing, the implementation of non-lazy loading class non-lazy classes

  1. through_getObjc2NonlazyClassListTo obtainMach-OList of classes that are not lazily loaded
  2. throughaddClassTableEntryInsert non-lazily loaded classes into the hash table and store them in memory. If they are already added, they will not be added. Ensure that the entire structure is added
  3. throughrealizeClassWithoutSwiftImplement the current class, as in previous 3readClassThe only memory read isAddress + Name, the class ofdataThe data is not loaded

10. Optimize the classes that are violated without being processed

// Realize newly-resolved future classes, in case CF manipulates them
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            Class cls = resolvedFutureClasses[i];
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class is not allowed to be future");
            }
            / / implementation class
            realizeClassWithoutSwift(cls, nil);
            cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }

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

    if (DebugNonFragileIvars) {
        // Implement all classes
        realizeAllClasses(a); }Copy the code

The main idea is to implement classes that are not handled and optimize classes that are violated


** Conclusion: ** Through the above simple analysis of _read_images, the focus is to analyze readClass and realizeClassWithoutSwift.

0x02 — readClassAnalysis of the

/*********************************************************************** * readClass * Read a class and metaclass as written by a compiler. Returns the new class pointer. This could be: Returns a new class pointer, possibly:  * - cls * - nil (cls has a missing weak-linked superclass) * - something else (space for this class was reserved by a future class) * * Note that all work performed by this function is preflighted by * mustReadClasses(). Do not change this function without updating that one. * * Locking: runtimeLock acquired by map_images or objc_readClassPair * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName = cls->mangledName(a);/ / name
    
    ** * ---- If you want to enter a custom, add a judgment yourself
    const char *LGPersonName = "LGPerson";
    if (strcmp(mangledName, LGPersonName) == 0) {
        auto kc_ro = (const class_ro_t *)cls->data(a);printf("%s -- Research focus --%s\n", __func__,mangledName);
    }
    // Return nil if there are missing weak-Linked classes in the parent of the current class
    if (missingWeakSuperclass(cls)) {
        // No superclass (probably weak-linked). 
        // Disavow any knowledge of this subclass.
        if (PrintConnecting) {
            _objc_inform("CLASS: IGNORING class '%s' with "
                         "missing weak-linked superclass", 
                         cls->nameForLogging());
        }
        addRemappedClass(cls, nil);
        cls->superclass = nil;
        return nil;
    }
    
    cls->fixupBackwardDeployingStableSwift(a);// Determine if it is a class to be processed later
    // Normally, you do not go to popFutureNamedClass because this is an operation that is specific to future classes
    // Through the breakpoint debugging, does not go into the if process, therefore does not operate on ro, RW
    Class replacing = nil;
    if (Class newCls = popFutureNamedClass(mangledName)) {
        // This name was previously allocated as a future class.
        // Copy objc_class to future class's struct.
        // Preserve future's rw data block.
        
        if (newCls->isAnySwift()) {
            _objc_fatal("Can't complete future class request for '%s' "
                        "because the real class is too big.", 
                        cls->nameForLogging());
        }
        // Read class data, set ro, rw
        // After debugging, it will not go here
        class_rw_t *rw = newCls->data(a);const class_ro_t *old_ro = rw->ro(a);memcpy(newCls, cls, sizeof(objc_class));
        rw->set_ro((class_ro_t *)newCls->data());
        newCls->setData(rw);
        freeIfMutable((char *)old_ro->name);
        free((void *)old_ro);
        
        addRemappedClass(cls, newCls);
        
        replacing = cls;
        cls = newCls;
    }
    // Check whether the class has been loaded into memory
    if(headerIsPreoptimized && ! replacing) {// class list built in shared cache
        // fixme strict assert doesn't work because of duplicates
        // ASSERT(cls == getClass(name));
        ASSERT(getClassExceptSomeSwift(mangledName));
    } else {
        addNamedClass(cls, mangledName, replacing);// Load the classes in the shared cache
        addClassTableEntry(cls);// Insert the table, i.e. read from the Mach-o file into memory
    }

    // for future reference: shared cache never contains MH_BUNDLEs
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA() - >data()->flags |= RO_FROM_BUNDLE;
    }
    
    return cls;
}
Copy the code
  1. First of all bycls->mangleName()Gets the name of the class
const char *mangledName(a) { 
        // fixme can't assert locks here
        ASSERT(this);

        if (isRealized() | |isFuture()) { // This initialization judgment is similar in lookupImp
            return data() - >ro()->name;// If already instantiated, get name from ro
        } else {
            return ((const class_ro_t *)data())->name;// Instead, get name from mach-o's data}}Copy the code
  1. The currentclsIf the parent class is missingweak-linkedClass, returnnil
  2. Then determine if it is a class to be processed in the futureifInside, becauseOperations that are specific to future classes to be processedAnd through debugging, also did not go toifLi, so it won’t fit insidero.rwOperation.
    1. data()ismachOThe data is not in memory yet.
    2. roIs assigned from the data strong type in machO.
    3. rwData fromroCopy the past.
  3. throughaddNameClasswillclsandnameIn order toKey/value pairIn the way ofgdb_objc_realized_classesIn this table. This table is used to hold all classes,nameforkey.clsforvalue.
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * addNamedClass load sharing classes in the cache insert table * Adds name = > cls to the named non-meta class map. Adds name=> CLS to named non-metaclass mapping * Warns about duplicate class names and keeps the old mapping. * Locking: runtimeLock must be held by the caller **********************************************************************/
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
    runtimeLock.assertLocked(a); Class old;if ((old = getClassExceptSomeSwift(name)) && old ! = replacing) {inform_duplicate(name, old, cls);

        // getMaybeUnrealizedNonMetaClass uses name lookups.
        // Classes not found by name lookup must be in the
        // secondary meta->nonmeta table.
        addNonMetaClass(cls);
    } else {
        // Add to the gdb_objC_realized_classes hash table
        NXMapInsert(gdb_objc_realized_classes, name, cls);
    }
    ASSERT(! (cls->data()->flags & RO_META));

    // wrong: constructed classes are already realized when they get here
    // ASSERT(! cls->isRealized());
}
Copy the code
  1. throughaddClassTableEntry, adds the initialized class toallocatedClassesTable, the tableallocatedClassesin_objc_initIn theruntime_initIs initialized in.
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * addClassTableEntry Add a class to the * Add a class table of all class to the table of all classes. If addMeta is true, * automatically adds the metaclass of the class as well. * Locking: runtimeLock must be held by the caller. **********************************************************************/
static void
addClassTableEntry(Class cls, bool addMeta = true)
{
    runtimeLock.assertLocked(a);// This class is allowed to be a known class via the shared cache or via
    // data segments, but it is not allowed to be in the dynamic table already.
    auto &set = objc::allocatedClasses.get(a);Runtime_init in objc_init creates the table

    ASSERT(set.find(cls) == set.end());

    if (!isKnownClass(cls))
        set.insert(cls);
    if (addMeta)
        // Add to the allocatedClasses hash table
        addClassTableEntry(cls->ISA(), false);
}
Copy the code

To pinpoint the class you want to debug, that is, to create your own class, you can add your own debugging code to the source code:

Conclusion:

To sum up, readClass mainly reads the data of each section of Mach-O into memory, i.e. inserts the data into the table, but the value has the class name and the class address, the other class data is not read, that is, the class is not implemented, the next chapter will detail how ro, RW, RXT initialization. 😊

Welcome big brother message correction 😄, code word is not easy, feel good to give a thumbs-up 👍 have any expression or understanding error please leave a message; Common progress