IOS dyld loading process (preliminary discussion)

Time:2021-12-30

Compilation process of IOS app code

  • source file: load h、. m、. CPP and other documents
  • Pretreatment: replace macros, delete comments, expand header files, and generate I file
  • compile: will I file is converted into assembly language to generate S file
  • assembly: convert assembly file into machine code file to generate O documentation
    link: Yes O reference other libraries in the file to generate the final executable file
IOS dyld loading process (preliminary discussion)

image.png

Static library

  • In the link phase, the static library will link and package the target program generated by the assembly and the referenced library into the executable file. At this time, the static library will not be changed, because it is copied directly to the target program during compilation
  • Benefits: after compilation, the library file is actually useless. The target program has no external dependencies and can be run directly
  • Disadvantages: since there will be two static libraries, it will increase the volume of the target program and consume a lot of memory, performance and speed

Dynamic library

The dynamic library is not linked to the target program when the program is compiled. The target program only stores references to the dynamic library and is loaded when the program is running

  • Advantages:
    1. Reduce the size of the app after packaging: because it does not need to be copied to the target program, it will not affect the volume of the target program. Compared with the static library, it reduces the size of the app.
    2. Share memory and save resources: the same library can be used by multiple programs. The purpose of updating the program is achieved by updating the dynamic library: due to the feature of loading only at runtime, the library can be replaced at any time without recompiling the code

  • Disadvantages: dynamic loading will bring some performance loss. Using dynamic library will also make the program dependent on the external environment. If the environment lacks dynamic library or the library version is incorrect, the program will not run

dyld

  • Dyld (the dynamic link editor) is Apple’s dynamic linker and an important part of Apple’s operating system. After the app is compiled and packaged into mach-o file in executable file format, dyld is responsible for connecting and loading the program.

    The startup flow chart of app is as follows:

    IOS dyld loading process (preliminary discussion)

    image.png
IOS dyld loading process (preliminary discussion)

image.png

1、__dyld_start:
2、dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)

uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[],
                const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue){

    dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);
    rebaseDyld(dyldsMachHeader);
    const char** envp = &argv[argc+1];
    const char** apple = envp;
    while(*apple != NULL) { ++apple; }
    ++apple;

    __guard_setup(apple);

#if DYLD_INITIALIZER_SUPPORT
    runDyldInitializers(argc, argv, envp, apple);
#endif
    _subsystem_init(apple);
    uintptr_t appsSlide = appsMachHeader->getSlide();
    return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}

3、dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);

uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, 
        int argc, const char* argv[], const char* envp[], const char* apple[], 
        uintptr_t* startGlue)
{
    //Get the cdhash of the main executable from the environment
    // Grab the cdHash of the main executable from the environment
    uint8_t 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;
    }
    getHostInfo(mainExecutableMH, mainExecutableSlide);// Gets information about the current running environment schema
    //[step 1: environment variable configuration]
    checkEnvironmentVariables(envp);// Check the set environment variables
    defaultUninitializedFallbackPaths(envp);// (dyld_fallback_library_path is nil, set the default value for it)
    

    //[step 2: shared cache]
    checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);// Check whether the shared cache is turned on (it must be turned on in IOS)
    mapSharedCache(mainExecutableSlide);// Check whether the shared cache is mapped to the shared area


    //[step 3: initialization of main program] instantiate imageloader for main executable
    sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);// Load the executable and generate an imageloader instance object
    gLinkContext.mainExecutable = sMainExecutable;
    gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);


    //[step 4: insert dynamic libraries] load any inserted Libraries
    if  ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
        //Load all dyld_ INSERT_ Libraries specifies the library
        //Traverse dyld_ INSERT_ Libraries environment variable, call loadinserteddylib to load
        for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
            loadInsertedDylib(*lib);
    }
    //Record count of inserted libraries so that a flat search will look at
    // inserted libraries, then main, then others. (insert stock in, then main, then others)
    sInsertedDylibCount = sAllImages.size()-1;
    // link main executable
    gLinkContext.linkingMainExecutable = true;
#if SUPPORT_ACCELERATE_TABLES
    if ( mainExcutableAlreadyRebased ) {
        // previous link() on main executable has already adjusted its internal pointers for ASLR
        // work around that by rebasing by inverse amount
        sMainExecutable->rebase(gLinkContext, -mainExecutableSlide);
    }
#endif
        //[step 5: link main program] link main program
        link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
        sMainExecutable->setNeverUnloadRecursive();
        if ( sMainExecutable->forceFlat() ) {
            gLinkContext.bindFlat = true;
            gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
        }
        
        //[step 6: link dynamic libraries] 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 the program uses dylibs (e.g. libsystem)
        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 be inserted
                //Register interacting info after all inserted libraries are bound so chaining works
                for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
                    ImageLoader* image = sAllImages[i+1];
                    //Registration symbol insertion
                    image->registerInterposing(gLinkContext);
                }
            }
        }

        
        //[step 7: weak sign binding]
        //Apply interacting to initial set of images
        for(int i=0; i < sImageRoots.size(); ++i) {
            sImageRoots[i]->applyInterposing(gLinkContext);
        }
        ImageLoader::applyInterposingToDyldCache(gLinkContext);
        // 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
        //Weak binding
        sMainExecutable->weakBind(gLinkContext);
        gLinkContext.linkingMainExecutable = false;
        sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);
        CRSetCrashLogMessage("dyld: launch, running initializers");
    
        //[step 8: execute initialization method]
        initializeMainExecutable(); 
        // notify any montoring proccesses that this process is about to enter main()
        notifyMonitoringDyldMain();

        //[step 9: find the main function as the main program entry]
        // find entry point for main executable
        result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
        if ( result != 0 ) {
            //The main executable uses LC \ u main. We need to use the helper in libdyld to call main ()
            // main executable uses LC_MAIN, we need to use helper in libdyld to call into main()
            if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
                *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
            else
                halt("libdyld.dylib support not present for LC_MAIN");
        }
        else {
            //The main executable uses LC \ u unixthread. Dyld needs to "start" in the program set for main()
            // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
            result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
            *startGlue = 0;
        }
    }

    return result;
}

[summary] the source chain of load is: _dyld_start — > dyldbootstrap:: Start — > dyld:: _main — > dyld:: initializemainexecutable — > imageloader:: runinitializers — > imageloader:: processinitializers — > imageloader:: recursive initialization — > dyld:: notifysingle (a callback process) – > snotifyobjcinit — > load_images (libobjcic. A.dylib)

IOS dyld loading process (preliminary discussion)

image.png

Recommended Today

Redis featured Q & A

Redis data type type brief introduction characteristic scene String (string) Binary security It can contain any data, such as JPG pictures or serialized objects. One key can store up to 512M It can be used to do the simplest data. It can cache a simple string or a JSON format string. The implementation of redis […]