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 Countpaths[]
=imageFilePath
, file pathmhdrs[]
=imageLoadAddress
And 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 creation
header_info
Linked list, and willheader_info
Added to thehList
In the - call
_read_images
- Call all files in the image file
load
methods
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 variables
OBJC_PRINT_IMAGE_TIMES = YES
print EACH_HEADER
traversehList
- The first time, the taggedPointer confounder is initialized, and then three-quarters of the load factor is opened up in memory and created
gdb_objc_realized_classes
,gdb_objc_realized_classes
A misnomer,All classes that are not actually shared in the cache, implemented or not
ts.log("IMAGE TIMES: fix up selector references");
- Repair the Mach – o
@selector
To traverse thehList
- from
Mach-O
Remove thesels
, the registeredsel
That is inserted into thenameSelctiors
- The current
sel
Address is not equal to the registeredsel
Address, then modified
- Repair the Mach – o
ts.log("IMAGE TIMES: discover classes");
- Look for a class. If you don’t know a class, take it from
mach-o
To re-read and load into memory - Traverse the read class,
addNamedClass
Inserts the class intogdb_objc_realized_classes
In the addClassTableEntry(cls)
, inserts classes and metaclasses intoallocatedClasses
In the
- Look for a class. If you don’t know a class, take it from
ts.log("IMAGE TIMES: remap classes");
- in
remapped_class_map
To find the - Remap classes that have not been loaded by the image file
- Not usually.
- in
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
- Fix old message sending
- The real thing doesn’t work
ts.log("IMAGE TIMES: discover protocols");
- Read the protocol in Mach-O into memory
- from
mach-o
Reads,protocollist
readProtocol
To createprotocol_t
Inserted into theprotocol_map
In the
ts.log("IMAGE TIMES: fix up @protocol references");
- Fixed pointer address for protocol in Mach-O
remapProtocolRef
Remap protocols in memory, reassign if they are different
ts.log("IMAGE TIMES: discover categories");
didInitialAttachCategories
The default isfalse
- Load the category if the image is already gone
load_image
ts.log("IMAGE TIMES: realize non-lazy classes");
- Implement classes that are not lazily loaded
- When a class implements
+load
Is the non-lazy loading method - so
+load
Causes the class to be implemented ahead of time addClassTableEntry
, add classes and metaclasses toallocatedClasses
In therealizeClassWithoutSwift
The implementation class, the point, is thatRo, rw, category
The processing of
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_images
indyld
Registration is a call- Mainly is to
mach-o
Map to memory - Repair the
mach-o
In thesel
With the memorynameSelctiors
mapping - will
mach-o
In theprotocol
Read into memoryprotocol_map
In the - If the class implements
load
Method will implement the class, not lazy loading