动态链接和延迟绑定

现在的程序几乎都是动态链接了 + 延迟绑定了. 这样可以节省宝贵的内存空间还能提升运行时的效率. 之前也零零散散地看了很多相关的文章. 今天就系统地总结一遍吧.

got表和plt表

首先从got表和plt表讲起. 以一个调用libc中write()函数为例. 来分析一下流程:

基础知识

  1. got表属于数据段, 是可写的. 表中存储的是指针. plt属于代码段, 其中每一项都存储了三个汇编指令.
  2. 当系统根据一个文件创建一个进程的时候, dynamic linker会把got表的第二项和第三项初始化为特殊的值, 具体是什么后面会解释.
  3. 对于每个重定位的函数, 其在got表和plt表中分别有一项存储该函数动态链接时需要使用的程序. 我们假设 PLT[2], 和GOT[4]存储libc 中 write 的对应信息

动态链接流程

  1. 当这个程序里面调用write()的时候就会跳到plt表中write对应的项.这儿就是PLT[2], 其中有三行汇编码
jmpq *GOT[4] # write 函数在got表中对应的项
pushq $0x01 # write()对应的编号, 记为reloc_arg.程序中的每个重定位函数都有一个独一无二的编号, 根据这个编号可以计算这个函数对应的got表的偏移, 动态链接需要的信息等等.
jmpq  4005a # PLT[0]

而GOT[4]表中初始值都是指向PLT[2]的第二行代码, 当程序刚加载完毕的时候, 每个重定位函数对应的got表中的地址都指向对应的plt表的第二个指令.

  1. 进入 PLT[0], 其中代码如下:
pushq *GOT[1] #一个特殊的地址, 指向动态链接所需要的信息, 后面会解释, 记为 link_map
jmpq *GOT[2] # dynamic linker 的地址

GOT[2] 中存储的就是_dl_runtime_resolve函数的地址了.
就相当于执行了_dl_runtime_resolve(link_map, reloc_arg).

  1. _dl_runtime_resolve()会根据reloc_arg计算出got表地址, 需要重定位函数的名称等信息, 然后根据这些信息找到函数的真实运行地址. 最后把got表中这个函数对应的项got[4]修改为真实地址.

  2. dynamic linker()执行结束之后就会跳转到write()函数里面.

  3. 之后在调用write()的时候仍然先跳到plt[2], 然后跳到*got[4], 此时got[4]中的地址就是write()函数的真实地址了. 因为只在第一次执行的时候才绑定真实的地址, 所以叫做延迟绑定(lazy binding)

dynmic linker工作流程

看完前面的内容, 对动态链接的过程已经大致了解了. 接下来我们深入分析一下最关键的一步:调用dynmic linker修改got表内容.

我画了如下示意图来更直观地表达动态链接的过程, 具体过程后面解释.
.dynamic是elf文件中的一个section, 其中包含了动态链接所需要的信息. 比如一些指向其它section的指针. 可以参考这个文档
.dynstr是elf文件中的一个section. 其包含的需要重定位的函数的名称. dynamic linker可以根据这些名称找到真实的运行时地址进而修改got表
.dynsym section是一个结构体数组, 结构体定义如下:

typedef struct
{
  Elf32_Word    st_name;   /* Symbol name (index in .synstr) */
  Elf32_Addr    st_value;  /* Symbol value */
  Elf32_Word    st_size;   /* Symbol size */
  unsigned char st_info;   /* Symbol type and binding */
  unsigned char st_other;  /* Symbol visibility under glibc>=2.2 */
  Elf32_Section st_shndx;  /* Section index */
} Elf32_Sym;

每个重定位函数在其中有一项, 可以根据这个找到重定位函数的名称.
.rel.plt section也是一个结构体数组, 结构体定义如下:

typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Word;
typedef struct
{
  Elf32_Addr    r_offset;               /* 该项对应的got表的项的地址 */
  Elf32_Word    r_info;                 /* Relocation type and symbol index */
} Elf32_Rel;
#define ELF32_R_SYM(val) ((val) >> 8)   //计算该项在.dynsym中的index
#define ELF32_R_TYPE(val) ((val) & 0xff) 
 .dynmic                                                    .dynstr
+-------> +-----------------+        +-------------------------------> +------------+
          | ................|        |                                 |  ........  |
          |                 |        |                                 +------------+
          |                 |        |                      +---------->  "read"    |
          +-----------------+        |                      |          +------------+
          |    STRTAB       +--------+    .dynsym           |          | .........  |
          +-----------------+           +--------------+    |          +------------+
          |    SYSTAB       +---------> |  .........   |    |
          +-----------------+           +--------------+    |
          |   PLTRELSZ      |           |  name_index  +----+
          +-----------------+           +--------------+
          |    PLTREL       |           |              | index = Elf32_R_SYM(r_info)
          +-----------------+           +--------------+ <-------------------+
          |    RELENT       |           |              |                     |
          +-----------------+           +--------------+                     |
          |    JMPREL       +---+       |  .........   |                     |
          +-----------------+   |       +--------------+                     |
          |                 |   |               .rel.plt                     |
          |                 |   +-----------------------> +--------------+   |
          |                 |                        +----+    r_offset  |   |
          |                 |         .got.plt       |    +--------------+   |
          |                 |       +-------------+  |    |    r_info    +---+
          |                 |       |    ......   |  |    +--------------+
          |                 |       +-------------+  |    | ...........  |
          |                 |       | read@got    | <+    |              |
          |                 |       +-------------+       |              |
          +-----------------+       |  .......    |       |              |
                                    |             |       +--------------+
                                    |             |
                                    |             |
                                    +-------------+

最后结合动态链接器的源码分析具体链接过程(源码可见: glibc/elf/dl-runtime.c: _dl_fixup 函数)

_dl_fixup(struct link_map *l, ElfW(Word) reloc_arg)
{
    // 首先通过参数reloc_arg计算重定位入口,这里的JMPREL即.rel.plt,reloc_offset即reloc_arg
    const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
    // 然后通过reloc->r_info找到.dynsym中对应的条目
    const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
    // 这里还会检查reloc->r_info的最低位是不是R_386_JUMP_SLOT=7
    assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
    // 接着通过strtab+sym->st_name找到符号表字符串,result为libc基地址
    result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
    // value为libc基址加上要解析函数的偏移地址,也即实际地址
    value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym->st_value) : 0);
    // 最后把value写入相应的GOT表条目中
    return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
}

参考博客:
ROP之return to dl-resolve
Executable and Linkable Format (ELF)

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

推荐阅读更多精彩内容