MachO动态库绑定过程详解

动态库绑定过程详解

首先动态库绑定分为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

屏幕快照 2019-10-30 下午5.36.18.png

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343