首先我們來看一個(gè)案例滨达,viewController
添加load方法
,main.m
文件添加一個(gè)c++函數(shù)
俯艰,思考一下load方法捡遍,main函數(shù)、c++函數(shù)的執(zhí)行順序
是什么竹握?
load方法啦辐,main函數(shù)谓传、c++函數(shù)的執(zhí)行順序
為:C++ -> load -> main
,這是為什么呢芹关?一般的程序入口不應(yīng)該是main函數(shù)
嗎?這就 是本篇內(nèi)容研究的重點(diǎn)dyld應(yīng)用程序的加載流程
续挟。
概念補(bǔ)充:
靜態(tài)庫
:在鏈接階段,將匯編生成的目標(biāo)文件和引用的一些庫一起鏈接打包到可執(zhí)行文件當(dāng)中侥衬。
優(yōu)點(diǎn)
:編譯完成后庫就沒有用處了诗祸,可以直接運(yùn)行,并且目標(biāo)程序沒有外部的依賴轴总。
缺點(diǎn)
:使用靜態(tài)庫會目標(biāo)程序的體積增加直颅,對內(nèi)存、性能消耗比較大肘习。
動態(tài)庫
:程序在編譯時(shí)不會看鏈接到目標(biāo)程序际乘,目標(biāo)程序只會存儲指向一些動態(tài)庫的引用,在程序運(yùn)行是才會被加載進(jìn)來漂佩。
優(yōu)點(diǎn)
:可以減少目標(biāo)程序的體積;并且共享內(nèi)存節(jié)約資源罪塔;由于運(yùn)行時(shí)才會被載入投蝉,所以可以動態(tài)的更改動態(tài)庫,達(dá)到更新程序目的征堪。
缺點(diǎn)
:由于動態(tài)庫在運(yùn)行時(shí)才會被加載瘩缆,所以程序就比較依賴外界的環(huán)境,如果外界動態(tài)庫缺失錯(cuò)誤就會造成程序錯(cuò)誤佃蚜。
dyld鏈接器
名詞解釋:dyld是蘋果的動態(tài)鏈接器
庸娱,蘋果操作系統(tǒng)重要的組成部分,在app被編譯打包成可執(zhí)行文件格式的Mach-O文件后谐算,交由dyld負(fù)責(zé)鏈接熟尉,加載程序
。
- 在案例demo的load方法添加一個(gè)斷點(diǎn)洲脂,通過堆棧信息來看一下APP啟動的第一個(gè)函數(shù)斤儿。
_dyld_start ()
,下面就根據(jù)dyld源碼進(jìn)行分析往果。 - 在dyld-750.6源碼中全局搜索
_dyld_start
疆液,發(fā)現(xiàn)是有匯編寫的,我們可以根據(jù)匯編注釋發(fā)現(xiàn)陕贮,調(diào)用的是dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
方法堕油, - 在源碼中搜索
dyldbootstrap
命名空間的start
方法查看方法的實(shí)現(xiàn)流程,源碼如下
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; }
++?;
// set up random value for stack canary
__guard_setup(?);
#if DYLD_INITIALIZER_SUPPORT
// run all C++ initializers inside dyld
runDyldInitializers(argc, argv, envp, ?);
#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);
}
核心返回的是_main
方法肮之,其中macho_header
是macho
文件的頭掉缺,而dyld加載的文件
就是Mach-O類型
的,即Mach-O類型
是可執(zhí)行文件類型
局骤。
-
進(jìn)入
_mian
函數(shù)攀圈,發(fā)現(xiàn)總共大約600多行,如果對dyld
加載流程不太了解的童鞋峦甩,可以根據(jù)_main函數(shù)
的返回值進(jìn)行反推赘来。main函數(shù)主要做了以下幾件事情。- 【第一步】環(huán)境變量配置
//檢查設(shè)置的環(huán)境變量
checkEnvironmentVariables(envp);
//如果DYLD_FALLBACK為nil凯傲,將其設(shè)置為默認(rèn)值
defaultUninitializedFallbackPaths(envp);
.....
//獲取當(dāng)前運(yùn)行環(huán)境架構(gòu)的信息
getHostInfo(mainExecutableMH, mainExecutableSlide);
- 【第二步:共享緩存】:檢查是否開啟了共享緩存犬辰,以及共享緩存是否映射到了共享區(qū)域,例如
UIKit冰单、CoreFoundation
等
//檢查共享緩存是否開啟幌缝,在iOS中必須開啟
checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
#if TARGET_OS_SIMULATOR
if ( sSharedCacheOverrideDir)
//檢查共享緩存是否映射到了共享區(qū)域
mapSharedCache();
#else
mapSharedCache();
#endif
}
// If we haven't got a closure mode yet, then check the environment and cache type
//如果還沒有關(guān)閉模式,請檢查環(huán)境和緩存類型
if ( sClosureMode == ClosureMode::Unset ) {
// First test to see if we forced in dyld2 via a kernel boot-arg
if ( dyld3::BootArgs::forceDyld2() ) {
sClosureMode = ClosureMode::Off;
} else if ( inDenyList(sExecPath) ) {
sClosureMode = ClosureMode::Off;
} else if ( sEnv.hasOverride ) {
sClosureMode = ClosureMode::Off;
} else if ( dyld3::BootArgs::forceDyld3() ) {
sClosureMode = ClosureMode::On;
} else {
sClosureMode = getPlatformDefaultClosureMode();
}
}
- 【第三步】主程序的初始化
//加載可執(zhí)行文件诫欠,并生成一個(gè)imageLoader實(shí)例對象
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
gLinkContext.mainExecutable = sMainExecutable;
gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
重點(diǎn)分析
(1)sMainExecutable
表示主程序變量涵卵,查看其賦值,是通過instantiateFromLoadedImage
方法初化的荒叼,進(jìn)入instantiateFromLoadedImage
源碼轿偎,其中創(chuàng)建一個(gè)ImageLoader實(shí)例對象
,通過instantiateMainExecutable
方法創(chuàng)建
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";
}
(2)instantiateMainExecutable
源碼被廓,為主程序創(chuàng)建鏡像坏晦,返回一個(gè)ImageLoade
r類型的image對象
,即主程序嫁乘。其中sniffLoadCommands函數(shù)
時(shí)獲取Mach-O
類型文件的Load Command
的相關(guān)信息昆婿,并對其進(jìn)行各種校驗(yàn)
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;
//確定此mach-o文件是否具有經(jīng)典或壓縮的LINKEDIT以及其具有的段數(shù)
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
}
- 【第四步:插入動態(tài)庫】
//加載所有DYLD_INSERT_LIBRARIES指定的庫
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
// record count of inserted libraries so that a flat search will look at
// inserted libraries, then main, then others.
sInsertedDylibCount = sAllImages.size()-1;
// link main executable
gLinkContext.linkingMainExecutable = true;
#if SUPPORT_ACCELERATE_TABLES
if ( mainExcutableAlreadyRebased ) {
// previous link() on main executable has already adjusted its internal pointers for ASLR
// work around that by rebasing by inverse amount
sMainExecutable->rebase(gLinkContext, -mainExecutableSlide);
}
#endif
- 【第五步:link主程序】
//鏈接主程序
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
sMainExecutable->setNeverUnloadRecursive();
if ( sMainExecutable->forceFlat() ) {
gLinkContext.bindFlat = true;
gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
}
- 【第六步:link動態(tài)庫】
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
//鏈接可執(zhí)行文件后執(zhí)行此操作,這樣插入的dylib(例如libSystem)插入的dylib不會在程序使用的dylib的前面
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);
}
}
}
- 【第七步:弱符號綁定】
// apply interposing to initial set of images
for(int i=0; i < sImageRoots.size(); ++i) {
//應(yīng)用符號插入
sImageRoots[i]->applyInterposing(gLinkContext);
}
ImageLoader::applyInterposingToDyldCache(gLinkContext);
// Bind and notify for the main executable now that interposing has been registered
//綁定并通知主要可執(zhí)行文件,現(xiàn)在插入已注冊
uint64_t bindMainExecutableStartTime = mach_absolute_time();
sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
uint64_t bindMainExecutableEndTime = mach_absolute_time();
ImageLoaderMachO::fgTotalBindTime += bindMainExecutableEndTime - bindMainExecutableStartTime;
gLinkContext.notifyBatch(dyld_image_state_bound, false);
// Bind and notify for the inserted images now interposing has been registered
//綁定并通知已插入鏡像已被注冊
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
}
}
// <rdar://problem/12186933> do weak binding only after all inserted images linked
//弱符號綁定
sMainExecutable->weakBind(gLinkContext);
gLinkContext.linkingMainExecutable = false;
sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);
- 【第八步:執(zhí)行初始化方法】
(1)initializeMainExecutable
源碼,主要是循環(huán)遍歷法精,都會執(zhí)行runInitializers方法
多律。
void initializeMainExecutable()
{
// record that we've reached this step
gLinkContext.startedInitializingMainExecutable = true;
// run initialzers for any inserted dylibs
// 主要是循環(huán)遍歷痴突,都會執(zhí)行runInitializers方法
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]);
}
(2)``runInitializers`源碼實(shí)現(xiàn)
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);
}
(3)processInitializers
源碼實(shí)現(xiàn),其中對鏡像列表調(diào)用recursiveInitialization
函數(shù)進(jìn)行遞歸實(shí)例化
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);
}
(4)recursiveInitialization
源碼實(shí)現(xiàn)
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();
}
這部分主要研究一下狼荞,notifySingle函數(shù)
和doInitialization函數(shù)
notifySingle函數(shù)
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);
}
}
上述源碼的關(guān)鍵語句(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
辽装,接下來我們看一下sNotifyObjCInit
registerObjCNotifiers
在哪里調(diào)用了,發(fā)現(xiàn)在_dyld_objc_notify_register
進(jìn)行了調(diào)用
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped)
{
dyld::registerObjCNotifiers(mapped, init, ![截屏2020-11-04 17.08.19.png](https://upload-images.jianshu.io/upload_images/2646923-d92fd4f7a1545140.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
);
}
注意:_dyld_objc_notify_register
的函數(shù)需要在libobjc源碼
中搜索
所以我們在objc4-781源碼中搜索_dyld_objc_notify_register
發(fā)現(xiàn)_objc_init
方法中調(diào)用相味。所以sNotifyObjCInit
的賦值的就是objc中的load_images
拾积,而load_images
會調(diào)用所有的+load方法
。所以綜上所述丰涉,notifySingle
是一個(gè)回調(diào)函數(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();
cache_init();
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
load加載
(1)進(jìn)入load_images
源碼,發(fā)現(xiàn)調(diào)用了call_load_methods();
方法
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();
}
(2)進(jìn)入call_load_methods();
源碼一死,發(fā)現(xiàn)函數(shù)內(nèi)循環(huán)調(diào)用call_class_loads
方法
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;
}
(3)call_class_loads
方法源碼肛度,該方法的實(shí)現(xiàn)(*load_method)(cls, @selector(load));
加載所有的load方法。
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);
}
【總結(jié)】:load的源碼鏈為:_dyld_start --> dyldbootstrap::start --> dyld::_main --> dyld::initializeMainExecutable --> ImageLoader::runInitializers --> ImageLoader::processInitializers --> ImageLoader::recursiveInitialization --> dyld::notifySingle(是一個(gè)回調(diào)處理) --> sNotifyObjCInit --> load_images(libobjc.A.dylib)
投慈。
doInitialization函數(shù)
(1)進(jìn)入
doInitialization
函數(shù)的源碼實(shí)現(xiàn)
{
CRSetCrashLogMessage2(this->getPath());
// mach-o has -init and static initializers
doImageInit(context);
doModInitFunctions(context);
CRSetCrashLogMessage2(NULL);
return (fHasDashInit || fHasInitializers);
}
這里也需要分成兩部分承耿,一部分是doImageInit函數(shù)
,一部分是doModInitFunctions函數(shù)
伪煤。
-
doImageInit函數(shù)
源代碼
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);
}
}
}
for循環(huán)加載方法
的調(diào)用加袋,這里需要注意的一點(diǎn)是,libSystem的初始化必須先運(yùn)行
抱既。
- 進(jìn)入
doModInitFunctions
源碼實(shí)現(xiàn)职烧,這個(gè)方法中加載了所有Cxx文件
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 = §ionsStart[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 ) {
Initializer* inits = (Initializer*)(sect->addr + fSlide);
const size_t count = sect->size / sizeof(uintptr_t);
// <rdar://problem/23929217> Ensure __mod_init_func section is within segment
if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
dyld::throwf("__mod_init_funcs section has malformed address range for %s\n", this->getPath());
for (size_t j=0; j < count; ++j) {
Initializer func = inits[j];
// <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
const char* installPath = getInstallPath();
if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) )
dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath());
}
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
{
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);
}
bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
// now safe to use malloc() and other calls in libSystem.dylib
dyld::gProcessInfo->libSystemInitialized = true;
}
}
}
else if ( type == S_INIT_FUNC_OFFSETS ) {
const uint32_t* inits = (uint32_t*)(sect->addr + fSlide);
const size_t count = sect->size / sizeof(uint32_t);
// Ensure section is within segment
if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
dyld::throwf("__init_offsets section has malformed address range for %s\n", this->getPath());
if ( seg->initprot & VM_PROT_WRITE )
dyld::throwf("__init_offsets section is not in read-only segment %s\n", this->getPath());
for (size_t j=0; j < count; ++j) {
uint32_t funcOffset = inits[j];
// verify initializers are in image
if ( ! this->containsAddress((uint8_t*)this->machHeader() + funcOffset) ) {
dyld::throwf("initializer function offset 0x%08X not in mapped image for %s\n", funcOffset, this->getPath());
}
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
// <rdar://problem/17973316> libSystem initializer must run first
const char* installPath = getInstallPath();
if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) )
dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath());
}
Initializer func = (Initializer)((uint8_t*)this->machHeader() + funcOffset);
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
#if __has_feature(ptrauth_calls)
func = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
#endif
bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
{
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);
}
bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
// now safe to use malloc() and other calls in libSystem.dylib
dyld::gProcessInfo->libSystemInitialized = true;
}
}
}
}
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
}
- 【第九步:尋找主程序入口即main函數(shù)】:從Load Command讀取LC_MAIN入口,如果沒有防泵,就讀取LC_UNIXTHREAD蚀之,這樣就來到了日常開發(fā)中熟悉的main函數(shù)了
// 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;
}
}