源码
- ( void) viewDidLoad {
[ super viewDidLoad];
UITextView *textView = [[ UITextView alloc]init];
[textView setBackgroundColor:[ UIColor redColor]];
}
问题
- UITextView符号来自动态库,动态库中的符号运行时如何获取?
- setBackgroundColor:是字符串还是字符串指针?
- 了解动态库类调用方法的过程,就能从汇编代码中获取App中所有动态库类调用的方式
问题1:UITextView符号
UITextView符号来自动态库,动态库中的符号运行时如何获取? 打断点,选择Debug-workflow->Always show Disassembly
本文为了快速介绍介绍具体汇编中的查找动态库符号的方式, 直接通过Mach-O View 进行汇编代码的查看。 我们看UITextView获取的汇编代码
adrp指令的作用是根据PC寄存器, 获取当前指令所在虚拟内存的页数(4kb为1页),然后利用虚拟内存的相对偏移, 通过ldr 增加相对偏移的值。因为Mach-O中没有ALSR,计算方式比较简单,即 UITextView符号的地址为: 0x100351000 + 0x518 - 0x10000000 = 0x351518. 注:(如果通过Xcode断点查看,计算方式更复杂一些, 0x100351000即目标符号在0x315页内存中(0x1000 = 4 * 1024 * 1024 = 1页,所以0x100351000是0x315页), 假如当前PC寄存器的值(当前汇编指令所在的地址)处于第n页, 则运行时UITextView符号对应的汇编为adrp x8, #0x100350000 - n)
继续, 那么0x351518在Mach-O中的什么位置呢?
在Segment:__Data Section:_objc_classrefs中,但是其值为00000000, 难道UITextView符号指向了空?
显然不是, 这里要介绍一下动态库的Binding。
因ALSR技术, 动态库在加载前在Mach-O中是无法完全确认内存地址的, 因动态库在加载到内存时,会分配一个随机地址作为动态库的Mach-O加载的起始地址。 下图即动态库Binding信息以及Bind过程的流程图(最低版本iOS13, iOS15存在变化,dyld使用了dyld_chained_fixups技术,binding与rebase同步进行, 对应的Mach-O结构也会放生变化,后面单独文章介绍)
通过上图发现,动态库的Binding符号存储在Mach-O的Binding Info Offset 处,即
Segment: LC_DYLD_INFO_ONLY Section: Dynamic Loader Info
中, 具体看Mach-O中对应的数据结构:
点击Actions展开详情:
发现, 在Binding Info中, 记录了UITextView所在的动态库(UIKit), 以及其对应类符号名称(_OBJC_CLASS$_UITextView), 注意蓝框处,记录了在UIKit动态库加载后, 要将_OBJC_CLASS_UITextView写入到0x100351518处,就是上面提到的_objc_classrefs对应的地址。这样,UITextView在运行时, dyld在Mach-O加载时, 通过动态库bingding将UITextView的地址写入到_objc_classrefs中(0x100351518处),这样0x100351518的值就不在是0x0000000000000000, 而是真正在UIKit中UITextView的符号地址了。
第二个问题:setBackgroundColor:到底是什么
通过汇编, setBackgroundColor:还是通过adrp的方式获取的符号地址,计算方式同上: setBackgroundColor符号的地址为: 0x100351000 + 0x4d8 - 0x10000000 = 0x3514d8. 那么0x3514d8在Mach-O中的什么位置呢?
在Segment:__Data Section:_objc_selrefs中,
大小为8字节, 所以还是个指针, 其指向的地址为:0x010034A202 ,我们看下0x010034A202在Mach-O中的位置:
原来, 0x010034A202在Segment: __Text Section: _objc_methname中,
其值为字符串"setBackgroundColor:",所以从汇编角度看,SEL是一个字符串指针,指向了字符串。
最后, 通过bl进行方法[textView setBackgroundColor:]的调用,通过图五,执行了bl 0x100349f04,在Mach-O中查看0x100349f04的内容:
0x100349f04是间接符号(应该像NSLog一样首次执行的时候绑定地址的意思,未验证?), 指向了objc_msgSend, 至此, UITextView对象textView如何调用setBackgroundColor:方法就从汇编层面上完全解读完毕。
关于Mach-O,欢迎前往58同城iOS团队开源项目,里面有大量Mach-O相关的功能与代码,如有所得,欢迎star:github.com/wuba/WBBlad…。