iOS dyld調(diào)用流程

一城菊、dyld概述

dyldthe dynamic link editor)動態(tài)鏈接器,是蘋果操作系統(tǒng)一個重要組成部分朽砰,在系統(tǒng)內(nèi)核做好程序準備工作之后交由dyld負責余下的工作。

二、dyld 源碼分析

新建一個工程拟枚,寫一個+load方法,分別在+loadmain函數(shù)上打斷點众弓,然后運行在真機上恩溅。

image.png

這個時候函數(shù)的調(diào)用棧如下:


image.png

匯編代碼如下:


image.png

dyld源碼中(dyld-832.7.3)找到dyldbootstrap::start函數(shù)(dyldInitialization.cpp),這也就是函數(shù)開始的地方谓娃。

接下來結合源碼分析怎么從start調(diào)用到loadmain方法脚乡。

2.1dyldbootstrap::start(dyldInitialization.cpp

可以通過搜索dyldbootstrap命名空間找到start源碼。核心代碼如下:

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

    //告訴debug server dyld啟動
    // 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
    //重定位dyld
    rebaseDyld(dyldsMachHeader);

    //棧溢出保護
    __guard_setup(apple);

    //初始化dyld
    _subsystem_init(apple);

    //調(diào)用dyld main函數(shù)
    return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}

start主要做了以下幾件事:

  • 告訴debug server dyld啟動
  • 重定位dyld
  • 棧溢出保護
  • 初始化dyld
  • 調(diào)用dyld main函數(shù)

其中start只做了一些配置和初始化的工作,核心邏輯在main函數(shù)中奶稠,start返回了main函數(shù)的返回值俯艰。

2.1.1dyld::_main(dyld2.cpp)

核心邏輯如下:

uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, 
        int argc, const char* argv[], const char* envp[], const char* apple[], 
        uintptr_t* startGlue)
{
    //內(nèi)核檢測代碼
    if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
        launchTraceID = dyld3::kdebug_trace_dyld_duration_start(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, (uint64_t)mainExecutableMH, 0, 0);
    }

    //Check and see if there are any kernel flags
    dyld3::BootArgs::setFlags(hexToUInt64(_simple_getenv(apple, "dyld_flags"), nullptr));

……

    // Grab the cdHash of the main executable from the environment
    //主程序可執(zhí)行文件
    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;
    }
    //獲取主程序Header,Slide(ASLR的偏移值)
    getHostInfo(mainExecutableMH, mainExecutableSlide);

……

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


……


    CRSetCrashLogMessage("dyld: launch started");
    //設置上下文锌订,將信息放入 gLinkContext 中
    setContext(mainExecutableMH, argc, argv, envp, apple);

    // Pickup the pointer to the exec path.
    sExecPath = _simple_getenv(apple, "executable_path");

    // <rdar://problem/13868260> Remove interim apple[0] transition code from dyld
    if (!sExecPath) sExecPath = apple[0];

……

    //根據(jù)環(huán)境變量 envp 配置進程是否受限制竹握,AMFI相關(Apple Mobile File Integrity蘋果移動文件保護)
    configureProcessRestrictions(mainExecutableMH, envp);

……

#if TARGET_OS_OSX
    if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) {
        pruneEnvironmentVariables(envp, &apple);
        // set again because envp and apple may have changed or moved
        //又設置一次上下文,在文件受限的時候可能更改了envp辆飘。
        setContext(mainExecutableMH, argc, argv, envp, apple);
    }
    else
#endif
    {
        //檢測環(huán)境變量并設置默認值啦辐,這個時候還沒有加載數(shù)據(jù)。
        checkEnvironmentVariables(envp);
        defaultUninitializedFallbackPaths(envp);
    }
……
    //打印環(huán)境變量蜈项,可以在"Scheme -> Arguments -> Environment Variables"中配置
    if ( sEnv.DYLD_PRINT_OPTS )
        printOptions(argv);
    if ( sEnv.DYLD_PRINT_ENV ) 
        printEnvironmentVariables(envp);

……

    // load shared cache芹关,加載共享緩存,只讀了主程序還沒有加載主程序紧卒。iOS必須有共享緩存侥衬。
    checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
    if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
#if TARGET_OS_SIMULATOR
        if ( sSharedCacheOverrideDir)
            mapSharedCache(mainExecutableSlide);
#else
        //加載共享緩存方法
        mapSharedCache(mainExecutableSlide);
#endif
    }

……


#if !TARGET_OS_SIMULATOR
    //dyld3 ClosureMode模式,iOS11引入ClosureMode常侦,iOS13后動態(tài)庫和三方庫都使用ClosureMode加載浇冰。
    if ( sClosureMode == ClosureMode::Off ) {
    //ClosureMode off打印log,往 if-else 后面走了
        if ( gLinkContext.verboseWarnings )
            dyld::log("dyld: not using closures\n");
    } else {
    //ClosureMode on
        //啟動模式 閉包模式 DYLD_LAUNCH_MODE_USING_CLOSURE
        sLaunchModeUsed = DYLD_LAUNCH_MODE_USING_CLOSURE;
        const dyld3::closure::LaunchClosure* mainClosure = nullptr;
        //主程序 info 和 Header
        dyld3::closure::LoadedFileInfo mainFileInfo;
        mainFileInfo.fileContent = mainExecutableMH;
        mainFileInfo.path = sExecPath;
……
        //第一次從共享緩存找閉包
        if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
            //先從共享緩存找實例閉包
            mainClosure = sSharedCacheLoadInfo.loadAddress->findClosure(sExecPath);
            if ( gLinkContext.verboseWarnings && (mainClosure != nullptr) )
                dyld::log("dyld: found closure %p (size=%lu) in dyld shared cache\n", mainClosure, mainClosure->size());
            if ( mainClosure != nullptr )
                //如果拿到設置狀態(tài)
                sLaunchModeUsed |= DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
        }

……

        //拿到閉包 && 驗證閉包聋亡,如果閉包失效
        if ( (mainClosure != nullptr) && !closureValid(mainClosure, mainFileInfo, mainExecutableCDHash, true, envp) ) {
            mainClosure = nullptr;
            //閉包失效設置狀態(tài)
            sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
        }

……
        // If we didn't find a valid cache closure then try build a new one
        //判斷mainClosure是否為空
        if ( (mainClosure == nullptr) && allowClosureRebuilds ) {
            // if forcing closures, and no closure in cache, or it is invalid, check for cached closure
            if ( !sForceInvalidSharedCacheClosureFormat )
                //緩存中找
                mainClosure = findCachedLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
            if ( mainClosure == nullptr ) {
                // if  no cached closure found, build new one
                //緩存中找不到則創(chuàng)建一個肘习,一直拿 mainClosure 是為了拿他創(chuàng)建主程序。
                mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
                if ( mainClosure != nullptr )
                    //創(chuàng)建失敗則設置狀態(tài)
                    sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH;
            }
        }

        // exit dyld after closure is built, without running program
        if ( sJustBuildClosure )
            _exit(EXIT_SUCCESS);

        // try using launch closure
        if ( mainClosure != nullptr ) {
            CRSetCrashLogMessage("dyld3: launch started");
            if ( mainClosure->topImage()->fixupsNotEncoded() )
                sLaunchModeUsed |= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE;
            Diagnostics diag;
            bool closureOutOfDate;
            bool recoverable;
            //啟動主程序坡倔,mainClosure 相當于加載器
            bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
                                              mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
            //啟動失敗或者過期 允許重建
            if ( !launched && closureOutOfDate && allowClosureRebuilds ) {
                // closure is out of date, build new one
                //再創(chuàng)建一個
                mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
                if ( mainClosure != nullptr ) {
                    diag.clearError();
                    sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH;
                    if ( mainClosure->topImage()->fixupsNotEncoded() )
                        sLaunchModeUsed |= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE;
                    else
                        sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_MINIMAL_CLOSURE;
                    //啟動
                    launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
                                                 mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
                }
            }
            if ( launched ) {
                //啟動成功保存白能量漂佩,主程序加載成功
                gLinkContext.startedInitializingMainExecutable = true;
                if (sSkipMain)
                    //主程序main函數(shù),dyld的main執(zhí)行完畢返回主程序的main
                    result = (uintptr_t)&fake_main;
                return result;
            }
            else {
                //失敗報錯
                if ( gLinkContext.verboseWarnings ) {
                    dyld::log("dyld: unable to use closure %p\n", mainClosure);
                }
                if ( !recoverable )
                    halt(diag.errorMessage());
            }
        }
    }
#endif // TARGET_OS_SIMULATOR
    // could not use closure info, launch old way
    //dyld2模式
    sLaunchModeUsed = 0;


    // install gdb notifier
    //兩個回調(diào)地址放入stateToHandlers數(shù)組中
    stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB);
    stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
    // make initial allocations large enough that it is unlikely to need to be re-alloced
    //分配初始化空間罪塔,盡可能大一些保證后面夠用投蝉。
    sImageRoots.reserve(16);
    sAddImageCallbacks.reserve(4);
    sRemoveImageCallbacks.reserve(4);
    sAddLoadImageCallbacks.reserve(4);
    sImageFilesNeedingTermination.reserve(16);
    sImageFilesNeedingDOFUnregistration.reserve(8);

……

    try {
        // add dyld itself to UUID list
        //dyld加入uuid列表
        addDyldImageToUUIDList();

#if SUPPORT_ACCELERATE_TABLES
#if __arm64e__
        // Disable accelerator tables when we have threaded rebase/bind, which is arm64e executables only for now.
        if ((sMainExecutableMachHeader->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E)
            sDisableAcceleratorTables = true;
#endif
        //主程序還沒有rebase
        bool mainExcutableAlreadyRebased = false;
        if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) {
            struct stat statBuf;
            if ( dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR "no-dyld2-accelerator-tables", &statBuf) != 0 )
                sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(&sSharedCacheLoadInfo.loadAddress->header, sSharedCacheLoadInfo.slide, mainExecutableMH, gLinkContext);
        }
//加載所有的可執(zhí)行文件 image list
reloadAllImages:
#endif


……


        CRSetCrashLogMessage(sLoadingCrashMessage);
        // instantiate ImageLoader for main executable
        //實例化主程序,加入到allImages(第一個靠dyld加載的image就是主程序)
        sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
        gLinkContext.mainExecutable = sMainExecutable;
        //代碼簽名
        gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);

#if TARGET_OS_SIMULATOR
        // check main executable is not too new for this OS
        //檢查主程序是否屬于當前系統(tǒng)
        {
            if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH, sExecPath) ) {
                throwf("program was built for a platform that is not supported by this runtime");
            }
            uint32_t mainMinOS = sMainExecutable->minOSVersion();

            // dyld is always built for the current OS, so we can get the current OS version
            // from the load command in dyld itself.
            uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion((const mach_header*)&__dso_handle);
            if ( mainMinOS > dyldMinOS ) {
    #if TARGET_OS_WATCH
                throwf("app was built for watchOS %d.%d which is newer than this simulator %d.%d",
                        mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF),
                        dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF));
    #elif TARGET_OS_TV
                throwf("app was built for tvOS %d.%d which is newer than this simulator %d.%d",
                        mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF),
                        dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF));
    #else
                throwf("app was built for iOS %d.%d which is newer than this simulator %d.%d",
                        mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF),
                        dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF));
    #endif
            }
        }
#endif


    #if SUPPORT_ACCELERATE_TABLES
        sAllImages.reserve((sAllCacheImagesProxy != NULL) ? 16 : INITIAL_IMAGE_COUNT);
    #else
        sAllImages.reserve(INITIAL_IMAGE_COUNT);
    #endif

#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
        //設置加載動態(tài)庫版本
        if (dyld::isTranslated()) {……}
#endif

        // Now that shared cache is loaded, setup an versioned dylib overrides
    #if SUPPORT_VERSIONED_PATHS
        //檢查版本路徑
        checkVersionedPaths();
    #endif


……

        // load any inserted libraries
        //DYLD_INSERT_LIBRARIES 插入動態(tài)庫
        if  ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
            //遍歷加載插入動態(tài)庫
            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.
        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
        //主程序的鏈接
        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;
        }

        // 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) {
                //i+1因為主程序征堪,插入的在主程序后面
                ImageLoader* image = sAllImages[i+1];
                //插入動態(tài)庫的鏈接
                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 SUPPORT_ACCELERATE_TABLES
        //判斷條件不滿足瘩缆,持續(xù) goto reloadAllImages
        if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) {
            // Accelerator tables cannot be used with implicit interposing, so relaunch with accelerator tables disabled
            ImageLoader::clearInterposingTuples();
            // unmap all loaded dylibs (but not main executable)
            for (long i=1; i < sAllImages.size(); ++i) {
                ImageLoader* image = sAllImages[i];
                if ( image == sMainExecutable )
                    continue;
                if ( image == sAllCacheImagesProxy )
                    continue;
                image->setCanUnload();
                ImageLoader::deleteImage(image);
            }
            // note: we don't need to worry about inserted images because if DYLD_INSERT_LIBRARIES was set we would not be using the accelerator table
            sAllImages.clear();
            sImageRoots.clear();
            sImageFilesNeedingTermination.clear();
            sImageFilesNeedingDOFUnregistration.clear();
            sAddImageCallbacks.clear();
            sRemoveImageCallbacks.clear();
            sAddLoadImageCallbacks.clear();
            sAddBulkLoadImageCallbacks.clear();
            sDisableAcceleratorTables = true;
            sAllCacheImagesProxy = NULL;
            sMappedRangesStart = NULL;
            mainExcutableAlreadyRebased = true;
            gLinkContext.linkingMainExecutable = false;
            resetAllImages();
            goto reloadAllImages;
        }
    #endif

……

        // 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];
                //綁定插入動態(tài)庫
                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);
        gLinkContext.linkingMainExecutable = false;

        sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);

        CRSetCrashLogMessage("dyld: launch, running initializers");
    #if SUPPORT_OLD_CRT_INITIALIZATION
        // Old way is to run initializers via a callback from crt1.o
        if ( ! gRunInitializersOldWay ) 
            initializeMainExecutable(); 
    #else
        // run all initializers
        //初始化主程序,到目前為止還沒有執(zhí)行到主程序中的代碼佃蚜。
        initializeMainExecutable(); 
    #endif

……
        {
            // find entry point for main executable
            //找到主程序入口 LC_MAIN
            result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
            if ( result != 0 ) {
                // 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 {
                // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
                result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
                *startGlue = 0;
            }
        }
    }
    catch(const char* message) {
        syncAllImages();
        halt(message);
    }
    catch(...) {
        dyld::log("dyld: launch failed\n");
    }

    CRSetCrashLogMessage("dyld2 mode");
#if !TARGET_OS_SIMULATOR
    if (sLogClosureFailure) {
        // We failed to launch in dyld3, but dyld2 can handle it. synthesize a crash report for analytics
        dyld3::syntheticBacktrace("Could not generate launchClosure, falling back to dyld2", true);
    }
#endif

    if (sSkipMain) {
        notifyMonitoringDyldMain();
        if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
            dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2);
        }
        ARIADNEDBG_CODE(220, 1);
        result = (uintptr_t)&fake_main;
        *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
    }
    //返回主程序
    return result;
}

main函數(shù)主要是返回了主程序的函數(shù)入口(main`):

  • 配置環(huán)境庸娱,獲取主程序HeaderSlideASLR
  • 加載共享緩存:mapSharedCache谐算。這個時候只讀了主程序還沒有加載主程序熟尉。iOS必須有共享緩存。
    checkSharedRegionDisable方法中說明了iOS必須有共享緩存:
    image.png
  • dyld2/dyld3ClosureMode閉包模式)加載程序:iOS11引入dyld3閉包模式洲脂,以回調(diào)的方式加載斤儿。閉包模式加載速度更快,效率更高。iOS13后動態(tài)庫和三方庫都使ClosureMode加載往果。
    • dyld3:
      • 使用mainClosure來加載疆液。
      • 找到/創(chuàng)建mainClosure后,通過launchWithClosure啟動主程序棚放,啟動失敗后會有重新創(chuàng)建mainClosure重新啟動的邏輯枚粘。成功后返回result(主程序入口main)。launchWithClosure中的邏輯和dyld2啟動主程序邏輯基本相同飘蚯。
    • dyld2:啟動主程序
      • 實例化主程序instantiateFromLoadedImage馍迄。
      • 插入&加載動態(tài)庫 loadInsertedDylib。加載在loadInsertedDylib中調(diào)用load(主程序和動態(tài)庫都會添加到allImagesloadAllImages
      • 鏈接主程序和鏈接插入動態(tài)庫(link局骤,主程序鏈接在前)攀圈。在這個過程中記錄了dyld加載的時長÷退Γ可以通過配置環(huán)境變量打印出來赘来。
      • 綁定符號(非懶加載、弱符號)凯傲,懶加載在調(diào)用時綁定犬辰。
      • 初始化主程序initializeMainExecutable,這個時候還沒有執(zhí)行到主程序中的代碼冰单。
      • 找到主程序入口 LC_MAIN幌缝,然后返回主程序。

DYLD_PRINT_OPTS诫欠,DYLD_PRINT_ENV環(huán)境變量配置涵卵,可以打印環(huán)境變量配置(在"Scheme -> Arguments -> Environment Variables"中配置):

環(huán)境變量配置

ASLRimage list0個主程序第一個地址。
ASLR

2.1.1.1mapSharedCache加載共享緩存

共享緩存專門緩存系統(tǒng)動態(tài)庫荒叼,如:UIKit轿偎、Foundation等。(自己的庫被廓、三方庫不行)

2.1.1.1.1 mapSharedCache

mapSharedCache真正調(diào)用的是loadDyldCache

static void mapSharedCache(uintptr_t mainExecutableSlide)
{
    ……
    //真正調(diào)用的是loadDyldCache
    loadDyldCache(opts, &sSharedCacheLoadInfo);
    ……
}
2.1.1.1.2 loadDyldCache
bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
{
    results->loadAddress        = 0;
    results->slide              = 0;
    results->errorMessage       = nullptr;

#if TARGET_OS_SIMULATOR
    // simulator only supports mmap()ing cache privately into process
    return mapCachePrivate(options, results);
#else
    if ( options.forcePrivate ) {
        // mmap cache into this process only
        //僅加載到當前進程
        return mapCachePrivate(options, results);
    }
    else {
        // fast path: when cache is already mapped into shared region
        bool hasError = false;
        //已經(jīng)加載不進行任何處理
        if ( reuseExistingCache(options, results) ) {
            hasError = (results->errorMessage != nullptr);
        } else {
            // slow path: this is first process to load cache
            //當前進程第一次加載
            hasError = mapCacheSystemWide(options, results);
        }
        return hasError;
    }
#endif
}

loadDyldCache3個邏輯:
1.僅加載到當前進程調(diào)用mapCachePrivate坏晦。不放入共享緩存,僅自己使用嫁乘。
2.已經(jīng)加載過不進行任何處理英遭。
3.當前進程第一次加載調(diào)用mapCacheSystemWide

動態(tài)庫的共享緩存在整個應用的啟動過程中是最先被加載的。

2.1.1.2 instantiateFromLoadedImage 實例化主程序(創(chuàng)建image

//header aslr path
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);
    //將主程序加入到all images
        addImage(image);
        return (ImageLoaderMachO*)image;
//  }
    
//  throw "main executable not a known format";
}
  • 傳入主程序的Header亦渗、ASLRpath實例化主程序生成image汁尺。
  • image加入all images中法精。

實際上實例化真正調(diào)用的是ImageLoaderMachO::instantiateMainExecutable

// 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;
    //獲取Load Commands
    sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
    // instantiate concrete class based on content of load commands
    //根據(jù) compressed 確定用哪個子類進行加載,ImageLoader是個抽象類,根據(jù)值選擇對應的子類實例化主程序
    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
}
  • 調(diào)用sniffLoadCommands生成相關信息搂蜓,比如compressed狼荞。
  • 根據(jù) compressed 確定用哪個子類進行加載,ImageLoader是個抽象類帮碰,根據(jù)值選擇對應的子類實例化主程序相味。

sniffLoadCommands

void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed,
                                            unsigned int* segCount, unsigned int* libCount, const LinkContext& context,
                                            const linkedit_data_command** codeSigCmd,
                                            const encryption_info_command** encryptCmd)
{
    //根據(jù)LC_DYLIB_INFO 和  LC_DYLD_INFO_ONLY 來獲取的
    *compressed = false;
    //segment數(shù)量
    *segCount = 0;
    //lib數(shù)量
    *libCount = 0;
    //代碼簽名和加密
    *codeSigCmd = NULL;
    *encryptCmd = NULL;
        ……
    // fSegmentsArrayCount is only 8-bits
    //segCount 最多 256 個
    if ( *segCount > 255 )
        dyld::throwf("malformed mach-o image: more than 255 segments in %s", path);

    // fSegmentsArrayCount is only 8-bits
    //libCount最多 4096 個
    if ( *libCount > 4095 )
        dyld::throwf("malformed mach-o image: more than 4095 dependent libraries in %s", path);

    if ( needsAddedLibSystemDepency(*libCount, mh) )
        *libCount = 1;

    // dylibs that use LC_DYLD_CHAINED_FIXUPS have that load command removed when put in the dyld cache
    if ( !*compressed && (mh->flags & MH_DYLIB_IN_CACHE) )
        *compressed = true;
}
  • compressed是根據(jù)LC_DYLIB_INFOLC_DYLD_INFO_ONLY來獲取的。
  • segCount最多256個殉挽。
  • libCount最多4096個丰涉。

2.1.1.3 loadInsertedDylib 插入&加載動態(tài)庫

static void loadInsertedDylib(const char* path)
{
    unsigned cacheIndex;
    try {
        LoadContext context;
        context.useSearchPaths      = false;
        context.useFallbackPaths    = false;
        context.useLdLibraryPath    = false;
        context.implicitRPath       = false;
        context.matchByInstallName  = false;
        context.dontLoad            = false;
        context.mustBeBundle        = false;
        context.mustBeDylib         = true;
        context.canBePIE            = false;
        context.origin              = NULL; // can't use @loader_path with DYLD_INSERT_LIBRARIES
        context.rpath               = NULL;
        //調(diào)用load,加載動態(tài)庫的真正函數(shù)
        load(path, context, cacheIndex);
    }
    catch (const char* msg) {
        if ( gLinkContext.allowInsertFailures )
            dyld::log("dyld: warning: could not load inserted library '%s' into hardened process because %s\n", path, msg);
        else
            halt(dyld::mkstringf("could not load inserted library '%s' because %s\n", path, msg));
    }
    catch (...) {
        halt(dyld::mkstringf("could not load inserted library '%s'\n", path));
    }
}
  • 根據(jù)上下文初始化配置調(diào)用load加載動態(tài)庫斯碌。

2.1.1.4ImageLoader::link鏈接主程序/動態(tài)庫

void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath)
{
    //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", imagePath, fDlopenReferenceCount, fNeverUnload);
    
    // clear error strings
    (*context.setErrorStrings)(0, NULL, NULL, NULL);
    //起始時間一死。用于記錄時間間隔
    uint64_t t0 = mach_absolute_time();
    //遞歸加載主程序依賴的庫,完成之后發(fā)通知傻唾。
    this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath);
    context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly);

    // we only do the loading step for preflights
    if ( preflightOnly )
        return;

    uint64_t t1 = mach_absolute_time();
    context.clearAllDepths();
    this->updateDepth(context.imageCount());

    __block uint64_t t2, t3, t4, t5;
    {
        dyld3::ScopedTimer(DBG_DYLD_TIMING_APPLY_FIXUPS, 0, 0, 0);
        t2 = mach_absolute_time();
        //Rebase修正ASLR
        this->recursiveRebaseWithAccounting(context);
        context.notifyBatch(dyld_image_state_rebased, false);

        t3 = mach_absolute_time();
        if ( !context.linkingMainExecutable )
            //綁定NoLazy符號
            this->recursiveBindWithAccounting(context, forceLazysBound, neverUnload);

        t4 = mach_absolute_time();
        if ( !context.linkingMainExecutable )
            //綁定弱符號
            this->weakBind(context);
        t5 = mach_absolute_time();
    }

    // interpose any dynamically loaded images
    if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0) ) {
        dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_INTERPOSING, 0, 0, 0);
        //遞歸應用插入的動態(tài)庫
        this->recursiveApplyInterposing(context);
    }

    // now that all fixups are done, make __DATA_CONST segments read-only
    if ( !context.linkingMainExecutable )
        this->recursiveMakeDataReadOnly(context);

    if ( !context.linkingMainExecutable )
        context.notifyBatch(dyld_image_state_bound, false);
    uint64_t t6 = mach_absolute_time();

    if ( context.registerDOFs != NULL ) {
        std::vector<DOFInfo> dofs;
        this->recursiveGetDOFSections(context, dofs);
        //注冊
        context.registerDOFs(dofs);
    }
    //計算結束時間.
    uint64_t t7 = mach_absolute_time();

    // clear error strings
    (*context.setErrorStrings)(0, NULL, NULL, NULL);
    //配置環(huán)境變量投慈,就可以看到dyld應用加載的時長。
    fgTotalLoadLibrariesTime += t1 - t0;
    fgTotalRebaseTime += t3 - t2;
    fgTotalBindTime += t4 - t3;
    fgTotalWeakBindTime += t5 - t4;
    fgTotalDOF += t7 - t6;
    // done with initial dylib loads
    fgNextPIEDylibAddress = 0;
}
  • 修正ASLR冠骄。
  • 綁定NoLazy符號伪煤。
  • 綁定弱符號。
  • 注冊凛辣。
  • 記錄時間抱既,可以通過配置看到dyld應用加載時長。

2.1.1.5 initializeMainExecutable 初始化主程序

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 ) {
        //從1開始到最后蟀给。(第0個為主程序)
        for(size_t i=1; i < rootCount; ++i) {
            //image初始化蝙砌,調(diào)用 +load 和 構造函數(shù)
            sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]);
        }
    }
    
    // run initializers for main executable and everything it brings up
    //調(diào)用主程序初始化
    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]);
}
  • 初始化images,下標從1開始跋理,然后再初始化主程序(下標0)择克,runInitializers
  • 可以配置環(huán)境變量DYLD_PRINT_STATISTICSDYLD_PRINT_STATISTICS_DETAILS打印相信信息前普。
2.1.1.5.1 dyld ImageLoader::runInitializers(ImageLoader.cpp)
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);
}
  • up.count值設置為1然后調(diào)用processInitializers肚邢。

dyld`ImageLoader::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.
    //這里count為1
    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);
}
  • 最終調(diào)用了recursiveInitialization

dyld ImageLoader::recursiveInitialization(ImageLoader.cpp)

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;
            //最終調(diào)用到objc里面了加載了所有l(wèi)oad方法
            context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
            
            // initialize this image
            //調(diào)用c++構造函數(shù)
            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();
}
  • 調(diào)用notifySingle最終調(diào)用到了objc中調(diào)用了所有的+ load方法拭卿。
  • 調(diào)用doInitialization最終調(diào)用了c++的系統(tǒng)構造函數(shù)骡湖。

c++系統(tǒng)構造函數(shù)

__attribute__((constructor)) void func() {
   printf("\n ---func--- \n");
}

dyld dyld::notifySingledyld2.cpp

//調(diào)用到objc里面去
static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
……

    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);
        //回調(diào)指針 sNotifyObjCInit 是在 registerObjCNotifiers 中賦值的。這里執(zhí)行會跑到objc的load_images中
        (*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);
        }
    }
……
}
  • notifySingle中找不到load image的調(diào)用(從堆棧信息中可以看到notifySingle之后是load image)峻厚。
  • 這個函數(shù)執(zhí)行一個回調(diào)sNotifyObjCInit响蕴。

搜索下回調(diào)sNotifyObjCInit的賦值操作,發(fā)現(xiàn)是在registerObjCNotifiers中賦值的
registerObjCNotifiers

//那么誰調(diào)用的 registerObjCNotifiers 惠桃? _dyld_objc_notify_register
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
    // record functions to call
    sNotifyObjCMapped   = mapped;
    //賦值 sNotifyObjCInit浦夷,傳進來的參數(shù)辖试。
    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());
        }
    }
}
  • registerObjCNotifiers賦值來自于第二個參數(shù)_dyld_objc_notify_init
  • 搜索發(fā)現(xiàn)是_dyld_objc_notify_register調(diào)用的registerObjCNotifiers劈狐。

** _dyld_objc_notify_register**(dyldAPIs.cpp

// _dyld_objc_notify_register 調(diào)用 registerObjCNotifiers
//這里找不到 _dyld_objc_notify_register 調(diào)用者罐孝。打符號斷點查看被objc-os.mm中 _objc_init 調(diào)用
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);
}
  • _dyld_objc_notify_register的調(diào)用者找不到。

打符號斷點_dyld_objc_notify_register排查調(diào)用情況:

image.png

可以看到是被_objc_init調(diào)用的肥缔。
_objc_init的調(diào)用在objc-os.mm中莲兢,查看源碼:

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

    //_objc_init 調(diào)用dyldAPIs.cpp 中_dyld_objc_notify_register,第二個參數(shù)是load_images
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}
  • 證實是_objc_init調(diào)用了_dyld_objc_notify_register续膳。
  • 第二個參數(shù)是load_images改艇。

load_imagesobjc-runtime-new.mm

load_images(const char *path __unused, const struct mach_header *mh)
{
    if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
        didInitialAttachCategories = true;
        loadAllCategories();
    }

    // Return without taking locks if there are no +load methods here.
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    //調(diào)用 call_load_methods
    call_load_methods();
}
  • 最終調(diào)用了call_load_methods

call_load_methods (objc-loadmethod.mm)

void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;

    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();
    
    //循環(huán)調(diào)用 call_class_loads姑宽,類的load方法在這一刻被調(diào)用
    do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            //調(diào)用每個類的load
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        //調(diào)用分類load,這里也就說明分類的 load 在所有類的load方法調(diào)用后才調(diào)用遣耍。(針對image而言)
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}
  • 調(diào)用call_class_loads加載類的+ load
  • 接著調(diào)用call_category_loads加載分類的+ load炮车。這里也就說明分類的 load在所有類的load方法調(diào)用后才調(diào)用舵变。(針對image而言)。

在這里也就調(diào)用到了+ load方法瘦穆,這也就是+ loadmain之前被調(diào)用的原因纪隙。

ImageLoaderMachO::doInitialization(ImageLoaderMachO.cpp)

bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
    CRSetCrashLogMessage2(this->getPath());

    // mach-o has -init and static initializers
    doImageInit(context);
    //加載c++構造函數(shù)
    doModInitFunctions(context);
    
    CRSetCrashLogMessage2(NULL);
    
    return (fHasDashInit || fHasInitializers);
}

加上以下代碼查看MachO文件:

__attribute__((constructor)) void func1() {
    printf("\n ---func1--- \n");
}

__attribute__((constructor)) void func2() {
    printf("\n ---func2--- \n");
}

會發(fā)現(xiàn)MachO中多了__mod_init_func

image.png

  • 調(diào)用doModInitFunctions函數(shù)加載c++構造函數(shù)(__attribute__((constructor))修飾的c函數(shù))

根據(jù)以上分析可以看到dyld是按image list順序從第1image調(diào)用runInitializers(可以看做是以image分組)。再調(diào)用下一個imagerunInitializers最后再調(diào)用主程序(下標為0)的runInitializers扛或。在runInitializers內(nèi)部先調(diào)用所有類的+load绵咱,再調(diào)用所有分類的+ load,最后調(diào)用c++的構造函數(shù)熙兔。
objc中調(diào)用load悲伶,dyld中調(diào)用doModInitFunctions
??如果在+ load中做了防護住涉,那么我們可以通過在+ load執(zhí)行前斷住外部符號做處理麸锉。這樣就可以繞過防護了。
防護最重要的就是不讓別人找到防護的邏輯舆声,只要能找到那么破解就很容易了花沉。
案例分析:你真的了解dyld么?

2.2整體流程

dyld加載流程

總結

總結
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末媳握,一起剝皮案震驚了整個濱河市碱屁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛾找,老刑警劉巖娩脾,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異打毛,居然都是意外死亡晦雨,警方通過查閱死者的電腦和手機架曹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闹瞧,“玉大人,你說我怎么就攤上這事展辞“掠剩” “怎么了?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵罗珍,是天一觀的道長洽腺。 經(jīng)常有香客問我,道長覆旱,這世上最難降的妖魔是什么蘸朋? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮扣唱,結果婚禮上藕坯,老公的妹妹穿的比我還像新娘。我一直安慰自己噪沙,他們只是感情好炼彪,可當我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著正歼,像睡著了一般辐马。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上局义,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天喜爷,我揣著相機與錄音忿等,去河邊找鬼晋控。 笑死,一個胖子當著我的面吹牛咆霜,可吹牛的內(nèi)容都是我干的穷绵。 我是一名探鬼主播轿塔,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼仲墨!你這毒婦竟也來了勾缭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤目养,失蹤者是張志新(化名)和其女友劉穎俩由,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體癌蚁,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡幻梯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年兜畸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碘梢。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡咬摇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出煞躬,到底是詐尸還是另有隱情肛鹏,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布恩沛,位于F島的核電站在扰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏雷客。R本人自食惡果不足惜芒珠,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搅裙。 院中可真熱鬧皱卓,春花似錦、人聲如沸呈宇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽甥啄。三九已至存炮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜈漓,已是汗流浹背穆桂。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留融虽,地道東北人享完。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像有额,于是被迫代替她去往敵國和親般又。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容