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:

inloadAdd a breakpoint to the function call stack/Use methodLLDB - btCommand 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 seestartThe 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 variablecdHash
  • checkEnvironmentVariables(envp)Check setting environment variables
  • defaultUninitializedFallbackPaths(envp)inDYLD_FALLBACKSets the default value when null
  • getHostInfo(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

  • checkSharedRegionDisableCheck whether shared cache is enabled (mandatory in iOS)
  • mapSharedCacheLoad the shared cache library, which is calledloadDyldCacheThere are several cases of functions:
    • Only the current process is loadedmapCachePrivate(Emulator only supports loading into current process)
    • The shared cache is loaded for the first timemapCacheSystemWide
    • The shared cache is not loaded for the first time, so nothing is done

3 initialization of the main program

callinstantiateFromLoadedImageThe function instantiates oneImageLoaderobjectthroughinstantiateMainExecutableMethod to createImageLoaderInstance objectsThe main idea here is to create an image of the main executable and return oneImageLoaderThe type ofimageObjects, that is,The main programOf the.sniffLoadCommandsThe function getsMach-OType fileLoad CommandAnd carry out all kinds of verification on it

4 Insert the dynamic library

traverseDYLD_INSERT_LIBRARIESEnvironment variable, callloadInsertedDylibLoad, through the environment variable we can inject some custom dynamic library code to complete security attack and defense,loadInsertedDylibFrom the internalDYLD_ROOT_PATH,LD_LIBRARY_PATH,DYLD_FRAMEWORK_PATHEqual path searchdylibIt 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

initializeMainExecutableSource code is mainly circular traversal, will be executedrunInitializersmethods runInitializers(consThe core code isprocessInitializersFunction call processInitializersThe mirror list function is calledrecursiveInitializationFunction is instantiated recursively recursiveInitializationThe function gets the initialization of the image. There are two core methodsnotifySingleanddoInitialization notifySingleDelta function, the point is(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());This sentence sNotifyObjCInitThere is only assignment registerObjCNotifiersFound in the_dyld_objc_notify_registerThis function is only provided at run time toobjcuseobjc4Look in the source code_dyld_objc_notify_register, found in_objc_initThe source code calls this method and passes in the parameters, sosNotifyObjCInitIs assigned toobjcIn theload_imagesSo in conclusion,A notifySingle is a callback function load_imagesAs can be seen incall_load_methodsThe method call call_load_methodsThe core of the method is throughdo-whileCycle callcall_class_loadsmethods call_class_loadsThis is calledThe load methodIs the classThe load method So far it has been confirmedload_imagesAll of them are calledloadFunction.

doInitializationTwo core methods are called indoImageInitanddoModInitFunctions doImageInitThe core of this isThe for loopLoad method call, one thing to note herelibSystemInitialization must run first doModInitFunctionsIs loaded with allCxxFiles, one thing to notice herelibSystemInitialization must run firstthroughdoImageInitanddoModInitFunctionsWay to knowlibSystemInitialization must be run first, where the stack information is checked for consistency libSystemInitialization functions in the librarylibSystem_initializerCall thelibdispatch_initfunction libdispatch_initMethod is called_os_object_initfunction _os_object_initMethod is called_objc_initfunctionCombine the above analysis from initialization_objc_initregistered_dyld_objc_notify_registerParameter 2 of, i.eload_imagesAnd to theSNotifySingle --> sNotifyObjCInie= parameter 2sNotifyObjcInit()Call, forming aThe closed loop

8 Look for the main program entry dyldAssembly source code implementation

dyldLoading process