Dyld源码阅读
Version:dyld-551.4
Lauange:C++
load()调用路径:3566行
load()->loadPhase0()->loadPhase1()->loadPhase2()->loadPhase4()->loadPhase5()打开或检查已经存在的动态库:dyld3::findInSharedCacheImage->loadPhase5load()->loadPhase5open()->loadPhase6()->加载3种Mach-O文件
1、动态库路径 iOS越狱手机
在Mac\iOS中,是使用了/usr/lib/dyld
程序来加载动态库
UIKit路径:/system/Library/Frameworks/UIKit.framework
动态库共享缓存 :/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64
0x00 load() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ImageLoader* load (const char * path, const LoadContext& context, unsigned & cacheIndex) { ImageLoader* image = loadPhase0(path, orgPath, context, cacheIndex, NULL ); if ( image != NULL ) { CRSetCrashLogMessage2(NULL ); return image; } image = loadPhase0(path, orgPath, context, cacheIndex, &exceptions); if ( image == NULL ) image = loadPhase2cache(path, orgPath, context, cacheIndex, &exceptions); #endif CRSetCrashLogMessage2(NULL ); }
0x01 loadPhase2() 0x02 loadPhase5 loadPhase5load() loadPhase5check() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static ImageLoader* loadPhase5 (const char * path, const char * orgPath, const LoadContext& context, unsigned & cacheIndex, std ::vector <const char *>* exceptions) { for (std ::vector <DylibOverride>::iterator it = sDylibOverrides.begin(); it != sDylibOverrides.end(); ++it) { if ( strcmp (it->installName, path) == 0 ) { path = it->override; break ; } } if ( exceptions != NULL ) return loadPhase5load(path, orgPath, context, cacheIndex, exceptions); else return loadPhase5check(path, orgPath, context); }
0x03 findInSharedCacheImage 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static bool findInSharedCacheImage (const char * path, bool searchByPath, const struct stat* stat_buf, const macho_header** mh, const char ** pathInCache, long * slide) { dyld3::SharedCacheFindDylibResults results; if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &results) ) { *mh = (macho_header*)results.mhInCache; *pathInCache = results.pathInCache; *slide = results.slideInCache; return true ; } return false ; } bool inSharedCache (const char * path) { return dyld3::pathIsInSharedCacheImage(sSharedCacheLoadInfo, path); } static int imageSorter (const void * l, const void * r) { const ImageLoader* left = *((ImageLoader**)l); const ImageLoader* right= *((ImageLoader**)r); return left->compare(right); }
findInSharedCacheImage
inSharedCache
1 $: clang++ -o dsc_extractor dsc_extractor.cpp
抽取动态共享缓存中的Mach-O 1 2 $: cd xxx/com.apple.dyld $: ./dsc_extractor dyld_shared_cache_arm64 arm64_file
dsc_extractor.cpp内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <stdio.h> #include <stddef.h> #include <dlfcn.h> typedef int (*extractor_proc) (const char * shared_cache_file_path, const char * extraction_root_path, void (^progress)(unsigned current, unsigned total)) ;int main (int argc, const char * argv[]) { if ( argc != 3 ) { fprintf (stderr , "usage: dsc_extractor <path-to-cache-file> <path-to-device-dir>\n" ); return 1 ; } void * handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle" , RTLD_LAZY); if ( handle == NULL ) { fprintf (stderr , "dsc_extractor.bundle could not be loaded\n" ); return 1 ; } extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress" ); if ( proc == NULL ) { fprintf (stderr , "dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n" ); return 1 ; } int result = (*proc)(argv[1 ], argv[2 ], ^(unsigned c, unsigned total) { printf ("%d/%d\n" , c, total); } ); fprintf (stderr , "dyld_shared_cache_extract_dylibs_progress() => %d\n" , result); return 0 ; }
dyld加载流程
dyldStartup.s: call __dyld_start ->call dyldbootstrap::start
-> dyldInitialization.cpp: call start()->_main()
->
dyld的start()方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 uintptr_t start (const struct macho_header* appsMachHeader, int argc, const char * argv[], intptr_t slide, const struct macho_header* dyldsMachHeader, uintptr_t * startGlue) { if ( slide != 0 ) { rebaseDyld(dyldsMachHeader, slide); } mach_init(); const char ** envp = &argv[argc+1 ]; const char ** apple = envp; while (*apple != NULL ) { ++apple; } ++apple; __guard_setup(apple); #if DYLD_INITIALIZER_SUPPORT runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple); #endif uintptr_t appsSlide = slideOfMainExecutable(appsMachHeader); return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); }
dyld的main()方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 uintptr_t _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char * argv[], const char * envp[], const char * apple[], uintptr_t * startGlue) { uintptr_t result = 0 ; sMainExecutableMachHeader = mainExecutableMH; setContext(mainExecutableMH, argc, argv, envp, apple); sExecPath = _simple_getenv(apple, "executable_path" ); }
0x05反汇编 Hopper Disassembler
[Xclient下载Hopper Disassembler](https://xclie nt.info/s/hopper-disassembler.html#versions)
2、Mach-O文件
Mac 内核xnu
工具:MachOView
来源:C/OC/Swift -> .O目标文件->Mach-O可执行文件
查看Mac内核中的Mach-O : xnu-6153.11.26->EXTERNAL_HEADERS->mach-o
2.1 常见Mach-O MH_EXECUTE-可执行文件
MH_OBJECT-目标文件或静态库
目标文件(.o)
静态库文件(.a)-静态库其实就是多个.o的集合
MH_DYLIB-动态库文件
MH_DYLIKER-动态链接编辑器
MH_DSYM:存储着二进制文件符号信息
.dSYM/Contents/Resources/DWARF/XXX
11种mach-o格式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #define MH_OBJECT 0x1 #define MH_EXECUTE 0x2 #define MH_FVMLIB 0x3 #define MH_CORE 0x4 #define MH_PRELOAD 0x5 #define MH_DYLIB 0x6 #define MH_DYLINKER 0x7 #define MH_BUNDLE 0x8 #define MH_DYLIB_STUB 0x9 #define MH_DSYM 0xa #define MH_KEXT_BUNDLE 0xb
Mach-o的作用
The layout of the file depends on the filetype. For all but the MH_OBJECT file type the segments are padded out and aligned on a segment alignment boundary for efficient demand pageing.
The MH_EXECUTE, MH_FVMLIB, MH_DYLIB,MH_DYLINKER and MH_BUNDLE file types also have the headers included as part of their first segment.
Mach-O的布局取决于文件类型。除了MH_OBJECT以外的所有的文件类型将分段填充并在分段对齐时-对齐有效请求分页的边界。
MH_EXECUTE,MH_FVMLIB,MH_DYLIB,MH_DYLINKER和MH_BUNDLE这些文件也有headers作为它们第一个segment。
The file type MH_OBJECT is a compact format intended as output of the assembler and input (and possibly output) of the link editor (the .oformat). All sections are in one unnamed segment with no segment padding.
This format is used as an executable format when the file is so small the segment padding greatly increases its size.
文件类型MH_OBJECT是一种紧凑格式,旨在作为汇编器和链接编辑器(.o的输入)(可能是输出格式)。所有sections都在一个未命名的segment中,没有segment的填充。
当文件太小时segment填充可以大大增加了它的size。此格式用作可执行格式。
The file type MH_PRELOAD is an executable format intended for things that are not executed under the kernel (proms, stand alones, kernels, etc).
The format can be executed under the kernel but may demand paged it and not preload it before execution.
文件类型MH_PRELOAD是一种可执行格式,用于非kernel内核下执行。(proms, stand alones, kernels, etc)
格式可以在内核下执行,但可能需要分页而不是在执行之前预加载它。
A core file is in MH_CORE format and can be any in an arbritray legal Mach-O file. Constants for the filetype field of the mach_header
核心文件为MH_CORE格式,可以是任意格式的Mach-O文件。mach_header的文件类型是常量
生成通用二进制文件: Architectures.hpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 struct x86 { typedef Pointer32<LittleEndian> P; }; struct x86_64 { typedef Pointer64<LittleEndian> P; }; struct arm { typedef Pointer32<LittleEndian> P; }; struct arm64 { typedef Pointer64<LittleEndian> P; };
Universal Binary
通用二进制文件,包含多种不同架构的二进制文件,比单个架构的文件大,也叫Fat Binary
由于执行过程中,只是调用一部分代码,所以运行起来也不需要额外的内存。
Xcode生成Universal Binary Standard architectures $(ARCHS_STANDARD)
1 2 #define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" #define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_"
FileAbstraction.hpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 template <typename _E> class Pointer32 { public: typedef uint32_t uint_t ; typedef _E E; static uint64_t getP (const uint_t & from) INLINE { return _E::get32(from); } static void setP (uint_t & into, uint64_t value) INLINE { _E::set32(into, (uint32_t )value); } template <typename T> static T round_up (T value) { return (value+3 ) & ~(T)3 ; } template <typename T> static T round_down (T value) { return value & ~(T)3 ; } }; template <typename _E> class Pointer64 { public: typedef uint64_t uint_t ; typedef _E E; static uint64_t getP (const uint_t & from) INLINE { return _E::get64(from); } static void setP (uint_t & into, uint64_t value) INLINE { _E::set64(into, value); } template <typename T> static T round_up (T value) { return (value+7 ) & ~(T)7 ; } template <typename T> static T round_down (T value) { return value & ~(T)7 ; } };
3、dyld与Mach-O的关系
dyld属于MH_DYLDLINKER类型的Mach-O文件
dyld负责加载三种类型Mach-O文件
mach-o loader:only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 if ( shortPage ) throw "file too short" ;if ( isCompatibleMachO(firstPages, path) ) { 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" ; } uint32_t headerAndLoadCommandsSize = sizeof (macho_header) + mh->sizeofcmds; if ( headerAndLoadCommandsSize > MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE ) throwf("malformed mach-o: load commands size (%u) > %u" , headerAndLoadCommandsSize, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE); if ( headerAndLoadCommandsSize > fileLength ) dyld::throwf("malformed mach-o: load commands size (%u) > mach-o file size (%llu)" , headerAndLoadCommandsSize, fileLength); if ( headerAndLoadCommandsSize > 4096 ) { unsigned readAmount = headerAndLoadCommandsSize - 4096 ; if ( pread(fd, &firstPages[4096 ], readAmount, fileOffset+4096 ) != readAmount ) throwf("pread of extra load commands past 4KB failed: %d" , errno); } ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPages, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext); return checkandAddImage(image, context); }
4、符号地址 符号地址= 基地址 - 偏移地址
获取基地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 uintptr_t get_load_address (void ) { const struct mach_header *exe_header = NULL ; for (uint32_t i = 0 ; i < _dyld_image_count(); i++) { const struct mach_header *header = _dyld_get_image_header(i); if (header->filetype == MH_EXECUTE) { exe_header = header; break ; } } return (uintptr_t )exe_header; }
获取偏移地址 1 2 3 4 5 6 7 8 9 10 11 12 uintptr_t get_slide_address (void ) { uintptr_t vmaddr_slide = NULL ; for (uint32_t i = 0 ; i < _dyld_image_count(); i++) { const struct mach_header *header = _dyld_get_image_header(i); if (header->filetype == MH_EXECUTE) { vmaddr_slide = _dyld_get_image_vmaddr_slide(i); break ; } } return (uintptr_t )vmaddr_slide; }
dSYM文件
编译时添加选项:DWARF with dSYM File,在编译打包完成之后就会生成调试符号文件(Mach-O文件)
文件查找:找到.xcarchive文件→show package contents→…一直到DWARF→工程二进制文件
atos命令 有了dSYM文件,就可以使用atos命令查找到具体代码行出现奔溃信息的地方
1 2 3 atos [-o executable] [-l loadAddress] [-arch architecture] [address ...] #-arch 选择框架arm64/arm32/x86_64
4、Crash收集