深入理解Mach-O文件格式_3.md
动态链接和加载是怎么做的
- 特殊的 LC_SEGMENT_64
__LINKEDIT
链接信息段用于存储dyld需要用到的信息包括:符号表、字符串表、重定位项表、 等 是一系列的数据综合
与代码段和数据段在 Data(数据区域)规整化一的存储结构不同的是,因为链接信息段存储着诸多类型不同的用于动态链接的加载命令(LoadCommands)所需要的数据,所以链接信息段在 Data(数据区域)的存储结构,会根据具体加载命令(LoadCommands)的不同而不同
以下加载命令需要额外的空间用于存储数据,其数据存储在__LINKEDIT 区域的数据部分:
01.LC_DYLD_INFO_ONLY
02.LC_SYMTAB
03.LC_DYSYMTAB
04.LC_FUNCTION_STARTS
05.LC_DATA_IN_CODE
06.LC_CODE_SIGNATURE
以下加载命令不需要额外的空间用于存储数据,其将所有信息存储在 LoadCommands 区域的加载命令本身:
01.LC_LOAD_DYLINKER
02.LC_UUID
03.LC_VERSION_MIN_IPHONEOS / LC_VERSION_MIN_MACOSX
04.LC_SOURCE_VERSION
05.LC_LOAD_DYLIB
06.LC_RPATH
iOS 15 以下传统的 LC_DYLD_INFO
主要包含 rebase bind weakbind lazybind,export info 下面分段讲解
// 所有的 信息按 字节序编码,不需要大小端转换
struct dyld_info_command {
uint32_t cmd; /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */
uint32_t cmdsize; /* sizeof(struct dyld_info_command) */
....
}
这里解释下,rebase 和 bind的区别。
在动态链接过程,
代码访问其他代码分为四种情况
- 代码访问同模块的代码,只需要 pc 相对访问即可,无需静态和动态重定位
- 代码访问同模块的数据,只需要 pc + 偏移即可, 无需静态和动态重定位
- 代码访问外部模块的数据,只需要 got 表,需要动态重定位,也就是绑定
- 代码访问外部模块的带阿米,只需要 got 表, 需要动态重定位,也就是绑定
至于 weak 绑定是绑定的 cxx 特殊case
lazy 绑定则是 got + plt 表为了优化性能的特殊case
那么数据对数据的访问不是指令,则需要 rebase 。比如如下代码
在静态链接的时候 p 执行 a在data段的绝对地址,但是 由于有了可执行文件加载的 ALSR 机制 再加上动态库加载地址本来就不是固定的加载地址,因此在 load 的时候
进行 rebase , Mach-O 里面最常见的就是 __attribute(constructor)
的数据了 存的是个绝对地址,启动需要rebase。
static int a = 101;
static int *p = &a;
rebase
每当 dyld 将图像加载到与其首选地址不同的地址时 (ASLR),Dyld 都会对 image 进行 rebase 。(换句话说没有 ASLR 就没有rebase)
每次 rebase 其实需要三列元信息
<seg-index, seg-offset, type>
(哪个segement,其实偏移,操作类型)
但为了压缩信息,每次rebase 会复用三列数据,那个变化了就更改那个列,
比如 起始给定了(0,1,1) 后面假设 type 需要调整到2就会给一个调整type的指令,后面紧跟2. 那么这个元组就变成了 (0,1,2)
rebase 的定义在 dyld的 template <typename P> void Adjustor<P>::adjustDataPointers()
方法内
struct dyld_info_command {
...
uint32_t rebase_off; /* file offset to rebase info */
uint32_t rebase_size; /* size of rebase info */
...
}
1字节指令解码
低 4 位是**立即数type**
高 4 位是**rebase指令类型**
掩码分别是
#define REBASE_OPCODE_MASK 0xF0
#define REBASE_IMMEDIATE_MASK 0x0F
/*
* reabse的数据是什么类型
*/
#define REBASE_TYPE_POINTER 1 // 指针类型
#define REBASE_TYPE_TEXT_ABSOLUTE32 2 //绝对地址
#define REBASE_TYPE_TEXT_PCREL32 3 // pc相对访问
/*
* 操作码 类型
*/
//
#define REBASE_OPCODE_DONE 0x00 // rebase 指令结束
// 设置type,高位是1,是设置指令,后四位是离立即数,上面也就3种type够用了
#define REBASE_OPCODE_SET_TYPE_IMM 0x10 // 立即数
/* 设置segent列,后面紧跟 1个 uleb 编码位 复制给offset
dyld源码为
case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
segIndex = immediate;
segOffset = read_uleb128(diag, p, end);
*/
#define REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x20
/* ,后面紧跟 1个 uleb 编码位 复制给offset
dyld源码为
case REBASE_OPCODE_ADD_ADDR_ULEB:
segOffset += read_uleb128(p, end);
*/
#define REBASE_OPCODE_ADD_ADDR_ULEB 0x30 // 设置offset立即数,
/* ,后面紧跟 1个 uleb 编码位 * rebase 长度(比如8位指针)复制给 offset
dyld源码为
case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
segOffset += immediate*sizeof(pint_t);
break;
*/
#define REBASE_OPCODE_ADD_ADDR_IMM_SCALED 0x40 //
/* 真正的rebase 操作 do 立即数 次rebase操作
dyld源码为
case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
for (int i=0; i < immediate; ++i) {
slidePointer(segIndex, segOffset, type);
segOffset += sizeof(pint_t);
}
template <typename P>
void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type)
{
cache_builder::ASLR_Tracker* aslrTracker = this->_mappingInfo[segIndex].aslrTracker;
pint_t* mappedAddrP = (pint_t*)((uint8_t*)_mappingInfo[segIndex].cacheLocation + segOffset);
uint32_t* mappedAddr32 = (uint32_t*)mappedAddrP;
pint_t valueP;
uint32_t value32;
switch ( type ) {
case REBASE_TYPE_POINTER:
valueP = (pint_t)P::getP(*mappedAddrP);
核心在这里设置为 valueP + slideForOrigAddress(valueP)
P::setP(*mappedAddrP, valueP + slideForOrigAddress(valueP));
aslrTracker->add(mappedAddrP);
break;
case REBASE_TYPE_TEXT_ABSOLUTE32:
value32 = P::E::get32(*mappedAddr32);
P::E::set32(*mappedAddr32, value32 + (uint32_t)slideForOrigAddress(value32));
break;
case REBASE_TYPE_TEXT_PCREL32:
// general text relocs not support
default:
_diagnostics.error("unknown rebase type 0x%02X in %s", type, _dylibID);
}
}
*/
#define REBASE_OPCODE_DO_REBASE_IMM_TIMES 0x50
/* 同上,只不过立即数编码不够,后面紧跟 1个 uleb 编码位
然后do ulebnum 次rebase操作
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
count = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
slidePointer(segIndex, segOffset, type);
segOffset += sizeof(pint_t);
}
break;
*/
#define REBASE_OPCODE_DO_REBASE_ULEB_TIMES 0x60
/** 做一次rebase 并且后面紧跟 uleb 数复制给 segoffset
case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
slidePointer(segIndex, segOffset, type);
segOffset += read_uleb128(p, end) + sizeof(pint_t);
*/
#define REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB 0x70
/*
// 跳过多少次,后面通常跟 count 和 skip 然后做 count 次 rebase,并且每次 对 segent 做
skip + sizeof(ptr) 个数
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
slidePointer(segIndex, segOffset, type);
segOffset += skip + sizeof(pint_t);
}
*/
#define REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB 0x80 和 skip
这次估计你能看懂rebas代码了
bind
struct dyld_info_command {
...
/*
同 rebase 信息一样,但是这次需要的是 5元组
` <seg-index, seg-offset, type, symbol-library-ordinal, symbol-name, addend>`
`(seg索引,segoffset,类型,符号来源的动态库以1开头,符号名,加数)`
uint32_t bind_off; /* file offset to binding info */
uint32_t bind_size; /* size of binding info */
...
}
1字节指令解码
低 4 位是**立即数type**
高 4 位是**rebase指令类型**
掩码分别是
#define BIND_OPCODE_MASK 0xF0
#define BIND_IMMEDIATE_MASK 0x0F
/*
* 绑定的类型
*/
#define BIND_TYPE_POINTER 1 // 指针类型
#define BIND_TYPE_TEXT_ABSOLUTE32 2 //绝对地址
#define BIND_TYPE_TEXT_PCREL32 3 // pc相对访问
// 特殊查找序号,分别是自己,主二进制,平坦的动态查找和弱符号冲突
#define BIND_SPECIAL_DYLIB_SELF 0
#define BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE -1
#define BIND_SPECIAL_DYLIB_FLAT_LOOKUP -2
#define BIND_SPECIAL_DYLIB_WEAK_LOOKUP -3
#define BIND_SYMBOL_FLAGS_WEAK_IMPORT 0x1 weak符号 0b0001
#define BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION 0x8 非weak 符号 0b1000
#define BIND_OPCODE_DONE 0x00 // 绑定结束
#define BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 0x10 // 修改 bibary 序号 数存在指令里面
#define BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB 0x20 // 修改 bibary 序号 后面跟一个 uleb 数
#define BIND_OPCODE_SET_DYLIB_SPECIAL_IMM 0x30 // 绑定特殊数字,就是起那么说的负数,或者0 比如 绑定 self符号,主二进制符号,平坦的动态查找,或者weak符号冲突检测
#define BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 0x40 // 修改flag看是否是若符号
#define BIND_OPCODE_SET_TYPE_IMM 0x50// 修改 type 数存在指令里面
#define BIND_OPCODE_SET_ADDEND_SLEB 0x60 // 修改加数,后面跟 uleb 数
#define BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x70 // 修改 segement index 数存在指令里面,后面跟一个uleb 修改 offset
#define BIND_OPCODE_ADD_ADDR_ULEB 0x80 // 修改offset 后面跟一个 uleb
#define BIND_OPCODE_DO_BIND 0x90 // 真正的绑定 把 5元组传递下去
#define BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB 0xA0 //绑定完加一个 offset uleb数
#define BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED 0xB0 // 绑定完添加 offset segmentOffset += immediate*ptrSize + ptrSize;
#define BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB 0xC0 // 后面跟 count 和 skip 做 count 次绑定,每次 offset 做跳过 segmentOffset += skip + ptrSize;
#define BIND_OPCODE_THREADED 0xD0 // 后面必须跟 后两个 子命令
#define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB 0x00 // 修改tablecount 没见过这是干啥的。不太看得懂 感觉像是 iOS 15 优化
#define BIND_SUBOPCODE_THREADED_APPLY 0x00 // 链式绑定 addChainStart(segmentIndex, segIndexSet, segmentOffset, DYLD_CHAINED_PTR_ARM64E, stop);
这次你可以看懂这个了把,例子中第一个还是个weak绑定。后面有元组信息发生变化就单独修改某个数据然后继续绑定。 大部分都是把 got
表的值从 0(占位符)改成真正的值。
weak_bind
某些 C++ 程序要求 dyld 具有唯一符号,以便进程中的所有二进制都使用某些代码/数据的相同副本。
weak绑定的操作符 和bind一样,但是他是按符号名字母需排序的。
weak绑定的动作在 bind 之后统一处理
dyld 能够按顺序遍历具有弱绑定信息的所有图像并寻找冲突,如果没有冲突就不更新了,如果有冲突就更新
听起来很拗口那么有啥用?
用处就是比如一开始所有的对象的 "operator new"
绑定到了 libstdc++.dylib
后面有个动态库覆盖了 "operator new"
符号,那么所有的对这个符号的依赖会自动转向这个库,如果后面还有动态库,那么继续覆盖。
感觉是为了实现一些hook的手法。
struct dyld_info_command {
...
uint32_t weak_bind_off; /* file offset to weak binding info */
uint32_t weak_bind_size; /* size of weak binding info */
}
lazy_bind
有一些符号不是需要立即绑定的可以推迟到首次使用绑定
lazy_bind info 和前面一样也是一堆 BIND Opcodes
但是通常来说 dyld 会忽略绑定这个符号, ld64(静态连接器)这里面套用了模版代码,让他们执行 懒加载桩,并且吧自己的偏移当做参数计算进去
struct dyld_info_command {
...
uint32_t lazy_bind_off; /* file offset to lazy binding info */
uint32_t lazy_bind_size; /* size of lazy binding infs */
...
}
export Info
- 导出的外部符号
dylib 导出的符号在是一个 trie 树 (或者交字典树)中进行编码。
导出区域是Trie 节点流。 第一个节点依次是 trie 的起始节点。
符号的节点以 uleb128 开头,它是到目前为止该字符串导出的符号信息的长度。
-
Case1 如果没有导出的符号,则节点以零字节开始。
后面紧跟一个 childCount,( NodeLable(CString风格),NextNode offset)* childcount -
case 2 如果有导出信息 后面继续跟
- 首先是一个 uleb128,其中包含 flags 。通常情况下,flag后面是一个 uleb128 数的偏移量,。如果flag是 EXPORT_SYMBOL_FLAGS_REEXPORT 则后面的是一个 uleb128 动态库序数,然后跟一个以 0结尾的字符串,如果字符串长度是0,则要去这个动态库重新找这个符号。
如果flag 是EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 后面跟两个 uleb128 数,stuboffset, 和 resolver offset , stub 是 -nonlazy符号。resolve 是一个必须被调用的函数,调用完填充给lazy符号 - 在可选的导出符号信息之后是一个字节,表示离开该节点的边的数量(0-255),然后是每个边。
每个边都是一个以零结尾的 UTF8 字符串,表示符号的额外字符,然后是一个 uleb128 offset量,指向该边指向的子节点。
uint32_t export_off; /* file offset to lazy binding info */
uint32_t export_size; /* size of lazy binding infs */
};
/*
* 符号标记
*/// 2 位掩码
#define EXPORT_SYMBOL_FLAGS_KIND_MASK 0x03
#define EXPORT_SYMBOL_FLAGS_KIND_REGULAR 0x00
#define EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL 0x01
#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
#define EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION 0x04
#define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08
#define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10
#define EXPORT_SYMBOL_FLAGS_STATIC_RESOLVER 0x20
iOS 15
当你的 iOS 库最低部署版本被升级到 iOS 15 后 静态链接器链接后的动态库和可执行文件不再有 LC_DYLD_INFO_ONLY
而是变成了
LC_DYLD_EXPORTS_TRIE
和 LC_DYLD_CHAINED_FIXUPS
其中
LC_DYLD_EXPORTS_TRIE
和之前一样 是一颗 Trie 树,提供外部符号
LC_DYLD_CHAINED_FIXUPS
将以前的 rebase
bind(bweakind)
合并为一个链式结构,i
PS lazybind
已废弃
并且仅一次 pagefault 就可完成 reabse bind,。
详细的可以参考iOS 15 如何让你的应用启动更快
各个 Section 都是干嘛额
Segement64
struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* includes sizeof section_64 structs */
char segname[16]; /* 段名字 */
uint64_t vmaddr; /* vmaddr 段的虚拟内存起始地址 */
uint64_t vmsize; /* vmsize 段的虚拟内存大小 */
uint64_t fileoff; /* 段在文件中的偏移量 */
uint64_t filesize; /* 段在文件中的大小 */
vm_prot_t maxprot; /* 段页面所需要的最高内存保护 */
vm_prot_t initprot; /* 段页面初始的内存保护 */
uint32_t nsects; /* 段中包含 section 的数量 */
uint32_t flags; /* 标志位 */
};
struct section_64 { /* for 64-bit architectures */
char sectname[16]; /* sectname section 名 */
char segname[16]; /* segname 该 section 所属的 segment 名 */
uint64_t addr; /* addr 该 section 在内存的启始位置 */
uint64_t size; /* size 该 section 的大小 */
uint32_t offset; /* offset 该 section 的文件偏移*/
uint32_t align; /* align 字节大小对齐 2的align的次方 */
uint32_t reloff; /* 重定位入口的文件偏移 */
uint32_t nreloc; /* 需要重定位的入口数量s */
uint32_t flags; /* 包含 section 的 type 和 attributes*/
uint32_t reserved1; /* reserved (for offset or index) */
uint32_t reserved2; /* reserved (for count or sizeof) */
uint32_t reserved3; /* reserved */
};
Segement 和 section 名称对链接器没啥用,但对人有理解价值
下面是一些常见的
#define SEG_PAGEZERO "__PAGEZERO" /* 可执行文件空访问保护段 */
#define SEG_TEXT "__TEXT" /* 传统的 unix 代码 segement*/
#define SECT_TEXT "__text" /* mach-o 更传统的部分 */
#define SEG_DATA "__DATA" /* 传统的 unix 数据 segement */
#define SECT_DATA "__data" /* mach-o 更传统的部分 */
/* no padding, no bss overlap */
#define SECT_BSS "__bss" /* segement 没有文件部分,但是有内存*/
#define SECT_COMMON "__common" /* 编译器全局符号初始化值*/
#define SEG_OBJC "__OBJC" /* objective-C runtime segment */
#define SECT_OBJC_SYMBOLS "__symbol_table" /* objc 符号表 */
#define SECT_OBJC_MODULES "__module_info" /* objc 模块信息 */
#define SECT_OBJC_STRINGS "__selector_strs" /* objc selector 字符包 */
#define SECT_OBJC_REFS "__selector_refs" /* objc selector 引用的字符包 */
#define SEG_LINKEDIT "__LINKEDIT" /* 静态链接器创建的segement Created with -seglinkedit option to ld(1) for 仅可执行文件或者动态库有,里面是 rebase bind weakrease 等动态加载库加载使用的信息 */
#define SEG_UNIXSTACK "__UNIXSTACK" /* unix stack*/
#define SEG_IMPORT "__IMPORT" /* dyld 特有的 segement 具有 读写可执行全局,*/
各个 Section 都是干嘛额
__TEXT,__text
指令 section 这里面放的都是具体的机器码,(机器码可以反汇编,不能说是汇编,因为汇编是程序员编写并不是和指令一一对齐。)r-x
权限
-
__TEXT,__stubs
函数调用桩,前面解释过 3个指令,用一种固定的模版代码获取got表或者lazy符号表里面供 dyld 填充的外部符号地址 -
__TEXT,__stub_helper
前面提过,dyld函数桩辅助函数,执行 dylddyld_stub_binder
绑定函数,目的是为了回填__DATA,__la_symbol_ptr
,的函数指针 -
__TEXT,__cstring
去重后的C 语言字符串 -
__TEXT,__constg_swiftt
swift 语言类型描述符 ,Swift 源代码里面有,具体干啥不知道 -
__TEXT,__swift5_builtin
swift5builtin反射描述信息,hopper 可以查看 -
__TEXT,__swift5_typeref
里是其他sections里引用到的 Swift mangled type name -
__TEXT,__swift5_reflstr
swift meta 引用的字符传 -
__TEXT,__swift5_fieldmd
该部分包含字段描述符数组。字段描述符包含单个类、结构或枚举声明的字段记录的集合。每个字段描述符可以有不同的长度,具体取决于类型包含的字段记录数 -
__TEXT,__swift5_types
此部分包含 32 位有符号整数数组。每个整数都是一个相对偏移量,指向 TEXT.const 部分中的标称类型描述符。 -
__TEXT,__swift5_assocty
该部分包含关联类型描述符的数组。关联类型描述符包含一致性的关联类型记录的集合。关联类型记录描述从关联类型到一致性的类型见证的映射 -
__TEXT,__swift5_proto
此部分包含 32 位有符号整数数组。每个整数都是一个相对偏移量,指向 TEXT.const 部分中的协议一致性 (protocol comformance descripter)描述符。 -
__TEXT,__swift5_protos
此部分包含 32 位有符号整数数组。每个整数都是一个相对偏移量,指向 TEXT.const 部分中的协议(protocol descriptor)描述符 -
__TEXT,__swift5_capture
捕获描述符描述闭包上下文对象的布局。与普通类型不同,闭包上下文的泛型替换来自对象,而不是元数据 -
__TEXT, __swift5_mpenum
实在不知道是啥 -
__TEXT,__swift5_replace
本节包含动态替换(dynamic
函数)信息。这本质上是 Objective-C 方法调配的 Swift 等价物。 -
__TEXT,__swift5_replac2
本节包含不透明类型的动态替换信息。目前尚不清楚为什么创建这个附加部分而不是 __swift5_replace。 -
__TEXT,__swift_hooks
-
__TEXT,__swift51_hooks
-
__TEXT,__swift56_hooks
-
__TEXT,__s_async_hooks
启动的特殊hooks -
__TEXT,__objc_methname
objc 的 selector名称,以C字符风格存储 -
__TEXT,__objc_classname
objc 的类名称,以C字符风格存储 -
__TEXT,__objc_methtype
objc 的typeencoding名称,以C字符风格存储 -
__TEXT,__objc_stub
objc 的函数桩,
+__ETEXT
-
__DATA, __literal16
-
__DATA, __literal8
16 位 和 8位 浮点数字面量值 -
__DATA, __const
编译时常量,里面都是具体的数据,由加载指令调用。
Swift 的各种描述符数据也存在这里面
Protocol conformance descriptor
Module descriptor
Protocol descriptor
Nominal type descriptors
Direct field offsets
Method descriptors -
__DATA,__got
non-lazy 间接符号表 -
__TEXT,__unwind_info
用来存储处理异常情况信息 -
__TEXT,__eh_frame
用于异常处理 -
__DATA,__mod_init_func
constructor 函数
__DATA,__la_symbol_ptr
lazy 符号指针, -
__DATA,__objc_classlist
Objective-C 类列表,以指针形式存储指向__DATA,__data
数据
-
__DATA,__objc_nlclslist
OC 的类的 +load 函数列表 -
__DATA,__objc_catlist
Objective-C 分类列表,以指针形式存储指向__DATA,__data
数据 -
__DATA,__objc_nlcatlist
OC 的分类的 +load 函数列表 -
__DATA,__objc_protolist
Objective-C 协议列表,以指针形式存储指向__DATA,__data
数据
-
__DATA,__objc_imageinfo
版本信息, -
__DATA,__objc_const
objc 编译时元数据, 包含协议描述信息, -
__DATA,__objc_selrefs
有那些 selector 被引用了 -
__DATA,__objc_classrefs
有那些类被引用了 -
__DATA,__objc_protorefs
有那些协议被引用了 -
__DATA.__objc_superrefs
基础了那些父类 -
__DATA,__objc_data
-
objc 编译时元数据, 包含类,方法,描述信息 可以理解为 objc_class 的 ro_t,
-
__DATA,__objc_ivar
-
__DATA_DIRTY,__objc_ivar
一些字典的优化 objc ivar 信息 -
__DATA,_data
通用的数据区
没见过的有
__DATA,__crash_info
LC_SEGMENT_64(__PAGEZERO):空指针陷阱段
这是一个不可读、不可写、不可执行的空间,能够在空指针访问时抛出异常(用于捕捉对空指针的引用)
在 64 位的操作系统上,这个段的虚拟内存大小是 4GB
4GB 并不是指该段物理文件的真实大小,也不是指该段所占物理内存的真实大小
4GB 是规定了进程地址空间的前 4GB 被映射为:不可读、不可写、不可执行的空间
这就是为什么当读写一个 NULL(0x0) 指针时会得到一个 EXC_BAD_ACCESS 错误
因为 LC_SEGMENT_64(PAGEZERO) 的物理文件大小为 0
所以 Data(数据区域)中没有与 LC_SEGMENT_64(PAGEZERO) 对应的部分
参考
- iOS 研习记—— 谈谈静态库与动态库
- iOS 研习记 聊聊 iOS 中的 Mach-O
- iOS之深入解析UmbrellaFramework的封装与应用
- iOS类加载流程(一):类加载流程的触发
- 逆向,插入一个 LC_ROUTINES执行些额外逻辑
- LEB128格式的说明
- OS Runtime源码解析initialize load attribute总结
- Fairplay DRM与混淆实现的研究
- iOS 上的自动链接( Auto Linking )
- 深入 iOS 静态链接器(一)— ld64
- iOS 逆向入门 - 动态库注入原理
- Dylib注入&劫持总结
- OS X平台的Dylib劫持技术(上)
- dyld详解
- LLD, THE LLVM Linker
- Atom Model Linker
- 深入研究了一下mach-o
- iOS 15 如何让你的应用启动更快
- About the LC_DYLD_INFO[_ONLY] command.
- Swift 专项
标签: 无标签
仅有一条评论
陈卉饰:文章真不错http://wap.jst-gpmx.cn/news/5036.html
陈卉饰2024-09-02 04:00回复