关于ios:iOS-macho-中外部函数lazy调用

39次阅读

共计 2210 个字符,预计需要花费 6 分钟才能阅读完成。

这里咱们探讨的是 iOS 是如何懒加载调用内部函数的, 比如说:NSLog
这里次要波及到__stubs、__stub_helper、__la_symbol_ptr、__got.
__stubs 桩代码,寄存的是懒加载内部函数的十六进制指令,通过 https://armconverter.com/?dis… 网页能够转换 hex code 和 arm64 汇编代码,这里个别是三行汇编,举例:

NOP
ldr x16,#0x12bc //(这里会依据以后指令地址 +0x12bc 计算失去地址跳转到__la_symbol_ptr)
br x16

__la_symbol_ptr 存储的懒加载函数指针, 编译时存储的是__stub_helper 地址,第一次懒加载实现之后,存储的值会被批改成真正的函数地址

__stub_helper 桩辅助指令, 次要是去获取函数地址后,调用_dyld_stub_binder 函数
绑定到__la_symbol_ptr 上

__got 非懒加载函数指针,_dyld_stub_binder 就是在此中央, 会在程序启动的机会立马绑定.

上面我以一个 demo 为例在 xcode 里和 MachOView 里来探寻一下,懒加载函数调用的过程:

NSLog(@"\nClass:%@ \naddress:%p\nContent:%@",object_getClass(str2),str2,str2);
NSLog(@"songxuhua");

第一个 NSLog 调用

咱们在两个 NSLog 处打上断点,xcode 运行程序,在 Debug->Debug Workflow 抉择 Always show disassembly, 能够在运行时展现汇编, 下图展现第一个断点处的汇编

如上图所示,可知是 bl 到 stub 里的 NSLog 的地址, 关上 MachOView,查看__stubs 发现存储的是一段数据,经由 arm 汇编和数据互转转换失去汇编如下

nop 
ldr x16, #0x1c74
br x16

让咱们看看 xcode 里, 按住 ctrl+step into 键,进入指令级的调试

进到 NSLog __stubs 的指令, 发现和下面 MachOView 转换的汇编如同差了 4 个字节, 不要焦急, 是因为 MachOView 里的 offset 是 63AC, 而 xcode 里的应该是做过偏移修改的(因为在 xcode 里的地址是 ldr 作为以后指令, 而 MachOView 是 nop 为以后指令),image list -o - f 得悉 ASLR 偏移为 d4c000,d523ac-d4c000=63ac,可知的确是跳入了 stubs 里

(lldb) image list -o -f |grep TestForObjc
[0] 0x0000000000d4c000 /Users/ijm/Library/Developer/Xcode/DerivedData/TestForObjc-agwmebvcxfuznzcevpmyvcrnwcuj/Build/Products/Debug-iphoneos/TestForObjc.app/TestForObjc


好,上面来确定下这段汇编做什么的, 首先第一行 nop,除了占 4 个 byte 位并无其余作用,
ldr x16,#0x1c70 大略是相当于从以后指令的地址 (63b0)+ 偏移 0x1c70 =0x8020
br x16 跳转执行 0x8020,咱们在 MachOView 找找 0x8020 是干啥的,发现指到了__la_symbol_ptr

__la_symbol_ptr 里的数据位 64d8, 这里就是__stub__helper 的中央了

比照下 xcode 里也是一样的, 只是加了 ASLR 偏移,d524d8-d4c000=64d8,刚好

咱们来看看_stub_helper 干了些啥, 首先 ldr w16,0x1000064e0 读取 64e0 的指存储在 w16 寄存器,而后 b 0x1000064c0, 跳转到了_stub_helper 顶部

能够看到的是跳转到_dyld_stub_binder 函数做绑定,ldr x16,#0x1b40,ldr 里地址带 #示意绝对以后地址偏移量,0xd524d0-0xd4c000+0x1b40=0x8010,0x8010 就到了_got 里, 改地址寄存着 dyld_stub_binder 的地址,动态的时候是 00000…, 在运行时启动的时候,non lazy 加载绑定上真正的函数地址

第二次 NSLog 调用

调试到第二个 NSLog 调用,按住 ctrl+step into 键,进入指令级的调试

持续 ctrl+step, 发现其最终跳入了 Foundation NSLog 的地址, 阐明在_la_symbol_ptr 曾经绑定上了 NSLog 函数地址, 间接 br 调用了

由此咱们总结如下: 第一次调用内部函数,会由 stubs–>la_symbol_ptr–>_stub_helper–>dyld_stub_binder 绑定后能力调用, 当前调用都说 stubs–>la_symbol_ptr 就能调用到指定函数

知识点总结:
1.ldr 绝对地址, 带 #立刻数的示意从以后地址偏移, 例如:ldr x16,#0x1b40 , 示意从以后指令地址 +0x1b40 的地址处读取值付给 x16 寄存器
2.ldr 相对地址, 带立刻数无 #前缀,示意相对地址, 例如:ldr w16,0x1000064e0, 示意从 0x1000064e0 地址处读取值复制给 w16 寄存器
3.ctrl+ step into 能够进入指令级的调试
4. 汇编里也能够打断点

正文完
 0