iOS dyld代码精简释义
dyld源码
阅读指引:
阅读本文需要一定的耐心,要知道dyld本身就不是一个很容易理解的内容。如果有耐心阅读完文章,相信一定会对dyld有一些新的理解。
本文通过dyld的_main函数作为切入点,对函数调用流程及函数内代码进行精简处理,从而更好地分析dyld的工作流程。
在阅读文章之前顺便思考以下几个问题:
1、dyld的作用?它的工作流程是怎样的?
2、OC中的runtime跟dyld有关系吗,runtime是怎么被启动的?
3、OC中的load方法是何时被调用的?是libObjc中runtime初始化时就被调用了吗?同一个项目中动态库、代码、静态库中的load方法他们的调用顺序是什么?
dyld main函数精简代码
uintptr_t _main(const macho_header mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char argv[], const char envp[], const char apple[], uintptr_t* startGlue)
{
// 获取主程序的信息
getHostInfo(mainExecutableMH, mainExecutableSlide);
// 设置主程序运行上下文
setContext(mainExecutableMH, argc, argv, envp, apple);
// 配置环境变量
configureProcessRestrictions(mainExecutableMH, envp);
// 加载共享缓存
mapSharedCache(mainExecutableSlide);
// 判断dyld3的启动闭包。dyld3系统会将启动数据进行缓存,下次启动时如果有缓存,直接从此步骤进行启动,返回主程序的main函数
bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
if ( launched ) {
gLinkContext.startedInitializingMainExecutable = true;
if (sSkipMain)
result = (uintptr_t)&fake_main;
return result;
}
// 实例化主程序的ImageLoader
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
// 加载插入的动态库
for (const char const lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) loadInsertedDylib(*lib);
// 链接主程序
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
// 遍历链接插入的动态库
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
image->setNeverUnloadRecursive();
}
// 实例化主程序
initializeMainExecutable();
// 返回主程序的main函数入口
result = (uintptr_t)&fake_main;
return result;
}
链接主程序
void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath)
{
// 递归加载动态库
this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath);
// 对应libObjc中的map_images( -> _read_images),OC类的处理
context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly);
}
void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath)
{
// 获取依赖的动态库
this->doGetDependentLibraries(libraryInfos);
// 加载动态库
for(unsigned int i=0; i < fLibraryCount; ++i){
setLibImage(i, dependentLib, depLibReExported, requiredLibInfo.upward);
}
}
static void notifyBatch(dyld_image_states state, bool preflightOnly)
{
notifyBatchPartial(state, false, NULL, preflightOnly, false);
}
static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification)
{
// 直接函数调用 - 调用_dyld_objc_notify_mapped,_dyld_objc_notify_mapped是_dyld_objc_notify_register方法中第一个参数
(*sNotifyObjCMapped)(objcImageCount, paths, mhs);
}
上文中的sNotifyObjCMapped定义:
_dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
dyld::registerObjCNotifiers(mapped, init, unmapped);
}
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// 三个参数的赋值
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
}
_dyld_objc_notify_register函数是在libObjc中调用,_dyld_objc_notify_mapped参数对应libObjc中的map_images,map_iamges确定类的结构、继承体系、类及类别的信息合并等;_dyld_objc_notify_init参数对应libObjc中的load_images。
链接插入的动态库
遍历插入的动态库执行链接主程序过程中相同的link方法
初始化主程序
void initializeMainExecutable()
{
// 先初始化插入的动态库
const size_t rootCount = sImageRoots.size();
for(size_t i=1; i < rootCount; ++i) {
sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]);
}
// 再初始化主程序
sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
}
void runInitializers(ImageLoader* image)
{
image->runInitializers(gLinkContext, initializerTimes[0]);
}
void ImageLoader::runInitializers(**const** LinkContext& context, InitializerTimingList& timingInfo)
{
processInitializers(context, thisThread, timingInfo, up);
context.notifyBatch(dyld_image_state_initialized, **false**);
}
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread, InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
{
for (uintptr_t i=0; i < images.count; ++i) {
images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups);
}
if ( ups.count > 0 )
processInitializers(context, thisThread, timingInfo, ups);
}
// 递归初始化Mach-O(动态库、主程序可执行文件都是Mach-O文件)
void ImageLoader::recursiveInitializationconst LinkContext& context, mach_port_t this_thread, const char pathToInitialize, InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
// 通知对应的objc,将要初始化对应的image
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
// 初始化image
bool hasInitializers = this->doInitialization(context);
// 向外发送完成image完成初始化的通知
context.notifySingle(dyld_image_state_initialized, this, NULL);
}
bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
// mach-o has -init and static initializers
doImageInit(context);
doModInitFunctions(context);
return (fHasDashInit || fHasInitializers);
}
void ImageLoaderMachO::doImageInit(**const** LinkContext& context)
{
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
// libStem库必须第一个进行初始化
// <* 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());
cmd = (**const** **struct** load_command*)(((**char***)cmd)+cmd->cmdsize);
}
}
static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
// 调用libObjc中注册的回调函数。函数真正的执行是在libObjc中执行的,但是是由dyld发起的
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
}
}
至此我们已经分析完毕dyld的主要工作流程。
疑点分析
研究过libObjc源码的同学应该还有一个疑问:_dyld_objc_notify_register是在libObjc中的_objc_init方法中添加的,那么_objc_init是什么时候进行的调用的呢? 细心的同学在查看主程序初始化过程最后的doImageInit方法时,会发现libSystem库必须要第一个进行初始化,可以继续去研究一下libSysem的初始化方法。
libSystem初始化
// 初始化了相当多的系统库,保留向个常见的,需要研究的
libSystem_initializer(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars)
{
__pthread_init(&libpthread_funcs, envp, apple, vars);
__malloc_init(apple);
_dyld_initializer();
libdispatch_init();
}
libDispatch初始化
libdispatch_init(void)
{
_dispatch_hw_config_init();
_dispatch_time_init();
_dispatch_vtable_init();
// OC的初始化!!!!
_os_object_init();
_voucher_init();
_dispatch_introspection_init();
}
_os_object_init(void)
{
// 调用libObjc中的_objc_init
_objc_init();
}
通过分析,可以得知_objc_init的调用流程:(libSystem)libSystem_initializer -> (libdispatch)libdispatch_init -> (libdispatch)_os_object_init -> (libObjc)_objc_init。 又在_objc_init中进行了runtime_init()、_dyld_objc_notify_register等操作。
问题解答及总结
OC中的runtime跟dyld有关系吗,runtime是怎么被启动的?
libSystem_initializer -> libdispatch_init -> _os_object_init -> _objc_init -> runtime_init
算有关系吧,因为毕竟dyld负责库的初始化工作
OC中的load方法是libObjc中runtime初始化时就被调用了吗?具体是什么时候被调用的?同一个项目中动态库、代码、静态库中的load方法他们的调用顺序是什么?
load方法是在dyld初始化主程序过程中进行的函数直接调用。
在runtime初始化过程中并没有被调用,load方法是在load_images中被调用,load_images在_objc_init中只是注册成了dyld中_dyld_objc_notify_register回调函数,并没有进行调用。
因为dyld初始化主程序会先遍历插入的动态库进行初始化,所以动态库的load_images会比主程序先调用,又因为静态库是拷贝作用,所以load方法的调用顺序是:动态库load、OC代码load、静态库load。
dyld的作用?它具体都干了些什么
dyld主要为应用程序启动做准备,主要工作流程如下:
- 设置上下文运行环境
- 加载共享缓存库
- 为主程序实例化ImageLoader
- 加载插入的动态库
- 链接主程序
- 链接插入的动态库
- 初始化主程序
- 返回主程序入口main函数
转载自:https://juejin.cn/post/7057114492651438116