动态库绑定过程详解
首先动态库绑定分为lazy bind 和no_lazy_bind,
lazy bind 主要用于模块外部的函数调用,由于调用者并不是每个函数都调用,延迟绑定有利于提高动态库的加载速度
no_lazy_bind: 主要用于模块外部调用一些全局的变量,由于通常暴露的外部变量较少,所以在启动时绑定,但少数函数除外,例如dyld_stub_binder。
no_lazy_bind 会在程序启动之后又dynamic_link_edit完成,想要了解可以看看动态链接库的原理。
这里主要分析一下 lazy bind过程。
先写一段demo
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[DemoFramework new] codingLife];
[[GoodBoy new] xuanGao];
helloWorld();
}
@end
其中helloWorld
是动态库一个函数,反编译一下
;
; Section __text
;
; Range 0x100001680 - 0x100001a33 (947 bytes)
; File offset 5760 (947 bytes)
; Flags : 0x80000400
;
-[ViewController viewDidLoad]:
0000000100001680 push rbp ; Objective C Implementation defined at 0x1000030d8 (instance), XREF=0x1000000d0
0000000100001681 mov rbp, rsp
0000000100001684 sub rsp, 0x30
0000000100001688 lea rax, qword [ss:rbp+var_20]
000000010000168c mov qword [ss:rbp+var_8], rdi
0000000100001690 mov qword [ss:rbp+var_10], rsi
0000000100001694 mov rsi, qword [ss:rbp+var_8]
0000000100001698 mov qword [ss:rbp+var_20], rsi
000000010000169c mov rsi, qword [ds:0x100003cb0] ; 0x100003cb0
00000001000016a3 mov qword [ss:rbp+var_18], rsi
00000001000016a7 mov rsi, qword [ds:0x100003c70] ; @selector(viewDidLoad), argument "selector" for method imp___stubs__objc_msgSendSuper2
00000001000016ae mov rdi, rax ; argument "super" for method imp___stubs__objc_msgSendSuper2
00000001000016b1 call imp___stubs__objc_msgSendSuper2
00000001000016b6 mov rax, qword [ds:objc_cls_ref_DemoFramework] ; objc_cls_ref_DemoFramework
00000001000016bd mov rsi, qword [ds:0x100003c78] ; @selector(new), argument "selector" for method imp___got__objc_msgSend
00000001000016c4 mov rdi, rax
00000001000016c7 call qword [ds:imp___got__objc_msgSend]
00000001000016cd mov rsi, qword [ds:0x100003c80] ; @selector(codingLife), argument "selector" for method imp___got__objc_msgSend
00000001000016d4 mov rdi, rax ; argument "instance" for method imp___got__objc_msgSend
00000001000016d7 mov qword [ss:rbp+var_28], rax
00000001000016db call qword [ds:imp___got__objc_msgSend]
00000001000016e1 mov rax, qword [ss:rbp+var_28]
00000001000016e5 mov rdi, rax
00000001000016e8 call qword [ds:imp___got__objc_release]
00000001000016ee mov rax, qword [ds:objc_cls_ref_GoodBoy] ; objc_cls_ref_GoodBoy
00000001000016f5 mov rsi, qword [ds:0x100003c78] ; @selector(new), argument "selector" for method imp___got__objc_msgSend
00000001000016fc mov rdi, rax
00000001000016ff call qword [ds:imp___got__objc_msgSend]
0000000100001705 mov rsi, qword [ds:0x100003c88] ; @selector(xuanGao), argument "selector" for method imp___got__objc_msgSend
000000010000170c mov rdi, rax ; argument "instance" for method imp___got__objc_msgSend
000000010000170f mov qword [ss:rbp+var_30], rax
0000000100001713 call qword [ds:imp___got__objc_msgSend]
0000000100001719 mov rax, qword [ss:rbp+var_30]
000000010000171d mov rdi, rax
0000000100001720 call qword [ds:imp___got__objc_release]
0000000100001726 mov al, 0x0
0000000100001728 call imp___stubs__helloWorld
000000010000172d add rsp, 0x30
0000000100001731 pop rbp
0000000100001732 ret
; endp
跟踪一下 call imp___stubs__helloWorld
;
; Section __stubs
;
; Range 0x100001a34 - 0x100001a64 (48 bytes)
; File offset 6708 (48 bytes)
; Flags : 0x80000408
;
imp___stubs__NSStringFromClass:
0000000100001a34 jmp qword [ds:imp___la_symbol_ptr__NSStringFromClass] ; XREF=0x100000120, _main+74
; endp
================ B E G I N N I N G O F P R O C E D U R E ================
imp___stubs__UIApplicationMain:
0000000100001a3a jmp qword [ds:imp___la_symbol_ptr__UIApplicationMain] ; XREF=_main+107
; endp
================ B E G I N N I N G O F P R O C E D U R E ================
imp___stubs__helloWorld:
0000000100001a40 jmp qword [ds:imp___la_symbol_ptr__helloWorld] ; XREF=-[ViewController viewDidLoad]+168
; endp
这是一个__stubs段,需要lazy_bind的函数都可以在这个段找到。
;
; Section __la_symbol_ptr
;
; Range 0x100003020 - 0x100003060 (64 bytes)
; File offset 12320 (64 bytes)
; Flags : 0x00000007
;
imp___la_symbol_ptr__NSStringFromClass:
0000000100003020 dq 0x0000000100001a7e ; XREF=0x100000488, imp___stubs__NSStringFromClass
imp___la_symbol_ptr__UIApplicationMain:
0000000100003028 dq 0x0000000100001aba ; XREF=imp___stubs__UIApplicationMain
imp___la_symbol_ptr__helloWorld:
0000000100003030 dq 0x0000000100001a74 ; XREF=imp___stubs__helloWorld
imp___la_symbol_ptr__objc_autoreleasePoolPop:
0000000100003038 dq 0x0000000100001a88 ; XREF=imp___stubs__objc_autoreleasePoolPop
imp___la_symbol_ptr__objc_autoreleasePoolPush:
0000000100003040 dq 0x0000000100001a92 ; XREF=imp___stubs__objc_autoreleasePoolPush
imp___la_symbol_ptr__objc_msgSendSuper2:
0000000100003048 dq 0x0000000100001a9c ; XREF=imp___stubs__objc_msgSendSuper2
imp___la_symbol_ptr__objc_retainAutoreleasedReturnValue:
0000000100003050 dq 0x0000000100001aa6 ; XREF=imp___stubs__objc_retainAutoreleasedReturnValue
imp___la_symbol_ptr__objc_storeStrong:
0000000100003058 dq 0x0000000100001ab0 ; XREF=imp___stubs__objc_storeStrong
;
__la_symbol_ptr 段,这里可以看到 imp___stubs__helloWorld = 0x0000000100001a74
所以这段代码最终会跳转到 0x0000000100001a74
;
; Section __stub_helper
;
; Range 0x100001a64 - 0x100001ac4 (96 bytes)
; File offset 6756 (96 bytes)
; Flags : 0x80000400
;
0000000100001a64 lea r11, qword [ds:0x100003008] ; 0x100003008 (imp___nl_symbol_ptr_dyld_stub_binder + 0x8), XREF=0x100000170, 0x100001a79, 0x100001a83, 0x100001a8d, 0x100001a97, 0x100001aa1, 0x100001aab, 0x100001ab5, 0x100001abf
0000000100001a6b push r11
0000000100001a6d jmp qword [ds:imp___nl_symbol_ptr_dyld_stub_binder]
0000000100001a73 nop
0000000100001a74 push 0x32 ; XREF=imp___la_symbol_ptr__helloWorld
0000000100001a79 jmp 0x100001a64
0000000100001a7e push 0x0 ; XREF=imp___la_symbol_ptr__NSStringFromClass
0000000100001a83 jmp 0x100001a64
0000000100001a88 push 0x44 ; XREF=imp___la_symbol_ptr__objc_autoreleasePoolPop
0000000100001a8d jmp 0x100001a64
0000000100001a92 push 0x63 ; XREF=imp___la_symbol_ptr__objc_autoreleasePoolPush
0000000100001a97 jmp 0x100001a64
0000000100001a9c push 0x83 ; XREF=imp___la_symbol_ptr__objc_msgSendSuper2
0000000100001aa1 jmp 0x100001a64
0000000100001aa6 push 0x9d ; XREF=imp___la_symbol_ptr__objc_retainAutoreleasedReturnValue
0000000100001aab jmp 0x100001a64
0000000100001ab0 push 0xc7 ; XREF=imp___la_symbol_ptr__objc_storeStrong
0000000100001ab5 jmp 0x100001a64
0000000100001aba push 0x19 ; XREF=imp___la_symbol_ptr__UIApplicationMain
0000000100001abf jmp 0x100001a64
这一段是核心代码
0000000100001a64 lea r11, qword [ds:0x100003008] ; 0x100003008 (imp___nl_symbol_ptr_dyld_stub_binder + 0x8), XREF=0x100000170, 0x100001a79, 0x100001a83, 0x100001a8d, 0x100001a97, 0x100001aa1, 0x100001aab, 0x100001ab5, 0x100001abf
0000000100001a6b push r11
0000000100001a6d jmp qword [ds:imp___nl_symbol_ptr_dyld_stub_binder]
0000000100001a73 nop
0000000100001a74 push 0x32 ; XREF=imp___la_symbol_ptr__helloWorld
0000000100001a79 jmp 0x100001a64
仔细分析一下其实执行顺序是这样的
0000000100001a74 push 0x32
0000000100001a64 lea r11, qword [ds:0x100003008] ; 0x100003008 (imp___nl_symbol_ptr_dyld_stub_binder + 0x8), XREF=0x100000170, 0x100001a79, 0x100001a83, 0x100001a8d, 0x100001a97, 0x100001aa1, 0x100001aab, 0x100001ab5, 0x100001abf
0000000100001a6b push r11
0000000100001a6d jmp qword [ds:imp___nl_symbol_ptr_dyld_stub_binder]
可以确定的是imp___nl_symbol_ptr_dyld_stub_binder 需要两个参数一个0x32, 一个是 qword [ds:0x100003008] 内存指针,看一下这块内存
; Section __nl_symbol_ptr
;
; Range 0x100003000 - 0x100003010 (16 bytes)
; File offset 12288 (16 bytes)
; Flags : 0x00000006
;
imp___nl_symbol_ptr_dyld_stub_binder:
0000000100003000 dq dyld_stub_binder ; XREF=0x100000398, 0x1000003e8, 0x100001a6d
0000000100003008 dq 0x0000000000000000 ; XREF=0x100001a64
大胆推测一下其中一个可能是偏移地址一个是基地址,之后可能需要花些时间研究一下。
整个逻辑应该是 偏移地址 + 基地址, 如果该快内存有值的话,那么就不需要查找真正的函数地址,否则则需要根据符号表去查找函数表的地址,然后写入该内存。
在回过头追踪下这行指令:
[ds:imp___nl_symbol_ptr_dyld_stub_binder]
; imp___nl_symbol_ptr_dyld_stub_binder = 0000000100003000
dyld_stub_binder 没有被初始化,因为它也是动态库函数,只有在加载的时候才会初始化。
静态分析 可执行文件结构
加载命令 加载动态执行库、加载动态链接库
关键函数 dyld_stub_binder
自举: 动态链接库也是一个动态库,在加载动态库时第一件事情就是加载动态链接库,所以可怜的动态链接库只能自己加载自己,别的动态库可以通过链接器实行动态加载,这种行为称为自举。 所以它是一个有点特殊的动态链接库
follow 动态绑定source code 梳理流程
.got global offset table
._no_lazy_symbol
._lazy_symbol
.dynamic_symbol_table
got 段 global offset table
_no_lazy_symbol
_lazy_symbol
dynamic_symbol_table