前面所學(xué)內(nèi)容回顧
- 1.對(duì)象底層-結(jié)構(gòu)體-alloc
- 2.對(duì)象的本質(zhì)-isa
- 3.類的結(jié)構(gòu) isa superclass cache bit-data() methodList?rw
- 4.cache:方法 bucket mask insert
- 5.objc_msgSend 消息的發(fā)送,快速查找
- 6.慢速查找,二分查找
- 7.動(dòng)態(tài)方法決議
- 8.消息轉(zhuǎn)發(fā):快速+慢速
應(yīng)用程序的加載過(guò)程
先了解App的編譯過(guò)程,可參考后面的博客LLVM,生成可執(zhí)行二進(jìn)制文件的過(guò)程
.h.m文件 -> 預(yù)編譯 -> 編譯 -> 匯編 -> 鏈接 -> 可執(zhí)行文件(存在于.app文件顯示包內(nèi)容)

實(shí)質(zhì),由后面LLVM得知,編譯分為七步,第六步鏈接器把編譯產(chǎn)生的.o目標(biāo)文件和.o目標(biāo)文件中應(yīng)用到的動(dòng)態(tài)庫(kù)(綁定)靜態(tài)庫(kù)(.dylib .a復(fù)制拷貝合并)文件進(jìn)行鏈接,生成一個(gè)mach-o文件即可執(zhí)行文件。


靜態(tài)庫(kù):編譯時(shí)會(huì)被完整的復(fù)制到可執(zhí)行文件中,被多次使用就有多份拷貝。靜態(tài)庫(kù)的加載在編譯期。靜態(tài)庫(kù)的后綴名是以.a或.framework結(jié)尾。

動(dòng)態(tài)庫(kù):鏈接時(shí)不復(fù)制,程序運(yùn)行時(shí)由系統(tǒng)動(dòng)態(tài)加載到內(nèi)存,系統(tǒng)只加載一次,多個(gè)程序共用(如系統(tǒng)的UIKit.framework等),節(jié)省內(nèi)存。動(dòng)態(tài)庫(kù)的加載在運(yùn)行期,把相同的庫(kù)用一份共享庫(kù)的實(shí)例加載進(jìn)來(lái),節(jié)省了整個(gè)內(nèi)存。共享內(nèi)存,節(jié)約資源,減少App打包后的大小。動(dòng)態(tài)庫(kù)的后綴名可以是.dylib或.tbd或.framework結(jié)尾,所有的系統(tǒng)庫(kù)都屬于動(dòng)態(tài)庫(kù),在iOS中一般使用framework作為動(dòng)態(tài)庫(kù)。

在使用app時(shí),靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)都會(huì)被加載到內(nèi)存中。當(dāng)多個(gè)app使用同一個(gè)庫(kù)時(shí),如果這個(gè)庫(kù)是動(dòng)態(tài)庫(kù),由于動(dòng)態(tài)庫(kù)是可以被多個(gè)app的進(jìn)程共用的,所以在內(nèi)存中只會(huì)存在一份;如果是靜態(tài)庫(kù),由于每個(gè)app的mach-o文件中都會(huì)存在一份,則會(huì)存在多份。相對(duì)靜態(tài)庫(kù),使用動(dòng)態(tài)庫(kù)可以減少app占用的內(nèi)存大小。
另外,使用動(dòng)態(tài)庫(kù)可以縮短app的啟動(dòng)時(shí)間。原因是,使用動(dòng)態(tài)庫(kù)時(shí),app的mach-o文件都會(huì)比較小;app依賴的動(dòng)態(tài)庫(kù)可能已經(jīng)存在于內(nèi)存中了(其他已啟動(dòng)的app也依賴了這個(gè)動(dòng)態(tài)庫(kù)),所以不需要重復(fù)加載。
動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù),編譯靜態(tài)庫(kù)體積更小,因?yàn)閯?dòng)態(tài)庫(kù)還會(huì)保留一些外部符號(hào),給外部調(diào)用,靜態(tài)庫(kù)合并成一個(gè)文件,不需要符號(hào)了,靜態(tài)庫(kù)沒(méi)有符號(hào)更小一點(diǎn)
之前總是有一個(gè)思維誤區(qū),說(shuō)iOS不能使用動(dòng)態(tài)庫(kù),否則Apple不給上架AppStore,準(zhǔn)確的來(lái)說(shuō),這句話是錯(cuò)誤的。?。。〔荒苌霞苁褂玫膭?dòng)態(tài)庫(kù)是使用了dlopen方式在運(yùn)行時(shí)加載動(dòng)態(tài)庫(kù)
使用動(dòng)態(tài)庫(kù)
使用動(dòng)態(tài)庫(kù)有兩種方式,一種是將動(dòng)態(tài)庫(kù)添加為依賴庫(kù),這樣會(huì)在工程啟動(dòng)時(shí)加載動(dòng)態(tài)庫(kù),一種是使用dlopen在運(yùn)行時(shí)加載動(dòng)態(tài)庫(kù),這兩種方式的區(qū)別在于加載動(dòng)態(tài)庫(kù)的時(shí)機(jī)。
在iOS中一般使用第一種方法,第二種方式一般在mac開(kāi)發(fā)中使用,如果在iOS中使用了這種方式,是不能上架到App Store的。
創(chuàng)建動(dòng)態(tài)庫(kù)
1.創(chuàng)建一個(gè)新工程,選擇iOS -> Cocoa Touch Framework

2.實(shí)現(xiàn)framework并指定對(duì)外的頭文件,指定DynamicDemo.h和BCPerson.h為對(duì)外的頭文件
#import <Foundation/Foundation.h>
//! Project version number for DynamicDemo.
FOUNDATION_EXPORT double DynamicDemoVersionNumber;
//! Project version string for DynamicDemo.
FOUNDATION_EXPORT const unsigned char DynamicDemoVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <DynamicDemo/PublicHeader.h>
#import <DynamicDemo/BCPerson.h>
@interface BCPerson : NSObject
@property (nonatomic, copy) NSString *name ;
- (void)watch;
- (void)eat;
@end

DynamicDemo動(dòng)態(tài)庫(kù)編譯后,在Products中會(huì)生成DynamicDemo.framework動(dòng)態(tài)庫(kù)
指定framework的架構(gòu)模式,這里選擇了Generic iOS Device機(jī)型,然后build一下,就會(huì)創(chuàng)建一個(gè)通用mach-o文件,包含了arm64和arm_v7兩種架構(gòu)。如果選擇了模擬器,會(huì)創(chuàng)建一個(gè)x86_64架構(gòu)的mach-o文件。
需要注意的是,App和它依賴的framework的架構(gòu)必須兼容,也就是說(shuō),在創(chuàng)建可執(zhí)行文件時(shí),要么都是真機(jī),要么都是模擬器。當(dāng)然,也可以分別在真機(jī)和模擬器兩種模式下創(chuàng)建framwork,然后使用lipo命令來(lái)將兩個(gè)framework內(nèi)部的同名mach-o文件合并成一個(gè)通用mach-o文件,這樣,不管App是什么架構(gòu)模式,都能正確使用這個(gè)framework了。
查看.framework是動(dòng)態(tài)庫(kù)靜態(tài)庫(kù)的方法
(1)cd xx.framework
(2)file xx 注釋:xx為.framwork下的二進(jìn)制文件
(3)判斷:靜態(tài)庫(kù)包含“current ar archive random library”字樣。動(dòng)態(tài)庫(kù)包含“dynamically linked shared library”字樣

使用動(dòng)態(tài)庫(kù)
1.創(chuàng)建一個(gè)新的工程DynamicFrameworkDemo,并引入DynamicDemo.framework,在main.m文件中調(diào)用這個(gè)framework中的方法



2.Cmd + B編譯DynamicFrameworkDemo,在products中生成的DynamicFrameworkDemo.app中右鍵顯示包內(nèi)容,發(fā)現(xiàn)多了一個(gè)Frameworks文件夾,此文件夾是用來(lái)專門(mén)存放自定義的動(dòng)態(tài)庫(kù)的。

動(dòng)態(tài)庫(kù)加載時(shí)間優(yōu)化
對(duì)動(dòng)態(tài)庫(kù)加載的時(shí)間優(yōu)化。每個(gè)App都進(jìn)行動(dòng)態(tài)庫(kù)加載,其中系統(tǒng)級(jí)別的動(dòng)態(tài)庫(kù)占據(jù)了絕大數(shù),而針對(duì)系統(tǒng)級(jí)別的動(dòng)態(tài)庫(kù)都是經(jīng)過(guò)系統(tǒng)高度優(yōu)化的,不用擔(dān)心時(shí)間的花費(fèi)。我們應(yīng)該關(guān)注于自己集成到App的那些動(dòng)態(tài)庫(kù),這也是最能消耗加載時(shí)間的地方。蘋(píng)果的建議是減少在App里動(dòng)態(tài)庫(kù)的集成或者有可能地將其多個(gè)動(dòng)態(tài)庫(kù)最終集成一個(gè)動(dòng)態(tài)庫(kù)后進(jìn)行導(dǎo)入,盡量保證將App現(xiàn)有的非系統(tǒng)級(jí)的動(dòng)態(tài)庫(kù)個(gè)數(shù)保證在6個(gè)以內(nèi)。
后面LLVM中得知,APP啟動(dòng)時(shí)候pre-main會(huì)加載動(dòng)態(tài)庫(kù),動(dòng)態(tài)庫(kù)太多會(huì)耗時(shí),增加App的啟動(dòng)時(shí)間,dylib loading,下圖是微信的pre-main時(shí)間,微信使用的動(dòng)態(tài)庫(kù)個(gè)數(shù),在Frameworks下剛剛好是蘋(píng)果官方建議的6個(gè)。


繼續(xù)回到應(yīng)用程序的加載
如下文件的執(zhí)行順序:load->C++函數(shù)->main()?為什么是這個(gè)順序呢?


App運(yùn)行后,動(dòng)態(tài)庫(kù)加載,靜態(tài)庫(kù)加載,可執(zhí)行二進(jìn)制文件加載,怎么把這些文件鏈接在一起呢?怎么加載?誰(shuí)先初始化?誰(shuí)先實(shí)例化?誰(shuí)先init?
dyld鏈接器來(lái)進(jìn)行主動(dòng)鏈接

總結(jié):App啟動(dòng),在下層有很多動(dòng)靜態(tài)庫(kù),成為鏡像文件images,交給dyld處理,從加載的內(nèi)存中讀出來(lái),讀到相應(yīng)的表里面,一個(gè)一個(gè)加載到主程序,進(jìn)行l(wèi)ink,初始化,相應(yīng)的庫(kù)也要進(jìn)行初始化,如runtime的初始化方法_objc_init,libSystem.init,GCD_OSinit,libdispatchinit
查看dyld的入口,程序走入load方法斷點(diǎn),因?yàn)閘oad方法比C++函數(shù)和main函數(shù)先執(zhí)行,查看堆棧,入口是dyld`_dyld_start,也可以通過(guò)匯編查看,應(yīng)用程序的加載從dyld_start開(kāi)始,下載一份dyld源碼查找dyld_start入口



dyld.start中看到call dyldbootstrap::start(app_mh,argc,argv,dyld_mh,&startGlue)方法

//
// 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
// 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);
}
這兒的macho_header文件,macho頭文件,是編譯過(guò)程中相應(yīng)的header,編譯后會(huì)生成xxx.app里面有一個(gè)xxx的可執(zhí)行文件,拖到MachOview中查看

dyld:main函數(shù),6191-6828行代碼都是,精簡(jiǎn)為如下步驟
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
int argc, const char* argv[], const char* envp[], const char* apple[],
uintptr_t* startGlue)
{
......
// 設(shè)置運(yùn)行環(huán)境,可執(zhí)行文件準(zhǔn)備工作
......
// load shared cache 加載共享緩存
mapSharedCache();
......
reloadAllImages:
......
// instantiate ImageLoader for main executable 加載可執(zhí)行文件并生成一個(gè)ImageLoader實(shí)例對(duì)象,實(shí)例化主程序
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
......
// load any inserted libraries 加載插入的動(dòng)態(tài)庫(kù)
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
// link main executable 鏈接主程序
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
......
// link any inserted libraries 鏈接所有插入的動(dòng)態(tài)庫(kù)
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
image->setNeverUnloadRecursive();
}
if ( gLinkContext.allowInterposing ) {
// only INSERTED libraries can 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];
// 注冊(cè)符號(hào)插入
image->registerInterposing(gLinkContext);
}
}
}
......
//弱符號(hào)綁定
sMainExecutable->weakBind(gLinkContext);
sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);
......
// run all initializers 執(zhí)行初始化方法
initializeMainExecutable();
// notify any montoring proccesses that this process is about to enter main()
notifyMonitoringDyldMain();
return result;
}
返回了result,看result的賦值,找到主程序sMainExecutable,從而找sMainExecutale的賦值
// main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
*startGlue = 0;
// instantiate ImageLoader for main executable
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
gLinkContext.mainExecutable = sMainExecutable;
gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
instantiateFromLoadedImage方法
// 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";
}
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;
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
}
sniffLoadCommands方法進(jìn)行l(wèi)oadCommands,構(gòu)建整個(gè)主程序的命令格式,通過(guò)MachOView可以查看,之后addImage(image),返回主程序。


有了主程序之后,接下來(lái)dyld的主要步驟
1.設(shè)置運(yùn)行環(huán)境,環(huán)境變量配置,為可執(zhí)行文件的加載做準(zhǔn)備工作;
2.映射共享緩存到當(dāng)前進(jìn)程的邏輯內(nèi)存空間;共享緩存:UIKit.framework,Foundation.framework,CoreFoundation.framework
3.主程序的實(shí)例化:主程序?qū)嵗蓪?duì)象出來(lái)
4.加載插入的動(dòng)態(tài)庫(kù);
5.鏈接主程序;
6.鏈接插入的動(dòng)態(tài)庫(kù);
7.執(zhí)行弱符號(hào)綁定(weakBind);
8.執(zhí)行主程序所有的初始化方法;
9.查找程序入口并返回main( ).
dyld:main函數(shù),6191-6828行代碼主要做了哪些事情
1.判斷版本信息version,

2.讀取平臺(tái)信息(platforms),
// Set the platform ID in the all image infos so debuggers can tell the process type
// FIXME: This can all be removed once we make the kernel handle it in rdar://43369446
if (gProcessInfo->version >= 16) {
__block bool platformFound = false;
((dyld3::MachOFile*)mainExecutableMH)->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
if (platformFound) {
halt("MH_EXECUTE binaries may only specify one platform");
}
gProcessInfo->platform = (uint32_t)platform;
platformFound = true;
});
if (gProcessInfo->platform == (uint32_t)dyld3::Platform::unknown) {
// There were no platforms found in the binary. This may occur on macOS for alternate toolchains and old binaries.
// It should never occur on any of our embedded platforms.
#if __MAC_OS_X_VERSION_MIN_REQUIRED
gProcessInfo->platform = (uint32_t)dyld3::Platform::macOS;
#else
halt("MH_EXECUTE binaries must specify a minimum supported OS version");
#endif
}
}
3.MachOfile文件所在的地址,
4.設(shè)置上下文setContext路徑信息,
5.CRSetCrashMessage信息的綁定,
6.環(huán)境變量數(shù)組envp,
7.共享緩存進(jìn)行處理load shared cache mapSharedCache()

8.主程序的初始化,
9.動(dòng)態(tài)庫(kù)的加入loadInsertDylib(*lib)

10.進(jìn)行l(wèi)ink

11.run整個(gè)images,跑起來(lái)initializeMainExecutable()
12.進(jìn)入main()

initializeMainExecutable()探索
for循環(huán)遞歸,跑插入的動(dòng)態(tài)庫(kù),initializeMainExecutable()->processInitializers()->images.imagesAndPaths[i].first->recursiveInitialization()動(dòng)態(tài)庫(kù)可能會(huì)引入其他動(dòng)態(tài)庫(kù),多表結(jié)構(gòu)調(diào)用,使用遞歸循環(huán)實(shí)例化->context.notifySingle->sNotifyObjCInit,這一層好像斷了,并沒(méi)有看到sNotifyObjCInit實(shí)現(xiàn)的地方,流程不能往下走了?
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]);
}
并沒(méi)有看到sNotifyObjCInit實(shí)現(xiàn)的地方,流程不能往下走了?但發(fā)現(xiàn)registerObjCNotifiers方法中給sNotifyObjCInit賦值為init,查看哪里調(diào)用了registerObjCNotifiers方法,
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());
}
}
}
發(fā)現(xiàn)只有一個(gè)地方調(diào)用了registerObjCNotifiers方法_dyld_objc_notify_register(參數(shù)1,參數(shù)2,參數(shù)3),參數(shù)二會(huì)將init賦值給sNotifyObjCInit,那在什么地方調(diào)用了_dyld_objc_notify_register方法呢?
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);
}
我們回到objc的源碼中搜索_dyld_objc_notify_register(參數(shù)1,參數(shù)2,參數(shù)3),發(fā)現(xiàn)在objc源碼中的_objc_init中調(diào)用了此方法,那什么時(shí)候調(diào)用了objc_init這個(gè)方法呢?
runinit執(zhí)行的時(shí)候要想執(zhí)行_dyld_objc_notify_register(參數(shù)1,參數(shù)2,參數(shù)3)屬于回調(diào)函數(shù),必須觸發(fā)_objc_init(void)函數(shù)的調(diào)用,什么地方執(zhí)行的objc_init呢?第二個(gè)參數(shù)正好為load_images。

/***********************************************************************
* _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();
cache_init();
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
我們?cè)?code>void ImageLoader::recursiveInitialization初始化的時(shí)候調(diào)用了bool hasInitializers = this->doInitialization(context);

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;
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();
}
從而進(jìn)入bool ImageLoaderMachO::doInitialization(const LinkContext& context)方法,進(jìn)入doImageInit(context);方法,得知libSystem動(dòng)態(tài)庫(kù)必須先初始化
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);
}

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 ) {
// <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);
}
}
}
再重新查看當(dāng)前的堆棧情況
堆棧里面的dyld的加載順序和我們剛剛分析的流程順序一模一樣:dyld_start--> dyldbootstrap::start-->dyld::main-->dyld::useSimulatorDyld-->start_sim-->dyld::main-->dyld::initializeMainExecutable()-->ImageLoader::runInitializers()-->ImageLoader::processInitializers()-->ImageLoader::recursiveInitialization()-->dyld::notifySingle()-->load_images-->+[ViewController load]



由上面的分析得出load_images是由void _objc_init(void)-->_dyld_objc_notify_register(&map_images, load_images, unmap_image);傳入load_images-->dyld::registerObjCNotifiers(mapped, init, unmapped);從而賦值sNotifyObjCInit = init = load_images;,在notifySingle方法中觸發(fā)sNotifyObjCInit從而調(diào)用load_images

具體結(jié)合堆棧流程如下

在ObjC源碼中的_objc_init中打斷點(diǎn)查看堆棧情況,發(fā)現(xiàn)正如前文猜測(cè),調(diào)用了libSystem_initializer,dyld`ImageLoaderMachO::doInitialization:-->dyld`ImageLoaderMachO::doModInitFunctions:-->libSystem.B.dylib`libSystem_initializer:,void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)方法中觸發(fā)了lisSystem_initializer

查看官方libSystem源碼
查看libSystem_initializer源碼,進(jìn)行了一系列的初始化,包括_dyld_initializer();dyld的初始化,有人可能會(huì)有疑問(wèn),為嘛dyld_start都執(zhí)行了,還需要初始化。并不是說(shuō)dyld.start了它就已經(jīng)初始化了,dyld也是一個(gè)庫(kù),也需要初始化
之后libdispatch_init();,由堆棧得知,libdispatch_init代碼來(lái)自于libdispatch.dylib libdispatch_init
static void
libSystem_initializer(int argc,
const char* argv[],
const char* envp[],
const char* apple[],
const struct ProgramVars* vars)
{
static const struct _libkernel_functions libkernel_funcs = {
.version = 3,
// V1 functions
.dlsym = dlsym,
.malloc = malloc,
.free = free,
.realloc = realloc,
._pthread_exit_if_canceled = _pthread_exit_if_canceled,
// V2 functions (removed)
// V3 functions
.pthread_clear_qos_tsd = _pthread_clear_qos_tsd,
};
static const struct _libpthread_functions libpthread_funcs = {
.version = 2,
.exit = exit,
.malloc = malloc,
.free = free,
};
static const struct _libc_functions libc_funcs = {
.version = 1,
.atfork_prepare = libSystem_atfork_prepare,
.atfork_parent = libSystem_atfork_parent,
.atfork_child = libSystem_atfork_child,
#if defined(HAVE_SYSTEM_CORESERVICES)
.dirhelper = _dirhelper,
#endif
};
__libkernel_init(&libkernel_funcs, envp, apple, vars);
__libplatform_init(NULL, envp, apple, vars);
__pthread_init(&libpthread_funcs, envp, apple, vars);
_libc_initializer(&libc_funcs, envp, apple, vars);
// TODO: Move __malloc_init before __libc_init after breaking malloc's upward link to Libc
__malloc_init(apple);
#if TARGET_OS_OSX
/* <rdar://problem/9664631> */
__keymgr_initializer();
#endif
// No ASan interceptors are invoked before this point. ASan is normally initialized via the malloc interceptor:
// _dyld_initializer() -> tlv_load_notification -> wrap_malloc -> ASanInitInternal
_dyld_initializer();
libdispatch_init();
_libxpc_initializer();
#if CURRENT_VARIANT_asan
setenv("DT_BYPASS_LEAKS_CHECK", "1", 1);
#endif
// must be initialized after dispatch
_libtrace_init();
#if !(TARGET_OS_EMBEDDED || TARGET_OS_SIMULATOR)
_libsecinit_initializer();
#endif
#if defined(HAVE_SYSTEM_CONTAINERMANAGER)
_container_init(apple);
#endif
__libdarwin_init();
__stack_logging_early_finished();
#if !TARGET_OS_IPHONE
/* <rdar://problem/22139800> - Preserve the old behavior of apple[] for
* programs that haven't linked against newer SDK.
*/
#define APPLE0_PREFIX "executable_path="
if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11){
if (strncmp(apple[0], APPLE0_PREFIX, strlen(APPLE0_PREFIX)) == 0){
apple[0] = apple[0] + strlen(APPLE0_PREFIX);
}
}
#endif
/* <rdar://problem/11588042>
* C99 standard has the following in section 7.5(3):
* "The value of errno is zero at program startup, but is never set
* to zero by any library function."
*/
errno = 0;
}

查看官網(wǎng)libdispatch源碼,查找libdispatch_init的實(shí)現(xiàn),會(huì)發(fā)現(xiàn)調(diào)用了_os_object_init();
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 HAVE_OS_FAULT_WITH_PAYLOAD && TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
if (_dispatch_getenv_bool("LIBDISPATCH_NO_FAULTS", false)) {
_dispatch_mode |= DISPATCH_MODE_NO_FAULTS;
} else if (getpid() == 1 ||
!os_variant_has_internal_diagnostics("com.apple.libdispatch")) {
_dispatch_mode |= DISPATCH_MODE_NO_FAULTS;
}
#endif // HAVE_OS_FAULT_WITH_PAYLOAD && TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
#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
#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();
}

繼續(xù)查看_os_object_init();發(fā)現(xiàn)調(diào)用了_objc_init()和前面堆棧中的調(diào)用一模一樣extern void _objc_init(void);,此時(shí)回回到ObjC的源碼void _objc_init(void)進(jìn)而將load_images傳入_dyld_objc_notify_register(&map_images, load_images, unmap_image);方法中進(jìn)而對(duì)dyld中的回調(diào)函數(shù)進(jìn)行賦值
有ObjC源碼_objc_init()調(diào)用過(guò)來(lái)傳入load_images
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);
}
傳入load_images到registerObjCNotifiers,對(duì)回調(diào)函數(shù)進(jìn)行賦值
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;//對(duì)回調(diào)函數(shù)進(jìn)行賦值
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());
}
}
}
---------------------libdispatch源碼-------------------------
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
}
----------------ObjC源碼------------------
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();
cache_init();
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
回調(diào)函數(shù)sNotifyObjCInit = init;,相當(dāng)于load_images,從而觸發(fā)dyld中notifySingle-->sNotifyObjCInit方法的回調(diào),從而加載load_imageslibobjc.A.dylib`load_images:
--------------------dyld源碼--------------------
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
if ( !image->inSharedCache() ) {
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);
}
}
總結(jié):閉環(huán)
dyld runinit --> do init --> libsys --> init --> libdispatch --> _os_objct_init --> libobjc.A.dylib --> _objc_init()
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);
}
參數(shù)二為load_images = init = sNotifyObjCInit
notifySingle --> sNotifyObjCInit:=參數(shù)2 sNotifyObjCInit()

load(),C++,main()函數(shù)調(diào)用順序?前文一直是load()-->C++-->main()的調(diào)用順序,為什么呢?
上一節(jié)知道回調(diào)函數(shù)走入load_images方法中,調(diào)用call_load_methods()方法,循環(huán)將所有的load方法調(diào)用一遍
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();
}
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;
}
load方法調(diào)用完畢后,接下來(lái)會(huì)回到dyld源碼中的doInitialization方法,從而調(diào)用doModInitFunctions方法,調(diào)用MachO文件中image所有的Cxx頭文件方法,怎么驗(yàn)證呢?看堆棧信息是否是調(diào)用了doModInitFunctions(context);發(fā)現(xiàn)一模一樣,
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);
}

C++方法走完之后,接下來(lái)會(huì)走哪個(gè)方法呢?我們斷點(diǎn)看匯編和寄存器



main函數(shù)作為入口,是被寫(xiě)定的一個(gè)函數(shù)編譯到內(nèi)存中,若改個(gè)名字,程序會(huì)報(bào)錯(cuò),main以固定的方式存在寄存器rax中,能否從dyld源碼中看到呢?

流程從
dyld_start-->bootstramp::start執(zhí)行完后call to main()
將C++函數(shù)和main()函數(shù)調(diào)換順序,還是一樣的執(zhí)行順序結(jié)果。dyld image init 注冊(cè)回調(diào)通知 - dyld_start -> dyld::main() -> main()

補(bǔ)充說(shuō)明:在dyld的main方法中加載共享緩存分為3種情況,共享緩存是指UIKit,Foundation等系統(tǒng)的動(dòng)態(tài)庫(kù),當(dāng)多個(gè)App啟動(dòng)時(shí),只有一份在內(nèi)存中,從共享緩存中去取,動(dòng)態(tài)庫(kù)的共享緩存在整個(gè)應(yīng)用啟動(dòng)過(guò)程中,最先被加載的一個(gè)

1.若強(qiáng)制私有,共享緩存只加載到當(dāng)前進(jìn)程中
2.共享緩存已經(jīng)存在,什么都不做
3.不存在,當(dāng)前進(jìn)程首次加載共享緩存
共享緩存被加載之后,加載依賴的framework和第三方動(dòng)態(tài)庫(kù),以及插入的動(dòng)態(tài)庫(kù)
iOS11.0之后,加載方式為dyld3,流程和dyld2一樣,方式closureMode閉包模式更加高效
每個(gè)App都會(huì)分配一個(gè)dyld嗎???
并不會(huì),dyld只有一個(gè),進(jìn)程內(nèi)存,每個(gè)進(jìn)程有個(gè)ASLR偏移地址,所有的應(yīng)用程序MachO可執(zhí)行文件都從ASLR開(kāi)始,外部的也要做ASLR的rebase修正,得到的都是ASLR+實(shí)際地址=虛擬地址,此虛擬地址只能當(dāng)前進(jìn)程能訪問(wèn)到,dyld在真實(shí)的物理地址中只有一份,但在各個(gè)進(jìn)程中都是唯一的,這個(gè)地址給到CPU會(huì)做地址翻譯,翻譯成真正的物理地址去調(diào)用dyld,dyld加載時(shí)先要進(jìn)行dyld的rebase操作,原來(lái)的dyld物理地址不能直接暴露給應(yīng)用程序,根據(jù)當(dāng)前進(jìn)程dyldASLR做rebase生成虛擬地址,rebase后的地址都是當(dāng)前虛擬內(nèi)存空間的地址,通過(guò)此地址找物理地址通過(guò)CPU去找,這樣應(yīng)用程序就無(wú)法更改dyld,相對(duì)每個(gè)進(jìn)程都是獨(dú)立的地址,每個(gè)地址都是自己的假地址
dyldbootstrap::start方法中,先進(jìn)行重定位dyld,進(jìn)程啟動(dòng)因?yàn)槭翘摂M地址,都需做重定位


dyld3進(jìn)行rebase后,最后直接return main函數(shù),進(jìn)入主程序也要rebase,開(kāi)始加載主程序MainExecuable,MachO文件進(jìn)入到內(nèi)存為Image,加載MachO頭文件,loadCommands,代碼簽名,代碼加密,實(shí)例化主程序加載到AllImages中,AllImages中添加的第一個(gè)是主程序的MachO文件
代碼簽名
檢測(cè)主程序
配置,設(shè)置加載動(dòng)態(tài)庫(kù)的版本
檢測(cè)關(guān)于動(dòng)態(tài)庫(kù)的加載
有個(gè)環(huán)境變量sEnv.DYLD_INSERT_LIBRARIES(將來(lái)在越獄環(huán)境中會(huì)一直使用,此環(huán)境變量程序員無(wú)法修改,root環(huán)境中可以修改)
判斷此環(huán)境變量不為NULL執(zhí)行l(wèi)oadInsertedDylib
加載插入的動(dòng)態(tài)庫(kù)
link鏈接主程序
設(shè)置起始時(shí)間,后面記錄結(jié)束時(shí)間,相減得到間隔時(shí)間,用于配置環(huán)境變量記錄加載時(shí)長(zhǎng),啟動(dòng)優(yōu)化用到
遞歸加載主程序依賴的庫(kù),完成之后發(fā)通知
Rebase修正ASLR,每個(gè)動(dòng)態(tài)庫(kù)都需要修正ASLE,都有偏移值,在MachOView中可以查看到

綁定NoLazy符號(hào)
綁定弱符號(hào)
遞歸應(yīng)用插入的動(dòng)態(tài)庫(kù)(共享緩存中的系統(tǒng)庫(kù)UIKit,Foudation以及用到的第三方動(dòng)態(tài)庫(kù)Flutter,andromeda,Lottie,OpenSSL等),通過(guò)在WeChat的load方法斷點(diǎn)image list得知


注冊(cè)
計(jì)算結(jié)束時(shí)間,目的是配置環(huán)境變量就能看到dyld加載的時(shí)長(zhǎng)
通過(guò)環(huán)境變量配置可打印加載動(dòng)態(tài)庫(kù)時(shí)間,Rebase修正時(shí)間,綁定時(shí)間,弱綁定時(shí)間等

在AllImages中添加主程序后鏈接加載插入的動(dòng)態(tài)庫(kù)(UIKit,Foundation,第三方庫(kù)等),此時(shí)從第2個(gè)位置開(kāi)始,因?yàn)榍懊娴?個(gè)位置已經(jīng)加入了主程序,第1個(gè)位置是dyld本身


鏈接插入的動(dòng)態(tài)庫(kù),鏈接方式和主程序一致,前期是主程序的鏈接
判斷所有的Image是否加載完成,沒(méi)有加載完成會(huì)持續(xù)的遞歸reloadAllImages
循環(huán)加載插入的動(dòng)態(tài)庫(kù)

綁定插入的動(dòng)態(tài)庫(kù)
弱符號(hào)綁定

初始化Main方法initialMainExecutable()
runInitializers()
processInitializers()
recursiveInitialization
context.notifySingle()
(*sNotifyObjcInit)(image->getRealPath(),image->machHeader());
call_all_methods加載所有的load方法,程序繼續(xù)執(zhí)行
doInitialization()
doModInitFunctions(context)執(zhí)行C++構(gòu)造方法
notifyMonitoringDyldMain()通知監(jiān)控進(jìn)程將要進(jìn)入主程序main函數(shù)
sMainExecutable->getEntryFromLC_MAIN()找到主程序main函數(shù)入口
流程圖
