應用程序的加載 dyld (上)

標籤: App啓動 dyldgit


本章節 主要介紹 dyld 的加載流程,瞭解在main函數以前,底層還作了什麼bootstrap


準備工做 dyld源碼 libdispatch-1271.120.2 源碼 Libsystem-1292.60.1 objc4-818.2緩存

1. dyld

1.1 簡介

  • dyld(the dynamic link editor)是蘋果的動態連接器,是蘋果操做系統的重要組成部分,在app被編譯打包成可執行文件格式的Mach-O文件後,交由dyld負責鏈接,加載程序
  • dyld貫穿了App啓動的過程,包含加載依賴庫、主程序,若是咱們須要進行性能優化、啓動優化等,不可避免的須要和dyld打交道

1.2 dyld的發展史

1.2.1 dyld 1.0 1996-2004

  • dyld 1包含在NeXTStep 3.3中,在此以前的NeXT使用靜態二進制數據。做用並非很大,
  • dyld 1是在系統普遍使用C++動態庫以前編寫的,因爲C++有許多特性,例如其初始化器的工做,在靜態環境工做良好,可是在動態環境中可能會下降性能。所以大型的C++動態庫會致使dyld須要完成大量的工做,速度變慢
  • 在發佈macOS 10.0Cheetah前,還增長了一個特性,即Prebinding預綁定。咱們可使用Prebinding技術爲系統中的全部dylib和應用程序找到固定的地址。dyld將會加載這些地址的全部內容。若是加載成功,將會編輯全部dylib和程序的二進制數據,來得到全部預計算。當下次須要將全部數據放入相同地址時就不須要進行額外操做了,將大大的提升速度。可是這也意味着每次啓動都須要編輯這些二進制數據,至少從安全性來講,這種方式並不友好。

1.2.2 dyld 2 2004-2017

dyld 2從2004年發佈至今,已經通過了多個版本迭代,咱們如今常見的一些特性,例如ASLRCode Signshare cache等技術,都是在dyld 2中引入的安全

dyld 2.0(2004-2007)

  • 2004年在macOS Tiger中推出了dyld 2
  • dyld 2dyld 1徹底重寫的版本,能夠正確支持C++初始化器語義,同時擴展了mach-o格式並更新dyld。從而得到了高效率C++庫的支持。
  • dyld 2具備完成的dlopendlsym(主要用於動態加載庫和調用函數)實現,且具備正確的語義,所以棄用了舊版的API
    • dlopen:打開一個庫,獲取句柄
    • dlsym:在打開的庫中查找符號的值
    • dlclose:關閉句柄。
    • dlerror:返回一個描述最後一次調用dlopen、dlsym,或 dlclose 的錯誤信息的字符串。
  • dyld設計目標提高啓動速度。所以僅進行有限的健全性檢查。主要是由於之前的惡意程序比較少
  • 同時dyld也有一些安全問題,所以對一些功能進行了改進,來提升dyld在平臺上的安全性
  • 因爲啓動速度的大幅提高,所以咱們能夠減小Prebinding的工做量。與編輯程序數據的區別在於,在這裏咱們僅編輯系統庫,且能夠僅在軟件更新時作這些事情。所以在軟件更新過程當中,可能會看到「優化系統性能」相似的文字。這就是在更新時進行Prebinding。如今dyld用於全部優化,其用途就是優化。所以後面有了dyld 2

dyld 2.x(2007-2017)

  • 在2004-20017這幾年間進行了大量改進,dyld 2的性能顯著提升
  • 首先,增長了大量的基礎架構平臺
    • 自從dyld 2在PowerPC發佈以後,增長了x86x86_64armarm64和許多的衍平生臺。
    • 還推出了iOStvOSwatchOS,這些都須要新的dyld功能
  • 經過多種方式增長安全性
    • 增長 codeSigning代碼簽名、
    • ASLR(Address space layout randomization)地址空間配置隨機加載:每次加載庫時,可能位於不一樣的地址
    • bound checking邊界檢查:mach-o文件中增長了Header的邊界檢查功能,從而避免惡意二進制數據的注入
  • 加強了性能
    • 能夠消除Prebinding,用share cache共享代碼代替

ASLR

  • ASLR是一種防範內存損壞漏洞被利用的計算機安全技術,ASLR經過隨機放置進程關鍵數據區域的地址空間來防止攻擊者跳轉到內存特定位置來利用函數
  • Linux已在內核版本2.6.12中添加ASLR
  • Apple在Mac OS X Leopard 10.5(2007年十月發行)中某些庫導入了隨機地址偏移,但其實現並無提供ASLR所定義的完整保護能力。而Mac OS X Lion 10.7則對全部的應用程序均提供了ASLR支持。
  • Apple在iOS 4.3內導入了ASLR

bounds checking 邊界檢查

  • 對mach-o header中的許多內容添加了重要的邊界檢查功能,從而能夠避免惡意二進制數據的注入

share cache 共享代碼

  • share cache最先實在iOS3.1macOS Snow Leopard中被引入,用於徹底取代Prebinding
  • share cache是一個單文件,包含大多數系統dylib,因爲這些dylib合併成了一個文件,因此能夠進行優化。
    • 從新調整全部文本段(_TEXT)數據段(_DATA),並重寫整個符號表,以此來減少文件的大小,從而在每一個進程中僅掛載少許的區域。容許咱們打包二進制數據段,從而節省大量的RAM
    • 本質是一個dylib預連接器,它在RAM上的節約是顯著的,在普通的iOS程序中運行能夠節約500-1g內存
    • 還能夠預生成數據結構,用來供dyld和Ob-C在運行時使用。從而沒必要在程序啓動時作這些事情,這也會節約更多的RAM和時間
  • share cache在macOS上本地生成,運行dyld共享代碼,將大幅優化系統性能

1.2.3 dyld 3 2017-至今

  • dyld 3是2017年WWDC推出的全新的動態連接器,它徹底改變了動態連接的概念,且將成爲大多數macOS系統程序的默認設置。2017 Apple OS平臺上的全部系統程序都會默認使用dyld 3.
  • dyld 3最先是在2017年的iOS 11中引入,主要用來優化系統庫。
  • 而在iOS 13系統中,iOS全面採用新的dyld 3來替代以前的dyld 2,由於dyld 3徹底兼容dyld 2,其API接口也是同樣的,因此,在大部分狀況下,開發者並不須要作額外的適配就能平滑過渡。

2. 經過bt堆棧信息查看app啓動是從哪裏開始的

load方法處加一個斷點,經過bt堆棧信息查看app啓動是從哪裏開始的 經過程序運行發現,是從dyld中的_dyld_start開始的性能優化

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    
    
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

__attribute__((constructor)) void ypyFunc(){
    printf("來了 : %s \n",__func__);
}

@interface ViewController ()

@end

@implementation ViewController

+ (void)load{
    NSLog(@"%s",__func__);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

@end
複製代碼
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000104ca5f24 002-應用程加載分析`+[ViewController load](self=ViewController, _cmd="load") at ViewController.m:17:5
    frame #1: 0x00000001aafd735c libobjc.A.dylib`load_images + 984
    frame #2: 0x0000000104e0a190 dyld`dyld::notifySingle(dyld_image_states, ImageLoader const*, ImageLoader::InitializerTimingList*) + 448
    frame #3: 0x0000000104e1a0d8 dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 512
    frame #4: 0x0000000104e18520 dyld`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 184
    frame #5: 0x0000000104e185e8 dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 92
    frame #6: 0x0000000104e0a658 dyld`dyld::initializeMainExecutable() + 216
    frame #7: 0x0000000104e0eeb0 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 4400
    frame #8: 0x0000000104e09208 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 396
    frame #9: 0x0000000104e09038 dyld`_dyld_start + 56
(lldb)
複製代碼

3. _dyld_start 流程分析

dyld-852源碼中查找_dyld_start,查找arm64架構發現,是由彙編實現,經過彙編註釋發現會調用dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)方法,是一個C++方法markdown

https://yupuyang.gitlab.io/images/dashi/dyld/_dyld_start.png

3.1 dyldbootstrap::start源碼

源碼中搜索dyldbootstrap找到命名做用空間,再在這個文件中查找start方法,其核心是返回值的調用了dyldmain函數,其中macho_headerMach-O的頭部,而dyld加載的文件就是Mach-O類型的,即Mach-O類型是可執行文件類型,由四部分組成:Mach-O頭部、Load Command、section、Other Data,能夠經過MachOView查看可執行文件信息數據結構

//
// This is code to bootstrap dyld. This work in normally done for a program by dyld and crt.
// In dyld we have to do this manually.
//
uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[], const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue) {

    // 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
    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;

	// set up random value for stack canary
	__guard_setup(apple);

#if DYLD_INITIALIZER_SUPPORT
	// run all C++ initializers inside dyld
	runDyldInitializers(argc, argv, envp, apple);
#endif

	_subsystem_init(apple);

	// now that we are done bootstrapping dyld, call dyld's main
	uintptr_t appsSlide = appsMachHeader->getSlide();
	return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}
複製代碼

3.2 dyld::_main函數源碼分析

進入dyld::_main的源碼實現,特別長,大約600多行,若是對dyld加載流程不太瞭解的童鞋,能夠根據_main函數的返回值進行反推,這裏就多做說明。在_main函數中主要作了一下幾件事情:架構

  • 【第一步:條件準備:環境,平臺,版本,路徑,主機信息】:根據環境變量設置相應的值以及獲取當前運行架構app

    https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/65cd7f372e454338a8e1e9529ce491a2~tplv-k3u1fbpfcp-zoom-1.image

  • 【第二步:加載共享緩存】:檢查是否開啓了共享緩存,以及共享緩存是否映射到共享區域,例如UIKitCoreFoundationdom

    https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e025e01427084aa5a2f40ce1a631399b~tplv-k3u1fbpfcp-zoom-1.image

  • 【第三步:主程序的初始化】:調用instantiateFromLoadedImage函數實例化了一個ImageLoader對象

    https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3725482bcff949cbb781e47548674632~tplv-k3u1fbpfcp-zoom-1.image

  • 【第四步:插入動態庫】:遍歷DYLD_INSERT_LIBRARIES環境變量,調用loadInsertedDylib加載

    https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c904dd0f95e04b8989633781a3cf6500~tplv-k3u1fbpfcp-zoom-1.image

  • 【第五步:link 主程序

    https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4ed269d91e354918ab6d88b57c917920~tplv-k3u1fbpfcp-zoom-1.image

  • 【第六步:link 動態庫

    https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/356c8e943e92466fbb7489ac6c69f68d~tplv-k3u1fbpfcp-zoom-1.image

  • 【第七步:弱引用綁定

    https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5928d6b86b25467892129740df49e411~tplv-k3u1fbpfcp-zoom-1.image

  • 【第八步:執行初始化方法

    https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/295d2aef374f40e58eafc8ef095a32e0~tplv-k3u1fbpfcp-zoom-1.image

  • 【第九步:尋找主程序入口main函數】:從Load Command讀取LC_MAIN入口,若是沒有,就讀取LC_UNIXTHREAD,這樣就來到了平常開發中熟悉的main函數了

    https://yupuyang.gitlab.io/images/dashi/dyld/find_entry_point_for_main_executable.png

下面主要分析下【第三步】和【第八步】

3.2.1 重點介紹 第三步:主程序初始化

  • sMainExecutable表示主程序變量,查看其賦值,是經過instantiateFromLoadedImage方法初始化

    // instantiate ImageLoader for main executable
    // 【第三步:主程序的初始化】
    		sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
    		gLinkContext.mainExecutable = sMainExecutable;
    		gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
    複製代碼

    instantiateFromLoadedImage初始化主程序

  • 進入instantiateFromLoadedImage源碼,其中建立一個ImageLoader實例對象,經過instantiateMainExecutable方法建立

    // The kernel maps in main executable before dyld gets control. We need to 
    // make an ImageLoader* for the already mapped in main executable.
    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);
    		addImage(image);
    		return (ImageLoaderMachO*)image;
    // }
    	
    // throw "main executable not a known format";
    }
    複製代碼
  • 進入instantiateMainExecutable源碼,其做用是爲主可執行文件建立映像,返回一個ImageLoader類型的image對象,即主程序。其中sniffLoadCommands函數時獲取Mach-O類型文件Load Command的相關信息,並對其進行各類校驗

    // 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;
    	sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
    	// instantiate concrete class based on content of load commands
    	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
    }
    複製代碼

3.2.2 重點介紹 第八步:執行初始化方法

  • 進入initializeMainExecutable源碼,主要是循環遍歷,都會執行runInitializers方法

    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]);
    }
    複製代碼
  • 全局搜索runInitializers(cons,找到以下源碼,其核心代碼是processInitializers函數的調用

    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);
    }
    複製代碼
  • 進入processInitializers函數的源碼實現,其中對鏡像列表調用recursiveInitialization函數進行遞歸實例化

    // <rdar://problem/14412057> upward dylib initializers can be run too soon
    // To handle dangling dylibs which are upward linked but not downward, all upward linked dylibs
    // have their initialization postponed until after the recursion through downward dylibs
    // has completed.
    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.
    //在鏡像列表中的全部鏡像上調用遞歸實例化,以創建未初始化的向上依賴關係的新列表
    	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);
    }
    複製代碼
  • 全局搜索recursiveInitialization(cons函數,其源碼實現以下

    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
    // 讓objc 知道咱們要加載此鏡像
    			uint64_t t1 = mach_absolute_time();
    			fState = dyld_image_state_dependents_initialized;
    			oldState = fState;
    			context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
    			
    			// initialize this image 初始化鏡像
    			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();//遞歸解鎖
    }
    複製代碼

在這裏,須要分紅兩部分探索,一部分是notifySingle函數,一部分是doInitialization函數,首先探索notifySingle函數

3.2.2.1 notifySingle 函數

  • 全局搜索notifySingle(函數,其重點是(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());這句

    static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo) {
    	//dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath());
    	std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sSingleHandlers);
    	if ( handlers != NULL ) {
    		dyld_image_info info;
    		info.imageLoadAddress	= image->machHeader();
    		info.imageFilePath		= image->getRealPath();
    		info.imageFileModDate	= image->lastModified();
    		for (std::vector<dyld_image_state_change_handler>::iterator it = handlers->begin(); it != handlers->end(); ++it) {
    			const char* result = (*it)(state, 1, &info);
    			if ( (result != NULL) && (state == dyld_image_state_mapped) ) { 
    				//fprintf(stderr, " image rejected by handler=%p\n", *it);
    				// make copy of thrown string so that later catch clauses can free it
    				const char* str = strdup(result);
    				throw str;
    			}
    		}
    	}
    	if ( state == dyld_image_state_mapped ) {//是否被映射
    		// <rdar://problem/7008875> Save load addr + UUID for images from outside the shared cache
    		// <rdar://problem/50432671> Include UUIDs for shared cache dylibs in all image info when using private mapped shared caches
    // 保存來自共享混存外部的鏡像地址 + UUID
    		if (!image->inSharedCache()
    			|| (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion)) {
    			dyld_uuid_info info;
    			if ( image->getUUID(info.imageUUID) ) {
    				info.imageLoadAddress = image->machHeader();
    				addNonSharedCacheImageUUID(info);
    			}
    		}
    	}
    	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);
    		}
    	}
        // mach message csdlc about dynamically unloaded images
    	if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) {
    		notifyKernel(*image, false);
    		const struct mach_header* loadAddress[] = { image->machHeader() };
    		const char* loadPath[] = { image->getPath() };
    		notifyMonitoringDyld(true, 1, loadAddress, loadPath);
    	}
    }
    複製代碼
  • 全局搜索sNotifyObjCInit,發現沒有找到實現,有賦值操做

    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;//重點
    	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在哪裏調用了,發如今_dyld_objc_notify_register進行了調用注意:_dyld_objc_notify_register的函數須要在libobjc源碼中搜索

    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);
    }
    複製代碼
  • objc4-818.2源碼中搜索_dyld_objc_notify_register,發如今_objc_init源碼中調用了該方法,並傳入了參數,因此sNotifyObjCInit賦值的就是objc中的load_images,而load_images會調用全部的+load方法。因此綜上所述,notifySingle是一個回調函數

    /*********************************************************************** * _objc_init * Bootstrap initialization. Registers our image notifier with dyld. * Called by libSystem BEFORE library initialization time **********************************************************************/
    
    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(&map_images, load_images, unmap_image);//重點
    
    #if __OBJC2__
        didCallDyldNotifyRegister = true;
    #endif
    }
    複製代碼

load函數加載

下面咱們進入load_images的源碼看看其實現,以此來證實load_images中調用了全部的load函數

  • 經過objc源碼中_objc_init源碼實現,進入load_images的源碼實現

    void 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)
        call_load_methods();
    }
    複製代碼
  • 進入call_load_methods源碼實現,能夠發現其核心是經過do-while循環調用+load方法

    /*********************************************************************** * call_load_methods * Call all pending class and category +load methods. * Class +load methods are called superclass-first. * Category +load methods are not called until after the parent class's +load. * * This method must be RE-ENTRANT, because a +load could trigger * more image mapping. In addition, the superclass-first ordering * must be preserved in the face of re-entrant calls. Therefore, * only the OUTERMOST call of this function will do anything, and * that call will handle all loadable classes, even those generated * while it was running. * * The sequence below preserves +load ordering in the face of * image loading during a +load, and make sure that no * +load method is forgotten because it was added during * a +load call. * Sequence: * 1. Repeatedly call class +loads until there aren't any more * 2. Call category +loads ONCE. * 3. Run more +loads if: * (a) there are more classes to load, OR * (b) there are some potential category +loads that have * still never been attempted. * Category +loads are only run once to ensure "parent class first" * ordering, even if a category +load triggers a new loadable class * and a new loadable category attached to that class. * * Locking: loadMethodLock must be held by the caller * All other locks must not be held. **********************************************************************/
    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. Repeatedly call class +loads until there aren't any more
            while (loadable_classes_used > 0) {
                call_class_loads();
            }
    
            // 2. Call category +loads ONCE
            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;
    }
    複製代碼
  • 進入call_class_loads源碼實現,瞭解到這裏調用的load方法證明咱們前文說起的類的load方法

    /*********************************************************************** * call_class_loads * Call all pending class +load methods. * If new classes become loadable, +load is NOT called for them. * * Called only by call_load_methods(). **********************************************************************/
    static void call_class_loads(void) {
        int i;
        
        // Detach current loadable list.
        struct loadable_class *classes = loadable_classes;
        int used = loadable_classes_used;
        loadable_classes = nil;
        loadable_classes_allocated = 0;
        loadable_classes_used = 0;
        
        // Call all +loads for the detached list.
        for (i = 0; i < used; i++) {
            Class cls = classes[i].cls;
            load_method_t load_method = (load_method_t)classes[i].method;
            if (!cls) continue; 
    
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
            }
            (*load_method)(cls, @selector(load));
        }
        
        // Destroy the detached list.
        if (classes) free(classes);
    }
    複製代碼

因此,load_images調用了全部的load函數,以上的源碼分析過程正好對應堆棧的打印信息

【總結】load的源碼鏈爲:_dyld_start --> dyldbootstrap::start --> dyld::_main --> dyld::initializeMainExecutable --> ImageLoader::runInitializers --> ImageLoader::processInitializers --> ImageLoader::recursiveInitialization --> dyld::notifySingle(是一個回調處理) --> sNotifyObjCInit --> load_images(libobjc.A.dylib)

那麼問題又來了,_objc_init是何時調用的呢?請接着往下看

3.2.2.2 doInitialization 函數

  • 走到objc_objc_init函數,發現走不通了,咱們回退到recursiveInitialization遞歸函數的源碼實現,發現咱們忽略了一個函數doInitialization

    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
    // 讓objc 知道咱們要加載此鏡像
    			uint64_t t1 = mach_absolute_time();
    			fState = dyld_image_state_dependents_initialized;
    			oldState = fState;
    			context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
    			
    			// initialize this image 初始化鏡像 //重點
    			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();//遞歸解鎖
    }
    複製代碼
  • 進入doInitialization函數的源碼實現這裏也須要分紅兩部分,一部分是doImageInit函數,一部分是doModInitFunctions函數

    bool ImageLoaderMachO::doInitialization(const LinkContext& context) {
    	CRSetCrashLogMessage2(this->getPath());
    
    	// mach-o has -init and static initializers
    	doImageInit(context);
    	doModInitFunctions(context);
    	
    	CRSetCrashLogMessage2(NULL);
    	
    	return (fHasDashInit || fHasInitializers);
    }
    複製代碼
    • 進入doImageInit源碼實現,其核心主要是for循環加載方法的調用,這裏須要注意的一點是,libSystem的初始化必須先運行

      void ImageLoaderMachO::doImageInit(const LinkContext& context) {
      	if ( fHasDashInit ) {
      		const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
      		const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
      		const struct load_command* cmd = cmds;
      		for (uint32_t i = 0; i < cmd_count; ++i) {
      			switch (cmd->cmd) {
      				case LC_ROUTINES_COMMAND:
      					Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide);
      #if __has_feature(ptrauth_calls)
      					func = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
      #endif
      					// <rdar://problem/8543820&9228031> verify initializers are in image
      					if ( ! this->containsAddress(stripPointer((void*)func)) ) {
      						dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
      					}
      					if ( ! dyld::gProcessInfo->libSystemInitialized ) {//libSystem初始化程序必須先運行,優先級很高
      						// <rdar://problem/17973316> libSystem initializer must run first
      						dyld::throwf("-init function in image (%s) that does not link with libSystem.dylib\n", this->getPath());
      					}
      					if ( context.verboseInit )
      						dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath());
      					{
      						dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
      						func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
      					}
      					break;
      			}
      			cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
      		}
      	}
      }
      複製代碼
    • 進入doModInitFunctions源碼實現,這個方法中加載了全部Cxx文件能夠經過測試程序的堆棧信息來驗證,在C++方法處加一個斷點

      void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) {
      	if ( fHasInitializers ) {
      		const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
      		const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
      		const struct load_command* cmd = cmds;
      		for (uint32_t i = 0; i < cmd_count; ++i) {
      			if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
      				const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
      				const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
      				const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
      				for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
      					const uint8_t type = sect->flags & SECTION_TYPE;
      					if ( type == S_MOD_INIT_FUNC_POINTERS ) {....}
      					else if ( type == S_INIT_FUNC_OFFSETS ) {....}
      			}
      			cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
      		}
      	}
      }
      複製代碼

      https://yupuyang.gitlab.io/images/dashi/dyld/cxx_bt.png

走到這裏,仍是沒有找到_objc_init的調用?怎麼辦呢?放棄嗎?固然不行,咱們還能夠經過_objc_init加一個符號斷點來查看調用_objc_init前的堆棧信息,

  • _objc_init加一個符號斷點,運行程序,查看_objc_init斷住後的堆棧信息

    https://yupuyang.gitlab.io/images/dashi/dyld/_objc_init_bt.png

  • libsystem Libsystem-1292.60.1 中查找libSystem_initializer,查看其中的實現

    https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/95e1f9998a3c473baaf111a97ec615b0~tplv-k3u1fbpfcp-zoom-1.image

    libSystem_initializer源碼實現

    // libsyscall_initializer() initializes all of libSystem.dylib
    // <rdar://problem/4892197>
    __attribute__((constructor))
    static void libSystem_initializer(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars) {
    	....
    	_libSystem_ktrace0(ARIADNE_LIFECYCLE_libsystem_init | DBG_FUNC_START);
    
    	__libkernel_init(&libkernel_funcs, envp, apple, vars);
    	_libSystem_ktrace_init_func(KERNEL);
    
    	__libplatform_init(NULL, envp, apple, vars);
    	_libSystem_ktrace_init_func(PLATFORM);
    
    	__pthread_init(&libpthread_funcs, envp, apple, vars);
    	_libSystem_ktrace_init_func(PTHREAD);
    
    	_libc_initializer(&libc_funcs, envp, apple, vars);
    	_libSystem_ktrace_init_func(LIBC);
    
    	// TODO: Move __malloc_init before __libc_init after breaking malloc's upward link to Libc
    	// Note that __malloc_init() will also initialize ASAN when it is present
    	__malloc_init(apple);
    	_libSystem_ktrace_init_func(MALLOC);
    
    #if TARGET_OS_OSX
    	/* <rdar://problem/9664631> */
    	__keymgr_initializer();
    	_libSystem_ktrace_init_func(KEYMGR);
    #endif
    
    	_dyld_initializer();//dyld 初始化
    	_libSystem_ktrace_init_func(DYLD);
    
    	libdispatch_init();// dispatch 初始化
    	_libSystem_ktrace_init_func(LIBDISPATCH);
    
    #if !TARGET_OS_DRIVERKIT
    	_libxpc_initializer();
    	_libSystem_ktrace_init_func(LIBXPC);
    
    #if CURRENT_VARIANT_asan
    	setenv("DT_BYPASS_LEAKS_CHECK", "1", 1);
    #endif
    #endif // !TARGET_OS_DRIVERKIT
    
    	// must be initialized after dispatch
    	_libtrace_init();
    	_libSystem_ktrace_init_func(LIBTRACE);
    
    #if !TARGET_OS_DRIVERKIT
    #if defined(HAVE_SYSTEM_SECINIT)
    	_libsecinit_initializer();
    	_libSystem_ktrace_init_func(SECINIT);
    #endif
    
    #if defined(HAVE_SYSTEM_CONTAINERMANAGER)
    	_container_init(apple);
    	_libSystem_ktrace_init_func(CONTAINERMGR);
    #endif
    
    	__libdarwin_init();
    	_libSystem_ktrace_init_func(DARWIN);
    #endif // !TARGET_OS_DRIVERKIT
    
    	__stack_logging_early_finished(&malloc_funcs);
    .....
    }
    複製代碼
  • 根據前面的堆棧信息,咱們發現走的是libSystem_initializer中會調用libdispatch_init函數,而這個函數的源碼是在libdispatch開源庫中的, libdispatch-1271.120.2 源碼libdispatch中搜索 libdispatch_init

    DISPATCH_EXPORT DISPATCH_NOTHROW void libdispatch_init(void) {
    	dispatch_assert(sizeof(struct dispatch_apply_s) <=
    			DISPATCH_CONTINUATION_SIZE);
    
    	if (_dispatch_getenv_bool("LIBDISPATCH_STRICT", false)) {
    		_dispatch_mode |= DISPATCH_MODE_STRICT;
    	}
    
    #if DISPATCH_DEBUG || DISPATCH_PROFILE
    #if DISPATCH_USE_KEVENT_WORKQUEUE
    	if (getenv("LIBDISPATCH_DISABLE_KEVENT_WQ")) {
    		_dispatch_kevent_workqueue_enabled = false;
    	}
    #endif
    #endif
    
    #if HAVE_PTHREAD_WORKQUEUE_QOS
    	dispatch_qos_t qos = _dispatch_qos_from_qos_class(qos_class_main());
    	_dispatch_main_q.dq_priority = _dispatch_priority_make(qos, 0);
    #if DISPATCH_DEBUG
    	if (!getenv("LIBDISPATCH_DISABLE_SET_QOS")) {
    		_dispatch_set_qos_class_enabled = 1;
    	}
    #endif
    #endif
    
    #if DISPATCH_USE_THREAD_LOCAL_STORAGE
    	_dispatch_thread_key_create(&__dispatch_tsd_key, _libdispatch_tsd_cleanup);
    #else
    	_dispatch_thread_key_create(&dispatch_priority_key, NULL);
    	_dispatch_thread_key_create(&dispatch_r2k_key, NULL);
    	_dispatch_thread_key_create(&dispatch_queue_key, _dispatch_queue_cleanup);
    	_dispatch_thread_key_create(&dispatch_frame_key, _dispatch_frame_cleanup);
    	_dispatch_thread_key_create(&dispatch_cache_key, _dispatch_cache_cleanup);
    	_dispatch_thread_key_create(&dispatch_context_key, _dispatch_context_cleanup);
    	_dispatch_thread_key_create(&dispatch_pthread_root_queue_observer_hooks_key,
    			NULL);
    	_dispatch_thread_key_create(&dispatch_basepri_key, NULL);
    #if DISPATCH_INTROSPECTION
    	_dispatch_thread_key_create(&dispatch_introspection_key , NULL);
    #elif DISPATCH_PERF_MON
    	_dispatch_thread_key_create(&dispatch_bcounter_key, NULL);
    #endif
    	_dispatch_thread_key_create(&dispatch_wlh_key, _dispatch_wlh_cleanup);
    	_dispatch_thread_key_create(&dispatch_voucher_key, _voucher_thread_cleanup);
    	_dispatch_thread_key_create(&dispatch_deferred_items_key,
    			_dispatch_deferred_items_cleanup);
    #endif
    	pthread_key_create(&_os_workgroup_key, _os_workgroup_tsd_cleanup);
    #if DISPATCH_USE_RESOLVERS // rdar://problem/8541707
    	_dispatch_main_q.do_targetq = _dispatch_get_default_queue(true);
    #endif
    
    	_dispatch_queue_set_current(&_dispatch_main_q);
    	_dispatch_queue_set_bound_thread(&_dispatch_main_q);
    
    #if DISPATCH_USE_PTHREAD_ATFORK
    	(void)dispatch_assume_zero(pthread_atfork(dispatch_atfork_prepare,
    			dispatch_atfork_parent, dispatch_atfork_child));
    #endif
    	_dispatch_hw_config_init();
    	_dispatch_time_init();
    	_dispatch_vtable_init();
    	_os_object_init();//重點
    	_voucher_init();
    	_dispatch_introspection_init();
    }
    複製代碼
  • 進入_os_object_init源碼實現,其源碼實現調用了_objc_init函數結合上面的分析,從初始化_objc_init註冊的_dyld_objc_notify_register的參數2,即load_images,到sNotifySingle --> sNotifyObjCInie=參數2 到sNotifyObjcInit()調用,造成了一個閉環

    void
    _os_object_init(void)
    {
    	_objc_init();// 重點
    	Block_callbacks_RR callbacks = {
    		sizeof(Block_callbacks_RR),
    		(void (*)(const void *))&objc_retain,
    		(void (*)(const void *))&objc_release,
    		(void (*)(const void *))&_os_objc_destructInstance
    	};
    	_Block_use_RR2(&callbacks);
    #if DISPATCH_COCOA_COMPAT
    	const char *v = getenv("OBJC_DEBUG_MISSING_POOLS");
    	if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
    	v = getenv("DISPATCH_DEBUG_MISSING_POOLS");
    	if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
    	v = getenv("LIBDISPATCH_DEBUG_MISSING_POOLS");
    	if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
    #endif
    }
    複製代碼

因此能夠簡單的理解爲sNotifySingle這裏是添加通知即addObserver_objc_init中調用_dyld_objc_notify_register至關於發送通知,即push,而sNotifyObjcInit至關於通知的處理函數,即selector

【總結】:_objc_init的源碼鏈:_dyld_start --> dyldbootstrap::start --> dyld::_main --> dyld::initializeMainExecutable --> ImageLoader::runInitializers --> ImageLoader::processInitializers --> ImageLoader::recursiveInitialization --> doInitialization -->libSystem_initializer(libSystem.B.dylib) --> _os_object_init(libdispatch.dylib) --> _objc_init(libobjc.A.dylib)

3.2.3 重點介紹 第九步:尋找主入口函數

  • 彙編調試,能夠看到顯示來到+[ViewController load]方法

    https://yupuyang.gitlab.io/images/dashi/dyld/loadbreakpoint.png

  • 繼續執行,來到ypyFunc的C++函數

    https://yupuyang.gitlab.io/images/dashi/dyld/ypyFunc.png

  • 點擊stepover,繼續往下,跑完了整個流程,會回到_dyld_start,而後調用main()函數,經過彙編完成main的參數賦值等操做dyld彙編源碼實現

    https://yupuyang.gitlab.io/images/dashi/dyld/_dyld_start2.png

    彙編調試回到_dyld_start LC_MAIN case, set up stack for call to main()

    #if __arm64__ && !TARGET_OS_SIMULATOR
    	.text
    	.align 2
    	.globl __dyld_start
    __dyld_start:
    	mov 	x28, sp
    	and     sp, x28, #~15		// force 16-byte alignment of stack
    	mov	x0, #0
    	mov	x1, #0
    	stp	x1, x0, [sp, #-16]!	// make aligned terminating frame
    	mov	fp, sp			// set up fp to point to terminating frame
    	sub	sp, sp, #16             // make room for local variables
    #if __LP64__
    	ldr     x0, [x28]               // get app's mh into x0
    	ldr     x1, [x28, #8]           // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
    	add     x2, x28, #16            // get argv into x2
    #else
    	ldr     w0, [x28]               // get app's mh into x0
    	ldr     w1, [x28, #4]           // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
    	add     w2, w28, #8             // get argv into x2
    #endif
    	adrp	x3,___dso_handle@page
    	add 	x3,x3,___dso_handle@pageoff // get dyld's mh in to x4
    	mov	x4,sp                   // x5 has &startGlue
    
    	// call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
    	bl	__ZN13dyldbootstrap5startEPKN5dyld311MachOLoadedEiPPKcS3_Pm
    	mov	x16,x0                  // save entry point address in x16
    #if __LP64__
    	ldr     x1, [sp]
    #else
    	ldr     w1, [sp]
    #endif
    	cmp	x1, #0
    	b.ne	Lnew
    
    	// LC_UNIXTHREAD way, clean up stack and jump to result
    #if __LP64__
    	add	sp, x28, #8             // restore unaligned stack pointer without app mh
    #else
    	add	sp, x28, #4             // restore unaligned stack pointer without app mh
    #endif
    #if __arm64e__
    	braaz   x16                     // jump to the program's entry point
    #else
    	br      x16                     // jump to the program's entry point
    #endif
    
    	// LC_MAIN case, set up stack for call to main()
    Lnew:	mov	lr, x1		    // simulate return address into _start in libdyld.dylib
    #if __LP64__
    	ldr	x0, [x28, #8]       // main param1 = argc
    	add	x1, x28, #16        // main param2 = argv
    	add	x2, x1, x0, lsl #3
    	add	x2, x2, #8          // main param3 = &env[0]
    	mov	x3, x2
    Lapple:	ldr	x4, [x3]
    	add	x3, x3, #8
    #else
    	ldr	w0, [x28, #4]       // main param1 = argc
    	add	x1, x28, #8         // main param2 = argv
    	add	x2, x1, x0, lsl #2
    	add	x2, x2, #4          // main param3 = &env[0]
    	mov	x3, x2
    Lapple:	ldr	w4, [x3]
    	add	x3, x3, #4
    #endif
    	cmp	x4, #0
    	b.ne	Lapple		    // main param4 = apple
    #if __arm64e__
    	braaz   x16
    #else
    	br      x16
    #endif
    
    #endif // __arm64__ && !TARGET_OS_SIMULATOR
    複製代碼

    dyld中main部分的彙編源碼實現

注意:main是寫定的函數,寫入內存,讀取到dyld,若是修改了main函數的名稱,會報錯

https://yupuyang.gitlab.io/images/dashi/dyld/rename_main.png

因此,綜上所述,最終dyld加載流程,以下圖所示,圖中也詮釋了前文中的問題:爲何是load-->Cxx-->main的調用順序

https://yupuyang.gitlab.io/images/dashi/dyld/dyld_blog.jpg

🌹 喜歡就點個贊吧👍🌹

🌹 以爲有收穫的,能夠來一波,收藏+關注,評論 + 轉發,以避免你下次找不到我😁🌹

🌹歡迎你們留言交流,批評指正,互相學習😁,提高自我🌹

相關文章
相關標籤/搜索