In the previous article we explored _objc_init, and then we saw _dyLD_OBJC_notify_register, which takes three arguments, There are two parameters that are called directly load_images and unmap_image, and then there is one parameter called map_images which is fetched by address access which is called by address callback, so the question is, when is map_images called? Is it when _dyLD_OBJC_NOTIFy_register registers? Or was it before it? So today we’re going to explore the timing and process of calling map_images.

map_images

_dyLD_OBJC_notify_register = = = = = = = = = = = = = = = = = = = = =

  • inAPIs.cppOf the file714line

  • As well asdyldAPIs.cppOf the file2204line

And then we go back to the implementation of the setObjCNotifiers and the implementation of the registerObjCNotifiers, and we can make a very clear distinction, so we should look at the implementation of the setObjCNotifiers, Since registerObjCNotifiers internally only calls init on all initialized images, let’s look at the implementation of the setObjCNotifiers:

The important thing to look at is when _objcNotifyMapped was called, so let’s look at the global search to see when it was called. (Elementary)

We find that it’s called in runImageCallbacks, so let’s go ahead and look at the call to that function, continue the global search, and finally find the two calls, so how do we tell which one to look at? The first place to call const MachOLoaded* AllImages::loadImage is const MachOLoaded* :loadImage. Finally, we return to the const MachOLoaded* AllImages::loadImage function.

Function is to read in the image and then fill in the allImages etc, then we continue to view the function invocation mechanism, then go to look at the second place call void allImages: : applyInitialImages (), then the global search to check the call, _dyLD_Initializer = _dyLD_initializer = _dyLD_initializer = _dyLD_initializer = _dyLD_initializer = _dyLD_initializer

So once we find this function let’s move on to where is it being called? After a global search, a critical comment was found as follows:

LibSystem_initializer = libSystem_initializer = libSystem_initializer = libSystem_initializer = libSystem_initializer = libSystem_initializer = libSystem_initializer = libSystem_initializer = libSystem_initializer = libSystem_initializer = libSystem_initializer = libSystem_initializer = libSystem_initializer

You can see that _dyLD_Initializer is called here. Here we also find the calling process of map_images and the loading time. This is when the program initializes all libsystem. dylib.

load_images

So let’s look at the load_images process, so let’s first look at _dyLD_OBJC_NOTIFy_register, and then let’s search, and we’ve seen two places, So shall we do it the same way or the other way this time? I think the more careful people have found a problem with a call to dyld and a call to AllImages, and in our case, a call to dyld to read AllImages, so let’s look at the registerObjCNotifiers function:

And then we look at the call to sNotifyObjCInit, and we see that there’s an implementation of the call to notifySingle, and we pass the value, and then we look at where notifySingle is called, and we search globally, It is called in the recursiveInitialization function as follows:

So what’s going on in this function? Let’s go back to objc source code:

Load (+load, +load, +load, +load);

We recursively read the +load methods of non-lazily loaded classes and the +load methods of non-lazily loaded classes into a list of +load methods, then go back to load_images and call call_load_methods to read all of the +load methods. Let’s also look at the implementation of this function:

Then let’s take a look at the class’s +load method call implementation:

And the classification of the + LAod method call implementation:

To this dyld basically a load process is complete, but we look at the source code when ignoring a small point, this point may cause the program execution order is not in accordance with their own assumption oh, what is it? Let’s go back and explore.

cxxFunction call timing

The +load method is used to load the main function, and the dyld function is used to load the CXX function. The CXX function is used to load the CXX function.

We can see from the example that the +load method is first used, then the CXX function is used, and then the main function is used. So the question is, when does the CXX function call load? Keeping up with me, let’s go back to the recursiveInitialization implementation:

NotifySingle (dyLD_image_state_dependents_initialized, this, &timingInfo) starts at line 1651 of this function. Bool hasInitializers = this->doInitialization(context); Function, loaded CXX function, you may ask, how do you know, open eyes ah? Don’t worry, let’s put a breakpoint in the KGFunc function in our case project and look at the stack information:

Bool hasInitializers = this->doInitialization(context); Is made. That’s why it’s after +load and before main.

fromdyldtomain

Now that we’ve explored the loading process for dyld, how do we transition from dyld to main? Let’s continue exploring this process. Ld_ld_start = / ld_ld_start = / dyld ld_start = / dyld ld_start = / dyld ld_start = / dyld ld_start = / dyld ld_start = / dyld ld_start = / dyld ld_start

If you are familiar with assembly, I want to see LC_MAIN, because for Mach-o file LC_MAIN is the IMP with main saved. Since this piece is implemented in assembly, let’s run the program to see if it is consistent.

Debug->Debug Workflow->Always Show Disassembly Then add a breakpoint to the KGFunc function and run the following code:

Then we look at the register values:

The register does hold the IMP of main, which explains the loading process from dyld to main.

conclusion

This is the end of the search for dyld, if there are other discoveries to add, through these two articles, understand the +load method and CXX method and main function execution order, but also found a point, non-lazy load class +load first according to isa bit recursion, Then it executes the +load method for the classification.