程序的加载与链接

程序的加载与链接(六) 动态库 lazy bind

2019-11-04  本文已影响0人  狼性刀锋

简述

看了很久的关于动态库链接加载的知识,但对其中的一些细节一直似懂非懂的,所以决定实践一下加深一下印象。本文主要介绍动态库符号的lazy bind 过程。

Demo

先实现一个简单的动态库

// .h
int add(int a,int b);
int sub(int a,int b);

// .c 
#import "*.h"

int add(int a,int b) {

    return a + b;
}

int sub(int a,int b) {
 
    return a - b;
}

实现一个简单的app

// ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    int result = add(3, 4);
//    void * addPoint = add;
//    void * subPoint = sub;
    
    result = sub(3, 4);
    
    NSLog(@"********");
    
    
}


传送门

符号表

屏幕快照 2019-11-04 上午11.17.37.png

间接符号表

所有需要间接访问的符号都存放在这里,包括lazy和no_lazy符号,类似于重定位表的格式


屏幕快照 2019-11-04 上午11.18.30.png

lazy bind 符号表

存放着lazy bind 符号地址信息, 例如: _add 符号地址0x100003038, 它的value 是多少呢?0x100001a80

屏幕快照 2019-11-04 下午3.57.35.png

0x100001a80 指向的是一段汇编代码:

0000000100001a70         lea        r11, qword [ds:0x100003008]                 ; 0x100003008 (imp___nl_symbol_ptr_dyld_stub_binder + 0x8), XREF=0x100000170, 0x100001a85, 0x100001a8f, 0x100001a99, 0x100001aa3, 0x100001aad, 0x100001ab7, 0x100001ac1, 0x100001acb, 0x100001ad5, 0x100001adf
0000000100001a77         push       r11
0000000100001a79         jmp        qword [ds:imp___nl_symbol_ptr_dyld_stub_binder]
0000000100001a7f         nop        
0000000100001a80         push       0x3f                                        ; XREF=imp___la_symbol_ptr__add
0000000100001a85         jmp        0x100001a70

整理一下就是:

0000000100001a80         push       0x3f    
0000000100001a70         lea        r11, qword [ds:0x100003008]                 ; 0x100003008 (imp___nl_symbol_ptr_dyld_stub_binder + 0x8), XREF=0x100000170, 0x100001a85, 0x100001a8f, 0x100001a99, 0x100001aa3, 0x100001aad, 0x100001ab7, 0x100001ac1, 0x100001acb, 0x100001ad5, 0x100001adf
0000000100001a77         push       r11
0000000100001a79         jmp        qword [ds:imp___nl_symbol_ptr_dyld_stub_binder]

稍后再解释它的作用

验证lazy bind

我现在要校验一下add函数调用之后, _add 符号的间接地址指针是否被修改了

屏幕快照 2019-11-04 下午2.40.41.png

这样就成功校验了,lazy bind 理论是正确的

: 观察完整的lazy bind 其实还有一步,就是在没有调用add符号之前,查看间接符号的值, 这个值等于 0x100001a80 + offset, 即指向上面提到的那段汇编代码

lazy bind 过程

  1. 调用 stub_add 函数


    屏幕快照 2019-11-04 下午3.14.31.png
  1. stub_add 会跳转到 间接符号表所指向的地址(间接寻址的关键指令)


    屏幕快照 2019-11-04 下午3.15.30.png
屏幕快照 2019-11-04 上午11.19.03.png

此时 :imp___la_symbol_ptr__add 指向的就是上述的那段汇编代码的地址, 静态地址: 0x100001a80 ,动态地址 : 0x100001a80 + offset

下面分别使用MachOView 和 Hopper disassembler 分析一下source code

屏幕快照 2019-11-04 下午3.17.51.png 屏幕快照 2019-11-04 下午3.19.41.png

imp___nl_symbol_ptr_dyld_stub_binder 所处的section:


屏幕快照 2019-11-04 下午4.32.06.png

该段代码最终会调用 imp___nl_symbol_ptr_dyld_stub_binder 函数,由动态链接库dyld.lib的stub_binde函数完成符号绑定操作。 imp___nl_symbol_ptr_dyld_stub_binder 需要两个参数

imp___nl_symbol_ptr_dyld_stub_binder 属于no_lazy_symbol point,这个在程序启动后就已经初始化完毕,它指向dyld.lib 的 stub_binder 函数地址, stub_binder完成查找_add函数的地址,并且写入间接符号表中,最终写入地址( 0x100001a80 + offset), 之后就不再需要绑定了

总结

程序加载过程

  1. LC_SEGMENT_64 : 加载代码段、数据段
  2. LC_LOAD_DYLINKER: 加载动态库链接器
  3. LC_LOAD_DYLIB: 加载动态库
  4. 完成 no lazy bind
  5. 程序启动后, 在第一调用动态库函数时完成 lazy bind 操作
上一篇 下一篇

猜你喜欢

热点阅读