So the first executable, that’s the main binary in an app, it’s also the main binary in an app extension. Executable 是 app 的二进制主文件,同时也是 app extension 的二进制主文件
我们一般可以在 Xcode 项目中的 Products 文件夹中找到它.
Dylib类型
A dylib is a dynamic library, on other platforms meet, you may know those as DSOs or DLLs. dylib 是动态库,在其他平台也叫 DSO 或者 DLL。
So it appears that the .dylib file is the actual library of binary code that your project is using and is located in the /usr/lib/ directory on the user’s device. The .tbd file, on the other hand, is just a text file that is included in your project and serves as a link to the required .dylib binary. Since this text file is much smaller than the binary library, it makes the SDK’s download size smaller.
动态链接库:在没有被加载到内存的前提下,当可执行文件被加载,动态库也随着被加载到内存中。在Linked Framework and Libraries设置的一些share libraries。【随着程序启动而启动】
动态加载库:当需要的时候再使用dlopen等通过代码或者命令的方式来加载。【在程序启动之后】
Bundle类型
Now a bundle’s a special kind of dylib that you cannot link against, all you can do is load it at run time by an dlopen and that’s used on a Mac OS for plug-ins. 现阶段 Bundle 是一种特殊类型的 dylib,你是无法对其进行链接的。你所能做的是在 Runtime 运行时去通过 dlopen 来加载它,它可以在 macOS 上用于插件。
Image和Framework
Image refers to any of these three types. 镜像文件包含了上述的三种文件类型
a framework is a dylib with a special directory structure around it to holds files needed by that dylib. 有很多东西都叫做 Framework,但在本文中,Framework 指的是一个 dylib,它周围有一个特殊的目录结构来保存该 dylib 所需的文件。
Mach-O结构分析
segment段
Mach-O镜像文件是由segments段组成的。
段的名称为大写格式
所有的段都是 page size 的倍数。
arm64 上段大小为 16 字节
其它架构为 4 字节
这里再提及一下虚拟内存和内存页的知识:
具有 VM 机制的操作系统,会对每个运行的进程创建一个逻辑地址空间 logical address space 或者叫虚拟地址空间 virtual address space;该空间的大小由操作系统位数决定:32 位的操作系统,其逻辑地址空间的大小为 4GB,64位的操作系统为 2^34 GB(其计算方式是 2^32 || 2^64(理论上是64位,x86 Intel 是48位))。
But sections are really just a subrange of a segment, they don’t have any of the constraints of being page size, but they are non-overlapping. 但是 sections 节实际上只是一个 segment 段的子范围,它们没有页面大小的任何限制,但是它们是不重叠的。
#if __arm64__ && !TARGET_OS_SIMULATOR .text .align 2 .globl __dyld_start __dyld_start: mov x28, sp and sp, x28, #~15// force 16-byte alignment of stack mov x0, #0 mov x1, #0 stp x1, x0, [sp, #-16]! // make aligned terminating frame mov fp, sp // set up fp to point to terminating frame sub sp, sp, #16// make room for local variables #if __LP64__ ldr x0, [x28] // get app's mh into x0 ldr x1, [x28, #8] // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment) add x2, x28, #16// get argv into x2 #else ldr w0, [x28] // get app's mh into x0 ldr w1, [x28, #4] // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment) add w2, w28, #8// get argv into x2 #endif adrp x3,___dso_handle@page add x3,x3,___dso_handle@pageoff // get dyld's mh in to x4 mov x4,sp // x5 has &startGlue
// // This is code to bootstrap dyld. This work in normally done for a program by dyld and crt. // In dyld we have to do this manually. // uintptr_tstart(const dyld3::MachOLoaded* appsMachHeader, int argc, constchar* argv[], const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue) {
// Emit kdebug tracepoint to indicate dyld bootstrap has started <rdar://46878536> dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);
// 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 重定向 rebaseDyld(dyldsMachHeader);
// 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 runDyldInitializers(argc, argv, envp, apple); #endif
_subsystem_init(apple);
// now that we are done bootstrapping dyld, call dyld's main uintptr_t appsSlide = appsMachHeader->getSlide(); return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); }
// // Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which // sets up some registers and call this function. // // Returns address of main() in target program which __dyld_start jumps to // uintptr_t _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, constchar* argv[], constchar* envp[], constchar* apple[], uintptr_t* startGlue) { ///省略代码 }
#if TARGET_OS_SIMULATOR // simulator only supports mmap()ing cache privately into process return mapCachePrivate(options, results); #else if ( options.forcePrivate ) { // mmap cache into this process only return mapCachePrivate(options, results); } else { // fast path: when cache is already mapped into shared region bool hasError = false; if ( reuseExistingCache(options, results) ) { hasError = (results->errorMessage != nullptr); } else { // slow path: this is first process to load cache hasError = mapCacheSystemWide(options, results); } return hasError; } #endif }
// The kernel maps in main executable before dyld gets control. We need to // make an ImageLoader* for the already mapped in main executable. static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, constchar* path) { // try mach-o loader if ( isCompatibleMachO((constuint8_t*)mh, path) ) { ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext); addImage(image); return (ImageLoaderMachO*)image; } throw"main executable not a known format"; }
// try all path permutations and try open() until first success std::vector<constchar*> exceptions; image = loadPhase0(path, orgPath, context, cacheIndex, &exceptions); #if !TARGET_OS_SIMULATOR // <rdar://problem/16704628> support symlinks on disk to a path in dyld shared cache if ( image == NULL) image = loadPhase2cache(path, orgPath, context, cacheIndex, &exceptions); #endif CRSetCrashLogMessage2(NULL); if ( image != NULL ) { // <rdar://problem/6916014> leak in dyld during dlopen when using DYLD_ variables for (std::vector<constchar*>::iterator it = exceptions.begin(); it != exceptions.end(); ++it) { free((void*)(*it)); } // if loaded image is not from cache, but original path is in cache // set gSharedCacheOverridden flag to disable some ObjC optimizations if ( !gSharedCacheOverridden && !image->inSharedCache() && image->isDylib() && dyld3::MachOFile::isSharedCacheEligiblePath(path) && inSharedCache(path) ) { gSharedCacheOverridden = true; } returnimage; } ...... }
// now that all fixups are done, make __DATA_CONST segments read-only if ( !context.linkingMainExecutable ) // 递归设置所有信息只读 this->recursiveMakeDataReadOnly(context);
if ( !context.linkingMainExecutable ) context.notifyBatch(dyld_image_state_bound, false); uint64_t t6 = mach_absolute_time();
if ( fState < dyld_image_state_dependents_initialized-1 ) { uint8_t oldState = fState; // break cycles fState = dyld_image_state_dependents_initialized-1; try { // initialize lower level libraries first // 先初始化底层依赖库 for(unsignedint i=0; i < libraryCount(); ++i) { ImageLoader* dependentImage = libImage(i); if ( dependentImage != NULL ) { // don't try to initialize stuff "above" me yet if ( libIsUpward(i) ) { uninitUps.imagesAndPaths[uninitUps.count] = { dependentImage, libPath(i) }; uninitUps.count++; } elseif ( dependentImage->fDepth >= fDepth ) { dependentImage->recursiveInitialization(context, this_thread, libPath(i), timingInfo, uninitUps); } } } // record termination order // 记录终端序号 if ( this->needsTermination() ) context.terminationRecorder(this);
// let objc know we are about to initialize this image // 单个通知注入 通知告知大家我要开始初始化啦,你们赶紧去做初始化的工作 uint64_t t1 = mach_absolute_time(); fState = dyld_image_state_dependents_initialized; oldState = fState; context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo); // initialize this image bool hasInitializers = this->doInitialization(context);
// let anyone know we finished initializing this image fState = dyld_image_state_initialized; oldState = fState; context.notifySingle(dyld_image_state_initialized, this, NULL); if ( hasInitializers ) { uint64_t t2 = mach_absolute_time(); timingInfo.addTime(this->getShortName(), t2-t1); } } catch (constchar* msg) { // this image is not initialized fState = oldState; recursiveSpinUnLock(); throw; } } recursiveSpinUnLock(); }