Introduction: How does iOS app run on the operating system? inmainWhat did the program do before the function?

On iOS, when the program is compiled, it generates a Mach-O file, which is stored on disk. In order for a program to work, it has to be loaded into memory, and the program is just a bunch of binary data. Only by loading data from disk into memory can a program run. On iOS, the Mach-O file needs to be loaded into memory in order for the app to run on the operating system. So how does the operating system read Mach-O into memory? This is dyld.

IOS program loading process

Our main function is the entry point to our program, so we can look at the breakpoint and see what we did before main, okay?

Through the breakpoint, you can find themainThere was one beforestartA function call

You can see it herestartFunction fromlibdyld.dylibTry symbolic breakpoints here, look at the call stack before start, and you’ll see that symbolic breakpoints don’t work. We can try to do it in+loadMethod, because the load method takes too long to execute.

Print the call stack using BT.

* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x000000010A774E17 002- Application load analysis' +[ViewController Load](self=ViewController, _cmd="load") at ViewController.m:17:5 frame #1: 0x00007fff201804e3 libobjc.A.dylib`load_images + 1442 frame #2: 0x000000010a788e54 dyld_sim`dyld::notifySingle(dyld_image_states, ImageLoader const*, ImageLoader::InitializerTimingList*) + 425 frame #3: 0x000000010a797887 dyld_sim`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 437 frame #4: 0x000000010a795bb0 dyld_sim`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 188 frame #5: 0x000000010a795c50 dyld_sim`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 82 frame #6: 0x000000010a7892a9 dyld_sim`dyld::initializeMainExecutable() + 199 frame #7: 0x000000010a78dd50 dyld_sim`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 4431 frame #8: 0x000000010a7881c7 dyld_sim`start_sim + 122 frame #9: 0x000000010f982a88 dyld`dyld::useSimulatorDyld(int, macho_header const*, char const*, int, char const**, char const**, char const**, unsigned long*, unsigned long*) + 2093 frame #10: 0x000000010f980162 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 1198 frame #11: 0x000000010f97a224 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 450 frame #12: 0x000000010f97a025 dyld`_dyld_start + 37Copy the code

_dyld_start -> dyldbootstrap::start -> dyld::_main -> dyld::useSimulatorDyld -> start_sim -> dyld::_main -> dyld::initializeMainExecutable -> mageLoader::runInitializers -> ImageLoader::processInitializers -> ImageLoader::recursiveInitialization -> dyld::notifySingle -> load_images. This is the flow of the simulator. As you can see, load comes before main, and then load comes before dyLD. We can download the dyLD open source code to view the implementation process according to the stack information.

Dyld source code analysis

By looking for_dyld_startFound in the assembly implementation, found in the commentsdyldbootstrap::startThrough thedyldbootstrapNamespace, findstartimplementation

// This is code to bootstrap dyld. This work in normally done for a program 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 tracepoint to indicate dyld bootstrap has started <rdar://46878536> dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0); // if kernel had to slide dyld, we need to fix up load sensitive locations // we have to do this before using any global variables rebaseDyld(dyldsMachHeader); // kernel sets up env pointer to be just past end of agv array const char** envp = &argv[argc+1]; // kernel sets up apple pointer to be just past end of envp array const char** apple = envp; while(*apple ! = NULL) { ++apple; } ++apple; // set up random value for stack canary __guard_setup(apple); #if DYLD_INITIALIZER_SUPPORT // run all C++ initializers inside dyld runDyldInitializers(argc, argv, envp, apple); #endif _subsystem_init(apple); // now that we are done bootstrapping dyld, call dyld's main uintptr_t appsSlide = appsMachHeader->getSlide(); return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); }Copy the code

In combination with the comments in the start function, we have a rough idea of doing some initialization work internally, such as setting the random offset of the virtual address space and running the dyLD internal initializer. Finally, dyld::_main is called. AppsSlide gets the address of the Mach-O file that needs to be loaded. As you can see, the Main program’s Mach-O is loaded first.

// // Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which // sets up some registers and call this function. // // Returns address of main() in target program which __dyld_start jumps to // uintptr_t _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], Const char* apple[], uintptr_t* startGlue) {close to 900+ lines of code, most of which is code to get and set up the environment.Copy the code

**getHostInfo(mainExecutableMH, mainExecutableSlide); ** Gets the main program architecture, environment variables.

uintptr_t result = 0;
sMainExecutableMachHeader = mainExecutableMH;
sMainExecutableSlide = mainExecutableSlide;

setContext(mainExecutableMH, argc, argv, envp, apple);
// Pickup the pointer to the exec path.
	sExecPath = _simple_getenv(apple, "executable_path");

Copy the code

Set the context information, and the mach-O file path.

configureProcessRestrictions(mainExecutableMH, envp); The corresponding processing limits are set based on environment variables.

// Check if we should force dyld3. Note we have to do this outside of the regular env parsing due to AMFI if ( dyld3::internalInstall() ) { if (const char* useClosures = _simple_getenv(envp, "DYLD_USE_CLOSURES")) { if ( strcmp(useClosures, "0") == 0 ) { sClosureMode = ClosureMode::Off; } else if ( strcmp(useClosures, "1") == 0 ) { #if ! __i386__ // don't support dyld3 for 32-bit macOS sClosureMode = ClosureMode::On; sClosureKind = ClosureKind::full; #endif } else if ( strcmp(useClosures, "2") == 0 ) { sClosureMode = ClosureMode::On; sClosureKind = ClosureKind::minimal; } else { dyld::warn("unknown option to DYLD_USE_CLOSURES. Valid options are: 0 and 1\n"); } } } // Check if we should force the shared cache __DATA_CONST to read-only or read-write if ( dyld3::BootArgs::forceReadWriteDataConst() ) { gEnableSharedCacheDataConst = false; } else if ( dyld3::BootArgs::forceReadOnlyDataConst() ) { gEnableSharedCacheDataConst = true; } else { // __DATA_CONST is enabled by default for arm64(e) for now #if __arm64__ && __LP64__ gEnableSharedCacheDataConst = true; #else gEnableSharedCacheDataConst = false; #endif } bool sharedCacheDataConstIsEnabled = gEnableSharedCacheDataConst; if ( dyld3::internalInstall() ) { if (const char* dataConst = _simple_getenv(envp, "DYLD_SHARED_REGION_DATA_CONST")) { if ( strcmp(dataConst, "RW") == 0 ) { gEnableSharedCacheDataConst = false; } else if ( strcmp(dataConst, "RO") == 0 ) { gEnableSharedCacheDataConst = true; } else { dyld::warn("unknown option to DYLD_SHARED_REGION_DATA_CONST. Valid options are: RW and RO\n"); }}}Copy the code

You can see from the comments that dyLD3 requires additional operations. For example, read and write permissions to ClosureMode, ClosureKind, and shared cache.

checkEnvironmentVariables(envp);
defaultUninitializedFallbackPaths(envp);
Copy the code

Check environment variables to see if third library loading is disabled.

//load shared cache
**checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide); 
Copy the code

Check whether the shared cache is disabled.

// instantiate ImageLoader for main executable
		sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
		gLinkContext.mainExecutable = sMainExecutable;
		gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
Copy the code

Instantiate the main program.

// The kernel maps in main executable before dyld gets control.  We need to 
// make an ImageLoader* for the already mapped in main executable.
static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path)
{
	// try mach-o loader
//	if ( isCompatibleMachO((const uint8_t*)mh, path) ) {
		ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
		addImage(image);
		return (ImageLoaderMachO*)image;
//	}
	
//	throw "main executable not a known format";
}
Copy the code

Instantiate the main program, create ImageLoader*, and add it to allImages

// create image for main executable
ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context)
{
	//dyld::log("ImageLoader=%ld, ImageLoaderMachO=%ld, ImageLoaderMachOClassic=%ld, ImageLoaderMachOCompressed=%ld\n",
	//	sizeof(ImageLoader), sizeof(ImageLoaderMachO), sizeof(ImageLoaderMachOClassic), sizeof(ImageLoaderMachOCompressed));
	bool compressed;
	unsigned int segCount;
	unsigned int libCount;
	const linkedit_data_command* codeSigCmd;
	const encryption_info_command* encryptCmd;
	sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
	// instantiate concrete class based on content of load commands
	if ( compressed ) 
		return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context);
	else
#if SUPPORT_CLASSIC_MACHO
		return ImageLoaderMachOClassic::instantiateMainExecutable(mh, slide, path, segCount, libCount, context);
#else
		throw "missing LC_DYLD_INFO load command";
#endif
}
Copy the code

Instantiate the implementation of the main program image, with parameters corresponding to the structure of the Mach-O file.

link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1); Link main program

// link any inserted 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); } } } if ( gLinkContext.allowInterposing ) { // <rdar://problem/19315404> dyld should support interposition even without  DYLD_INSERT_LIBRARIES for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) { ImageLoader* image = sAllImages[i]; if ( image->inSharedCache() ) continue; image->registerInterposing(gLinkContext); }}Copy the code

After the link main program is complete, the link function is looping to insert the dynamic library. For example, the system’s libSystem

// Bind and notify for the main executable now that interposing has been registered uint64_t bindMainExecutableStartTime  = mach_absolute_time(); 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); } } // <rdar://problem/12186933> do weak binding only after all inserted images linked sMainExecutable->weakBind(gLinkContext);Copy the code

Weak binding occurs when all dependent libraries are linked.

// run all initializers
initializeMainExecutable(); 
Copy the code

Run the initialization of the main program.

void initializeMainExecutable() { // record that we've reached this step gLinkContext.startedInitializingMainExecutable = true; // run initialzers for any inserted dylibs ImageLoader::InitializerTimingList initializerTimes[allImagesCount()]; initializerTimes[0].count = 0; const size_t rootCount = sImageRoots.size(); if ( rootCount > 1 ) { for(size_t i=1; i < rootCount; ++i) { sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]); } } // run initializers for main executable and everything it brings up sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]); // register cxa_atexit() handler to run static terminators in all loaded images when this process exits if ( gLibSystemHelpers ! = NULL ) (*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL, NULL); // dump info if requested if ( sEnv.DYLD_PRINT_STATISTICS ) ImageLoader::printStatistics((unsigned int)allImagesCount(),  initializerTimes[0]); if ( sEnv.DYLD_PRINT_STATISTICS_DETAILS ) ImageLoaderMachO::printStatisticsDetails((unsigned int)allImagesCount(), initializerTimes[0]); }Copy the code

The inner loop executes the dynamic library image initializer.

void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo)
{
	uint64_t t1 = mach_absolute_time();
	mach_port_t thisThread = mach_thread_self();
	ImageLoader::UninitedUpwards up;
	up.count = 1;
	up.imagesAndPaths[0] = { this, this->getPath() };
	processInitializers(context, thisThread, timingInfo, up);
	context.notifyBatch(dyld_image_state_initialized, false);
	mach_port_deallocate(mach_task_self(), thisThread);
	uint64_t t2 = mach_absolute_time();
	fgTotalInitTime += (t2 - t1);
}
Copy the code
// <rdar://problem/14412057> upward dylib initializers can be run too soon // To handle dangling dylibs which are upward  linked but not downward, all upward linked dylibs // have their initialization postponed until after the recursion through downward dylibs // has  completed. void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread, InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images) { uint32_t maxImageCount = context.imageCount()+2; ImageLoader::UninitedUpwards upsBuffer[maxImageCount]; ImageLoader::UninitedUpwards& ups = upsBuffer[0]; ups.count = 0; // Calling recursive init on all images in images list, building a new list of // uninitialized upward dependencies. for (uintptr_t i=0; i < images.count; ++i) { images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups); } // If any upward dependencies remain, init them. if ( ups.count > 0 ) processInitializers(context, thisThread, timingInfo, ups); }Copy the code

RecursiveInitialization recursiveInitialization dependency.

void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize, InitializerTimingList& timingInfo, UninitedUpwards& uninitUps) { recursive_lock lock_info(this_thread); recursiveSpinLock(lock_info); if ( fState < dyld_image_state_dependents_initialized-1 ) { uint8_t oldState = fState; // break cycles fState = dyld_image_state_dependents_initialized-1; try { // initialize lower level libraries first for(unsigned int i=0; i < libraryCount(); ++i) { ImageLoader* dependentImage = libImage(i); if ( dependentImage ! = NULL ) { // don't try to initialize stuff "above" me yet if ( libIsUpward(i) ) { uninitUps.imagesAndPaths[uninitUps.count] = { dependentImage, libPath(i) }; uninitUps.count++; } else if ( dependentImage->fDepth >= fDepth ) { dependentImage->recursiveInitialization(context, this_thread, libPath(i), timingInfo, uninitUps); } } } // record termination order if ( this->needsTermination() ) context.terminationRecorder(this); // let objc know we are about to initialize this image uint64_t t1 = mach_absolute_time(); fState = dyld_image_state_dependents_initialized; oldState = fState; context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo); // initialize this image bool hasInitializers = this->doInitialization(context); // let anyone know we finished initializing this image fState = dyld_image_state_initialized; oldState = fState; context.notifySingle(dyld_image_state_initialized, this, NULL); if ( hasInitializers ) { uint64_t t2 = mach_absolute_time(); timingInfo.addTime(this->getShortName(), t2-t1); } } catch (const char* msg) { // this image is not initialized fState = oldState; recursiveSpinUnLock(); throw; } } recursiveSpinUnLock(); }Copy the code

context.notifySingle(dyld_image_state_initialized, this, NULL); Execute the callback that completes initialization.

static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo) { //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath()); std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sSingleHandlers); if ( handlers ! = NULL ) { dyld_image_info info; info.imageLoadAddress = image->machHeader(); info.imageFilePath = image->getRealPath(); info.imageFileModDate = image->lastModified(); for (std::vector<dyld_image_state_change_handler>::iterator it = handlers->begin(); it ! = handlers->end(); ++it) { const char* result = (*it)(state, 1, &info); if ( (result ! = NULL) && (state == dyld_image_state_mapped) ) { //fprintf(stderr, " image rejected by handler=%p\n", *it); // make copy of thrown string so that later catch clauses can free it const char* str = strdup(result); throw str; } } } if ( state == dyld_image_state_mapped ) { // <rdar://problem/7008875> Save load addr + UUID for images from outside the shared cache // <rdar://problem/50432671> Include UUIDs for shared cache dylibs in all image info when using  private mapped shared caches if (! image->inSharedCache() || (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion)) { dyld_uuid_info info; if ( image->getUUID(info.imageUUID) ) { info.imageLoadAddress = image->machHeader(); addNonSharedCacheImageUUID(info); } } } if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit ! = NULL) && image->notifyObjC() ) { uint64_t t0 = mach_absolute_time(); dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0); (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); uint64_t t1 = mach_absolute_time(); uint64_t t2 = mach_absolute_time(); uint64_t timeInObjC = t1-t0; uint64_t emptyTime = (t2-t1)*100; if ( (timeInObjC > emptyTime) && (timingInfo ! = NULL) ) { timingInfo->addTime(image->getShortName(), timeInObjC); } } // mach message csdlc about dynamically unloaded images if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) { notifyKernel(*image, false); const struct mach_header* loadAddress[] = { image->machHeader() }; const char* loadPath[] = { image->getPath() }; notifyMonitoringDyld(true, 1, loadAddress, loadPath); }}Copy the code

*(sNotifyObjCInit)(image->getRealPath(), image->machHeader()); Determine the dependent image file initialization status and whether sNotifyObjCInit is null, then call sNotifyObjCInit,

/ _dyld_objc_notify_init
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
	// record functions to call
	sNotifyObjCMapped	= mapped;
	sNotifyObjCInit		= init;
	sNotifyObjCUnmapped = unmapped;

	// call 'mapped' function with all images mapped so far
	try {
		notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
	}
	catch (const char* msg) {
		// ignore request to abort during registration
	}

	// <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
	for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
		ImageLoader* image = *it;
		if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
			dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
			(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
		}
	}
}

Copy the code

Found assigning sNotifyObjCInit in the registerObjCNotifiers method

// _dyld_objc_notify_register
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped)
{
	dyld::registerObjCNotifiers(mapped, init, unmapped);
}
Copy the code

_dyLD_OBJC_notify_register calls the registerObjCNotifiers

This method is actually called from the objC dynamic library

/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/

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

Question: How do I jump from DYLD to OBJC?

Run objC source code, set breakpoints in _objc_init and look at the stack to see how the call is made.

The original is inlibdispatch.dylibCalled in the dynamic library_os_object_init.

void libdispatch_init(void) { dispatch_assert(sizeof(struct dispatch_apply_s) <= DISPATCH_CONTINUATION_SIZE); if (_dispatch_getenv_bool("LIBDISPATCH_STRICT", false)) { _dispatch_mode |= DISPATCH_MODE_STRICT; } #if DISPATCH_DEBUG || DISPATCH_PROFILE #if DISPATCH_USE_KEVENT_WORKQUEUE if (getenv("LIBDISPATCH_DISABLE_KEVENT_WQ")) { _dispatch_kevent_workqueue_enabled = false; } #endif #endif #if HAVE_PTHREAD_WORKQUEUE_QOS dispatch_qos_t qos = _dispatch_qos_from_qos_class(qos_class_main()); _dispatch_main_q.dq_priority = _dispatch_priority_make(qos, 0); #if DISPATCH_DEBUG if (! getenv("LIBDISPATCH_DISABLE_SET_QOS")) { _dispatch_set_qos_class_enabled = 1; } #endif #endif #if DISPATCH_USE_THREAD_LOCAL_STORAGE _dispatch_thread_key_create(&__dispatch_tsd_key, _libdispatch_tsd_cleanup); #else _dispatch_thread_key_create(&dispatch_priority_key, NULL); _dispatch_thread_key_create(&dispatch_r2k_key, NULL); _dispatch_thread_key_create(&dispatch_queue_key, _dispatch_queue_cleanup); _dispatch_thread_key_create(&dispatch_frame_key, _dispatch_frame_cleanup); _dispatch_thread_key_create(&dispatch_cache_key, _dispatch_cache_cleanup); _dispatch_thread_key_create(&dispatch_context_key, _dispatch_context_cleanup); _dispatch_thread_key_create(&dispatch_pthread_root_queue_observer_hooks_key, NULL); _dispatch_thread_key_create(&dispatch_basepri_key, NULL); #if DISPATCH_INTROSPECTION _dispatch_thread_key_create(&dispatch_introspection_key , NULL); #elif DISPATCH_PERF_MON _dispatch_thread_key_create(&dispatch_bcounter_key, NULL); #endif _dispatch_thread_key_create(&dispatch_wlh_key, _dispatch_wlh_cleanup); _dispatch_thread_key_create(&dispatch_voucher_key, _voucher_thread_cleanup); _dispatch_thread_key_create(&dispatch_deferred_items_key, _dispatch_deferred_items_cleanup); #endif pthread_key_create(&_os_workgroup_key, _os_workgroup_tsd_cleanup); #if DISPATCH_USE_RESOLVERS // rdar://problem/8541707 _dispatch_main_q.do_targetq = _dispatch_get_default_queue(true); #endif _dispatch_queue_set_current(&_dispatch_main_q); _dispatch_queue_set_bound_thread(&_dispatch_main_q); #if DISPATCH_USE_PTHREAD_ATFORK (void)dispatch_assume_zero(pthread_atfork(dispatch_atfork_prepare, dispatch_atfork_parent, dispatch_atfork_child)); #endif _dispatch_hw_config_init(); _dispatch_time_init(); _dispatch_vtable_init(); _os_object_init(); _voucher_init(); _dispatch_introspection_init(); }Copy the code

Call _os_object_init() from libdispatch_init

// notify any montoring proccesses that this process is about to enter main()
		notifyMonitoringDyldMain();
Copy the code

Jump to main program entry

_DYLD_START -> DYLDbootstrap ::start -> DYLD ::_main

Dyld ::_main

  1. Environment Variable Configuration
  2. Shared cache
  3. The main program initializes
  4. Insert dynamic library
  5. Link main program
  6. Linked dynamic library
  7. Weak binding
  8. Perform initialization
  9. Jump to main program entry main