编译流程
应用程序加载
基础
- 库:可执行的二进制文件,能被操作系统加载到内存中。动态库和静态库的区别主要在于他们的链接方式。
- 基本流程
流程探索
Pre
我们知道+load()方法会在main函数之前执行,在AppDelegate中添加+load()方法并打上断点,既可以找到dyld调用入口了
打开dyld-852版本并进行探索。首先找到
_dyld_start函数,可以看到是汇编层面的。
#if __arm64__ && !TARGET_OS_SIMULATOR
.text
.align 2
.globl __dyld_start
__dyld_start:
···
// call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
···
#endif
看不懂汇编没关系,在其注释中我们可以看到这么一句dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue),来到dyldbootstrap的命名空间下找到start函数。
uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, **int** argc, const char* argv[],const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue)
{
··· // 准备工作
return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}
可见会调用dyld的main函数,该函数首先做了这些事:条件准备:环境、平台、版本、路径、主机信息等;共享缓存的处理等。
【反推法】我们可以看到
main的代码量是非常大的,探索源码时碰到这种无法入手的情况下,可以尝试从返回值入手。返回值result是在哪里定义并生成的呢?
主要流程
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
首先我们可以看到,调用sMainExecutable的getEntryFromLC_MAIN函数获取result。我们就可以从sMainExecutable入手。
- mapSharedCache共享缓存加载
mapSharedCache(mainExecutableSlide);
- 实例化主程序,sMainExecutable初始化
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
// instantiateFromLoadedImage
static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path) {
ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
addImage(image); // 按Mach-o格式解析代码
return (ImageLoaderMachO*)image;
}
- link主程序
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, **true**, ImageLoader::RPathChain(NULL, NULL), -1);
- loadInsertDylib加载插入的动态库
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
- link插入的动态库
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
image->setNeverUnloadRecursive();
}
if ( gLinkContext.allowInterposing ) {
for(**unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
image->registerInterposing(gLinkContext);
}
}
}
- 弱引用绑定主程序
// 为主程序递归绑定、链接相关数据
sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
// 弱引用绑定主程序
sMainExecutable->weakBind(gLinkContext);
sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);
- initializeMainExecutable初始化【重点】
initializeMainExecutable();
- 通知dyld可以进main()函数了
notifyMonitoringDyldMain();
- 获取主程序入口
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
if ( result != 0 ) {
if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
*startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
else
halt("libdyld.dylib support not present for LC_MAIN");
} else {
result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
*startGlue = 0;
}
重点流程initializeMainExecutable探索
- 为所有插入的动态库执行
initialize
ImageLoader::InitializerTimingList initializerTimes[allImagesCount()];
initializerTimes[0].count = 0;
const size_t rootCount = sImageRoots.size();
if ( rootCount > 1 ) {
for(size_t i=1; i < rootCount; ++i) {
sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]);
}
}
- 执行主程序的
initialize
sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
runInitializers
void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo) {
···
// 两个重点流程
processInitializers(context, thisThread, timingInfo, up);
context.notifyBatch(dyld_image_state_initialized, **false**);
···
}