Application loading

Dynamic and static libraries

  • Libraries: Binary forms of executable code that are loaded into memory by the operating system
  • Libraries in iOS system are divided into two types: static libraries and dynamic libraries
  • Static library forms:.a and.framework
  • Dynamic library forms:.framework and.dylib
  • Static libraries: When linked, are copied completely into the executable, causing duplication
  • Dynamic library: when linking, it is loaded into the memory by the program, which is only loaded once and shared between programs. For example, libraries in the iOS system are basically dynamic libraries, which can save memory

  • Dynamic libraries need to check the signature in the packagedebugThe signature must containTeamIdentifierIf the verification fails, the system reportsimage not foundThe error
  • Can be achieved bycodesignTo verify
codesign -dv /path/to/YourApp.app
codesign -dv /path/to/youFramework.framework
Copy the code

The build process

  • To load the library into memory, you need to know the linker Apple usesdyld

What is a dyld

  • Full name: The Dynamic Link Editor, is apple’s dynamic link editor, is an important part of apple’s operating system, after the system kernel has prepared the program, dyLD is responsible for the rest of the work. And it’s open source, so anyone can download the source code on apple’s website to read and understand how it works, the source code
  • Link process

  • The app launched
  • dyldLoading system libraries
  • inobjc_intitRegistered in

  • Load the image recursively
  • Call main

Dyld process

Before the load method

  • We all know thatThe load of a classMethods in themainBefore delta function, so we have delta functionloadMethod to create a breakpointloadPrevious stack information

  • _dyld_start
  • dyldbootstrap::start
  • dyld::initializeMainExecuttale
  • ImageLoader::runInitalizers
  • ImageLoader::processInitaializers
  • ImageLoader::recursiveInitialization
  • dyld::notifySingle
  • load_images
  • +load

Objc_init before

  • You can see the whole process:

System – > _dyld_start – > dyldbootstrap: : start – > dyld: : _main – > dyld: : initializeMainExecuttale – > ImageLoader::runInitalizers -> ImageLoader::processInitaializers -> ImageLoader::recursiveInitialization -> imageLoaderMachO::doInitialization -> imageLoaderMachO::doModInitFunctions -> libSystem_initializer -> libdispatch_init -> os_object_init -> _objc_init -> (environ_init(), tls_init(), static_init(), “) _dyld_objc_notify_register ->

  • objc_initWith a_dyld_objc_notify_register(&map_images, load_images, unmap_image)registration
  • We are inobjc_initRegistered in thedyldNotice and putload_iamgesandmap_imagesTransfer the past

The flow chart

  • throughdyldFirst mapimage
  • Then the links are mapped recursivelyimageAll dependencies of
  • thenrebaseThe relocationimage, the memory segment of image is modified
  • thenbound, to the repositionedimageTo bind
  • And then recursively initialize itimage, ready to start initialization, needload_images, is calledruntimeSource,
  • Recursive loop initializationimageTo determine whether the initialization is performedlibsystemIf not, it will be initializedlibsystem.libdispatch.objc_init
  • And then call_dyld_objc_notify_registerThat will bemap_imagesandload_images.unmap_imagesAnd gave them todyldcall
  • As soon as you sign upmap_images

_dyld_start -> dyldbootstrap::start

  • Look at the very beginning of the entrance_dyld_startTake a look at the assembly code

  • Actually calleddyldthestart, then we can base ondyldThe source code to explore its specific process
  • Open the source code and finddyld_startIt is implemented in assembly, and it has different processing according to different CPU architectures, so I just took it out herearmCode under the architecture
#if __arm__
    .text
    .align 2
__dyld_start:
    mov	r8, sp		// save stack pointer
    sub	sp, #16		// make room for outgoing parameters
    bic     sp, sp, #15	// force 16-byte alignment

    // 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

    bl	__ZN13dyldbootstrap5startEPKN5dyld311MachOLoadedEiPPKcS3_Pm
    ldr	r5, [sp, #12]
    cmp	r5, #0
    bne	Lnew

    // traditional case, clean up stack and jump to result
    add	sp, r8, #4	// remove the mach_header argument.
    bx	r0		// jump to the program's entry point
Copy the code
  • The official notes are also clearly marked and will be calleddyldbootstrap::start.dyldbootstrapIt’s a namespace, so we can go directly to the correspondingC++code

dyldbootstrap::start

//
// 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>
    // Mark dyLD started
    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
     // Relocation fixes dyLD locations
     // Before the global variable
    rebaseDyld(dyldsMachHeader);

	// kernel sets up env pointer to be just past end of agv array
        // The kernel sets the env pointer to the end of the AGV array
	const char** envp = &argv[argc+1];
	
	// kernel sets up apple pointer to be just past end of envp array
        // The kernel sets the Apple pointer to the end of the ENVP array
	const char** apple = envp;
	while(*apple ! =NULL) { ++apple; }
	++apple;

	// set up random value for stack canary
        // Set the stack canary to a random value
	__guard_setup(apple);

#if DYLD_INITIALIZER_SUPPORT
	// run all C++ initializers inside dyld
        // run all c++ initializers in dyld
	runDyldInitializers(argc, argv, envp, apple);
#endif
      // lib.a is initialized
	_subsystem_init(apple);

	// now that we are done bootstrapping dyld, call dyld's main
        // When initialized, call main
	uintptr_t appsSlide = appsMachHeader->getSlide();
	return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}
Copy the code
  • instart,dyldA lot of initialization
  • Redirection fixeddyld
  • The last call uses the most important onemainfunction

Dyld: : _main function

  • Into themainFunction, found that the code in the function is very long, difficult to read
  • You can focus on the return valueresult, go up and findresultWhere the value is assigned, in order to understand
  • throughresultAnd found thatsMainExecutableIt’s connected to it, so you can explore itsMainExecutableThis variable, literally, isThe main program
  • To find the firstsMainExecutableThe initialization place
static MainExecutablePointerType	sMainExecutable = NULL;

sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
Copy the code
  • addDyldImageToUUIDList();Add dyld Image to the UUID list
  • mapSharedCache(mainExecutableSlide);Loading the Shared cache
  • loadInsertedDylib(*lib);Loading the Dynamic library
  • sMainExecutable->rebase(gLinkContext, -mainExecutableSlide);relocation
  • sMainExecutable->weakBind(gLinkContext);Weak binding only after all insert image links, ` sMainExecutable – > recursiveMakeDataReadOnly (gLinkContext);
  • initializeMainExecutable(); Initialize all
  • notifyMonitoringDyldMain();The notification process is about to entermain()function
  • reuslt = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();, set the entrance tomain()function

The main conclusion

  • Setting up the Operating Environment
  • Loading the Shared cache
  • Instantiate the main program
  • Loading the Dynamic library
  • Weakly bound main program
  • Link to the inserted image
  • Perform initialization
  • Tell the process to execute main()
  • Return to execution entrymain()

dyld::main() -> initializeMainExecutable

void initializeMainExecutable(a)
{
	// 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();
        // Get the number of image files, iterate through initialization
	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 
        // Initializes the main program and all its associated properties
	sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
	
	// register cxa_atexit() handler to run static terminators in all loaded images when this process exits
        / / registered cxa_atexit ()
	if( gLibSystemHelpers ! =NULL ) 
		(*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL.NULL);

	// dump info if requested
        // Dump information
	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
  • You can see what’s going to be called internallyrunInitializers

initializeMainExecutable -> runInitializers

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() };
    (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

runInitializers -> notifyBatch

static void notifyBatch(dyld_image_states state, bool preflightOnly)
{
	notifyBatchPartial(state, false, NULL, preflightOnly, false);
}
Copy the code

runInitializers -> processInitializers(

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) {
        // Start recursive initialization
		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

processInitializers -> recursiveInitialization

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(a); ++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
                        // Record the termination menu
			if ( this->needsTermination() )
				context.terminationRecorder(this);

			// let objc know we are about to initialize this image
                        // Let the object know that we are going to initialize the image
			uint64_t t1 = mach_absolute_time(a); fState = dyld_image_state_dependents_initialized; oldState = fState; context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
			
			// initialize this image
                        // Initialize the image
			bool hasInitializers = this->doInitialization(context);

			// let anyone know we finished initializing this image
                        // Notification has finished initialization
			fState = dyld_image_state_initialized;
			oldState = fState;
			context.notifySingle(dyld_image_state_initialized, this.NULL);
			
			if ( hasInitializers ) {
				uint64_t t2 = mach_absolute_time(a); timingInfo.addTime(this->getShortName(), t2-t1); }}catch (const char* msg) {
			// this image is not initialized
			fState = oldState;
			recursiveSpinUnLock(a);throw; }}recursiveSpinUnLock(a); }Copy the code
  • dyld_image_state_dependents_initializedTo informobjcTo initializeimage
  • doInitializationInitialize theimage
  • dyld_image_state_initializedTo informobjcInitialization is completeimage