我将描述当前iOS的情况,旧版本有些不同。
符号存根确实将函数指针加载到 PC 中。对于标准的“惰性”(按需)导入,指针驻留在__lazy_symbol
部分并最初指向一个辅助例程__stub_helper
部分,例如:
__symbolstub1 _AudioServicesAddSystemSoundCompletion
__symbolstub1 LDR PC, _AudioServicesAddSystemSoundCompletion$lazy_ptr
__symbolstub1 ; End of function _AudioServicesAddSystemSoundCompletion
__lazy_symbol _AudioServicesAddSystemSoundCompletion$lazy_ptr DCD _AudioServicesAddSystemSoundCompletion$stubHelper
__stub_helper _AudioServicesAddSystemSoundCompletion$stubHelper
__stub_helper LDR R12, =nnn ; symbol info offset in the lazy bind table
__stub_helper B dyld_stub_binding_helper
功能dyld_stub_binding_helper
是第一个__stub_helper
部分,本质上只是一个蹦床dyld_stub_binder
dyld 中的函数,向其传递我所说的“符号信息偏移”值。该值是惰性绑定信息流内的偏移量(由 LC_DYLD_INFO 或 LC_DYLD_INFO_ONLY 加载命令指向),它是一种带有 dyld 命令的字节码流。延迟导入的典型顺序如下所示:
72: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(M, 0xYYYYY)
19: BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(NNNN)
40: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x00, '_AudioServicesAddSystemSoundCompletion')
90: BIND_OPCODE_DO_BIND()
这里 dyld 会执行以下操作:
- 查找名为“_AudioServicesAddSystemSoundCompletion”的函数
加载中列出的 dylib 列表中的 dylib 编号 NNNN
命令。
- 查找可执行文件的段号 M(最有可能是 __DATA)
- 将函数指针写入偏移量 YYYYY 处。
- 跳转到查找到的地址,以便实际函数完成其工作
写入的地址恰好是_AudioServicesAddSystemSoundCompletion$lazy_ptr
投币口。那么,下次_AudioServicesAddSystemSoundCompletion
被调用时,它会直接跳转到导入的函数,而不经过dyld。
N.B.:您不应该立即查看文件中的偏移量 05fc0。这addr
field 是虚拟地址,您应该查找包含段命令并查看它启动的 VA 以及它的文件偏移量是多少,然后进行数学计算。通常 __TEXT 段从 1000 开始。
然而,实际的符号存根确实看起来像您粘贴的那样,可能您有一个 fat mach-o,其 fat header 占用了前 1000 个字节,因此偏移量是对齐的。