Write in front: iOS underlying principle exploration is my usual development and learning in the accumulation of a section of advanced road. Record my continuous exploration of the journey, I hope to be helpful to all readers.Copy the code
The directory is as follows:
- IOS underlying principles of alloc exploration
- The underlying principles of iOS are explored
- The underlying principles of iOS explore the nature of objects & isa’s underlying implementation
- Isa-basic Principles of iOS (Part 1)
- Isa-basic Principles of iOS (Middle)
- Isa-class Basic Principles of iOS Exploration (2)
- IOS fundamentals explore the nature of Runtime Runtime & methods
- Objc_msgSend: Exploring the underlying principles of iOS
- Slow lookups in iOS Runtime
- A dynamic approach to iOS fundamentals
- The underlying principles of iOS explore the message forwarding process
Summary of the above column
- Summary of iOS underlying principles of exploration
Sort out the details
- Summary of iOS development details
preface
This is the beginning of a new chapter. Explore a process of application loading.
- In the development process, we will through our code to achieve our functional business, after the completion of the code, how to write into the system’s memory to execute it?
- In the development process, we will use a lot of dynamic libraries, static libraries. Such as
UIKit
、AVFoundation
、CoreFoundation
And so on. How do these dynamic and static library files get loaded into memory for us to call? - So how does OBJC get started?
That’s what we’re going to explore today.
To prepare
- Dyld download source code
- Libdispatch library source code
- LibSystem library source code
- The source code of objc
The project file
library
Libraries: Executable binaries that can be loaded into memory by the operating system.
- Static libraries: When linked, static libraries are copied to the executable file in its entirety and have multiple redundant copies when used multiple times. Common formats
.a
,.lib
,.framework
. - Dynamic library: link does not copy, program run by the system dynamically loaded into memory, for program call, the system only loaded once, multiple programs shared, saving memory. Common formats
.dylib
、.framework
.
The build process
Source code files -> precompiled -> Compiled -> Assembly -> Links (dynamic and static libraries) -> executable
- Precompile: replace macros, remove comments, expand header files, produce.i files
- Compilation: converts the. I file to assembly language, producing. S files
- Assembly: to convert assembly files into machine code files, producing. O files
- Link: References to other libraries in the.o file to produce the final executable
So how do the dynamic and static libraries referenced by the program get loaded into memory?
Dynamic linker dyly
Dyld (The Dynamic Link Editor) is apple’s dynamic linker, which is an important part of Apple’s operating system. After the program preparation of the system kernel, DyLD is responsible for the remaining work. It is also open source, so anyone can download the source code on apple’s website to read how it works and learn the details of how the system loads the dynamic library.
Dyld process exploration
Now, let’s take a look at what happens in the middle between app startup and calling main. We make a breakpoint at the beginning line of main and run the project.
You can see that there is only a start before main. With the symbol breakpoint, we also do not break the start. We can tell from the logs that the load method of the ViewController is called before main, so let’s break it and see.
_dyLD_start = _dyLD_start = _dyLD_start
With that in mind, let’s sort out the stack information, which will be the main task of today’s exploration, as a guide:
- dyld
_dyld_start
- dyld
dyldbootstrap::start
- dyld
dyld::_main
- dyld
dyld::initializeMainExecutable()
- dyld
ImageLoader::runInitializers
- dyld
ImageLoader::processInitializers
- dyld
ImageLoader::recursiveInitialization
- dyld
dyld::notifySingle
- libobjc.A.dylib
load_images
+[ViewController load]
We learn from_dyld_start
start
_DYLD_START = dyLD = dyLD = DYLD = DYLD = DYLD = DYLD = DYLD
In the dyLDstartup. s file, you can see multiple results in different environments. __i386__, __x86_64__, __arm__, __arm64__ are slightly different, but eventually the c++ function dyldbootstrap::start: is called.
// call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
ldr r0, [r8] // r0 = mach_header
ldr r1, [r8, #4] // r1 = argc
add r2, r8, #8 // r2 = argv
adr r3, __dyld_start
sub r3 ,r3, #0x1000 // r3 = dyld_mh
add r4, sp, #12
str r4, [sp, #0] // [sp] = &startGlue
Copy the code
In the case that dyLDbootstrap ::start failed, we searched dyldbootstrap and found a namespace. Instead of Posting the entire code, we directly located its start function and eventually called dyld::_main inside.
// // This is code boot dyld. This work is usually done by dyLD and CRT. // In dyLD, we have to do this manually. // uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[], const dyld3::MachOLoaded* dyldsMachHeader, Uintptr_t * startGlue) {// Emit kdebug trace points to indicate that dyLD boot has started <rdar://46878536> dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0); // If the kernel must slide dyld, we need to fix loading sensitive locations // We must do this before using any global variables rebaseDyld(dyldsMachHeader); // The kernel sets the env pointer just past the end of the agV array const char** envp = &argv[argc+1]; // The kernel sets the apple pointer to the end of the envp array const char** apple = envp; while(*apple ! = NULL) { ++apple; } ++apple; // Set stack canary's random value __guard_setup(apple); # runDyldInitializers(argc, ArgV, ENVP, Apple) in dyld #endif _subsystem_init(apple); // Now that we have finished bootstrapping dyld, call main uintptr_t appsSlide = appsMachHeader->getSlide(); return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); }Copy the code
Inside dyld::_main, according to the final return result, we find that there is a deep connection through sMainExecutable. And then the sMainExecutable search,
/ / give priority to the executable file to instantiate ImageLoader sMainExecutable = instantiateFromLoadedImage (mainExecutableMH mainExecutableSlide, sExecPath);Copy the code
Following the source code is a process of slowly exploring the unknown, and then the source code flow into the layer by layer exploration, we have summarized the following flow chart.
The flow chart of dyld
And when you look at the flow, you can see that you end up at objc_init,
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * _objc_init boot initialization. Register our image notification program with DYLD. * before the library initialization time is called libSystem * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / void _objc_init (void) { static bool initialized = false; if (initialized) return; initialized = true; // fixme defer initialization until an objc-using image is found? environ_init(); tls_init(); static_init(); runtime_init(); exception_init(); #if __OBJC2__ cache_t::init(); #endif _imp_implementationWithBlock_init(); _dyld_objc_notify_register(&map_images, load_images, unmap_image); #if __OBJC2__ didCallDyldNotifyRegister = true; #endif }Copy the code
You can see that the _dyLD_OBJC_notify_register (&map_images, load_images, unmap_image) method is called; .
- In this method
map_images
When was it called? - What does a process look like inside the load_images method when it executes?
- And finally, where we are now
dyld
How does the phase entermain
What about the main program of the function?
In the next article, we will continue the process analysis above to answer the three questions we raised above.