dyld源碼
蘋果官方資源opensource
objc4-838可編譯聯(lián)調(diào)源碼
一模她、了解相關(guān)概念
1.靜態(tài)庫(kù)督勺、動(dòng)態(tài)庫(kù)
通常程序都會(huì)依賴系統(tǒng)一些庫(kù), 庫(kù)是什么呢? 其實(shí)庫(kù)就是一些可執(zhí)行的二進(jìn)制文件, 能被操作系統(tǒng)加載到內(nèi)存里面中舍哄。庫(kù)分為兩種:靜態(tài)庫(kù) / 動(dòng)態(tài)庫(kù)
-
靜態(tài)庫(kù)
:是一堆.o文件的集合。格式.a
,.lib
等。鏈接階段時(shí)靜態(tài)庫(kù)會(huì)被完整地復(fù)制, 一起打包在可執(zhí)行文件中,被多次使用就有多份冗余拷貝掌逛。-
優(yōu)點(diǎn)
: 編譯完成之后, 鏈接到目標(biāo)程序中, 同時(shí)打包到可執(zhí)行文件里面, 不會(huì)有外部依賴。 -
缺點(diǎn)
: 靜態(tài)庫(kù)會(huì)有兩份, 所以會(huì)導(dǎo)致目標(biāo)程序體積增大, 對(duì)內(nèi)存, 性能, 速度消耗很大穆咐。并且相同靜態(tài)庫(kù)每個(gè)app中都會(huì)拷貝一份颤诀。
-
-
動(dòng)態(tài)庫(kù)
:是一個(gè)已經(jīng)完全鏈接后的鏡像。格式.framework
等对湃。程序編譯時(shí)并不會(huì)鏈接到目標(biāo)程序中崖叫,目標(biāo)程序只會(huì)存儲(chǔ)指向動(dòng)態(tài)庫(kù)的引用,在程序運(yùn)行時(shí)才被載入拍柒。蘋果大部分都是動(dòng)態(tài)庫(kù)心傀。-
優(yōu)點(diǎn)
: 不需要拷貝到目標(biāo)程序, 減少App包的體積;多個(gè)App可以使用同一個(gè)動(dòng)態(tài)庫(kù), 共享內(nèi)存, 節(jié)約資源拆讯;由于運(yùn)行時(shí)才會(huì)去加載, 那么可以在App不使用時(shí)隨時(shí)對(duì)庫(kù)進(jìn)行替換或更新, 更新靈活脂男。 -
缺點(diǎn)
: 動(dòng)態(tài)載入會(huì)帶來(lái)一部分性能損失, 同時(shí)動(dòng)態(tài)庫(kù)也會(huì)使得程序依賴于外部環(huán)境。一旦動(dòng)態(tài)庫(kù)沒有或消失, 程序會(huì)出現(xiàn)問題种呐。
-
1.Xcode編譯command + B
產(chǎn)生可執(zhí)行文件的過程:
- a.
預(yù)編譯
處理源代碼文件中以#
開頭的預(yù)編譯的命令(#import
/#include
等等)宰翅;把包含的文件插入到指定的位置;替換代碼中的宏定義爽室;刪除代碼里的注釋等等汁讼。產(chǎn)生.i
文件。 - b.
編譯
詞法分析阔墩;語(yǔ)法分析嘿架;語(yǔ)義分析;生成相應(yīng)的匯編代碼文件啸箫。 產(chǎn)生.s
文件(匯編文件) - c.
匯編
將匯編代碼文件翻譯成機(jī)器指令耸彪,機(jī)器指令會(huì)生成.o
文件。 - d.
鏈接
把之前所有操作的文件鏈接到程序里面來(lái), 對(duì).o
文件中引用其他庫(kù)的地方進(jìn)行引用, 生成最后的可執(zhí)行文件
忘苛。動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)區(qū)別其實(shí)就是鏈接的區(qū)別蝉娜。-
靜態(tài)鏈接
: 鏈接器對(duì).o
文件進(jìn)行符號(hào)收集唱较、解析和重定位;把所有的.o
粘合在一起從而形成可執(zhí)行文件召川。(在靜態(tài)鏈接之后就不會(huì)再有靜態(tài)庫(kù)了)
-
動(dòng)態(tài)鏈接
:dyld
動(dòng)態(tài)鏈接(下面有介紹)
-
2.點(diǎn)擊App啟動(dòng)的過程:
- a.系統(tǒng)調(diào)用
exec()
分配內(nèi)存绊汹、開辟進(jìn)程 - b.app對(duì)應(yīng)的
可執(zhí)行文件
加載到內(nèi)存 - c.
dyld動(dòng)態(tài)鏈接器
加載到內(nèi)存:load dyld
- d.
dyld動(dòng)態(tài)鏈接器
進(jìn)行動(dòng)態(tài)鏈接:rebase
->bing
->Objc
->initalizers
- e.調(diào)起
main()
函數(shù)
啟動(dòng)時(shí)間的劃分可以把main()
函數(shù)作為關(guān)鍵點(diǎn)分割成兩塊
t1階段
,main()
之前的處理所需時(shí)間扮宠,稱為pre-main
t2階段
,main()
及main()
之后處理所需時(shí)間
t2階段耗時(shí)的主要是業(yè)務(wù)代碼
3.Mach-O是什么狐榔?
Mach-O
: Mach Object
文件格式的縮寫坛增,它是一種用于可執(zhí)行文件,目標(biāo)文件.o
薄腻,動(dòng)態(tài)庫(kù)收捣,內(nèi)核轉(zhuǎn)儲(chǔ)的文件格式
。作為a.out
格式的替代庵楷,Mach-O
提供了更強(qiáng)的擴(kuò)展性罢艾,并提升了符號(hào)表中信息的訪問速度。
三種Mach-O
格式的文件:可執(zhí)行文件
尽纽、dylib動(dòng)態(tài)庫(kù)
咐蚯、Bundle資源文件包
Mach-O
由四部分組成:Mach-O頭部
、Load Command
弄贿、section
春锋、Other Data
,可以通過MachOView
可查看可執(zhí)行文件信息差凹。
二期奔、什么是dyld
本文主要介紹dyld
源碼執(zhí)行流程。
在app
啟動(dòng)加載過程中類和分類加載都不可避免的觸及dyld
危尿,所以了解dyld
源碼可以讓我們更好的理解app
的工作原理呐萌。
什么是dyld?
dyld(the dynamic link editor 動(dòng)態(tài)鏈接器)
:是蘋果操作系統(tǒng)的一個(gè)重要的組成部分谊娇。在iOS/Mac OSX系統(tǒng)中肺孤,僅有很少量的進(jìn)程只需要內(nèi)核就能完成加載,基本上所有的進(jìn)程都是動(dòng)態(tài)鏈接的邮绿,所以Mach-O鏡像文件中會(huì)有很多對(duì)外部的庫(kù)和符號(hào)的引用渠旁,但是這些引用并不能直接用,在啟動(dòng)時(shí)還必須要通過這些引用進(jìn)行內(nèi)容的填補(bǔ)船逮,這個(gè)填補(bǔ)工作就是由動(dòng)態(tài)鏈接器dyld來(lái)完成的顾腊,也就是符號(hào)綁定。
在app被編譯打包成可執(zhí)行文件格式的Mach-O文件后挖胃,在程序啟動(dòng)時(shí)交由dyld負(fù)責(zé)鏈接加載程序杂靶。
dyld
是開源的梆惯,可以通過蘋果官網(wǎng)下載它的dyld源碼,本文的對(duì)應(yīng)的版本是941.5
吗垮。dyld4是iOS15發(fā)布的垛吗。
-
dyld3
: 相比dyld2新增預(yù)構(gòu)建/閉包, 目的是將一些啟動(dòng)數(shù)據(jù)創(chuàng)建為閉包存到本地,下次啟動(dòng)將不再重新解析數(shù)據(jù)烁登,而是直接讀取閉包內(nèi)容 -
dyld4
: 采用pre-build + just-in-time
預(yù)構(gòu)建/閉包+實(shí)時(shí)解析的雙解析模式, 將根據(jù)緩存有效與否選擇合適的模式進(jìn)行解析, 同時(shí)也處理了閉包失效時(shí)候需要重建閉包的性能問題怯屉。
三、dyld工作流程
新建一個(gè)工程饵沧,在ViewController.m
里重寫+(void)load
方法锨络,在load方法上打個(gè)斷點(diǎn)調(diào)試,運(yùn)行app后在斷點(diǎn)處輸出其函數(shù)調(diào)用的堆棧:
新版本dyld: start
開始接下來(lái)走 dyld4中prepare
方法, 源碼入口需要在start
中開始探索狼牺。
(舊版本dyld: _dyld_start
開始, 接下來(lái)走dyldbootstrap
, 源碼入口需要在dyld_start
開始羡儿。)
1.start函數(shù)是dyld的開始
// dyld的入口點(diǎn)。內(nèi)核加載dyld并跳轉(zhuǎn)到__dyld_start是钥,它設(shè)置一些寄存器并調(diào)用這個(gè)函數(shù)掠归。
// 注意:這個(gè)函數(shù)永遠(yuǎn)不會(huì)返回,它調(diào)用exit()悄泥。因此虏冻,堆棧保護(hù)程序是無(wú)用的,因?yàn)橛肋h(yuǎn)不會(huì)執(zhí)行epilog码泞。標(biāo)記函數(shù)no-return禁用堆棧保護(hù)兄旬。堆棧保護(hù)器也會(huì)導(dǎo)致armv7k代碼生成的問題,因?yàn)樗ㄟ^prolog中的GOT槽訪問隨機(jī)值余寥,但dyld還沒有rebased领铐。
void start(const KernelArgs* kernArgs) __attribute__((noreturn)) __asm("start");
void start(const KernelArgs* kernArgs)
{
// 發(fā)出kdebug跟蹤點(diǎn)來(lái)指示dyld引導(dǎo)程序已經(jīng)啟動(dòng) <rdar://46878536>
// 注意:這是在dyld rebased之前調(diào)用的,所以kdebug_trace_dyld_marker()不能使用任何全局變量
dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);
// 走所有的fixups chains 和 rebase dyld
// 注意:withChainStarts()和fixupAllChainedFixups()不能使用任何靜態(tài)數(shù)據(jù)指針宋舷,因?yàn)樗鼈冞€沒有rebased
const MachOAnalyzer* dyldMA = getDyldMH(); // 獲取Mach-O分析器
assert(dyldMA->hasChainedFixups());
uintptr_t slide = (long)dyldMA; // 所有基于修復(fù)鏈的圖像的基址為0绪撵,因此slide == load address
__block Diagnostics diag;
dyldMA->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
dyldMA->fixupAllChainedFixups(diag, starts, slide, dyld3::Array<const void*>(), nullptr);
});
diag.assertNoError();
// 現(xiàn)在,我們可以調(diào)用使用DATA的函數(shù)
mach_init(); // mach消息初始化
// 為stack canary設(shè)置隨機(jī)值
__guard_setup(kernArgs->findApple()); // 棧溢出保護(hù)
// 設(shè)置以便open_with_subsystem()工作
_subsystem_init(kernArgs->findApple());
// 在__DATA_CONST中將ProcessConfig對(duì)象變?yōu)橹蛔x之前祝蝠,使用placement new來(lái)構(gòu)造它
ProcessConfig& config = *new ((ProcessConfig*)sConfigBuffer) ProcessConfig(kernArgs, sSyscallDelegate);
// 使__DATA_CONST只讀(內(nèi)核映射為r/w)
dyldMA->forEachSegment(^(const MachOAnalyzer::SegmentInfo& segInfo, bool& stop) {
if ( segInfo.readOnlyData ) {
const uint8_t* start = (uint8_t*)(segInfo.vmAddr + slide);
size_t size = (size_t)segInfo.vmSize;
sSyscallDelegate.mprotect((void*)start, size, PROT_READ);
}
});
#if !SUPPPORT_PRE_LC_MAIN
// 堆棧分配RuntimeLocks音诈。它們不能位于通常是只讀的Allocator池中
RuntimeLocks sLocks;
#endif
// 創(chuàng)建Allocator和APIs/RuntimeState對(duì)象
APIs& state = APIs::bootstrap(config, sLocks);
// 加載程序的所有依賴項(xiàng)并將它們綁定在一起
MainFunc appMain = prepare(state, dyldMA);
// 現(xiàn)在使所有的dyld分配的數(shù)據(jù)結(jié)構(gòu)只讀
state.decWritable();
// 調(diào)用main(),如果它返回绎狭,調(diào)用exit()并返回結(jié)果
// 注意:這是經(jīng)過組織的细溅,以便在程序的主線程中回溯時(shí)只在“main”下面顯示“start”。
int result = appMain(state.config.process.argc, state.config.process.argv, state.config.process.envp, state.config.process.apple);
// 如果我們到達(dá)這里儡嘶,main()返回(而不是程序調(diào)用exit())
#if TARGET_OS_OSX
// <rdar://74518676> libSystemHelpers不是為模擬器設(shè)置的喇聊,因此直接調(diào)用_exit()
if ( MachOFile::isSimulatorPlatform(state.config.process.platform) )
_exit(result);
#endif
state.libSystemHelpers->exit(result);
}
- 走所有的
fixups chains
和rebase dyld
- mach消息初始化:
mach_init()
- 棧溢出保護(hù):
__guard_setup(kernArgs->findApple());
- 構(gòu)建進(jìn)程配置:
config = ProcessConfig(kernArgs, sSyscallDelegate);
class VIS_HIDDEN ProcessConfig
{
public:
// used in unit tests to config and test ProcessConfig objects
ProcessConfig(const KernelArgs* kernArgs, SyscallDelegate&);
#if !BUILDING_DYLD
void reset(const MachOAnalyzer* mainExe, const char* mainPath, const DyldSharedCache* cache);
#endif
//
// Contains config info derived from Kernel arguments
//
class Process
{
public:
Process(const KernelArgs* kernArgs, SyscallDelegate&);
const MachOAnalyzer* mainExecutable;
const char* mainExecutablePath;
const char* mainUnrealPath; // raw path used to launch
uint32_t mainExecutableSDKVersion;
uint32_t mainExecutableSDKVersionSet;
uint32_t mainExecutableMinOSVersion;
uint32_t mainExecutableMinOSVersionSet;
dyld3::Platform basePlatform;
dyld3::Platform platform;
const char* dyldPath;
int argc;
const char* const* argv;
const char* const* envp;
const char* const* apple;
const char* progname;
DyldCommPage commPage;
const GradedArchs* archs;
int pid;
bool isTranslated;
bool catalystRuntime; // Mac Catalyst app or iOS-on-mac app
bool enableDataConst; // Temporarily allow disabling __DATA_CONST for bringup
bool proactivelyUseWeakDefMap;
const char* appleParam(const char* key) const;
const char* environ(const char* key) const;
const char* strdup(const char*) const; // allocates into read-only region
void* roalloc(size_t) const; // allocates into read-only region
private:
friend class ProcessConfig;
uint32_t findVersionSetEquivalent(dyld3::Platform versionPlatform, uint32_t version) const;
const char* pathFromFileHexStrings(SyscallDelegate& sys, const char* encodedFileInfo);
const char* getDyldPath(SyscallDelegate& sys);
const char* getMainPath(SyscallDelegate& syscall);
const char* getMainUnrealPath(SyscallDelegate& syscall);
dyld3::Platform getMainPlatform();
const GradedArchs* getMainArchs(SyscallDelegate& osDelegate);
bool usesCatalyst();
};
//
// Contains security related config info
//
class Security
{
public:
Security(Process& process, SyscallDelegate&);
bool internalInstall;
bool allowAtPaths;
bool allowEnvVarsPrint;
bool allowEnvVarsPath;
bool allowEnvVarsSharedCache;
bool allowClassicFallbackPaths;
bool allowInsertFailures;
bool allowInterposing;
bool skipMain;
private:
uint64_t getAMFI(const Process& process, SyscallDelegate& syscall);
void pruneEnvVars(Process& process);
};
//
// Contains logging related config info
//
class Logging
{
public:
Logging(const Process& process, const Security& security, SyscallDelegate&);
bool libraries;
bool segments;
bool fixups;
bool initializers;
bool apis;
bool notifications;
bool interposing;
bool loaders;
bool searching;
bool env;
int descriptor;
bool useStderr;
bool useFile;
private:
};
//
// Contains dyld cache related config info
//
class DyldCache
{
public:
DyldCache(Process&, const Security&, const Logging&, SyscallDelegate&);
const DyldSharedCache* addr;
uintptr_t slide;
const char* path;
const objc_opt::objc_opt_t* objCCacheInfo;
const SwiftOptimizationHeader* swiftCacheInfo;
dyld3::Platform platform;
uint32_t osVersion;
uint32_t dylibCount;
bool indexOfPath(const char* dylibPath, uint32_t& dylibIndex) const;
void makeDataConstWritable(const Logging&, const SyscallDelegate&, bool writable) const;
private:
friend class ProcessConfig;
bool uuidOfFileMatchesDyldCache(const Process& process, const SyscallDelegate& syscall, const char* path) const;
void setPlatformOSVersion(const Process& proc);
void setupDyldCommPage(Process& process, const Security& security, SyscallDelegate& syscall);
};
//
// Contains path searching config info
//
class PathOverrides
{
public:
PathOverrides(const Process&, const Security&, const Logging&, const DyldCache&, SyscallDelegate&);
enum Type { pathDirOverride, versionedOverride, suffixOverride, catalystPrefix, simulatorPrefix, rawPath,
rpathExpansion, loaderPathExpansion, executablePathExpansion, implictRpathExpansion, customFallback, standardFallback };
void forEachPathVariant(const char* requestedPath, dyld3::Platform platform, bool disableCustomFallbacks, bool& stop,
void (^handler)(const char* possiblePath, Type type, bool& stop)) const;
void forEachInsertedDylib(void (^handler)(const char* dylibPath, bool &stop)) const;
bool dontUsePrebuiltForApp() const;
bool hasInsertedDylibs() const { return (_insertedDylibCount != 0); }
uint32_t insertedDylibCount() const { return _insertedDylibCount; }
const char* simRootPath() const { return _simRootPath; }
static const char* getLibraryLeafName(const char* path);
static const char* typeName(Type);
private:
void setEnvVars(const char* envp[], const char* mainExePath);
void addEnvVar(const Process& process, const Security& security, const char* keyEqualsValue, bool isLC_DYLD_ENV, char* crashMsg);
void processVersionedPaths(const Process& proc, SyscallDelegate&, const DyldCache&, dyld3::Platform, const GradedArchs&);
void forEachEnvVar(void (^handler)(const char* envVar)) const;
void forEachExecutableEnvVar(void (^handler)(const char* envVar)) const;
void setString(const Process& process, const char*& var, const char* value);
static void forEachInColonList(const char* list1, const char* list2, void (^callback)(const char* path, bool& stop));
const char* getFrameworkPartialPath(const char* path) const;
void handleListEnvVar(const char* key, const char** list, void (^handler)(const char* envVar)) const;
void handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const;
void forEachDylibFallback(dyld3::Platform platform, bool disableCustom, void (^handler)(const char* fallbackDir, Type type, bool& stop)) const;
void forEachFrameworkFallback(dyld3::Platform platform, bool disableCustom, void (^handler)(const char* fallbackDir, Type type, bool& stop)) const;
void forEachImageSuffix(const char* path, Type type, bool& stop, void (^handler)(const char* possiblePath, Type type, bool& stop)) const;
void addSuffix(const char* path, const char* suffix, char* result) const;
void checkVersionedPath(const Process&, const char* path, SyscallDelegate& delegate, const DyldCache&, dyld3::Platform platform, const GradedArchs& archs);
void addPathOverride(const Process&, const char* installName, const char* overridePath);
enum class FallbackPathMode { classic, restricted, none };
struct DylibOverride { DylibOverride* next; const char* installName; const char* overridePath; };
const char* _dylibPathOverridesEnv = nullptr;
const char* _frameworkPathOverridesEnv = nullptr;
const char* _dylibPathFallbacksEnv = nullptr;
const char* _frameworkPathFallbacksEnv = nullptr;
const char* _versionedDylibPathsEnv = nullptr;
const char* _versionedFrameworkPathsEnv = nullptr;
const char* _dylibPathOverridesExeLC = nullptr;
const char* _frameworkPathOverridesExeLC = nullptr;
const char* _dylibPathFallbacksExeLC = nullptr;
const char* _frameworkPathFallbacksExeLC = nullptr;
const char* _versionedFrameworkPathExeLC = nullptr;
const char* _versionedDylibPathExeLC = nullptr;
const char* _insertedDylibs = nullptr;
const char* _imageSuffix = nullptr;
const char* _simRootPath = nullptr; // simulator only
DylibOverride* _versionedOverrides = nullptr; // linked list of VERSIONED overrides
FallbackPathMode _fallbackPathMode = FallbackPathMode::classic;
uint32_t _insertedDylibCount = 0;
};
// wrappers for macOS that causes special three libsystem dylibs to not exist if they match what is in dyld cache
bool simulatorFileMatchesDyldCache(const char* path) const;
bool fileExists(const char* path, FileID* fileID=nullptr, bool* notAFile=nullptr) const;
// if there is a dyld cache and the supplied path is in the dyld cache at that path or a symlink, return canonical path
const char* canonicalDylibPathInCache(const char* dylibPath) const;
// all instance variables are organized into groups
SyscallDelegate syscall;
Process process;
Security security;
Logging log;
DyldCache dyldCache;
PathOverrides pathOverrides;
};
- 創(chuàng)建Allocator和APIs/RuntimeState對(duì)象:
APIs& state = APIs::bootstrap(config, sLocks);
APIs繼承自RuntimeState
class VIS_HIDDEN APIs : public RuntimeState
{
public:
#if BUILDING_DYLD
static APIs& bootstrap(const ProcessConfig& c, RuntimeLocks& locks);
#else
static APIs& bootstrap(const ProcessConfig& c);
#endif
//
// private call from libdyld.dylib into dyld to tell that libSystem.dylib is initialized
//
virtual void _libdyld_initialize(const LibSystemHelpers* helpers);
//
// APIs from macOS 10.2
//
virtual uint32_t _dyld_image_count();
virtual const mach_header* _dyld_get_image_header(uint32_t index);
virtual intptr_t _dyld_get_image_vmaddr_slide(uint32_t index);
virtual const char* _dyld_get_image_name(uint32_t index);
virtual void _dyld_register_func_for_add_image(void (*func)(const mach_header* mh, intptr_t vmaddr_slide));
virtual void _dyld_register_func_for_remove_image(void (*func)(const mach_header* mh, intptr_t vmaddr_slide));
virtual int32_t NSVersionOfLinkTimeLibrary(const char* libraryName);
virtual int32_t NSVersionOfRunTimeLibrary(const char* libraryName);
virtual int _NSGetExecutablePath(char* buf, uint32_t* bufsize);
virtual void _dyld_fork_child();
//
// APIs from macOS 10.4
//
virtual int dladdr(const void*, Dl_info* result);
virtual void* dlopen(const char* path, int mode);
virtual int dlclose(void* handle);
virtual char* dlerror();
virtual void* dlsym(void* handle, const char* symbol);
virtual bool dlopen_preflight(const char* path);
//
// APIs deprecated in macOS 10.5 and not on any other platform
//
virtual NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage* objectFileImage);
virtual NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* address, size_t size, NSObjectFileImage* objectFileImage);
virtual bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage);
virtual bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName);
virtual void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t* size);
virtual const char* NSNameOfModule(NSModule m);
virtual const char* NSLibraryNameForModule(NSModule m);
virtual NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options);
virtual bool NSUnLinkModule(NSModule module, uint32_t options);
virtual bool NSIsSymbolNameDefined(const char* symbolName);
virtual bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint);
virtual bool NSIsSymbolNameDefinedInImage(const mach_header* image, const char* symbolName);
virtual NSSymbol NSLookupAndBindSymbol(const char* symbolName);
virtual NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint);
virtual NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName);
virtual NSSymbol NSLookupSymbolInImage(const mach_header* image, const char* symbolName, uint32_t options);
virtual void* NSAddressOfSymbol(NSSymbol symbol);
virtual NSModule NSModuleForSymbol(NSSymbol symbol);
virtual void NSLinkEditError(NSLinkEditErrors* c, int* errorNumber, const char** fileName, const char** errorString);
virtual bool NSAddLibrary(const char* pathName);
virtual bool NSAddLibraryWithSearching(const char* pathName);
virtual const mach_header* NSAddImage(const char* image_name, uint32_t options);
virtual bool _dyld_image_containing_address(const void* address);
virtual void _dyld_lookup_and_bind(const char* symbol_name, void** address, NSModule* module);
virtual void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module);
virtual void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module);
//
// Added macOS 10.6
//
virtual intptr_t _dyld_get_image_slide(const mach_header* mh);
virtual const char* dyld_image_path_containing_address(const void* addr);
#if !__USING_SJLJ_EXCEPTIONS__
virtual bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info);
#endif
//
// Added iOS 6, macOS 10.8
//
virtual uint32_t dyld_get_sdk_version(const mach_header* mh);
virtual uint32_t dyld_get_min_os_version(const mach_header* mh);
virtual uint32_t dyld_get_program_sdk_version();
virtual uint32_t dyld_get_program_min_os_version();
//
// Added iOS 7, macOS 10.9
//
virtual bool dyld_process_is_restricted();
//
// Added iOS 8, macOS 10.10
//
virtual bool dyld_shared_cache_some_image_overridden();
virtual void _tlv_atexit(void (*termFunc)(void* objAddr), void* objAddr);
virtual void _tlv_bootstrap();
virtual void _tlv_exit();
//
// Added iOS 9, macOS 10.11, watchOS 2.0
//
virtual int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info));
virtual const mach_header* dyld_image_header_containing_address(const void* addr);
virtual const char* dyld_shared_cache_file_path();
virtual uint32_t dyld_get_program_sdk_watch_os_version();
virtual uint32_t dyld_get_program_min_watch_os_version();
//
// Added iOS 10, macOS 10.12, watchOS 3.0
//
virtual void _dyld_objc_notify_register(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped);
virtual bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid);
virtual bool _dyld_get_shared_cache_uuid(uuid_t uuid);
virtual bool _dyld_is_memory_immutable(const void* addr, size_t length);
virtual int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info));
//
// Added iOS 11, macOS 10.13, bridgeOS 2.0
//
virtual const void* _dyld_get_shared_cache_range(size_t* length);
virtual uint32_t dyld_get_program_sdk_bridge_os_version();
virtual uint32_t dyld_get_program_min_bridge_os_version();
//
// Added iOS 12, macOS 10.14
//
virtual dyld_platform_t dyld_get_active_platform();
virtual dyld_platform_t dyld_get_base_platform(dyld_platform_t platform);
virtual bool dyld_is_simulator_platform(dyld_platform_t platform);
virtual bool dyld_sdk_at_least(const mach_header* mh, dyld_build_version_t version);
virtual bool dyld_minos_at_least(const mach_header* mh, dyld_build_version_t version);
virtual bool dyld_program_sdk_at_least(dyld_build_version_t version);
virtual bool dyld_program_minos_at_least(dyld_build_version_t version);
virtual void dyld_get_image_versions(const mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version));
virtual void _dyld_images_for_addresses(unsigned count, const void* addresses[], dyld_image_uuid_offset infos[]);
virtual void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable));
//
// Added iOS 13, macOS 10.15
//
virtual void _dyld_atfork_prepare();
virtual void _dyld_atfork_parent();
virtual bool dyld_need_closure(const char* execPath, const char* dataContainerRootDir);
virtual bool dyld_has_inserted_or_interposing_libraries(void);
virtual bool _dyld_shared_cache_optimized(void);
virtual bool _dyld_shared_cache_is_locally_built(void);
virtual void _dyld_register_for_bulk_image_loads(void (*func)(unsigned imageCount, const mach_header* mhs[], const char* paths[]));
virtual void _dyld_register_driverkit_main(void (*mainFunc)(void));
virtual void _dyld_missing_symbol_abort();
virtual const char* _dyld_get_objc_selector(const char* selName);
virtual void _dyld_for_each_objc_class(const char* className, void (^)(void* classPtr, bool isLoaded, bool* stop));
virtual void _dyld_for_each_objc_protocol(const char* protocolName, void (^)(void* protocolPtr, bool isLoaded, bool* stop));
//
// Added iOS 14, macOS 11
//
virtual uint32_t _dyld_launch_mode();
virtual bool _dyld_is_objc_constant(DyldObjCConstantKind kind, const void* addr);
virtual bool _dyld_has_fix_for_radar(const char* rdar);
virtual const char* _dyld_shared_cache_real_path(const char* path);
virtual bool _dyld_shared_cache_contains_path(const char* path);
virtual void* dlopen_from(const char* path, int mode, void* addressInCaller);
#if !__i386__
virtual void * dlopen_audited(const char * path, int mode);
#endif
virtual const struct mach_header* _dyld_get_prog_image_header();
//
// Added iOS 15, macOS 12
//
virtual void obsolete() __attribute__((noreturn));
virtual void _dyld_visit_objc_classes(void (^callback)(const void* classPtr));
virtual uint32_t _dyld_objc_class_count(void);
virtual bool _dyld_objc_uses_large_shared_cache(void);
virtual _dyld_protocol_conformance_result _dyld_find_protocol_conformance(const void *protocolDescriptor,
const void *metadataType,
const void *typeDescriptor) const;
virtual _dyld_protocol_conformance_result _dyld_find_foreign_type_protocol_conformance(const void *protocol,
const char *foreignTypeIdentityStart,
size_t foreignTypeIdentityLength) const;
virtual uint32_t _dyld_swift_optimizations_version() const;
virtual void runAllInitializersForMain();
virtual void _dyld_before_fork_dlopen();
virtual void _dyld_after_fork_dlopen_parent();
virtual void _dyld_after_fork_dlopen_child();
private:
#if BUILDING_DYLD
APIs(const ProcessConfig& c, RuntimeLocks& locks, Allocator* alloc) : RuntimeState(c, locks, *alloc) {}
#else
APIs(const ProcessConfig& c, Allocator* alloc) : RuntimeState(c, *alloc) {}
#endif
// internal helpers
uint32_t getSdkVersion(const mach_header* mh);
dyld_build_version_t mapFromVersionSet(dyld_build_version_t version);
uint32_t linkedDylibVersion(const dyld3::MachOFile* mf, const char* installname);
uint32_t deriveVersionFromDylibs(const dyld3::MachOFile* mf);
void forEachPlatform(const dyld3::MachOFile* mf, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version));
uint32_t basePlaform(uint32_t reqPlatform) const;
bool findImageMappedAt(const void* addr, const MachOLoaded** ml, bool* neverUnloads = nullptr, const char** path = nullptr, const void** segAddr = nullptr, uint64_t* segSize = nullptr, uint8_t* segPerms = nullptr);
void clearErrorString();
void setErrorString(const char* format, ...) __attribute__((format(printf, 2, 3)));
const Loader* findImageContaining(void* addr);
bool flatFindSymbol(const char* symbolName, void** symbolAddress, const mach_header** foundInImageAtLoadAddress);
bool validLoader(const Loader* maybeLoader);
void forEachImageVersion(const mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version));
};
//
// Note: need to force vtable ptr auth so that libdyld.dylib from base OS and driverkit use same ABI
//
class [[clang::ptrauth_vtable_pointer(process_independent, address_discrimination, type_discrimination)]] RuntimeState
{
public:
const ProcessConfig& config;
Allocator& longTermAllocator;
const Loader* mainExecutableLoader = nullptr;
Vector<ConstAuthLoader> loaded;
const Loader* libSystemLoader = nullptr;
const Loader* libdyldLoader = nullptr;
const void* libdyldMissingSymbol = 0;
#if BUILDING_DYLD
RuntimeLocks& _locks;
#endif
dyld4::ProgramVars* vars = nullptr;
const LibSystemHelpers* libSystemHelpers = nullptr;
Vector<InterposeTupleAll> interposingTuplesAll;
Vector<InterposeTupleSpecific> interposingTuplesSpecific;
uint64_t weakDefResolveSymbolCount = 0;
WeakDefMap* weakDefMap = nullptr;
#if BUILDING_DYLD
RuntimeState(const ProcessConfig& c, RuntimeLocks& locks, Allocator& alloc)
#else
RuntimeState(const ProcessConfig& c, Allocator& alloc = *Allocator::bootstrap())
#endif
: config(c), longTermAllocator(alloc), loaded(&alloc),
#if BUILDING_DYLD
_locks(locks),
#endif
interposingTuplesAll(&alloc), interposingTuplesSpecific(&alloc),
_notifyAddImage(&alloc), _notifyRemoveImage(&alloc),
_notifyLoadImage(&alloc), _notifyBulkLoadImage(&alloc),
_tlvInfos(&alloc), _loadersNeedingDOFUnregistration(&alloc),
_missingFlatLazySymbols(&alloc), _dynamicReferences(&alloc),
_dlopenRefCounts(&alloc), _dynamicNeverUnloads(&alloc) {}
void setMainLoader(const Loader*);
void add(const Loader*);
MainFunc mainFunc() { return _driverKitMain; }
void setMainFunc(MainFunc func) { _driverKitMain = func; }
void setDyldLoader(const Loader* ldr);
uint8_t* appState(uint16_t index);
uint8_t* cachedDylibState(uint16_t index);
const MachOLoaded* appLoadAddress(uint16_t index);
void setAppLoadAddress(uint16_t index, const MachOLoaded* ml);
const MachOLoaded* cachedDylibLoadAddress(uint16_t index);
void log(const char* format, ...) const __attribute__((format(printf, 2, 3))) ;
void vlog(const char* format, va_list list);
void setObjCNotifiers(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped);
void addNotifyAddFunc(const Loader* callbackLoader, NotifyFunc);
void addNotifyRemoveFunc(const Loader* callbackLoader, NotifyFunc);
void addNotifyLoadImage(const Loader* callbackLoader, LoadNotifyFunc);
void addNotifyBulkLoadImage(const Loader* callbackLoader, BulkLoadNotifier);
void notifyObjCInit(const Loader* ldr);
void buildInterposingTables();
void withNotifiersReadLock(void (^work)());
void withNotifiersWriteLock(void (^work)());
void addPermanentRanges(const Array<const Loader*>& neverUnloadLoaders);
bool inPermanentRange(uintptr_t start, uintptr_t end, uint8_t* perms, const Loader** loader);
void notifyLoad(const dyld3::Array<const Loader*>& newLoaders);
void notifyUnload(const dyld3::Array<const Loader*>& removeLoaders);
void notifyDebuggerLoad(const Loader* oneLoader);
void notifyDebuggerLoad(const dyld3::Array<const Loader*>& newLoaders);
void notifyDebuggerUnload(const dyld3::Array<const Loader*>& removingLoaders);
void notifyDtrace(const dyld3::Array<const Loader*>& newLoaders);
void incDlRefCount(const Loader* ldr); // used by dlopen
void decDlRefCount(const Loader* ldr); // used by dlclose
void setLaunchMissingDylib(const char* missingDylibPath, const char* clientUsingDylib);
void setLaunchMissingSymbol(const char* missingSymbolName, const char* dylibThatShouldHaveSymbol, const char* clientUsingSymbol);
void addMissingFlatLazySymbol(const Loader* ldr, const char* symbolName, uintptr_t* bindLoc);
void rebindMissingFlatLazySymbols(const dyld3::Array<const Loader*>& newLoaders);
void removeMissingFlatLazySymbols(const dyld3::Array<const Loader*>& removingLoaders);
bool hasMissingFlatLazySymbols() const;
void addDynamicReference(const Loader* from, const Loader* to);
void removeDynamicDependencies(const Loader* removee);
void setSavedPrebuiltLoaderSet() { _wrotePrebuiltLoaderSet = true; }
bool didSavePrebuiltLoaderSet() const { return _wrotePrebuiltLoaderSet; }
void setVMAccountingSuspending(bool mode);
bool hasOverriddenCachedDylib() { return _hasOverriddenCachedDylib; }
void setHasOverriddenCachedDylib() { _hasOverriddenCachedDylib = true; }
pthread_key_t dlerrorPthreadKey() { return _dlerrorPthreadKey; }
typedef void (*TLV_TermFunc)(void* objAddr);
void initialize();
void setUpTLVs(const MachOAnalyzer* ma);
void addTLVTerminationFunc(TLV_TermFunc func, void* objAddr);
void exitTLV();
void withLoadersReadLock(void (^work)());
void withLoadersWriteLock(void (^work)());
void incWritable();
void decWritable();
void initializeClosureMode();
const PrebuiltLoaderSet* processPrebuiltLoaderSet() const { return _processPrebuiltLoaderSet; }
const PrebuiltLoaderSet* cachedDylibsPrebuiltLoaderSet() const { return _cachedDylibsPrebuiltLoaderSet; }
uint8_t* prebuiltStateArray(bool app) const { return (app ? _processDylibStateArray : _cachedDylibsStateArray); }
const PrebuiltLoader* findPrebuiltLoader(const char* loadPath) const;
bool saveAppClosureFile() const { return _saveAppClosureFile; }
bool failIfCouldBuildAppClosureFile() const { return _failIfCouldBuildAppClosureFile; }
bool saveAppPrebuiltLoaderSet(const PrebuiltLoaderSet* pblset) const;
bool inPrebuiltLoader(const void* p, size_t len) const;
#if !BUILDING_DYLD
void resetCachedDylibs(const PrebuiltLoaderSet* dylibs, uint8_t* stateArray);
void setProcessPrebuiltLoaderSet(const PrebuiltLoaderSet* appPBLS);
void resetCachedDylibsArrays();
#endif
// this need to be virtual to be callable from libdyld.dylb
virtual void _finalizeListTLV(void* l);
virtual void* _instantiateTLVs(pthread_key_t key);
protected:
// Helpers to reset locks across fork()
void takeLockBeforeFork();
void releaseLockInForkParent();
void resetLockInForkChild();
void takeDlopenLockBeforeFork();
void releaseDlopenLockInForkParent();
void resetDlopenLockInForkChild();
private:
//
// The PermanentRanges structure is used to make dyld_is_memory_immutable()
// fast and lock free. The table contains just ranges of memory that are in
// images that will never be unloaded. Dylibs in the dyld shared cache are
// never in this table. A PermanentRanges struct is allocated at launch for
// app and its non-cached dylibs, because they can never be unloaded. Later
// if a dlopen() brings in non-cached dylibs which can never be unloaded,
// another PermanentRanges is allocated with the ranges brought in by that
// dlopen. The PermanentRanges struct are chained together in a linked list
// with state._permanentRanges pointing to the start of the list.
// Because these structs never change, they can be read without taking a lock.
// That makes finding immutable ranges lock-less.
//
class PermanentRanges
{
public:
static PermanentRanges* make(RuntimeState& state, const Array<const Loader*>& neverUnloadLoaders);
bool contains(uintptr_t start, uintptr_t end, uint8_t* perms, const Loader** loader) const;
PermanentRanges* next() const;
void append(PermanentRanges*);
private:
void addPermanentRange(uintptr_t start, uintptr_t end, bool immutable, const Loader* loader);
void add(const Loader*);
struct Range
{
uintptr_t start;
uintptr_t end;
const Loader* loader;
uintptr_t permissions;
};
// FIXME: we could pack this structure better to reduce memory usage
std::atomic<PermanentRanges*> _next = nullptr;
uintptr_t _rangeCount = 0;
Range _ranges[1];
};
// keep dlopen counts in a side table because it is rarely used, so it would waste space for each Loader object to have its own count field
friend class Reaper;
friend class RecursiveAutoLock;
struct DlopenCount {
const Loader* loader;
uintptr_t refCount;
};
// when a thread_local is first accessed on a thread, the thunk calls into dyld
// to allocate the variables. The pthread_key is the index used to find the
// TLV_Info which then describes how much to allocate and how to initalize that memory.
struct TLV_Info
{
const MachOAnalyzer* ma;
pthread_key_t key;
uint32_t initialContentOffset;
uint32_t initialContentSize;
};
// used to record _tlv_atexit() entries to clean up on thread exit
struct TLV_Terminator
{
TLV_TermFunc termFunc;
void* objAddr;
};
struct TLV_TerminatorList {
TLV_TerminatorList* next = nullptr;
uintptr_t count = 0;
TLV_Terminator elements[7];
void reverseWalkChain(void (^work)(TLV_TerminatorList*));
};
struct RegisteredDOF
{
const Loader* ldr;
int registrationID;
};
struct MissingFlatSymbol
{
const Loader* ldr;
const char* symbolName;
uintptr_t* bindLoc;
};
struct DynamicReference
{
const Loader* from;
const Loader* to;
};
struct HiddenCacheAddr { const void* cacheAddr; const void* replacementAddr; };
enum { kMaxBootTokenSize = 128 };
void appendInterposingTuples(const Loader* ldr, const uint8_t* dylibTuples, uint32_t tupleCount);
void garbageCollectImages();
void garbageCollectInner();
void removeLoaders(const dyld3::Array<const Loader*>& loadersToRemove);
void withTLVLock(void (^work)());
void setUpLogging();
void buildAppPrebuiltLoaderSetPath(bool createDirs);
bool fileAlreadyHasBootToken(const char* path, const Array<uint8_t>& bootToken) const;
bool buildBootToken(dyld3::Array<uint8_t>& bootToken) const;
void loadAppPrebuiltLoaderSet();
bool allowOsProgramsToSaveUpdatedClosures() const;
bool allowNonOsProgramsToSaveUpdatedClosures() const;
void allocateProcessArrays(uintptr_t count);
void checkHiddenCacheAddr(const Loader* t, const void* targetAddr, const char* symbolName, dyld3::OverflowSafeArray<HiddenCacheAddr>& hiddenCacheAddrs) const;
_dyld_objc_notify_mapped _notifyObjCMapped = nullptr;
_dyld_objc_notify_init _notifyObjCInit = nullptr;
_dyld_objc_notify_unmapped _notifyObjCUnmapped = nullptr;
Vector<NotifyFunc> _notifyAddImage;
Vector<NotifyFunc> _notifyRemoveImage;
Vector<LoadNotifyFunc> _notifyLoadImage;
Vector<BulkLoadNotifier> _notifyBulkLoadImage;
Vector<TLV_Info> _tlvInfos;
Vector<RegisteredDOF> _loadersNeedingDOFUnregistration;
Vector<MissingFlatSymbol> _missingFlatLazySymbols;
Vector<DynamicReference> _dynamicReferences;
const PrebuiltLoaderSet* _cachedDylibsPrebuiltLoaderSet = nullptr;
uint8_t* _cachedDylibsStateArray = nullptr;
const char* _processPrebuiltLoaderSetPath = nullptr;
const PrebuiltLoaderSet* _processPrebuiltLoaderSet = nullptr;
uint8_t* _processDylibStateArray = nullptr;
const MachOLoaded** _processLoadedAddressArray = nullptr;
bool _saveAppClosureFile;
bool _failIfCouldBuildAppClosureFile;
PermanentRanges* _permanentRanges = nullptr;
MainFunc _driverKitMain = nullptr;
Vector<DlopenCount> _dlopenRefCounts;
Vector<const Loader*> _dynamicNeverUnloads;
std::atomic<int32_t> _gcCount = 0;
pthread_key_t _tlvTerminatorsKey = 0;
pthread_key_t _dlerrorPthreadKey = 0;
int _logDescriptor = -1;
bool _logToSyslog = false;
bool _logSetUp = false;
bool _hasOverriddenCachedDylib = false;
bool _wrotePrebuiltLoaderSet = false;
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
bool _vmAccountingSuspended = false;
#endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
};
- 加載程序的所有依賴項(xiàng)并將state綁定在一起:
prepare(state, dyldMA);
- 調(diào)用main():
appMain
2.prepare:加載程序的所有依賴項(xiàng)
-
gProcessInfo
是存儲(chǔ)dyld所有鏡像信息的結(jié)構(gòu)體:,保存著mach_header
,dyld_uuid_info
,dyldVersion
等等信息蹦狂。
gProcessInfo的聲明:
struct dyld_all_image_infos* gProcessInfo = &dyld_all_image_infos;
struct dyld_all_image_infos {
uint32_t version; /* 1 in Mac OS X 10.4 and 10.5 */
uint32_t infoArrayCount;
#if defined(__cplusplus) && (BUILDING_LIBDYLD || BUILDING_DYLD)
std::atomic<const struct dyld_image_info*> infoArray;
#else
const struct dyld_image_info* infoArray;
#endif
dyld_image_notifier notification;
bool processDetachedFromSharedRegion;
/* the following fields are only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later */
bool libSystemInitialized;
const struct mach_header* dyldImageLoadAddress;
/* the following field is only in version 3 (Mac OS X 10.6, iPhoneOS 3.0) and later */
void* jitInfo;
/* the following fields are only in version 5 (Mac OS X 10.6, iPhoneOS 3.0) and later */
const char* dyldVersion;
const char* errorMessage;
uintptr_t terminationFlags;
/* the following field is only in version 6 (Mac OS X 10.6, iPhoneOS 3.1) and later */
void* coreSymbolicationShmPage;
/* the following field is only in version 7 (Mac OS X 10.6, iPhoneOS 3.1) and later */
uintptr_t systemOrderFlag;
/* the following field is only in version 8 (Mac OS X 10.7, iPhoneOS 3.1) and later */
uintptr_t uuidArrayCount;
const struct dyld_uuid_info* uuidArray; /* only images not in dyld shared cache */
/* the following field is only in version 9 (Mac OS X 10.7, iOS 4.0) and later */
struct dyld_all_image_infos* dyldAllImageInfosAddress;
/* the following field is only in version 10 (Mac OS X 10.7, iOS 4.2) and later */
uintptr_t initialImageCount;
/* the following field is only in version 11 (Mac OS X 10.7, iOS 4.2) and later */
uintptr_t errorKind;
const char* errorClientOfDylibPath;
const char* errorTargetDylibPath;
const char* errorSymbol;
/* the following field is only in version 12 (Mac OS X 10.7, iOS 4.3) and later */
uintptr_t sharedCacheSlide;
/* the following field is only in version 13 (Mac OS X 10.9, iOS 7.0) and later */
uint8_t sharedCacheUUID[16];
/* the following field is only in version 15 (macOS 10.12, iOS 10.0) and later */
uintptr_t sharedCacheBaseAddress;
#if defined(__cplusplus) && (BUILDING_LIBDYLD || BUILDING_DYLD)
// We want this to be atomic in libdyld so that we can see updates when we map it shared
std::atomic<uint64_t> infoArrayChangeTimestamp;
#else
uint64_t infoArrayChangeTimestamp;
#endif
const char* dyldPath;
mach_port_t notifyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
#if __LP64__
uintptr_t reserved[11-(DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT/2)];
#else
uintptr_t reserved[9-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
#endif
// The following fields were added in version 18 (previously they were reserved padding fields)
uint64_t sharedCacheFSID;
uint64_t sharedCacheFSObjID;
/* the following field is only in version 16 (macOS 10.13, iOS 11.0) and later */
uintptr_t compact_dyld_image_info_addr;
size_t compact_dyld_image_info_size;
uint32_t platform; // FIXME: really a dyld_platform_t, but those aren't exposed here.
/* the following field is only in version 17 (macOS 10.16) and later */
uint32_t aotInfoCount;
const struct dyld_aot_image_info* aotInfoArray;
uint64_t aotInfoArrayChangeTimestamp;
uintptr_t aotSharedCacheBaseAddress;
uint8_t aotSharedCacheUUID[16];
};
- 進(jìn)行pre-build, 創(chuàng)建
mainLoader
鏡像裝載器
ps : 如果熟悉dyld3
的小伙伴知道, 舊版本是創(chuàng)建一個(gè)ImageLoader
鏡像裝載器
- 創(chuàng)建
just-in-time
是dyld4
一個(gè)新特性,dyld4
在保留了dyld3
的mach-o
解析器基礎(chǔ)上誓篱,同時(shí)也引入了just-in-time
的加載器來(lái)優(yōu)化朋贬。
dyld3
出于對(duì)啟動(dòng)速度的優(yōu)化的目的, 增加了預(yù)構(gòu)建(閉包)
。App第一次啟動(dòng)或者App發(fā)生變化時(shí)會(huì)將部分啟動(dòng)數(shù)據(jù)創(chuàng)建為閉包存到本地窜骄,那么App下次啟動(dòng)將不再重新解析數(shù)據(jù)锦募,而是直接讀取閉包內(nèi)容。當(dāng)然前提是應(yīng)用程序和系統(tǒng)應(yīng)很少發(fā)生變化邻遏,但如果這兩者經(jīng)常變化等, 就會(huì)導(dǎo)閉包丟失或失效糠亩。
dyld4
采用了 pre-build + just-in-time
的雙解析模式,預(yù)構(gòu)建 pre-build 對(duì)應(yīng)的就是 dyld3 中的閉包
准验,just-in-time
可以理解為實(shí)時(shí)解析削解。當(dāng)然just-in-time
也是可以利用 pre-build
的緩存的,所以性能可控沟娱。有了just-in-time
, 目前應(yīng)用首次啟動(dòng)团搞、系統(tǒng)版本更新同蜻、普通啟動(dòng)逝变,dyld4
則可以根據(jù)緩存是否有效去選擇合適的模式進(jìn)行解析达皿。
- 裝載uuid腾供、動(dòng)態(tài)庫(kù)疹启、可執(zhí)行文件
記錄插入信息, 遍歷所有dylibs, 一些記錄檢查操作繼續(xù)往下走
- 插入緩存
Loader::applyInterposingToDyldCache
的實(shí)現(xiàn):
void Loader::applyInterposingToDyldCache(RuntimeState& state)
{
const DyldSharedCache* dyldCache = state.config.dyldCache.addr;
if ( dyldCache == nullptr )
return; // no dyld cache to interpose
if ( state.interposingTuplesAll.empty() )
return; // no interposing tuples
// make the cache writable for this block
DyldCacheDataConstScopedWriter patcher(state);
state.setVMAccountingSuspending(true);
for ( const InterposeTupleAll& tuple : state.interposingTuplesAll ) {
uint32_t imageIndex;
uintptr_t cacheOffsetOfReplacee = tuple.replacee - (uintptr_t)dyldCache;
if ( !dyldCache->addressInText(cacheOffsetOfReplacee, &imageIndex) )
continue;
// Convert from a cache offset to an image offset
uint64_t mTime;
uint64_t inode;
const dyld3::MachOAnalyzer* imageMA = (dyld3::MachOAnalyzer*)(dyldCache->getIndexedImageEntry(imageIndex, mTime, inode));
if ( imageMA == nullptr )
continue;
uint32_t dylibOffsetOfReplacee = (uint32_t)((dyldCache->unslidLoadAddress() + cacheOffsetOfReplacee) - imageMA->preferredLoadAddress());
dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t dylibVMOffsetOfImpl, const char* exportName) {
// Skip patching anything other than this symbol
if ( dylibVMOffsetOfImpl != dylibOffsetOfReplacee )
return;
uintptr_t newLoc = tuple.replacement;
dyldCache->forEachPatchableUseOfExport(imageIndex, dylibVMOffsetOfImpl,
^(uint64_t cacheVMOffset, MachOLoaded::PointerMetaData pmd, uint64_t addend) {
uintptr_t* loc = (uintptr_t*)((uintptr_t)dyldCache + cacheVMOffset);
uintptr_t newValue = newLoc + (uintptr_t)addend;
#if __has_feature(ptrauth_calls)
if ( pmd.authenticated ) {
newValue = dyld3::MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(newValue, loc, pmd.usesAddrDiversity, pmd.diversity, pmd.key);
*loc = newValue;
if ( state.config.log.interposing )
state.log("interpose: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
loc, (void*)newValue, pmd.diversity, pmd.usesAddrDiversity, MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::keyName(pmd.key));
return;
}
#endif
if ( state.config.log.interposing )
state.log("interpose: *%p = 0x%0llX (dyld cache patch) to %s\n", loc, newLoc + addend, exportName);
*loc = newValue;
});
});
}
state.setVMAccountingSuspending(false);
}
接下來(lái)的工作是一些其他通知和寫入操作磅网。
- 運(yùn)行初始化方法
// 運(yùn)行所有的初始化
state.runAllInitializersForMain();
3.runAllInitializersForMain:運(yùn)行初始化方法
// 這是從dyldMain.cpp中提取出來(lái)的虱岂,以支持使用crt1.o的舊macOS應(yīng)用程序
void APIs::runAllInitializersForMain()
{
// 首先運(yùn)行l(wèi)ibSystem的初始化器
const_cast<Loader*>(this->libSystemLoader)->beginInitializers(*this);
this->libSystemLoader->runInitializers(*this);
gProcessInfo->libSystemInitialized = true;
// 在運(yùn)行l(wèi)ibSystem的初始化器后唉铜,告訴objc在libSystem的子dylibs上運(yùn)行任何+load方法
this->notifyObjCInit(this->libSystemLoader);
// <rdar://problem/32209809> 調(diào)用'init'函數(shù)對(duì)所有已經(jīng)init'ed的圖像 (below libSystem)
// 使用下標(biāo)進(jìn)行迭代台舱,以便在+加載dloopen時(shí)數(shù)組不會(huì)在我們下面增長(zhǎng)
for ( uint32_t i = 0; i != this->loaded.size(); ++i ) {
const Loader* ldr = this->loaded[i];
if ( ldr->analyzer(*this)->isDylib() && (strncmp(ldr->analyzer(*this)->installName(), "/usr/lib/system/lib", 19) == 0) ) {
// 檢查安裝名稱而不是路徑,以處理libsystem子dylibs的DYLD_LIBRARY_PATH覆蓋
const_cast<Loader*>(ldr)->beginInitializers(*this);
this->notifyObjCInit(ldr);
}
}
// 自底向上運(yùn)行所有其他初始化器潭流,首先運(yùn)行插入的dylib初始化器
// 使用下標(biāo)進(jìn)行迭代竞惋,以便在初始化式dloopen時(shí)數(shù)組不會(huì)在下面增長(zhǎng)
for ( uint32_t i = 0; i != this->loaded.size(); ++i ) {
const Loader* ldr = this->loaded[i];
ldr->runInitializersBottomUpPlusUpwardLinks(*this);
// stop as soon as we did main executable
// normally this is first image, but if there are inserted N dylibs, it is Nth in the list
if ( ldr->analyzer(*this)->isMainExecutable() )
break;
}
}
- 運(yùn)行
libSystem
的初始化器
- 告訴
objc
在libSystem
的子dylibs上運(yùn)行所有的+load
方法
(可執(zhí)行文件、動(dòng)態(tài)庫(kù)等等上面的load)
notifyObjCInit
這個(gè)工作在第4部分介紹
-
link
動(dòng)態(tài)庫(kù)和主程序:runInitializersBottomUpPlusUpwardLinks
runInitializersBottomUpPlusUpwardLinks
這個(gè)工作在第5部分介紹
4.notifyObjCInit
在libSystem
的初始化時(shí)灰嫉,在其子dylibs
上需要通知objc
運(yùn)行所有的+load
方法
notifyObjCInit
的定義是在RuntimeState
類的聲明里:
_dyld_objc_notify_init
在setObjCNotifiers
被賦值的:
那setObjCNotifiers
是在_dyld_objc_notify_register
被調(diào)用的
而_dyld_objc_notify_register
是在objc4源碼里的 objc_init()
里被調(diào)用的
最后objc_init()
在庫(kù)初始化時(shí)間之前由libSystem
調(diào)用拆宛。
ps: load_images
的內(nèi)容在下章節(jié),知道它是用來(lái)進(jìn)行加載和調(diào)用+load
方法就夠了讼撒。
5.link
動(dòng)態(tài)庫(kù)和主程序: runInitializersBottomUpPlusUpwardLinks
void Loader::runInitializersBottomUpPlusUpwardLinks(RuntimeState& state) const
{
//state.log("runInitializersBottomUpPlusUpwardLinks() %s\n", this->path());
state.incWritable();
// 遞歸地運(yùn)行所有初始化器 initializers
STACK_ALLOC_ARRAY(const Loader*, danglingUpwards, state.loaded.size());
this->runInitializersBottomUp(state, danglingUpwards);
//state.log("runInitializersBottomUpPlusUpwardLinks(%s), found %d dangling upwards\n", this->path(), danglingUpwards.count());
// 返回所有向上鏈接的image浑厚,并檢查它們是否已初始化 (might be danglers)
STACK_ALLOC_ARRAY(const Loader*, extraDanglingUpwards, state.loaded.size());
for ( const Loader* ldr : danglingUpwards ) {
//state.log("running initializers for dangling upward link %s\n", ldr->path());
ldr->runInitializersBottomUp(state, extraDanglingUpwards);
}
if ( !extraDanglingUpwards.empty() ) {
// 如果有兩個(gè)向上懸空的image,請(qǐng)?jiān)俅螜z查初始化器initializers
danglingUpwards.resize(0);
for ( const Loader* ldr : extraDanglingUpwards ) {
//state.log("running initializers for dangling upward link %s\n", ldr->path());
ldr->runInitializersBottomUp(state, danglingUpwards);
}
}
state.decWritable();
}
runInitializersBottomUp
void Loader::runInitializersBottomUp(RuntimeState& state, Array<const Loader*>& danglingUpwards) const
{
// 如果已經(jīng)初始化器已經(jīng)運(yùn)行就什么都不做
if ( (const_cast<Loader*>(this))->beginInitializers(state) )
return;
//state.log("runInitializersBottomUp(%s)\n", this->path());
// 在運(yùn)行我的初始化器之前根盒,確保這個(gè)image下面的所有東西都已初始化
const uint32_t depCount = this->dependentCount();
for ( uint32_t i = 0; i < depCount; ++i ) {
DependentKind childKind;
if ( Loader* child = this->dependent(state, i, &childKind) ) {
if ( childKind == DependentKind::upward ) {
// 向上添加到列表稍后處理
if ( !danglingUpwards.contains(child) )
danglingUpwards.push_back(child);
}
else {
child->runInitializersBottomUp(state, danglingUpwards);
}
}
}
// tell objc to run any +load methods in this image (done before C++ initializers)
state.notifyObjCInit(this);
// run initializers for this image
this->runInitializers(state);
}
6.調(diào)起主程序入口
在 prepare
函數(shù)調(diào)用最后返回的就是主程序入口 MainFunc
在start
函數(shù)里調(diào)用了主程序入口
最后給個(gè)總結(jié)圖
dyld4應(yīng)用程序加載流程圖
dyld3應(yīng)用程序加載流程圖: