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_images
loadingimage
To 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_images
isReference types
The outside world changes and changes with it.load_images
isValue 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 is
map_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_images
The 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
_getObjc2ClassRefs
Is to obtainMach-O
Static segment in__objc_classrefs
namelyThe references to classes
_getObjc2SuperRefs
Is to obtainMach-O
Static segment in__objc_superrefs
namelyA reference to a parent class
- It can be learned from the comments that by
remapClassRef
The 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:readProtocol
Read 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
- through
_getObjc2NonlazyClassList
To obtainMach-O
List of classes that are not lazily loaded - through
addClassTableEntry
Insert 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 - through
realizeClassWithoutSwift
Implement the current class, as in previous 3readClass
The only memory read isAddress + Name
, the class ofdata
The 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 — readClass
Analysis 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
- First of all by
cls->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
- The current
cls
If the parent class is missingweak-linked
Class, returnnil
- Then determine if it is a class to be processed in the future
if
Inside, becauseOperations that are specific to future classes to be processed
And through debugging, also did not go toif
Li, so it won’t fit insidero
.rw
Operation.data()
ismachO
The data is not in memory yet.ro
Is assigned from the data strong type in machO.rw
Data fromro
Copy the past.
- through
addNameClass
willcls
andname
In order toKey/value pair
In the way ofgdb_objc_realized_classes
In this table. This table is used to hold all classes,name
forkey
.cls
forvalue
.
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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
- through
addClassTableEntry
, adds the initialized class toallocatedClasses
Table, the tableallocatedClasses
in_objc_init
In theruntime_init
Is 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. 😊