Hi 👋

  • Wechat: RyukieW
  • 📦 Archive of technical articles
  • 🐙 making
My personal project Minesweeper Elic Endless Ladder Dream of books
type The game financial
AppStore Elic Umemi

This article is based on dyLD-832.7.3 and objC4-818.2 source code

  • Series of articles:
    • [iOS app startup (I)] Dyld and main function
    • [iOS App Startup (2)] Environment configuration and Runtime initialization
    • [iOS app Startup (3)] Image file reading and loading
    • The process of realizing and initialize a Class is illustrated
    • [iOS app launch (4)] Classified loading

preface

Each application relies on many libraries, and every time the application is started, the executable files in MachO are loaded into memory.

So how does this process work?

I. Entry point

The entry to the application is main, so what did main do before that?

We make a breakpoint at main:

(LLDB) thread #1, queue = 'com.apple.main-thread', stop reason = breakPoint 1.1 * frame #0: 0x00000001001f9ef8 ObjcMsgSend`main(argc=1, argv=0x000000016fc0b878) at main.m:17:50 frame #1: 0x00000001a2d56140 libdyld.dylib`start + 4Copy the code

Found no useful information. Try the breakpoint before Main.

The pointcut _dyLD_START is found by analyzing the stack information

Dyld – dynamic linker

2.1 _dyld_start

The corresponding implementation was found in dyLD source code. For assembly code, we found an important comment:

dyldbootstrap::start

Dyld ::_main = dyld::_main = dyld::_main

2.2 dyld: : _main

Over a thousand lines of code. It’s important.

We can also see from the comments:

  • Here is thedyldThe entrance of the
  • Finally return the programmain()function

Because the code is very long, I have made some comments about the key points so that you can look at the source code

Source code process annotation contrast

  • As long as these two environment variable parameters are set, relevant parameters and environment variable information will be printed when App starts

  • Loading the Shared cache

  • Initialize ImageLoader for the main program

  • Now that the shared cache is loaded, set up versioned dylib overrides

  • Load all the inserted libraries and add the jailbreak plugin here
  • Keeping track of the number of libraries that have been inserted so that a uniform search will look at the inserted libraries first, then main, then other.

  • Link main program

  • Link all inserted libraries. Do this after linking the main executable so that the inserted dylib (such as libSystem) does not come before the dylib used by the program

  • Only inserted libraries can be inserted. After binding all the inserted libraries, register the insertion information for linking to work

  • Bind and notify the main executable that the inserted file is now registered
  • Bind and notify that the inserted image that is now inserted is registered

  • Perform all initialization methods
  • Notify any monitoring procedure that this procedure is about to enter main()

  • Find the main pointer

2.3 sMainExecutable

As can be seen from the process analysis above, the main function pointer for the final return comes from the sMainExecutable.

The initialization place of the sMainExecutable can be located

2.4 initializeMainExecutable initializes the main program

Core source code and process notes

void initializeMainExecutable(a)
{
#pragmaMark-ryukie, let's make a note. Initialization is underway
	// record that we've reached this step
	gLinkContext.startedInitializingMainExecutable = true;
#pragmaMark-ryukie initializes all inserted libraries
	// run initialzers for any inserted dylibs
	ImageLoader::InitializerTimingList initializerTimes[allImagesCount()];
	initializerTimes[0].count = 0;
	const size_t rootCount = sImageRoots.size(a);if ( rootCount > 1 ) {
		for(size_t i=1; i < rootCount; ++i) {
			sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]); }}#pragmaMark-ryukie runs the initializer of the main executable and its dependencies
	// run initializers for main executable and everything it brings up 
	sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]); . }Copy the code

This can be verified with the stack information at the +load breakpoint:

This is where the main process ends, and the detailed process will continue in a later article

3. Summary & flow chart

  • Program execution from_dyld_startstart
  • Enter thedyld_mainfunction
  • Configure environment variables as wellrebase_dyld
  • loadingShared cache
    • Dynamic library of the systemIt’s all here
  • dyld2/dyld(ClosureMode)Decide which mode to proceed with
  • Instantiate the main programImageLoaderMachOAnd join theallImagesIn the
  • To thisShared cacheAfter loading, set versioneddylibcover
    • Now that shared cache is loaded, setup an versioned dylib overrides
  • Load the inserted library
    • Prison break pluginJoin in here
  • Keeping track of the number of libraries that have been inserted so that a uniform search will look at the inserted libraries first, then main, then other.
    • record count of inserted libraries so that a flat search will look at inserted libraries, then main, then others.
  • Link main program
    • Binding symbols (non lazy loading, weak symbols), etc
  • Link all inserted libraries
    • Do this after linking the main executable so that the inserted dylib (such as libSystem) does not come before the dylib used by the program
    • Only inserted libraries can be inserted. After binding all the inserted libraries, register the insertion information for linking to work
  • Bind and notifyMain program executable file, now the inserted is registered
  • Bind and notifyThe library that is now insertedThe inserted Image has been registered
  • Execute all initialization methods:ImageLoader
    • Initialize all inserted libraries, initializers that run the main executable, and their dependencies
    • ImageLoader:
      • runInitializers:
        • processInitializers:
          • ecursiveInitialization:
            • notifySingle:
              • This function performs a callback, which is_objc_initClass as a function of the clothingload_images
                • load_imagesIn the implementationclass_load_metgodsfunction
                  • class_load_metgodsIn the callcall_class_loadsFunction: loop calls to each classloadfunction
            • doModInitFunction
              • The constructor of the global C++ object is called internally__attribute__((constructor))The C function
  • Notify any monitoring process that this process is about to entermain()
  • getmain()Pointer andreturn