dyld源码
苹果官方资源opensource
objc4-838可编译联调源码
一、了解相关概念
1.静态库、动态库
通常程序都会依赖系统一些库, 库是什么呢? 其实库就是一些可执行的二进制文件, 能被操作系统加载到内存里面中。库分为两种:静态库 / 动态库
-
静态库
:是一堆.o文件的集合。格式.a
,.lib
等。链接阶段时静态库会被完整地复制, 一起打包在可执行文件中,被多次使用就有多份冗余拷贝。-
优点
: 编译完成之后, 链接到目标程序中, 同时打包到可执行文件里面, 不会有外部依赖。 -
缺点
: 静态库会有两份, 所以会导致目标程序体积增大, 对内存, 性能, 速度消耗很大。并且相同静态库每个app中都会拷贝一份。
-
-
动态库
:是一个已经完全链接后的镜像。格式.framework
等。程序编译时并不会链接到目标程序中,目标程序只会存储指向动态库的引用,在程序运行时才被载入。苹果大部分都是动态库。-
优点
: 不需要拷贝到目标程序, 减少App包的体积;多个App可以使用同一个动态库, 共享内存, 节约资源;由于运行时才会去加载, 那么可以在App不使用时随时对库进行替换或更新, 更新灵活。 -
缺点
: 动态载入会带来一部分性能损失, 同时动态库也会使得程序依赖于外部环境。一旦动态库没有或消失, 程序会出现问题。
-
1.Xcode编译command + B
产生可执行文件的过程:
- a.
预编译
处理源代码文件中以#
开头的预编译的命令(#import
/#include
等等);把包含的文件插入到指定的位置;替换代码中的宏定义;删除代码里的注释等等。产生.i
文件。 - b.
编译
词法分析;语法分析;语义分析;生成相应的汇编代码文件。 产生.s
文件(汇编文件) - c.
汇编
将汇编代码文件翻译成机器指令,机器指令会生成.o
文件。 - d.
链接
把之前所有操作的文件链接到程序里面来, 对.o
文件中引用其他库的地方进行引用, 生成最后的可执行文件
。动态库与静态库区别其实就是链接的区别。-
静态链接
: 链接器对.o
文件进行符号收集、解析和重定位;把所有的.o
粘合在一起从而形成可执行文件。(在静态链接之后就不会再有静态库了)
-
动态链接
:dyld
动态链接(下面有介绍)
-
2.点击App启动的过程:
- a.系统调用
exec()
分配内存、开辟进程 - b.app对应的
可执行文件
加载到内存 - c.
dyld动态链接器
加载到内存:load dyld
- d.
dyld动态链接器
进行动态链接:rebase
->bing
->Objc
->initalizers
- e.调起
main()
函数
启动时间的划分可以把main()
函数作为关键点分割成两块
t1阶段
,main()
之前的处理所需时间,称为pre-main
t2阶段
,main()
及main()
之后处理所需时间
t2阶段耗时的主要是业务代码
3.Mach-O是什么?
Mach-O
: Mach Object
文件格式的缩写,它是一种用于可执行文件,目标文件.o
,动态库,内核转储的文件格式
。作为a.out
格式的替代,Mach-O
提供了更强的扩展性,并提升了符号表中信息的访问速度。
三种Mach-O
格式的文件:可执行文件
、dylib动态库
、Bundle资源文件包
Mach-O
由四部分组成:Mach-O头部
、Load Command
、section
、Other Data
,可以通过MachOView
可查看可执行文件信息。
二、什么是dyld
本文主要介绍dyld
源码执行流程。
在app
启动加载过程中类和分类加载都不可避免的触及dyld
,所以了解dyld
源码可以让我们更好的理解app
的工作原理。
什么是dyld?
dyld(the dynamic link editor 动态链接器)
:是苹果操作系统的一个重要的组成部分。在iOS/Mac OSX系统中,仅有很少量的进程只需要内核就能完成加载,基本上所有的进程都是动态链接的,所以Mach-O镜像文件中会有很多对外部的库和符号的引用,但是这些引用并不能直接用,在启动时还必须要通过这些引用进行内容的填补,这个填补工作就是由动态链接器dyld来完成的,也就是符号绑定。
在app被编译打包成可执行文件格式的Mach-O文件后,在程序启动时交由dyld负责链接加载程序。
dyld
是开源的,可以通过苹果官网下载它的dyld源码,本文的对应的版本是941.5
。dyld4是iOS15发布的。
-
dyld3
: 相比dyld2新增预构建/闭包, 目的是将一些启动数据创建为闭包存到本地,下次启动将不再重新解析数据,而是直接读取闭包内容 -
dyld4
: 采用pre-build + just-in-time
预构建/闭包+实时解析的双解析模式, 将根据缓存有效与否选择合适的模式进行解析, 同时也处理了闭包失效时候需要重建闭包的性能问题。
三、dyld工作流程
新建一个工程,在ViewController.m
里重写+(void)load
方法,在load方法上打个断点调试,运行app后在断点处输出其函数调用的堆栈:
新版本dyld: start
开始接下来走 dyld4中prepare
方法, 源码入口需要在start
中开始探索。
(旧版本dyld: _dyld_start
开始, 接下来走dyldbootstrap
, 源码入口需要在dyld_start
开始。)
1.start函数是dyld的开始
// dyld的入口点。内核加载dyld并跳转到__dyld_start,它设置一些寄存器并调用这个函数。
// 注意:这个函数永远不会返回,它调用exit()。因此,堆栈保护程序是无用的,因为永远不会执行epilog。标记函数no-return禁用堆栈保护。堆栈保护器也会导致armv7k代码生成的问题,因为它通过prolog中的GOT槽访问随机值,但dyld还没有rebased。
void start(const KernelArgs* kernArgs) __attribute__((noreturn)) __asm("start");
void start(const KernelArgs* kernArgs)
{
// 发出kdebug跟踪点来指示dyld引导程序已经启动 <rdar://46878536>
// 注意:这是在dyld rebased之前调用的,所以kdebug_trace_dyld_marker()不能使用任何全局变量
dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);
// 走所有的fixups chains 和 rebase dyld
// 注意:withChainStarts()和fixupAllChainedFixups()不能使用任何静态数据指针,因为它们还没有rebased
const MachOAnalyzer* dyldMA = getDyldMH(); // 获取Mach-O分析器
assert(dyldMA->hasChainedFixups());
uintptr_t slide = (long)dyldMA; // 所有基于修复链的图像的基址为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();
// 现在,我们可以调用使用DATA的函数
mach_init(); // mach消息初始化
// 为stack canary设置随机值
__guard_setup(kernArgs->findApple()); // 栈溢出保护
// 设置以便open_with_subsystem()工作
_subsystem_init(kernArgs->findApple());
// 在__DATA_CONST中将ProcessConfig对象变为只读之前,使用placement new来构造它
ProcessConfig& config = *new ((ProcessConfig*)sConfigBuffer) ProcessConfig(kernArgs, sSyscallDelegate);
// 使__DATA_CONST只读(内核映射为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
// 创建Allocator和APIs/RuntimeState对象
APIs& state = APIs::bootstrap(config, sLocks);
// 加载程序的所有依赖项并将它们绑定在一起
MainFunc appMain = prepare(state, dyldMA);
// 现在使所有的dyld分配的数据结构只读
state.decWritable();
// 调用main(),如果它返回,调用exit()并返回结果
// 注意:这是经过组织的,以便在程序的主线程中回溯时只在“main”下面显示“start”。
int result = appMain(state.config.process.argc, state.config.process.argv, state.config.process.envp, state.config.process.apple);
// 如果我们到达这里,main()返回(而不是程序调用exit())
#if TARGET_OS_OSX
// <rdar://74518676> libSystemHelpers不是为模拟器设置的,因此直接调用_exit()
if ( MachOFile::isSimulatorPlatform(state.config.process.platform) )
_exit(result);
#endif
state.libSystemHelpers->exit(result);
}
- 走所有的
fixups chains
和rebase dyld
- mach消息初始化:
mach_init()
- 栈溢出保护:
__guard_setup(kernArgs->findApple());
- 构建进程配置:
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;
};
- 创建Allocator和APIs/RuntimeState对象:
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
};
- 加载程序的所有依赖项并将state绑定在一起:
prepare(state, dyldMA);
- 调用main():
appMain
2.prepare:加载程序的所有依赖项
-
gProcessInfo
是存储dyld所有镜像信息的结构体:,保存着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];
};
- 进行pre-build, 创建
mainLoader
镜像装载器
ps : 如果熟悉dyld3
的小伙伴知道, 旧版本是创建一个ImageLoader
镜像装载器
- 创建
just-in-time
是dyld4
一个新特性,dyld4
在保留了dyld3
的mach-o
解析器基础上,同时也引入了just-in-time
的加载器来优化。
dyld3
出于对启动速度的优化的目的, 增加了预构建(闭包)
。App第一次启动或者App发生变化时会将部分启动数据创建为闭包存到本地,那么App下次启动将不再重新解析数据,而是直接读取闭包内容。当然前提是应用程序和系统应很少发生变化,但如果这两者经常变化等, 就会导闭包丢失或失效。
dyld4
采用了 pre-build + just-in-time
的双解析模式,预构建 pre-build 对应的就是 dyld3 中的闭包
,just-in-time
可以理解为实时解析。当然just-in-time
也是可以利用 pre-build
的缓存的,所以性能可控。有了just-in-time
, 目前应用首次启动、系统版本更新、普通启动,dyld4
则可以根据缓存是否有效去选择合适的模式进行解析。
- 装载uuid、动态库、可执行文件
记录插入信息, 遍历所有dylibs, 一些记录检查操作继续往下走
- 插入缓存
Loader::applyInterposingToDyldCache
的实现:
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);
}
接下来的工作是一些其他通知和写入操作。
- 运行初始化方法
// 运行所有的初始化
state.runAllInitializersForMain();
3.runAllInitializersForMain:运行初始化方法
// 这是从dyldMain.cpp中提取出来的,以支持使用crt1.o的旧macOS应用程序
void APIs::runAllInitializersForMain()
{
// 首先运行libSystem的初始化器
const_cast<Loader*>(this->libSystemLoader)->beginInitializers(*this);
this->libSystemLoader->runInitializers(*this);
gProcessInfo->libSystemInitialized = true;
// 在运行libSystem的初始化器后,告诉objc在libSystem的子dylibs上运行任何+load方法
this->notifyObjCInit(this->libSystemLoader);
// <rdar://problem/32209809> 调用'init'函数对所有已经init'ed的图像 (below libSystem)
// 使用下标进行迭代,以便在+加载dloopen时数组不会在我们下面增长
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);
}
}
// 自底向上运行所有其他初始化器,首先运行插入的dylib初始化器
// 使用下标进行迭代,以便在初始化式dloopen时数组不会在下面增长
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;
}
}
- 运行
libSystem
的初始化器
- 告诉
objc
在libSystem
的子dylibs上运行所有的+load
方法
(可执行文件、动态库等等上面的load)
notifyObjCInit
这个工作在第4部分介绍
-
link
动态库和主程序:runInitializersBottomUpPlusUpwardLinks
runInitializersBottomUpPlusUpwardLinks
这个工作在第5部分介绍
4.notifyObjCInit
在libSystem
的初始化时,在其子dylibs
上需要通知objc
运行所有的+load
方法
notifyObjCInit
的定义是在RuntimeState
类的声明里:
_dyld_objc_notify_init
在setObjCNotifiers
被赋值的:
那setObjCNotifiers
是在_dyld_objc_notify_register
被调用的
而_dyld_objc_notify_register
是在objc4源码里的 objc_init()
里被调用的
最后objc_init()
在库初始化时间之前由libSystem
调用。
ps: load_images
的内容在下章节,知道它是用来进行加载和调用+load
方法就够了。
5.link
动态库和主程序: runInitializersBottomUpPlusUpwardLinks
void Loader::runInitializersBottomUpPlusUpwardLinks(RuntimeState& state) const
{
//state.log("runInitializersBottomUpPlusUpwardLinks() %s\n", this->path());
state.incWritable();
// 递归地运行所有初始化器 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() ) {
// 如果有两个向上悬空的image,请再次检查初始化器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
{
// 如果已经初始化器已经运行就什么都不做
if ( (const_cast<Loader*>(this))->beginInitializers(state) )
return;
//state.log("runInitializersBottomUp(%s)\n", this->path());
// 在运行我的初始化器之前,确保这个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.调起主程序入口
在 prepare
函数调用最后返回的就是主程序入口 MainFunc
在start
函数里调用了主程序入口
最后给个总结图
dyld4应用程序加载流程图
dyld3应用程序加载流程图: