
dyld链接image后如何加载到内存中?
- dyld -> images -> 内存 -> Person (方法、协议…)
- images(macho) -> 地址 -> 表 -> 类 -> 初始化(rw - ro)
objc_init分析
_objc_init 中注册回调_dyld_objc_notify_register之前,还有一些列初始化操作。
- environ_init() 读取影响运行时的环境变量。如果需要,还可以打印环境变量帮助
- tls_init() 关于线程key的绑定,比如:线程数据的析构函数
- static_init() 运行C++静态构造函数。在dyld调用我们的静态构造函数之前,libc 会调用 _objc_init()
- runtime_init() runtime运行时环境初始化
- exception_init() libobjc异常处理系统初始化
- cache_t::init() 缓存条件初始化
- _imp_implementationWithBlock_init() 启动回调机制。通常不会做什么,因为所有的初始化都是惰性的
- _dyld_objc_notify_register(&map_images, load_images, unmap_image)
- 通过x/4gx 查看isa的内存地址
- 通过p/t 查看二进制地址,其末位是1,代表nonpointerIsa ```cpp CCPerson *p = [CCPerson alloc];
x/4gx p 0x108db8e90: 0x011d8001000083d1 0x0000000000000000 0x108db8ea0: 0x0000000108db8f70 0x0000000108db91b0
p/t 0x011d8001000083d1 (long) $1 = 0b0000000100011101100000000000000100000000000000001000001111010001
- **环境变量的全部配置指令,在终端输入**`**export OBJC_HELP=1**`- 接着在`Scheme`中设置环境变量并勾选- 重新运行指令,x/4gx 和 p/t, 观察到其末位变为0,为非nonpointerIsa```cppx/4gx p0x108c74670: 0x00000001000083d0 0x00000000000000000x108c74680: 0x65766153534e5b2d 0x6573206c656e6150p/t 0x00000001000083d0(long) $1 = 0b0000000000000000000000000000000100000000000000001000001111010000
- 同理,在环境变量中勾选打印load_methods,可以将全部load方法打印出来
tls_init()
本地线程线程池的初始化和析构
void tls_init(void){#if SUPPORT_DIRECT_THREAD_KEYSpthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);#else_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);#endif}
static_init()
全局静态C++函数调用
- libc calls _objc_init() before dyld would call our static constructors
_objc_init() 调用先于dyld中的调用
static void static_init(){size_t count;auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);for (size_t i = 0; i < count; i++) {inits[i]();}auto offsets = getLibobjcInitializerOffsets(&_mh_dylib_header, &count);for (size_t i = 0; i < count; i++) {UnsignedInitializer init(offsets[i]);init();}}
runtime_init()
runtime初始化两张表
allocatedClasses,已经被开辟过class的一张表
unattachedCategories,分类未链接表
void runtime_init(void){objc::unattachedCategories.init(32);objc::allocatedClasses.init();}void init(Ts &&... Args) {new (_storage) Type(std::forward<Ts>(Args)...);}
exception_init()
异常捕获,程序底层有runloop循环执行,在发生异常时会捕获exceptions
- 如果上层对uncaught_handler进行了赋值,下了句柄,相对于底层对上层有了回调,因此可以在上层处理此异常,防止了崩溃

void exception_init(void){old_terminate = std::set_terminate(&_objc_terminate);}static void _objc_terminate(void){if (PrintExceptions) {_objc_inform("EXCEPTIONS: terminating");}if (! __cxa_current_exception_type()) {// No current exception.(*old_terminate)();}else {// There is a current exception. Check if it's an objc exception.@try {__cxa_rethrow();} @catch (id e) {// It's an objc object. Call Foundation's handler, if any.(*uncaught_handler)((id)e);(*old_terminate)();} @catch (...) {// It's not an objc object. Continue to C++ terminate.(*old_terminate)();}}}
_dyld_objc_notify_register
- &map_images,引用类型,指针传递,保证内外部变化同步,映射整个镜像文件,非常重要,一处发送错误,整个都会发生错乱
- loadImages
map_images——重点
- 除了lookUpImpOrForward,第二个重点函数
- 将mach-o文件映射到内存中,map_images -> map_images_nolock -> _read_images ```cpp _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
**_read_images主要做了哪些事情**1. **条件控制进行一次的加载**1. **修复预编译阶段的**[@selector(selector)](/selector)**混乱问题**1. **错误混乱的类处理**1. **修复重映射一些没有被镜像文件加载进来的类**1. **修复一些消息**1. **当我们的类里面有协议的时候readProtocol**1. **修复没有被加载的协议**1. **分类处理**1. **类的加载处理**1. **没有被处理的类,优化那些被侵犯的类**<a name="NjdfJ"></a>#### 条件控制进行一次的加载- initializeTaggedPointerObfuscator小对象数据类型,进行混淆- cache的imp & mask 出现过类似的混淆```cppstatic void initializeTaggedPointerObfuscator(void){if (!DisableTaggedPointerObfuscation) {// Pull random data into the variable, then shift away all non-payload bits.arc4random_buf(&objc_debug_taggedpointer_obfuscator,sizeof(objc_debug_taggedpointer_obfuscator));objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;#if OBJC_SPLIT_TAGGED_POINTERS// The obfuscator doesn't apply to any of the extended tag mask or the no-obfuscation bit.objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);// Shuffle the first seven entries of the tag permutator.int max = 7;for (int i = max - 1; i >= 0; i--) {int target = arc4random_uniform(i + 1);swap(objc_debug_tag60_permutations[i],objc_debug_tag60_permutations[target]);}#endif} else {// Set the obfuscator to zero for apps linked against older SDKs,// in case they're relying on the tagged pointer representation.objc_debug_taggedpointer_obfuscator = 0;}}
表的创建 — 容量表
- namedClassesSize总容量 = totalClasses 4 / 3 , 例如 totalClasses = 8时,开辟空间 x = (totalClasses 4 / 3) * 3/4时进行扩容
NXCreateMapTable根据容量创建表-总表
相对位置修复,mach-o文件和dyld中的sel名字相同,其地址可能不同,所以进行局部修复
- 在llvm就加载了sel, sel = 名字 + 地址
-
错误混乱的类处理
resolvedFutureClasses处理未被加载的类,部分类被删除时混乱,还存在内存中
resolvedFutureClasses = (Class *)realloc(resolvedFutureClasses,(resolvedFutureClassCount+1) * sizeof(Class));resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
核心重点readClass
readClass传入cls,所以这里我们可以打印观察读取的类
- readClass对类做了处理,在mach-o中,class都是符号,得出结果则是NSStackBlock这种处理过的类 ```cpp Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
printf(“%s -CC: 要研究的: - %s\n”,func,cls->nonlazyMangledName()); ```
- 这里是先加载系统类,再加载我们创建的类


将类加载到hashMap和表中,这里还未涉及class_rw、class_ro操作
- addNamedClass(Class cls, constchar *name, Class replacing = nil) -> NXMapInsert(gdb_objc_realized_classes, name, cls),name和cls(地址)以key、value形式存储在hash表中
- ro、rw相关处理都与realizeClassWithoutSwift(cls, ni)相关

