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_initEnvironment 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 thexcodeLet’s set it up in thereOBJC_PRINT_IMAGESSet this environment variable, and it will print out. throughEdit Scheme ->RunYou can set it.

  • tls_init: binding on the thread key, such as the destructor per field
  • static_init: Runs the C ++ static constructor. Call yourself before dyld is called
  • runtime_init: Mainly initializationunattachedCategoriesandallocatedClassesTwo tables.
  • exception_init: Exception initialization, which we often useobjc_setUncaughtExceptionHandlerHandle 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_registerThat will bemap_images load_images unmap_imageMethod 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 theclassInsert into the master table

  • Continuing execution is called recursivelyaddClassTableEntry.
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 newQHPersonClass, and then addloadMethod, and add the following code to the code so we can explore our custom class

Continue executing the entry torealizeClassWithoutSwiftMethods.

  • 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

  • proverwe->properties.attachLists(&proplist, 1);andrwe->protocols.attachLists(&protolist, 1);.
  • If there is a method, it will be calledprepareMethodLists, this method actually sorts methods by address, which can be verified by the following code

  • Add the classification to the main classattachToClassThis approach will be analyzed in the next blog
  • somethodizeClassThis 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 aremainIt callsQHPersonMethods. 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 implementsloadMethods, (The lazy loading) will passDyld ->objc_init->(instantiate the main program)->read_image->read_classTo load the class.
  • If it’s not implementedloadMethods, (Lazy loading), will go the first time an object method is calledMessage forwarding processTo achieve
  • Lazy loadingThis approach will be greatly optimizedStartup time of the app.