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:

  1. IOS underlying principles of alloc exploration
  2. The underlying principles of iOS are explored
  3. The underlying principles of iOS explore the nature of objects & isa’s underlying implementation
  4. Isa-basic Principles of iOS (Part 1)
  5. Isa-basic Principles of iOS (Middle)
  6. Isa-class Basic Principles of iOS Exploration (2)
  7. IOS fundamentals explore the nature of Runtime Runtime & methods
  8. Objc_msgSend: Exploring the underlying principles of iOS
  9. Slow lookups in iOS Runtime
  10. A dynamic approach to iOS fundamentals
  11. 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 asUIKitAVFoundationCoreFoundationAnd 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
  • dylddyldbootstrap::start
  • dylddyld::_main
  • dylddyld::initializeMainExecutable()
  • dyldImageLoader::runInitializers
  • dyldImageLoader::processInitializers
  • dyldImageLoader::recursiveInitialization
  • dylddyld::notifySingle
  • libobjc.A.dylibload_images
  • +[ViewController load]

We learn from_dyld_startstart

_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; .

  1. In this methodmap_imagesWhen was it called?
  2. What does a process look like inside the load_images method when it executes?
  3. And finally, where we are nowdyldHow does the phase entermainWhat 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.