preface
In the last blog we learned about dyld->libsystem-> libDispatch ->_objc_init. So let’s see what’s going on in objc_init.
Objc_init analysis
environ_init
Environment variable initialization
View the environment variables in the source code with the following code:
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
_objc_inform("%s: %s", opt->env, opt->help);
_objc_inform("%s is set", opt->env);
}
Copy the code
You can also enter export OBJC_HELP=1 in the terminal, and then LLDB: to print the following information
We can go to thexcode
Let’s set it up in thereOBJC_PRINT_IMAGES
Set this environment variable, and it will print out. throughEdit Scheme ->Run
You can set it.
tls_init
: binding on the thread key, such as the destructor per fieldstatic_init
: Runs the C ++ static constructor. Call yourself before dyld is calledruntime_init
: Mainly initializationunattachedCategories
andallocatedClasses
Two tables.exception_init
: Exception initialization, which we often useobjc_setUncaughtExceptionHandler
Handle an exception after a crash, or crash log upload to the server.cache_t::init()
The cache condition is initialized_imp_implementationWithBlock_init
: Starts the callback mechanism. Normally this doesn’t do much because all initialization is lazy, but for some processes we can’t wait to load trampolines dylib._dyld_objc_notify_register
That will bemap_images
load_images
unmap_image
Method assigned to dyld to trigger the call
Map_images analysis
map_images_nolock
This method basically finds all of macho’s images and calls _read_images
_read_images analysis
There’s a lot of code in this one, and apple engineers did a lot of log printing at the time. We can break this down into smaller modules.
Conditional control for the first load
Create NXMapTable with the size of 4/3 *totalClasses. This table is a class master table; Unlike the allocatedClasses table above, the next table stores allocatedClasses that have been alloc.
Fixed @selector confusion during precompile
.
Why does it need to be repaired? This is because the address of the method we obtained from the image is not added with ASLR, which is the base address. It needs to be repaired after adding the base address through dyLD.
ReadClass readClass and process classes that have not been cleaned
Fixed remapping of some remapClassRef classes that were not loaded by the image file
Fix some message fixupMessageRef
Read the protocol of the class readProtocol
Fixed remapProtocolRef not loaded
Load the category load_categories_NOLock
Load class realizeClassWithoutSwift
Future classes that implement the new resolution
Let’s focus on readClass
ReadClass analysis
Add the following code to the readClass method so you can explore how our custom class reads.
The breakpoint continues execution:
Found no ro,rw related assignments performed. The breakpoint continues to the next step and callsaddNamedClass
.
At this moment theclass
Insert into the master table
- Continuing execution is called recursively
addClassTableEntry
.
addClassTableEntry(Class cls, bool addMeta = true)
{
runtimeLock.assertLocked();
// 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();
ASSERT(set.find(cls) == set.end());
if(! isKnownClass(cls)) set.insert(cls);if (addMeta)
addClassTableEntry(cls->ISA(), false);
}
Copy the code
Insert the class into the allocatedClasses table, and if there are metaclasses, insert the metaclasses.
Class realizeClassWithoutSwift
- Create a new
QHPerson
Class, and then addload
Method, and add the following code to the code so we can explore our custom class
Continue executing the entry torealizeClassWithoutSwift
Methods.
- Ro is assigned to Rw
- Set the superclass and metaclass relationships
- Joining with the class
MethodizeClass analysis
MethodizeClass = methodizeClass; methodizeClass = methodizeClass; methodizeClass = methodizeClass;
Print RWE: Found to nil
- prove
rwe->properties.attachLists(&proplist, 1);
andrwe->protocols.attachLists(&protolist, 1);
. - If there is a method, it will be called
prepareMethodLists
, this method actually sorts methods by address, which can be verified by the following code
- Add the classification to the main class
attachToClass
This approach will be analyzed in the next blog - so
methodizeClass
This is mainly about method sorting and adding categories to the main class.
Lazy loading and non-lazy loading
Above we added the load method to the custom class, now we remove it. And then run again. Found a strange phenomenon:
Why is this?? So here we aremain
It callsQHPerson
Methods. Continue to run, this time entered, but not the previous process:
Through the stack, you can see the process of sending the message after the main function.
conclusion
- If the custom class implements
load
Methods, (The lazy loading
) will passDyld ->objc_init->(instantiate the main program)->read_image->read_class
To load the class. - If it’s not implemented
load
Methods, (Lazy loading
), will go the first time an object method is calledMessage forwarding process
To achieve Lazy loading
This approach will be greatly optimizedStartup time of the app
.