Documents:
Libdispatch: openSource.apple.com/libDispatch: opensource.apple.com/libDispatch: openSource.apple.com/libSystem: opensource.apple.com/libDispatch: openSource.apple.com/libDispatch: openSource.apple.com/libDispatch: openSource.apple.com/libDispatch: openSource.apple.com/libDispatch: openSource.apple.com/
Previously:
Before exploring and analyzing app startup, we need to first understand the compilation process of app code in iOS, as well as dynamic and static libraries.
The build process
1. Precompile: process precompiled instructions starting with #in code, such as deleting #define and expanding macro definition, inserting files included in #include into this instruction location, etc. (replace macro, delete comment, expand header file, generate.i file) For precompiled files processed by the lexical analysis, syntax analysis and semantic analysis, and to optimize source code and then generate assembly code (soon. I file conversion to assembly language, produce. S file) 3, assembly: through assembler converts assembly code to machine can execute instruction, and generate the target file. O file 4, link: Link object files to executable files. In this process, the linker links different object files together, because different object files may have variables or functions that reference each other. For example, we often call methods and variables in Foundation framework and UIKit framework, but these frameworks are not in the same object file as our code. This requires the linker to link them to our own code
The process is as follows
The reuse of shared code by Foundation and UIKit is collectively called a library — a binary of executable code that can be written to memory by the operating system. It is divided into static libraries and dynamic libraries
Static library
When linking, it copies the executable file in its entirety. When used multiple times, there are multiple redundant copies. Such as. A,. Lib, non – system framework are static libraries
The dynamic library
Link is not copied, the program is dynamically loaded into memory by the system when it is running, for the program to call, the system is only loaded once, multiple programs share, saving memory. Dylib and.framework are dynamic libraries
dyld:
Introduction to the
Dyld (The dynamic link editor) is a dynamic linker that links and loads programs. It is an important part of The MacOS and exists in The (/usr/lib/dyld) directory of The MacOS. After the application is compiled and packaged into an executable file format, DyLD is responsible for linking and loading the program. The overall process is as follows
dyld_shared_cache
Since more than one application needs to use the UIKit system dynamic library, it is impossible to load all the system dynamic libraries at every application load time. Since iOS3.1, apple has compiled all system libraries (private and public) into a large cache file called dyld_shared_cache. The cache file exists under the iOS/System/Library/Caches/com. Apple. Dyld/directory
Dyld loading process:
inload
Add a breakpoint to the function call stack/Use methodLLDB - bt
Command printing, you can see the original starting point_dyld_start
_dyld_start
The dyLDbootstrap ::start method is the original start method.
dyldbootstrap::start
Dyldbootstrap ::start is a C++ syntax, where dyldbootstrap represents the namespace and start represents the method in the namespace.
You can seestart
The core of this approach isdyld::main
dyld::main
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], Const char* envp[], const char* apple[], uintptr_t* startGlue // Make the cdHash of the main executable from the environment // make the cdHash of the main executable from the environment mainExecutableCDHashBuffer[20]; const uint8_t* mainExecutableCDHash = nullptr; if ( const char* mainExeCdHashStr = _simple_getenv(apple, "executable_cdhash") ) { unsigned bufferLenUsed; if ( hexStringToBytes(mainExeCdHashStr, mainExecutableCDHashBuffer, sizeof(mainExecutableCDHashBuffer), bufferLenUsed) ) mainExecutableCDHash = mainExecutableCDHashBuffer; } // Use the Mach-o header to get the current running architecture information getHostInfo(mainExecutableMH, mainExecutableSlide); Code omission...... // Check whether shared cache is enabled. // Load shared Cache checkSharedRegionDisable((DyLD3 ::MachOLoaded*)mainExecutableMH, mainExecutableSlide) must be enabled in iOS. if ( gLinkContext.sharedRegionMode ! = ImageLoader::kDontUseSharedRegion ) { #if TARGET_OS_SIMULATOR if ( sSharedCacheOverrideDir) mapSharedCache(mainExecutableSlide); #else // check whether the shared cache maps to the shared region mapSharedCache(mainExecutableSlide); #endif code omitted...... // load the executable file, Instantiate ImageLoader for main EXECUTABLE sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath); Code omission...... // Load any INSERTED Libraries if (senv. DYLD_INSERT_LIBRARIES! = NULL ) { for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib ! = NULL; ++lib) loadInsertedDylib(*lib); } code omitted...... Link (sMainExecutable, senV. DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1); Code omission...... // link any plugin libraries // do this after linking main executable so that any dylibs pulled in by inserted // dylibs (e.g. libSystem) will not be in front of dylibs the program uses if ( sInsertedDylibCount > 0 ) { for(unsigned int i=0; i < sInsertedDylibCount; ++i) { ImageLoader* image = sAllImages[i+1]; link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1); image->setNeverUnloadRecursive(); } if ( gLinkContext.allowInterposing ) { // only INSERTED libraries can interpose // register interposing info after all inserted libraries are bound so chaining works for(unsigned int i=0; i < sInsertedDylibCount; ++i) { ImageLoader* image = sAllImages[i+1]; image->registerInterposing(gLinkContext); }}} code omitted...... sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true); uint64_t bindMainExecutableEndTime = mach_absolute_time(); ImageLoaderMachO::fgTotalBindTime += bindMainExecutableEndTime - bindMainExecutableStartTime; gLinkContext.notifyBatch(dyld_image_state_bound, false); // Bind and notify for the inserted images now interposing has been registered if ( sInsertedDylibCount > 0 ) { for(unsigned int i=0; i < sInsertedDylibCount; ++i) { ImageLoader* image = sAllImages[i+1]; image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true, nullptr); }} code omitted...... // <rdar://problem/12186933> do weak binding only after all inserted images linked sMainExecutable->weakBind(gLinkContext); Code omission...... // Run all Initializers initializeMainExecutable(); Code omission...... Executable uses LC_UNIXTHREAD dyld needs to let "start" in program set up for main() result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD(); }#### 1 Configure environment variablesCopy the code
Through the above code analysis, the general process is as follows
1 Configure environment variables
- Platform, version, path, host information determination
- Gets the main executable from an environment variable
cdHash
checkEnvironmentVariables(envp)
Check setting environment variablesdefaultUninitializedFallbackPaths(envp)
inDYLD_FALLBACK
Sets the default value when nullgetHostInfo(mainExecutableMH, mainExecutableSlide)
Get program architecture
Xcode sets these two environment variable parameters. When App starts, relevant parameters and environment variable information will be printed as follows
2 Shared Cache
checkSharedRegionDisable
Check whether shared cache is enabled (mandatory in iOS)mapSharedCache
Load the shared cache library, which is calledloadDyldCache
There are several cases of functions:- Only the current process is loaded
mapCachePrivate
(Emulator only supports loading into current process) - The shared cache is loaded for the first time
mapCacheSystemWide
- The shared cache is not loaded for the first time, so nothing is done
- Only the current process is loaded
3 initialization of the main program
callinstantiateFromLoadedImage
The function instantiates oneImageLoader
objectthroughinstantiateMainExecutable
Method to createImageLoader
Instance objectsThe main idea here is to create an image of the main executable and return oneImageLoader
The type ofimage
Objects, that is,The main program
Of the.sniffLoadCommands
The function getsMach-O
Type fileLoad Command
And carry out all kinds of verification on it
4 Insert the dynamic library
traverseDYLD_INSERT_LIBRARIES
Environment variable, callloadInsertedDylib
Load, through the environment variable we can inject some custom dynamic library code to complete security attack and defense,loadInsertedDylib
From the internalDYLD_ROOT_PATH
,LD_LIBRARY_PATH
,DYLD_FRAMEWORK_PATH
Equal path searchdylib
It also checks the code signature and throws an exception if it is invalid
5 Link main program
5 Link Dynamic library
6 Weak symbol binding
7 Execute the initialization method
initializeMainExecutable
Source code is mainly circular traversal, will be executedrunInitializers
methods runInitializers(cons
The core code isprocessInitializers
Function call processInitializers
The mirror list function is calledrecursiveInitialization
Function is instantiated recursively recursiveInitialization
The function gets the initialization of the image. There are two core methodsnotifySingle
anddoInitialization
notifySingle
Delta function, the point is(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
This sentence sNotifyObjCInit
There is only assignment registerObjCNotifiers
Found in the_dyld_objc_notify_register
This function is only provided at run time toobjc
use 在objc4
Look in the source code_dyld_objc_notify_register
, found in_objc_init
The source code calls this method and passes in the parameters, sosNotifyObjCInit
Is assigned toobjc
In theload_images
So in conclusion,A notifySingle is a callback function
从load_images
As can be seen incall_load_methods
The method call call_load_methods
The core of the method is throughdo-while
Cycle callcall_class_loads
methods call_class_loads
This is calledThe load method
Is the classThe load method
So far it has been confirmedload_images
All of them are calledload
Function.
doInitialization
Two core methods are called indoImageInit
anddoModInitFunctions
doImageInit
The core of this isThe for loop
Load method call, one thing to note herelibSystem
Initialization must run first doModInitFunctions
Is loaded with allCxx
Files, one thing to notice herelibSystem
Initialization must run firstthroughdoImageInit
anddoModInitFunctions
Way to knowlibSystem
Initialization must be run first, where the stack information is checked for consistency libSystem
Initialization functions in the librarylibSystem_initializer
Call thelibdispatch_init
function libdispatch_init
Method is called_os_object_init
function _os_object_init
Method is called_objc_init
functionCombine the above analysis from initialization_objc_init
registered_dyld_objc_notify_register
Parameter 2 of, i.eload_images
And to theSNotifySingle --> sNotifyObjCInie= parameter 2
到sNotifyObjcInit()
Call, forming aThe closed loop
8 Look for the main program entry dyld
Assembly source code implementation