preface

In the normal development process, the entry function of app is main(), but before the main() function is called, how does the system do? What exactly did you do? The following is a detailed analysis of the app loading process.

The preparatory work

Before analyzing the app loading process, let’s understand a few concepts.

  • The build process

Compilation process: the system generates the corresponding assembly code by precompiling and compiling the source file (.h. m, etc.), and then loads the library file into memory through DYLD, and then links the assembly and library file into an executable file.

  • Dynamic and static libraries

    • The dynamic library: Dynamic link. There will only be one copy, shared in memory.The dynamic libraryReduced package volume size. For example, the system’s dynamic library:Foundation,UIKitAnd so on.
    • Static library: Static link.Static libraryIt will repeat during loading, wasting space.
  • Download dyLD source code

  • Download the Libsystem source code

  • Download the libDispatch source code

dyld

Dyld (Dynamic linker) : Is an important part of apple’s operating system, loading all libraries and executables.

Since load() is called earlier than main(), create a Project, add the system load() function to the controller, and run with a breakpoint:

  • Through stack analysis: in execution_dyld_startAfter that, calldyldbootstrap::start
  • inDyld sourceIn the search_dyld_startAnd found that it was implemented regardless of the architecturedyldbootstrap::start

So the dyLD Bootstrap ::start method is an entry point into the overall dyLD process.

Dyld loading process analysis

Search dyldbootstrap for its start method. (Project choice DyLD_executables)

Start function

uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, 
                int argc, 
                const char* argv[], 
                const dyld3::MachOLoaded* dyldsMachHeader, 
                uintptr_t* startGlue) 
{
    // Issue the kdebug flag, dyLD is started
    dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0.0.0.0);
    // Redirect dyld
    rebaseDyld(dyldsMachHeader);
    // Sets a stack random value for stack protection
    __guard_setup(apple);
#if DYLD_INITIALIZER_SUPPORT
    // run all C++ initializers
    runDyldInitializers(argc, argv, envp, apple);
#endif
    // Initialize the stack pin apple
    _subsystem_init(apple);
    // Get the app header offset ASLR
    uintptr_t appsSlide = appsMachHeader->getSlide(a);// Call dyld's main function.
    return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}
Copy the code

RebaseDyld (redefine dyLD)

static void rebaseDyld(const dyld3::MachOLoaded* dyldMH)
{
    // Go through all the header files and correct the dyld address according to the offset
    const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)dyldMH;
    assert(ma->hasChainedFixups());
    // Since all fixup chaining images have a zero base address, slide is the loading address
    uintptr_t slide = (long)ma;
    __block Diagnostics diag;
    ma->withChainStarts(diag, 0And ^ (const dyld_chained_starts_in_image* starts) {
        ma->fixupAllChainedFixups(diag, starts, slide, dyld3::Array<const void* > (),nullptr);
    });
    diag.assertNoError(a);// The redirect is complete and Mach is initialized
    mach_init(a);// Once the fix is complete, mark the data constant segment in dyLD as read-only
    ma->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
        if ( info.readOnlyData ) {
            ::mprotect(((uint8_t*)(dyldMH))+info.vmAddr, (size_t)info.vmSize, VM_PROT_READ); }}); }Copy the code
  • According to the offset, the correctiondyldaddress
  • Redirect complete, setdyldConstant read only

The main function

Main is the main function of dyld, not the common main function of APP programs. (Due to the main function, only the main code is analyzed.)

The preparatory work

// Get the cdHash of the main program
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;
}

// Set architecture (CPU) information
getHostInfo(mainExecutableMH, mainExecutableSlide);

// Set the main program header and memory offset
uintptr_t result = 0;
sMainExecutableMachHeader = mainExecutableMH;
sMainExecutableSlide = mainExecutableSlide;

// Set the context and put the information into the gLinkContext (notifySingle is assigned to it)
setContext(mainExecutableMH, argc, argv, envp, apple);

// Whether the configuration process is restricted
configureProcessRestrictions(mainExecutableMH, envp);

// Check whether dyLD3 should be mandatory. AMFI related (Apple Mobile File Integrity)
if ( dyld3::internalInstall()) {if (const char* useClosures = _simple_getenv(envp, "DYLD_USE_CLOSURES")) {
        if ( strcmp(useClosures, "0") = =0 ) {
            // Close closure mode
            sClosureMode = ClosureMode::Off;
        } else if ( strcmp(useClosures, "1") = =0 ) {
#if! __i386__// don't support dyld3 for 32-bit macOS
            // If not i386, enable closure mode
            sClosureMode = ClosureMode::On;
            sClosureKind = ClosureKind::full;
#endif
        } else if ( strcmp(useClosures, "2") = =0 ) {
            // Enable closure modesClosureMode = ClosureMode::On; sClosureKind = ClosureKind::minimal; }}}#if TARGET_OS_OSX
// Check whether the environment variable has changed (the main program is not loaded at this time)
if(! gLinkContext.allowEnvVarsPrint && ! gLinkContext.allowEnvVarsPath && ! gLinkContext.allowEnvVarsSharedCache ) {pruneEnvironmentVariables(envp, &apple);
    // Set again (because enVP and Apple may have changed or moved at this point)
    setContext(mainExecutableMH, argc, argv, envp, apple);
}
else
#endif
{
    // Check environment variables
    checkEnvironmentVariables(envp);
    // Set the default path
    defaultUninitializedFallbackPaths(envp);
}

// Load the shared cache, UIKit, Foundation dynamic library. (The main program is not loaded at this point, but the MachO header has been read)
checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
if( gLinkContext.sharedRegionMode ! = ImageLoader::kDontUseSharedRegion ) {#if TARGET_OS_SIMULATOR
if ( sSharedCacheOverrideDir)
    // Set the shared cache offset address
    mapSharedCache(mainExecutableSlide);
#else
// Set the shared cache offset address
mapSharedCache(mainExecutableSlide);
#endif
Copy the code
  • Of the main programhashaddress
  • Set up theCPUSchema information, setting the default context
  • Determine whether or notdyld3
  • Checking Environment Variables
  • Loading the Shared cache

Closure mode judgment

if ( sClosureMode == ClosureMode::Off ) {
    // Do not use closures
} else {
    // Use closures. (Whether it is actually used depends on the closure address.)
    sLaunchModeUsed = DYLD_LAUNCH_MODE_USING_CLOSURE;
    const dyld3::closure::LaunchClosure* mainClosure = nullptr;
    // Set the main information required for loading
    dyld3::closure::LoadedFileInfo mainFileInfo;
    mainFileInfo.fileContent = mainExecutableMH;
    mainFileInfo.path = sExecPath;
    mainFileInfo.sliceOffset = 0;
    mainFileInfo.sliceLen = - 1;
    
    // Determine if the closure cache address is empty
    if( sSharedCacheLoadInfo.loadAddress ! =nullptr ) {
        // If not empty, set closure (loaded from cache)
        mainClosure = sSharedCacheLoadInfo.loadAddress->findClosure(sExecPath);
        if( mainClosure ! =nullptr )
            // If the closure is not empty, prove the closure mode
            sLaunchModeUsed |= DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
    }
   
    // sClosureMode determines whether closure mode is used
    if ( sClosureMode == ClosureMode::On ) {
        // Closure mode
        allowClosureRebuilds = true;
    } else if( (sClosureMode == ClosureMode::PreBuiltOnly) && (mainClosure ! =nullptr) ) {
        allowClosureRebuilds = true;
    }
    
    // Validate closures
    if( (mainClosure ! =nullptr) &&!closureValid(mainClosure, mainFileInfo, 
        mainExecutableCDHash, true, envp) ) {
        // If the authentication fails, clear the closure address and reset the mode
        mainClosure = nullptr;
        sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
    }
    
    // Check whether the closure address is empty and whether creation is allowed
    if ( (mainClosure == nullptr) && allowClosureRebuilds ) {
        if ( !sForceInvalidSharedCacheClosureFormat )
            // Cache lookup closure
            mainClosure = findCachedLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
        if ( mainClosure == nullptr ) {
            // Create a new closure
            mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
            if( mainClosure ! =nullptr) sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH; }}}Copy the code
  • Determine whether or notclosuremodel
  • Determine the cacheclosureAddress, and find.
  • validationclosure
  • judgeclosureAddress, if not, is created from the cache, if not, a new one is created.

Dyld3 (closure) load

// If the closure address exists, load it using dyLD3
if( mainClosure ! =nullptr ) {
    // Start the closure
    bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress,
                                     (dyld3::MachOLoaded*)mainExecutableMH, 
                                     mainExecutableSlide, 
                                     argc, argv, envp, apple, diag, 
                                     &result, startGlue, &closureOutOfDate, 
                                     &recoverable);
    if ( !launched && closureOutOfDate && allowClosureRebuilds ) {
        // If the closure fails to start and times out, rebuilding is also allowed. The closure is created again
        mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
        if( mainClosure ! =nullptr ) {
            // If the closure is created successfully
            sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH;
            // Start again
            launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable); }}// If the startup succeeds
    if ( launched ) {
        // Update the main program loading success flag
        gLinkContext.startedInitializingMainExecutable = true;
        if (sSkipMain)
            // Get the main program address and return it.
            result = (uintptr_t)&fake_main;
        return result;
    } else {
        // If the startup fails, save the error information
        if ( !recoverable )
            halt(diag.errorMessage()); }}Copy the code
  • Start theclosure
  • If startup fails, rebuildclosure, if the reconstruction is successfulOnce again,Start.
  • Start thesuccessful, gets the main program address, and returns.
  • Start thefailureTo save the error information.

MainExecutable instantiation (dyld2)

// instantiate the main program
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
// Update the main program
gLinkContext.mainExecutable = sMainExecutable;
// Update code signature status
gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
Copy the code

instantiateFromLoadedImage

static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, 
                                                    uintptr_t slide, 
                                                    const char* path)
{
    // Load the image file
    ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path,
                                                                    gLinkContext);
    // Add to allImages in the main application
    addImage(image);
    return (ImageLoaderMachO*)image;
}
Copy the code

MainExecutable links (dyld2)

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

// Link the dynamic library
if ( sInsertedDylibCount > 0 ) {
   for (unsigned int i = 0; i < sInsertedDylibCount; ++i) {
       // The shared cache already has a main program, so it starts at 1
       ImageLoader* image = sAllImages[i+1];
       // Link the image file
       link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL.NULL), - 1);
       // Load recursively
       image->setNeverUnloadRecursive(a); }}}Copy the code
  • Link main program
  • Linked dynamic library: loads the dynamic library from1Start.
  • Recursive loadingThe image file is not loaded

link

void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, 
          const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex)
{
    // Add itself to allImages first
    if ( image->isBundle() && !image->isLinked())addImage(image);
    try {
        // Mirror address
        const char* path = image->getPath(a);// Link to the image file
        image->link(gLinkContext, forceLazysBound, false, neverUnload, loaderRPaths, path);
    }
    catch (const char* msg) {
        // Garbage collection
        garbageCollectImages(a);throw; }}Copy the code

image->link

void ImageLoader::link(const LinkContext& context, bool forceLazysBound, 
                       bool preflightOnly, bool neverUnload, 
                       const RPathChain& loaderRPaths, const char* imagePath)
{
    // Recursively load the library and implement the notification (recursive completion)
    this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath);
    context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly);
    // Clear all Images
    context.clearAllDepths(a);this->updateDepth(context.imageCount());
    {
        // Recursively redirect ASLR and implement notification (redirect complete)
        this->recursiveRebaseWithAccounting(context);
        context.notifyBatch(dyld_image_state_rebased, false);
        
        // Recursively bind lazy loading symbols
        if ( !context.linkingMainExecutable )
            this->recursiveBindWithAccounting(context, forceLazysBound, neverUnload);
        
        // Bind the weak reference symbol
        if ( !context.linkingMainExecutable )
            this->weakBind(context);
        }
        // Recursive insertion
        if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0)) {this->recursiveApplyInterposing(context);
    }
    // Recursively set to read-only
    if ( !context.linkingMainExecutable )
        this->recursiveMakeDataReadOnly(context);
    // When all is done, implement notification (binding is done)
    if ( !context.linkingMainExecutable )
        context.notifyBatch(dyld_image_state_bound, false);
}
Copy the code
  • Recursive loadingThe dynamic library
  • Recursive redirectionASLR
  • Recursive bindingLazy loadingsymbol
  • The bindingA weak referencesymbol
  • Recursive applicationinsert
  • A recursive setread-only
  • All done, implement notification (status: binding complete)

reloadAllImages

// Whether to reload the image file
if( (sAllCacheImagesProxy ! =NULL) && ImageLoader::haveInterposingTuples()) {// Cancel all loaded images except the main program, so start at 1
    for (long i = 1; i < sAllImages.size(a); ++i) { ImageLoader* image = sAllImages[i];if ( image == sMainExecutable )
            continue;
        / / not loaded
        image->setCanUnload(a);/ / delete
        ImageLoader::deleteImage(image);
    }
    // Cancel all Images
    resetAllImages(a);// Reload
    goto reloadAllImages;
}
Copy the code

MainExecutable binding (dyld2)

// Bind the main program
sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
// Implement notification (binding complete)
gLinkContext.notifyBatch(dyld_image_state_bound, false);

// Recursively bind inserted Images, starting at 1 by default
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); }}// Bind the weak reference symbol of the main program
sMainExecutable->weakBind(gLinkContext);
// Set read-only data for the main program
sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);
Copy the code

MainExecutable initialization (dyld2)

void initializeMainExecutable(a) {
    // Start initializing the main program tag
    gLinkContext.startedInitializingMainExecutable = true;
    // Initialize all rootImages
    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]); }}// Initializes the loader for the main program and all content
    sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
}
Copy the code

runInitializers

void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo) {
    ImageLoader::UninitedUpwards up;
    up.count = 1;
    up.imagesAndPaths[0] = { this.this->getPath() };
    // Process initialization
    processInitializers(context, thisThread, timingInfo, up);
    // Implement notification (initialization is complete)
    context.notifyBatch(dyld_image_state_initialized, false);
}
Copy the code

processInitializers

void ImageLoader::processInitializers(const LinkContext& context, 
                                      mach_port_t thisThread, 
                                      InitializerTimingList& timingInfo, 
                                      ImageLoader::UninitedUpwards& images)
{
    for (uintptr_t i=0; i < images.count; ++i) {
        // Recursively initializes all images in the images list, building a new dependency.
        images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups); }}Copy the code

recursiveInitialization

// Initialize the child dynamic library
for (unsigned int i = 0; i < libraryCount(a); ++i) { ImageLoader* dependentImage =libImage(i);
    if( dependentImage ! =NULL ) {
        // Get the upper-level lib
        if ( libIsUpward(i) ) {
            uninitUps.imagesAndPaths[uninitUps.count] = { dependentImage, libPath(i) };
            uninitUps.count++;
        } else if ( dependentImage->fDepth >= fDepth ) {
            // If the child is not loaded, perform deep recursive initialization
            dependentImage->recursiveInitialization(context, this_thread, libPath(i), timingInfo, uninitUps); }}}// Send notification (all attached images have been loaded)
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);

// Initialize the overall context
bool hasInitializers = this->doInitialization(context);

// Send notification (initialization completed)
context.notifySingle(dyld_image_state_initialized, this.NULL);
Copy the code

doInitialization

bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
    // Initialize the image
    doImageInit(context);
    // initializes the global C++ object constructor, internally initializing the libSystem library
    doModInitFunctions(context);
}
Copy the code
notifySingle

RecursiveInitialization sends a notification to DYLD_IMAGe_STATE_dependentS_INITIALIZED, so the sNotifyObjCInit callback is implemented.

static void notifySingle(dyld_image_states state, 
                        const ImageLoader* image, 
                        ImageLoader::InitializerTimingList* timingInfo)
{
    // The state is DYLD_IMAGe_STATE_dependentS_INITIALIZED and
    // sNotifyObjCInit the callback exists and
    // The loading image tag exists
    if( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit ! =NULL) && 
        image->notifyObjC()) {// Implement sNotifyObjCInit notification callback
        (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
}
Copy the code
registerObjCNotifiers
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
    / / register map_images
    sNotifyObjCMapped = mapped;
    / / register load_images
    sNotifyObjCInit = init;
    / / register unmap_images
    sNotifyObjCUnmapped = unmapped;
}
Copy the code
_dyld_objc_notify_register
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped)
{
    // Register notification
    dyld::registerObjCNotifiers(mapped, init, unmapped);
}
Copy the code

At this point, the _DYLD_OBJC_Notify_register method is not in the DYLD source library.

Later, the process is combed by using the backward method.

notifyMonitoringDyldMain

// Tell the monitor to enter the main() function of dyld
notifyMonitoringDyldMain(a);// Get the address of the main program entry
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN(a);Copy the code

Backstepping method

Check the stack information using the symbol breakpoint _dyLD_OBJC_notify_register:

_objc_init

_objc_init belongs to libobjc.a.dylib

_objc_init source code:

_os_object_init

Os_object_init belongs to libdispatch. Dylib.

_os_object_init source code:

libdispatch_init

Libdispatch_init belongs to libdispatch. Dylib

Libdispatch_init source code:

libSystem_initializer

LibSystem_initializer belongs to libsystem.b.dilib.

LibSystem_initializer source code:

doModInitFunctions

As analyzed above, doModInitFunctions source code contains initialization of the libSystem library

summary

Through analysis, when the main program initialization is complete, register the notification first, and then perform initialization. The specific process is as follows:

  • doModInitFunctions(libSystemLoad) –libSystem_initializerlibdispatch_init_os_object_init_objc_init_dyld_objc_notify_register

Dyld loading flowchart

supplement

map_images

The first parameter map_images to be registered with the _dyLD_OBJC_notify_register is:

void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
    / / register map_images
    sNotifyObjCMapped = mapped;
    / / register load_images
    sNotifyObjCInit = init;
    / / register unmap_images
    sNotifyObjCUnmapped = unmapped;
    
    try {
        // Call the mapped function and map all the images
        notifyBatchPartial(dyld_image_state_bound, true.NULL.false.true);
    } catch (const char* msg) {
        // Ignore the request}}Copy the code

(sNotifyObjCMapped) (notifyBatchPartial) (sNotifyObjCMapped)

NotifyBatchPartial is called after the registerObjCNotifiers have been registered.

conclusion

Map_images is called in the following order: _DYLD_OBJC_notify_register → registerObjCNotifiers → notifyBatchPartial → sNotifyObjCMapped (MAP_images)

Dyld2 and dyld3

dyld2

  • dyld2isdyld1A completely rewritten version of.
  • The right supportc++Initializer, extendedmach-oThe format has been updateddyld.
  • With integritydlopenanddlsymImplementation, discarding the old versionAPI(the old versionAPIStill only locatedmacOS).
  • dyld2Is designed to increase speed, so only limited health checks are performed.
  • dyld1There are some security issues,dyld2Some functional improvements have been made to improve security.
  • Speed significantly improves performance and reduces the amount of pre-binding. Different from thedyld1Compile your program data,dyld2Only system libraries are compiled. You can do this only during software updates. So you might see it in software updatesOptimize system performance“, which is updating the prebinding.

dyld3

Dyld3 is a new dynamic connector, iOS11 above the system procedures are used by default dyLD3, in iOS13 completely replace DYLD2. Dyld3 is mainly improved in the following three aspects:

  • Performance. Improve startup speed.dyld3Can help us get the program up and running faster.
  • Security.dyld2Increased security is hard to follow in the real world.
  • Testability and reliability.

Dyld2 and DYLD3 loading comparison

Dyld2 process:

  • Parse mach-o headers & Find dependenciesAnalysis:macho headers, identify which libraries are required. Recursively analyze the dependent libraries until you get all of themdylibLibrary. ordinaryiOSApplication needs to3-600.adylibData is huge and requires a lot of processing.
  • Map mach-o files: Mapping allmachoThe file puts them into the address space (mapped into memory).
  • Perform symbol lookups: Performs symbol lookup. Such as the useprintfFunction, will findprintfIf it is in the library system, then find its address and copy it to the function pointer in the application.
  • Bind and rebase: Binding and redirection. Copy these Pointers. All Pointers must use base addresses (ASLRThe existence of.
  • Run initializers: Runs all initializers. After that, preparations for execution beginmainFunction.

Dyld3 process:

  • Dyld3 is an out-of-process Macho parser and compiler

    • Parsing all search paths,rpaths, environment variables.
    • Analysis of themachoBinary data.
    • Performs all symbol lookups.
    • Create closures.
    • Normal tests can be performed.
    • Most programs start using caching and never need to call out of processmachoAnalyzer or compiler.
    • Start closure ratiomachoEven simpler, they are memory-mapped files that don’t need to be parsed in a complicated way and can be easily validated for speed purposes.
  • Dyld3 is also an in-process engine

    • Check that closures are correct.
    • Use closures to map alldylibs.
    • Binding and redirection.
    • Run all initializers, then jump to main programmain().
  • Start the closure cache service

    • systemappThe closure pattern is built into a shared cache.
    • Third-party applications are built at install time and rebuilt with software updates.
    • inmacOSThe upper daemon engine can be called in the daemon, which is not required on other platforms.

dyld3No need to analyzemacho HeadersOr perform a symbol lookup. inAppThere is no such process at startup, which greatly improves the startup speed of the program.

Please refer to the official video for details.