uintptr_tstart(conststruct macho_header* appsMachHeader, int argc, constchar* argv[], intptr_t slide, conststruct macho_header* dyldsMachHeader, uintptr_t* startGlue) { // if kernel had to slide dyld, we need to fix up load sensitive locations // we have to do this before using any global variables //获取内核对于dyld本身的动态偏移 slide = slideOfMainExecutable(dyldsMachHeader); bool shouldRebase = slide != 0; if ( shouldRebase ) { //rebase Dyld rebaseDyld(dyldsMachHeader, slide); }
// allow dyld to use mach messaging //mach消息初始化。 mach_init();
// kernel sets up env pointer to be just past end of agv array //环境变量 constchar** envp = &argv[argc+1]; // kernel sets up apple pointer to be just past end of envp array constchar** apple = envp; while(*apple != NULL) { ++apple; } ++apple;
// set up random value for stack canary __guard_setup(apple);
#if DYLD_INITIALIZER_SUPPORT // run all C++ initializers inside dyld //初始化 在dyld中的 所有的C++构造器 runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple); #endif
// now that we are done bootstrapping dyld, call dyld's main //至此bootstrap dyld已经全部完成,调用 dyld 的 main 函数 uintptr_t appsSlide = slideOfMainExecutable(appsMachHeader); return dyld:: _main(appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); }
// // For a regular executable, the crt code calls dyld to run the executables initializers. // For a static executable, crt directly runs the initializers. // dyld (should be static) but is a dynamic executable and needs this hack to run its own initializers. // We pass argc, argv, etc in case libc.a uses those arguments // staticvoidrunDyldInitializers(conststruct macho_header* mh, intptr_t slide, int argc, constchar* argv[], constchar* envp[], constchar* apple[]) { for (const Initializer* p = &inits_start; p < &inits_end; ++p) { (*p)(argc, argv, envp, apple); } }
// validate it is a file (not directory) if ( (stat_buf.st_mode & S_IFMT) != S_IFREG ) throw"not a file";
uint8_t firstPages[MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE]; uint8_t *firstPagesPtr = firstPages; bool shortPage = false; // min mach-o file is 4K // 最小的mach-o文件就是4k(保证一个页框大小) if ( fileLength < 4096 ) { if ( pread(fd, firstPages, (size_t)fileLength, 0) != (ssize_t)fileLength ) throwf("pread of short file failed: %d", errno); shortPage = true; } else { // optimistically read only first 4KB // 优先读取header文件的4kb,因为一个分页最小是4k。 if ( pread(fd, firstPages, 4096, 0) != 4096 ) throwf("pread of first 4K failed: %d", errno); } // if fat wrapper, find usable sub-file // 如果这个动态库是个fat文件,就直接找到合适的“子文件” const fat_header* fileStartAsFat = (fat_header*)firstPages; if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { if ( OSSwapBigToHostInt32(fileStartAsFat->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) throwf("fat header too large: %u entries", OSSwapBigToHostInt32(fileStartAsFat->nfat_arch)); if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) { if ( (fileOffset+fileLength) > (uint64_t)(stat_buf.st_size) ) throwf("truncated fat file. file length=%llu, but needed slice goes to %llu", stat_buf.st_size, fileOffset+fileLength); if (pread(fd, firstPages, 4096, fileOffset) != 4096) throwf("pread of fat file failed: %d", errno); } else { throw"no matching architecture in universal wrapper"; } } // try mach-o loader if ( shortPage ) throw"file too short";
if ( isCompatibleMachO(firstPages, path) ) {
// only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded const mach_header* mh = (mach_header*)firstPages; switch ( mh->filetype ) { case MH_EXECUTE: case MH_DYLIB: case MH_BUNDLE: break; default: throw"mach-o, but wrong filetype"; }
if ( headerAndLoadCommandsSize > 4096 ) { // read more pages // 如果 head 和 LC_COMMANDS的大小大于已读取的4096,那么就继续把 headerAndLoadCommandsSize 读完 unsigned readAmount = headerAndLoadCommandsSize - 4096; if ( pread(fd, &firstPages[4096], readAmount, fileOffset+4096) != readAmount ) throwf("pread of extra load commands past 4KB failed: %d", errno); }
#if TARGET_IPHONE_SIMULATOR // <rdar://problem/14168872> dyld_sim should restrict loading osx binaries if ( !isSimulatorBinary(firstPages, path) ) { #if TARGET_OS_WATCH throw"mach-o, but not built for watchOS simulator"; #elif TARGET_OS_TV throw"mach-o, but not built for tvOS simulator"; #else throw"mach-o, but not built for iOS simulator"; #endif } #endif
#if __MAC_OS_X_VERSION_MIN_REQUIRED if ( gLinkContext.marzipan ) { const dyld3::MachOFile* mf = (dyld3::MachOFile*)firstPages; bool isiOSMacBinary = mf->supportsPlatform(dyld3::Platform::iOSMac) || iOSMacWhiteListed(path); bool isProhibitedMacOSBinary = !isiOSMacBinary && iOSMacBlackListed(path); if ( (context.enforceIOSMac && !isiOSMacBinary) || isProhibitedMacOSBinary ) { throw"mach-o, but not built for iOSMac"; } } #endif
#if __arm64e__ if ( (sMainExecutableMachHeader->cpusubtype == CPU_SUBTYPE_ARM64_E) && (mh->cpusubtype != CPU_SUBTYPE_ARM64_E) ) throw"arm64 dylibs cannot be loaded into arm64e processes"; #endif ImageLoader* image = nullptr; { dyld3::ScopedTimer timer(DBG_DYLD_TIMING_MAP_IMAGE, path, 0, 0); //调用 instantiateFromFile 来实例化 ImageLoader image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPagesPtr, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext); timer.setData4((uint64_t)image->machHeader()); } // validate // 加入全局链表 returncheckandAddImage(image, context); } // try other file formats here... // throw error about what was found switch (*(uint32_t*)firstPages) { case MH_MAGIC: case MH_CIGAM: case MH_MAGIC_64: case MH_CIGAM_64: throw"mach-o, but wrong architecture"; default: throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", firstPages[0], firstPages[1], firstPages[2], firstPages[3], firstPages[4], firstPages[5], firstPages[6],firstPages[7]); } }
这部分的主要任务就是,从本地文件中加载指定的动态库,并且返回ImageLoader实例对象。
步骤如下:
优先读取这个文件的4k,因为一个分页最小是4k,并且解析为 fat_header。
如果这个动态库是个fat文件,就直接找到合适CPU架构的“子文件”
检查文件类型,大小等。
如果 head 和 Load_Commands 的大小大于已读取的4k,那么就继续把剩下的 headerAndLoadCommandsSize 读完,实例化ImageLoader。因为实例化ImageLoader需要整个 Load_Commands 部分。
static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& context) { // now sanity check that this loaded image does not have the same install path as any existing image // 在sAllImages 中检查一下,是否有同样路径的动态库,如果有,则返回链表中原有的那个镜像 constchar* loadedImageInstallPath = image->getInstallPath(); if ( image->isDylib() && (loadedImageInstallPath != NULL) && (loadedImageInstallPath[0] == '/') ) { for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { ImageLoader* anImage = *it; constchar* installPath = anImage->getInstallPath(); if ( installPath != NULL) { if ( strcmp(loadedImageInstallPath, installPath) == 0 ) { //dyld::log("duplicate(%s) => %p\n", installPath, anImage); removeImage(image); ImageLoader::deleteImage(image); return anImage; } } } }
// some API's restrict what they can load if ( context.mustBeBundle && !image->isBundle() ) throw"not a bundle"; if ( context.mustBeDylib && !image->isDylib() ) throw"not a dylib";
// regular main executables cannot be loaded if ( image->isExecutable() ) { if ( !context.canBePIE || !image->isPositionIndependentExecutable() ) throw"can't load a main executable"; } // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list if ( ! image->isBundle() ) //加入全局链表中 addImage(image); return image; }
voidinitializeMainExecutable() { // record that we've reached this step gLinkContext.startedInitializingMainExecutable = true;
// run initialzers for any inserted dylibs ImageLoader::InitializerTimingList initializerTimes[allImagesCount()]; initializerTimes[0].count = 0; constsize_t rootCount = sImageRoots.size(); if ( rootCount > 1 ) { for(size_t i=1; i < rootCount; ++i) { sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]); } } // run initializers for main executable and everything it brings up sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]); // register cxa_atexit() handler to run static terminators in all loaded images when this process exits if ( gLibSystemHelpers != NULL ) (*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL, NULL);
// dump info if requested if ( sEnv.DYLD_PRINT_STATISTICS ) ImageLoader::printStatistics((unsignedint)allImagesCount(), initializerTimes[0]); if ( sEnv.DYLD_PRINT_STATISTICS_DETAILS ) ImageLoaderMachO::printStatisticsDetails((unsignedint)allImagesCount(), initializerTimes[0]); }
// call 'mapped' function with all images mapped so far try { notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true); } catch (constchar* msg) { // ignore request to abort during registration }
// <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem) for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { ImageLoader* image = *it; if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) { dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0); (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); } } }
遍历 sAllImages ,回调给 _dyld_objc_notify_init init函数。runtime就可以根据mach-o的 class 有关的 section 开始初始化了。