dyld 簡介
dyld(the dynamic link editor)是蘋果的動態(tài)鏈接器,用來加載所有的庫和可執(zhí)行文件玉罐,是蘋果操作系統(tǒng)一個重要組成部分,在系統(tǒng)內(nèi)核做好程序準(zhǔn)備工作之后,交由 dyld 負(fù)責(zé)余下的工作选浑。而且它是開源的杖玲,任何人可以通過蘋果官網(wǎng)下載它的源碼來閱讀理解它的運(yùn)作方式顿仇,那下面我們就通過源碼嘗試看一下 dyld
的加載流程。
下面我們通過對控制器的 load
方法添加斷點摆马,看看 dyld
是從哪里開始執(zhí)行的:
從函數(shù)的調(diào)用棧我們可以看到臼闻,dyld
是從 start
方法開始執(zhí)行的,那我我們就打開源碼找到 start
方法囤采,來一步一步的分析 dyld
的調(diào)用過程述呐。這里用到是832.7.3版本。
dyld 入口函數(shù) start
uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[],
const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue)
{
// 首先發(fā)出kdebug消息蕉毯,告訴dyld已經(jīng)開始啟動了
dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);
// 第一步乓搬,重定位dyld地址,把dyld加載進(jìn)來(虛擬內(nèi)存下代虾,一個進(jìn)程啟動都需要做地址重定位)
rebaseDyld(dyldsMachHeader);
// kernel sets up env pointer to be just past end of agv array
const char** envp = &argv[argc+1];
// kernel sets up apple pointer to be just past end of envp array
const char** apple = envp;
while(*apple != NULL) { ++apple; }
++apple;
// 棧溢出的保護(hù)
__guard_setup(apple);
#if DYLD_INITIALIZER_SUPPORT
// run all C++ initializers inside dyld
runDyldInitializers(argc, argv, envp, apple);
#endif
// 初始化dyld
_subsystem_init(apple);
// now that we are done bootstrapping dyld, call dyld's main
uintptr_t appsSlide = appsMachHeader->getSlide();
// 調(diào)用main(最核心的代碼在main函數(shù)里面进肯,因為dyld幾乎所有的邏輯都在main里面處理)
return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}
在 start
方法里面主要是做了一些準(zhǔn)備工作,是開始啟動階段棉磨,最核心的代碼在 main
函數(shù)里面江掩,下面我們接著進(jìn)入 main
函數(shù)。
dyld 的 main 函數(shù)
現(xiàn)在我們來到 main
函數(shù)
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
int argc, const char* argv[], const char* envp[], const char* apple[],
uintptr_t* startGlue)
{
// 1. 內(nèi)核檢測代碼,這里省略......
// 2.主程序配置相關(guān)
uint8_t mainExecutableCDHashBuffer[20];
// 主程序的hash
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;
}
// mainExecutableMH :主程序的header频敛,mainExecutableSlide :主程序的slide
// 設(shè)置cpu项郊,cpu的類型等信息
getHostInfo(mainExecutableMH, mainExecutableSlide);
// 對header跟slide賦值
uintptr_t result = 0;
sMainExecutableMachHeader = mainExecutableMH;
sMainExecutableSlide = mainExecutableSlide;
// 3.設(shè)置上下文,把配置信息保存到gLinkContext這個變量里面斟赚,到這里都是做一些配置相關(guān)的操作并保存下來
setContext(mainExecutableMH, argc, argv, envp, apple);
// 配置進(jìn)程是否受限着降,envp:環(huán)境變量
// AMFI相關(guān)(Apple Mobile File Integrity蘋果移動文件保護(hù))
configureProcessRestrictions(mainExecutableMH, envp);
// 判斷是否要強(qiáng)制使用dyld3 (dyld3是iOS11之后新增的一種閉包模式,處理效率更高拗军,加載速度更快)
if ( dyld3::internalInstall() ) {...}
// 4. 檢測環(huán)境變量任洞,并給環(huán)境變量設(shè)置默認(rèn)值
checkEnvironmentVariables(envp);
defaultUninitializedFallbackPaths(envp);
// 打印相關(guān)的環(huán)境變量(在load之前打印)
if ( sEnv.DYLD_PRINT_OPTS )
printOptions(argv);
if ( sEnv.DYLD_PRINT_ENV )
printEnvironmentVariables(envp);
// 5. 加載共享緩存发侵,UIKit交掏,F(xiàn)oundtion等動態(tài)庫 (在這之前還沒有加載主程序,但是已經(jīng)拿到主程序的header刃鳄,開始讀主程序了盅弛,根據(jù)主程序的header可以知道主程序的cup,架構(gòu)叔锐,系統(tǒng)等信息)
// 共享緩存: 用來存儲UIkit等系統(tǒng)庫的挪鹏,保證這些庫只加載一份,因為iOS進(jìn)程之間訪問受限愉烙,所以要放到共享緩存里面
checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
// 6.加載各項我們所依賴的framework讨盒,還有三方庫
// 7.開始判斷是加載`dyld2`還是`dyld3`。
dyld3 的加載流程
// ClosureMode: 閉包模式,iOS11后引入的步责,iOS13開始動態(tài)庫返顺,三方庫都用ClosureMode模式加載
// 判斷是否是閉包模式,是的話就先開始創(chuàng)建閉包模式加載器
if ( sClosureMode == ClosureMode::Off ) {...} else {
// 給sLaunchModeUsed變量設(shè)置一個表示蔓肯,表示用閉包模式去啟動dyld
sLaunchModeUsed = DYLD_LAUNCH_MODE_USING_CLOSURE;
// 1. 配置主程序的信息:mainFileInfo遂鹊,主程序的header:mainExecutableMH
const dyld3::closure::LaunchClosure* mainClosure = nullptr;
dyld3::closure::LoadedFileInfo mainFileInfo;
mainFileInfo.fileContent = mainExecutableMH;
// check for closure in cache first
if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
// 先去共享緩存中取查看 mainClosure 實例
mainClosure = sSharedCacheLoadInfo.loadAddress->findClosure(sExecPath);
// 如果實例對象為空就主動創(chuàng)建
if ( mainClosure == nullptr ) {
mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
}
// 2. 這里開始進(jìn)入到啟動
if ( mainClosure != nullptr ) {
// 開啟啟動并返回啟動結(jié)果
bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
// 如果啟動失敗或者closureOutOfDate過期
if ( !launched && closureOutOfDate && allowClosureRebuilds ) {
// 再次創(chuàng)建一個mainClosure實例
mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
// 并再次啟動
launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
}
}
// 如果啟動成功,就保存變量省核,主程序加載成功
if ( launched ) {
gLinkContext.startedInitializingMainExecutable = true;
// 拿到主程序的main函數(shù)稿辙,并返回result
if (sSkipMain)
result = (uintptr_t)&fake_main;
return result;
}
// 如果啟動失敗,就報響應(yīng)的錯誤信息
else {
if ( gLinkContext.verboseWarnings ) {
dyld::log("dyld: unable to use closure %p\n", mainClosure);
}
if ( !recoverable )
halt(diag.errorMessage());
}
dyld2 的加載流程
// 如果不是閉包模式 dyld2模式
sLaunchModeUsed = 0;
// 把 notifyGDB 跟 updateAllImages 回調(diào)放到 sBatchHandlers 數(shù)組
sBatchHandlers)->push_back(notifyGDB);
stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
// 分配一些初始化空間气忠,盡量空間分配的大一點邻储,保證后面夠用
sImageRoots.reserve(16);
sAddImageCallbacks.reserve(4);
sRemoveImageCallbacks.reserve(4);
sAddLoadImageCallbacks.reserve(4);
sImageFilesNeedingTermination.reserve(16);
sImageFilesNeedingDOFUnregistration.reserve(8);
// 把dyld加入到UUIDList列表
addDyldImageToUUIDList();
// 開始加載主程序
// mainExcutableAlreadyRebased:主程序Rebased狀態(tài)標(biāo)識
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);
}
// 實例化主程序 image:可執(zhí)行文件(dyld第一個加載的image是主程序)
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
gLinkContext.mainExecutable = sMainExecutable;
// 代碼簽名
gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
// 檢測主程序是否屬于當(dāng)前系統(tǒng),當(dāng)前設(shè)備版本
{
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 ) {...}
// 相關(guān)配置
if (dyld::isTranslated()) {...}
// 設(shè)置動態(tài)庫的版本
checkVersionedPaths();
// 先判斷有沒有DYLD_INSERT_LIBRARIES這個環(huán)境變量旧噪,有的話插入所有動態(tài)庫
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
// 記錄插入動態(tài)庫
gLinkContext.linkingMainExecutable = true;
// 鏈接主程序吨娜,在此函數(shù)開始時記錄開始時間,然后遞歸加載主程序依賴的庫淘钟,加載完畢后會發(fā)送通知宦赠,修正ASLR,綁定NoLazy符號,綁定弱符號勾扭,遞歸應(yīng)用插入的動態(tài)庫毡琉,注冊,最后記錄結(jié)束時間
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
if ( sInsertedDylibCount > 0 ) {
// 循環(huán)遍歷把動態(tài)庫加入到allImage
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 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);
}
}
}
// 循環(huán)拿出image妙色,綁定插入的動態(tài)庫
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);
}
}
// 弱符號綁定
sMainExecutable->weakBind(gLinkContext);
實例化主程序的過程
這里其實就是實例化一個iamge的過程桅滋,其他動態(tài)庫跟三方庫的實例化過程跟主程序的實例化過程一樣。
// 實例化一個iamge的過程
static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path) {
// 加載主程序的MachO文件(header身辨,LoadCommands等)
ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
// 把iamge加入到allImage數(shù)組
addImage(image);
return (ImageLoaderMachO*)image;
}
// 進(jìn)入到instantiateMainExecutable函數(shù)
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;
sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
// 根據(jù)compressed的值選擇不同的子類來實例化image
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
}
進(jìn)入到sniffLoadCommands函數(shù)
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)
{
*compressed = false;
*segCount = 0;
*libCount = 0;
*codeSigCmd = NULL; // 代碼簽名
*encryptCmd = NULL; // 代碼加密
// segCount丐谋,libCount的數(shù)量限制
if ( *segCount > 255 )
dyld::throwf("malformed mach-o image: more than 255 segments in %s", path);
// fSegmentsArrayCount is only 8-bits
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;
}
initializeMainExecutable 函數(shù)
在 initializeMainExecutable
函數(shù)之前都是做一些初始化與加載準(zhǔn)備工作,這里才是開始真正進(jìn)入到主程序煌珊。
- 進(jìn)入到 initializeMainExecutable 函數(shù)
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 ) {
for(size_t i=1; i < rootCount; ++i) {
sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]);
}
}
// run initializers for main executable and everything it brings up
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]);
}
- 通過函數(shù)調(diào)用棧我們可以看到号俐,initializeMainExecutable函數(shù)之后進(jìn)入到
ImageLoader
函數(shù),不管dyld2還是dyld3都會走到這
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);
}
- 同樣根據(jù)函數(shù)調(diào)用棧來到
recursiveInitialization
函數(shù)定庵,看一下最終是怎么調(diào)到load_images
函數(shù)的吏饿。
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
}
- 進(jìn)到
notifySingle
函數(shù), 但是在這里我們并沒有找到load_images
函數(shù)的調(diào)用,因為load_images
是objc這個庫里面的洗贰,那他們是怎么調(diào)用的呢找岖?
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);
(*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);
}
}
}
- load_images 調(diào)用過程
在notifySingle函數(shù)的1019行我們可以看到這個地方有一個回調(diào)指針,在這里會有一個判斷敛滋,sNotifyObjCInit不為空就會調(diào)用這個回調(diào),那sNotifyObjCInit在哪里賦值的呢兴革?
我們在當(dāng)前文件搜索會在 registerObjCNotifiers
函數(shù)看到是在這里賦值的, 賦值為init绎晃,那我們來看一下是哪里調(diào)用了 registerObjCNotifiers
函數(shù),并傳了一個 init杂曲。我們?nèi)炙阉饕幌隆?/p>
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
}
在這里我們看到registerObjCNotifiers在這被調(diào)用了庶艾,并傳入了一個init。
接著再搜索 _dyld_objc_notify_register
會發(fā)現(xiàn)找不到了擎勘。那我們用符號斷點的方式咱揍,看看誰調(diào)用了 _dyld_objc_notify_register
。
運(yùn)行之后棚饵,通過打印堆棧信息我們可以看到 _dyld_objc_notify_register
函數(shù)是被libobjc文件里面的 _objc_init
函數(shù)調(diào)用了煤裙。這里我們需要看objc的源碼,找到 _objc_init
函數(shù)噪漾,并看看這個地方給 _dyld_objc_notify_register
方法傳了哪些值硼砰。
// _objc_init函數(shù)
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();
// 在這里我們看到了_dyld_objc_notify_register函數(shù)的調(diào)用,且第二個參數(shù)是load_images
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
在這里我們找到了 _dyld_objc_notify_register
的調(diào)用欣硼,并看到了第二個參數(shù)是 load_images
,也就是init是 load_images
题翰,也就找到了在 notifySingle
函數(shù)的1019行調(diào)用的是 load_images
函數(shù)。這里就看到了從 start
到 load_images
的一個完整過程。
call_load_methods 函數(shù)
從 _objc_init
函數(shù)進(jìn)的 load_images
函數(shù)豹障,接著進(jìn)入到 call_load_methods
函數(shù)冯事,在這里我們可以看到353行 call_class_loads
,類的 load
方法在這個時候被調(diào)用了血公。
// 1.從 `_objc_init` 函數(shù)進(jìn)的 `load_images` 函數(shù)
void
load_images(const char *path __unused, const struct mach_header *mh)
{
call_load_methods();
}
// 2.進(jìn)入到 `call_load_methods` 函數(shù)
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();
do {
// 1. 類的load方法調(diào)用
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. 分類的load方法調(diào)用
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;
}
C++全局構(gòu)造函數(shù)的加載
看完了 load_images
函數(shù)的調(diào)用過程之后昵仅,我們來到Demo,試一個現(xiàn)象坞笙。
#import "AppDelegate.h"
#import "ViewController.h"
__attribute__((constructor)) void func1() {
printf("func1 執(zhí)行了");
}
__attribute__((constructor)) void func2() {
printf("func2 執(zhí)行了");
}
@interface AppDelegate ()
在 AppDelegate.m
文件添加 __attribute__((constructor)) void func1
與 __attribute__((constructor)) void func2
函數(shù)岩饼,這兩個函數(shù)是全局C++構(gòu)造函數(shù),工程編譯之后薛夜,把編譯文件拖到MachOView應(yīng)用之后籍茧,可以看到文件結(jié)構(gòu)里面多了__mod_init_func文件。且工程運(yùn)行的時候梯澜,通過 printf
打印順序我們可以看到寞冯,func1
和 func12
在 load
之后 main
之前執(zhí)行,那是哪塊代碼來控制 func1
和 func12
類型的函數(shù)的加載呢晚伙?
現(xiàn)在我們回到 dyld 源碼吮龄,來的 recursiveInitialization
函數(shù),并最終來到 ImageLoaderMachO
函數(shù)咆疗。在這里可以看到 doModInitFunctions
函數(shù)其實就是負(fù)責(zé)構(gòu)造函數(shù)的加載漓帚。
// 1.進(jìn)入到ImageLoaderMachO函數(shù)
bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
CRSetCrashLogMessage2(this->getPath());
// mach-o has -init and static initializers
doImageInit(context);
// 加載init構(gòu)造方法
doModInitFunctions(context);
CRSetCrashLogMessage2(NULL);
return (fHasDashInit || fHasInitializers);
}
initializeMainExecutable函數(shù)之后
現(xiàn)在我們回到 dyld 的 main 函數(shù),在 之后我們找到7114行午磁,在這里調(diào)用了 (uintptr_t)gLibSystemHelpers->startGlueToCallExit
函數(shù)尝抖,獲取主程序的 main
。把主程序的 main
函數(shù)的地址賦值給 result
迅皇,并判斷 result
是否有值昧辽,最后在 dyld main函數(shù)的結(jié)尾返回 result
。
{
// find entry point for main executable
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;
}
}
}
// dyld main函數(shù)的結(jié)尾
return result;
最后總結(jié)
- DYLD : 動態(tài)鏈接器登颓,加載所有的庫和可執(zhí)行文件搅荞。
- dyld 加載流程
- 程序執(zhí)行從_dyld_start開始 -> dyld`dyldbootstrap::start
- 進(jìn)入 dyld: main 函數(shù)
- 配置一些環(huán)境 : rebase_dyld
- 加載共享緩存
- 判斷加載模式 DYLD2/DYLD3(閉包模式)
- 實例化主程序
- 加載動態(tài)庫(首先是插入動態(tài)庫) (主程序和動態(tài)庫都會添加到 allImages 里面)
- 鏈接主程序、綁定符號(這里綁定的都非懶加載的框咙、弱符號)等等
- 最關(guān)鍵的: 初始化方法 initializeMainExecutable (初始化主程序)
- dyld`ImageLoader::runInitializers
- dyld`ImageLoader::processInitializers:
- dyld`ImageLoader::recursiveInitialization:
-
dyld`dyld::notifySingle:函數(shù)
- 此函數(shù)執(zhí)行一個回調(diào)
- 通過斷點調(diào)試: 此函數(shù)是_objc_init初始化賦值的一個函數(shù)Load_images
- Load_images里面執(zhí)行class_load_methods函數(shù)
- call_class_loads函數(shù): 循環(huán)調(diào)用各個類的 load 方法
- Load_images里面執(zhí)行class_load_methods函數(shù)
-
doModInitFunction 函數(shù)
- 內(nèi)部會調(diào)動全局 C++對象的構(gòu)造函數(shù)attribute((constructor))的 C 函數(shù)
-
- dyld`ImageLoader::recursiveInitialization:
- dyld`ImageLoader::processInitializers:
- dyld`ImageLoader::runInitializers
- 返回主程序的入口函數(shù)咕痛,開始進(jìn)入主程序的 main 函數(shù)
不足之處,還請指正......