This is the 11th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

1. The application is loaded

1.1 library

Every program runs on basic libraries, such as UIKit, CoreFoundation, etc. Libraries are executable binary files that the operating system loads into memory. Libraries come in two forms: static libraries (.a,.lib) and dynamic libraries (.so,.dll). The main difference between the two libraries is linking.

Static libraries: The static library is loaded at compile time and copied into the executable file at link time. The static library does not change because it is copied directly into the target program at compile time

  • advantages:The compiledExecution file ofNo external library support is required.You can just use it.
  • disadvantages: Multiple apps will be used byMultiple copies of.Can't shareandOccupies more redundant memory. All the functions are in the library, so whenModify the functionThe need whenrecompile.

Dynamic libraries: Programs are not linked to the target program at compile time. The target program only stores a reference to the dynamic library, which is dynamically loaded into memory by the system as the program runs.

  • advantages: Libraries are dynamic,It is loaded at runtime, soWhen you change functions in the library, you do not need to recompile.The same library can be used by more than one program, memory,It's only loaded once.Save memory space. Because you don’t need to copy it to the target, it doesn’t affect the volume of the target, soThe size of the app is reduced relative to the static library.
  • disadvantages: The dynamic library is loaded into memory by the system at runtime ifEnvironment lacks librariesorThe version of the library is incorrectThen the program willUnable to run. And because it is not integrated into the object code during compilation, the library function is only called when it is executed, soThe first load is time-consuming.

1.2 Compilation Process

The compilation process is shown in the following figure, which is mainly divided into the following steps:

Source files: load.h,.m,.cpp, etc. Precompile: Replace macros, delete comments, expand header files, produce.i files compilation: The compiler converts.i files to assembly language, produce.s files Assembly: Converts assembly files to machine code, produce.o files link: References to other libraries in the.o file are made to generate the final executable

1.3 DYLD (linker)

Now that we’re done with libraries, how do libraries load into memory? Which brings us to something really awesome and important: the DYLD.

DYLD (The Dynamic Link Editor) is an important part of Apple’s operating system. After the app is compiled and packed into a Mach-O file in executable format, DYLD is responsible for connecting and loading the program.

1.4 APP startup process

Before we explore, let’s take a look at the APP startup flow chart.

2. Dyld exploration

Start the exploration of dyld by opening the program, then typing a breakpoint in main and running. After you run it, you can see that there’s a start call before main, so is there anything else in front of it?

Click start and you’ll see libdyld, which is the main character of this time, the DYLD linker. So how do you explore that next?

Try adding a break point for start, but it doesn’t get stuck. It still stops at the same point as main, proving that the function called below is not named start, but that the load method is called. The load method precedes the main function.

So let’s try to put a breakpoint on main and run it. You can see where it starts is _dyLD_start. That’s the start we saw earlier with main. You can view it in the sidebar

Or LLDB input BT to view

Next, go to the dyld source code. Dyldbootstrap ::start: dyldbootstrap::start: dyldbootstrap::start

Dyldbootstrap ::start (); / / start (); / / start (); / / start ();

Click on dyld::_main, and there are nearly a thousand lines of code.

In this case, we are directly backward from the return value.

Look for where to assign to result and see that both assignments are related to sMainExecutable.

Next search for sMainExecutable. Midway through we saw some bind work done by sMainExecutable which proved to be exactly what we were looking for.

Finally find the place where sMainExecutable is assigned.

Click to view instantiateFromLoadedImage.

Points into instantiateMainExecutable view, including sniffLoadCommands is to get the Mach – O type file Load in Command of the relevant information, and carries on the check.

Next go back to dyld::_main and continue to see initializeMainExecutable().

The first step is to see how many mirrors you’ll get in the class, then run the runInitializers one by one, and then run the runInitializers for sMainExecutable.

Click runInitializers. Since it’s initialization, the core is processInitializers.

In the processInitializers point, you can see that the images are recursively instantiated by calling recursiveInitialization one by one.

Next, search for recursiveInitialization. The recursive initialization of the dependent file is discovered here, and two important functions are called: notifySingle and doInitialization.

Let’s look at the notifySingle, see that the dot doesn’t go in, and then we’ll search the notifySingle to see where it’s assigned.

Click in when you find an assignment. The key here is (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()).

Next, search for sNotifyObjCInit and find that the type is _dyLD_OBJC_NOTIFy_init.

If I keep looking, I see that there’s an assignment operation here, that is, it says assign the second parameter of the registerObjCNotifiers to the sNotifyObjCInit.

Next look for the registerObjCNotifiers and find that they are called at _dyLD_objC_notify_register.

Then I searched for _dyLD_OBJC_NOTIFy_register in the source code and found that _dyLD_OBJC_NOTIFy_register was called on _objC_init.

I’m going to put a break point here and run it. We’ve already explored _dyLD_start to recursiveInitialization. Now we’ll explore the process from _objc_init to recursiveInitialization.

The previous function of _objc_init, _OS_object_init, is in libDispatch, so download the libDispatch source and search for _OS_object_init. It turns out that _objc_init is actually called.

A search for libdispatch_init shows that _OS_object_init is indeed called.

Next, download the libSystem source code, then look for libSystem_initializer, and find that it does call libDispatch_init.

Next, back to dyld, look for doModInitFunctions. The libSystem Initializer must run first, which means that both libDispatch and objc will depend on libSystem. So doModInitFunctions are doing libSystem loading.

LibSystem initializer is the same as libSystem initializer, and func is the same as libSystem initializer.

When you see where doModInitFunctions are called, it is inside doInitialization.

When you look at where doInitialization is called, you go back to recursiveInitialization.

And as I said before, sNotifyObjCInit in notifySingle is eventually assigned to _dyLD_OBJC_NOTIFY_register, which is called from _objc_init, _objc_init is called by doInitialization. What is the relationship between doInitialization and notifySingle? _objc_init calls _dyLD_OBJC_notify_register (&map_images, load_images, unmap_image), where map_images is some of the key data to communicate the previous content, But if you assign a value to a method, you don’t know if the method is called. The map_images method is executed only when sNotifyObjCMapped is called.

NotifySingle registers a notification and sends a notification to notifySingle when the image is loaded. So how is it notified? Let’s break it down next time.