ELF文件动态重定向

本文分析ELF文件在运行时动态重定向的实现。


以下面的两个程序为例:

$ cat mlib.c
int boo()
{
    return 0;
}


int foo()
{
    void *fn = boo;
    boo();
    return 0;
}

$ cat ./main.c
int boo();


int main()
{
    void *fn = boo;$ cat ./main.c
int boo();


int main()
{
    void *fn = boo;
    boo();
}
    boo();
}

$ gcc -o libmlib.so -shared -fPIC ./mlib.c
$ gcc -o main -lmlib -L. ./main.c

main函数中引用的函数boo定义在shared library中。

loader如何实现对shared library中的函数调用进行重定向

初始时,main函数中对boo的调用实际call的是boo@plt,地址为0x400560,位置处于section .plt中(参见表一)。


(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000400660 <+0>:     push   %rbp
   0x0000000000400661 <+1>:     mov    %rsp,%rbp
=> 0x0000000000400664 <+4>:     sub    $0x10,%rsp
   0x0000000000400668 <+8>:     movq   $0x400560,-0x8(%rbp)
   0x0000000000400670 <+16>:    mov    $0x0,%eax
   0x0000000000400675 <+21>:    callq  0x400560  <---- call boo()
   0x000000000040067a <+26>:    leaveq
   0x000000000040067b <+27>:    retq
End of assembler dump.


对boo@plt进行反汇编,可以看出第一条jmp语句跳到了下一行0x400566。0x2被push到了堆栈。0x2应该代表.rela.plt中的第三项(0为第一项),该信息将被传递给loader。


(gdb) disassemble 0x400560
Dump of assembler code for function boo@plt:
   0x0000000000400560 <+0>:     jmpq   *0x2004f2(%rip)        # 0x600a58
   0x0000000000400566 <+6>:     pushq  $0x2
   0x000000000040056b <+11>:    jmpq   0x400530


(gdb) x /xg 0x600a58
0x600a58 : 0x0000000000400566


push操作后,jmp到了0x400530,接着又把0x600a38中的数据push到了堆栈。注意0x600a38是.got.plt 中的第二个entry(每个entry的size是8)。最后又jmp到了0x00007ffff7def2d0(地址0x600a40中的数据)。


(gdb) x /5i 0x400530
   0x400530:    pushq  0x200502(%rip)        # 0x600a38
   0x400536:    jmpq   *0x200504(%rip)        # 0x600a40
   0x40053c:    nopl   0x0(%rax)
   0x400540 <__libc_start_main@plt>:    jmpq   *0x200502(%rip)        # 0x600a48 <[email protected]>
   0x400546 <__libc_start_main@plt+6>:  pushq  $0x0


(gdb) x /xg 0x600a38
0x600a38:       0x00007ffff7ffe190
(gdb) x /xg 0x600a40
0x600a40:       0x00007ffff7def2d0


从shared library map中可以看出,0x00007ffff7def2d0对应到/lib64/ld-linux-x86-64.so.2中。loader将利用之前push的两个参数0x2和0x00007ffff7ffe190对[email protected](即0x600a58)中的数据进行更新,修改成函数boo的实际地址。


(gdb) info sharedlibrary
From                To                  Syms Read   Shared Object Library
0x00007ffff7ddbba0  0x00007ffff7df4baa  Yes (*)     /lib64/ld-linux-x86-64.so.2
                                        No          linux-vdso.so.1
0x00007ffff7bda5b0  0x00007ffff7bda6c8  Yes (*)     ./libmlib.so
0x00007ffff784c470  0x00007ffff7978aa0  Yes (*)     /usr/lib/libc.so.6
(*): Shared library is missing debugging information.


将程序运行到断点0x000000000040067a,检查0x600a58的数值,可以看出已经变成了函数boo的地址。


(gdb) x /xg 0x600a58
0x600a58 : 0x00007ffff7bda698
(gdb) p boo

$1 = {} 0x7ffff7bda698


loader如何实现对shared library中变量的重定向

在main函数中对boo的地址进行了引用。可以看出boo的地址就是0x400560,即,而非boo的真正地址。后面可以看出,即使在libmlib.so中对boo的引用同样也指向0x400560。××这样做的目的应当是确保在main和library当中对同一函数的地址引用是相等的××。


   0x0000000000400668 <+8>:     movq   $0x400560,-0x8(%rbp)


在shared library中的foo函数中对boo也做了引用,可以看出boo的地址被用0x200978中的数据间接代替了,而且0x200978中的数值是0,显然不对。


gdb ./libmlib.so
(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00000000000006a3 <+0>:     push   %rbp
   0x00000000000006a4 <+1>:     mov    %rsp,%rbp
   0x00000000000006a7 <+4>:     sub    $0x10,%rsp
   0x00000000000006ab <+8>:     mov    0x2002c6(%rip),%rax        # 0x200978
   0x00000000000006b2 <+15>:    mov    %rax,-0x8(%rbp)
   0x00000000000006b6 <+19>:    mov    $0x0,%eax
   0x00000000000006bb <+24>:    callq  0x590
   0x00000000000006c0 <+29>:    mov    $0x0,%eax
   0x00000000000006c5 <+34>:    leaveq
   0x00000000000006c6 <+35>:    retq
(gdb) x /xg 0x200978
0x200978:       0x0000000000000000


这是因为在.rela.dyn中有一个boo的重定向项。当libmlib.so被load进main中时,loader将会根据相应symbol的地址修正所有R_X86_64_GLOB_DAT重定向项的值。这些重定向项所指向的需要修改的地址都位于section .got中(参见表三)。


$ readelf -r ./libmlib.so


Relocation section '.rela.dyn' at offset 0x430 contains 9 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000200780  000000000008 R_X86_64_RELATIVE                    660
000000200788  000000000008 R_X86_64_RELATIVE                    620
0000002009b8  000000000008 R_X86_64_RELATIVE                    2009b8
000000200958  000200000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000200960  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000200968  000400000006 R_X86_64_GLOB_DAT 0000000000000000 _Jv_RegisterClasses + 0
000000200970  000500000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000200978  000a00000006 R_X86_64_GLOB_DAT 0000000000000698 boo + 0
000000200980  000600000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize + 0


Relocation section '.rela.plt' at offset 0x508 contains 3 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
0000002009a0  000300000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0
0000002009a8  000a00000007 R_X86_64_JUMP_SLO 0000000000000698 boo + 0
0000002009b0  000600000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_finalize + 0


下面可以看出,在load之后foo中所引用的boo的地址更新成了0x400560,即


gdb ./main


(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00007ffff7bda6a3 <+0>:     push   %rbp
   0x00007ffff7bda6a4 <+1>:     mov    %rsp,%rbp
   0x00007ffff7bda6a7 <+4>:     sub    $0x10,%rsp
   0x00007ffff7bda6ab <+8>:     mov    0x2002c6(%rip),%rax        # 0x7ffff7dda978
   0x00007ffff7bda6b2 <+15>:    mov    %rax,-0x8(%rbp)
   0x00007ffff7bda6b6 <+19>:    mov    $0x0,%eax
   0x00007ffff7bda6bb <+24>:    callq  0x7ffff7bda590
   0x00007ffff7bda6c0 <+29>:    mov    $0x0,%eax
   0x00007ffff7bda6c5 <+34>:    leaveq
   0x00007ffff7bda6c6 <+35>:    retq
End of assembler dump.
(gdb) x /xg 0x7ffff7dda978

0x7ffff7dda978: 0x0000000000400560



与重定向相关的section

.plt

重定向的入口代码。对于每一个需要重定向的函数,都会类似下面的代码。其中push的数值应该代表.rela.plt中的对应entry。
(gdb) disassemble 0x400560
Dump of assembler code for function boo@plt:
   0x0000000000400560 <+0>:     jmpq   *0x2004f2(%rip)        # 0x600a58
   0x0000000000400566 <+6>:     pushq  $0x2
   0x000000000040056b <+11>:    jmpq   0x400530

.got

R_X86_64_GLOB_DAT重定向项所指向的需要修改的地址。用于重定向对于变量的引用。

.got.plt

每一项占8个字节。
第一项的数值是0x0000000000600848,即dynamic section的起始地址。
第二项的数值是0x00007ffff7ffe190,据说代表link_map的起始地址?loader用它进行relocation.
第三项的数值是0x00007ffff7def2d0,是loader的_dl_runtime_resolve函数起始地址。


(gdb) disassemble 0x00007ffff7def2d0
Dump of assembler code for function _dl_runtime_resolve:


之后的每一项对应一个函数的重定向地址。初始时,如[email protected]所示,该值指向下一条汇编指令。重定向完成后,该值指向真正的函数地址。


(gdb) x /8xg 0x600a30
0x600a30:       0x0000000000600848      0x00007ffff7ffe190
0x600a40:       0x00007ffff7def2d0      0x00007ffff784e920

附表

表一,main的section
Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400200  00000200
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             000000000040021c  0000021c
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             000000000040023c  0000023c
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400260  00000260
       000000000000003c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002a0  000002a0
       0000000000000120  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           00000000004003c0  000003c0
       00000000000000b5  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400476  00000476
       0000000000000018  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400490  00000490
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             00000000004004b0  000004b0
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004004c8  000004c8
       0000000000000048  0000000000000018   A       5    12     8
  [11] .init             PROGBITS         0000000000400510  00000510
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400530  00000530
       0000000000000040  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000400570  00000570
       0000000000000184  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         00000000004006f4  000006f4
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         0000000000400700  00000700
       0000000000000004  0000000000000004  AM       0     0     4
  [16] .eh_frame_hdr     PROGBITS         0000000000400704  00000704
       0000000000000034  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000400738  00000738
       00000000000000f4  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000600830  00000830
       0000000000000008  0000000000000000  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600838  00000838
       0000000000000008  0000000000000000  WA       0     0     8
  [20] .jcr              PROGBITS         0000000000600840  00000840
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000600848  00000848
       00000000000001e0  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000600a28  00000a28
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .got.plt          PROGBITS         0000000000600a30  00000a30
       0000000000000030  0000000000000008  WA       0     0     8
  [24] .data             PROGBITS         0000000000600a60  00000a60
       0000000000000010  0000000000000000  WA       0     0     8
  [25] .bss              NOBITS           0000000000600a70  00000a70
       0000000000000008  0000000000000000  WA       0     0     4
  [26] .comment          PROGBITS         0000000000000000  00000a70
       000000000000004e  0000000000000001  MS       0     0     1
  [27] .shstrtab         STRTAB           0000000000000000  00000abe
       0000000000000108  0000000000000000           0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00001348
       0000000000000648  0000000000000018          29    47     8
  [29] .strtab           STRTAB           0000000000000000  00001990
       000000000000022f  0000000000000000           0     0     1

表二,main的重定向信息
注意.rela.plt中的第三行boo的重定向信息,其中offset是000000600a58,刚好在section .got.plt中。
$ readelf -r ./main


Relocation section '.rela.dyn' at offset 0x4b0 contains 1 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000600a28  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0


Relocation section '.rela.plt' at offset 0x4c8 contains 3 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000600a48  000200000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main + 0
000000600a50  000300000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0
000000600a58  000800000007 R_X86_64_JUMP_SLO 0000000000400560 boo + 0

表三,libmlib.so的section(部分)
$ readelf -S ./libmlib.so
There are 27 section headers, starting at offset 0xad8:


Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .note.gnu.build-i NOTE             0000000000000190  00000190
       0000000000000024  0000000000000000   A       0     0     4
  [ 2] .gnu.hash         GNU_HASH         00000000000001b8  000001b8
       0000000000000040  0000000000000000   A       3     0     8
  ..........
  [19] .got              PROGBITS         0000000000200958  00000958
       0000000000000030  0000000000000008  WA       0     0     8





你可能感兴趣的:(Linux)