ld-linux.so.2 重定向,ELF文件動態重定向

本文详细剖析了ELF文件在运行时如何实现动态重定向,通过分析main函数和shared library中对函数boo的调用,展示了从boo@plt到 Boo@got.plt的重定向过程。动态加载器根据.rela.plt和.got.plt中的信息,更新函数调用地址,确保正确执行。同时,对于变量的重定向,通过.R_X86_64_GLOB_DAT类型的重定向项完成地址更新。
摘要由CSDN通过智能技术生成

本文分析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 :     push   %rbp

0x0000000000400661 :     mov    %rsp,%rbp

=> 0x0000000000400664 :     sub    $0x10,%rsp

0x0000000000400668 :     movq   $0x400560,-0x8(%rbp)

0x0000000000400670 :    mov    $0x0,%eax

0x0000000000400675 :    callq  0x400560  

0x000000000040067a :    leaveq

0x000000000040067b :    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 :     jmpq   *0x2004f2(%rip)        # 0x600a58

0x0000000000400566 :     pushq  $0x2

0x000000000040056b :    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>:    jmpq   *0x200502(%rip)        # 0x600a48 <__libc_start_main>

0x400546 <__libc_start_main>:  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對boo@got.plt(即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 :     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 :     push   %rbp

0x00000000000006a4 :     mov    %rsp,%rbp

0x00000000000006a7 :     sub    $0x10,%rsp

0x00000000000006ab :     mov    0x2002c6(%rip),%rax        # 0x200978

0x00000000000006b2 :    mov    %rax,-0x8(%rbp)

0x00000000000006b6 :    mov    $0x0,%eax

0x00000000000006bb :    callq  0x590

0x00000000000006c0 :    mov    $0x0,%eax

0x00000000000006c5 :    leaveq

0x00000000000006c6 :    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 :     push   %rbp

0x00007ffff7bda6a4 :     mov    %rsp,%rbp

0x00007ffff7bda6a7 :     sub    $0x10,%rsp

0x00007ffff7bda6ab :     mov    0x2002c6(%rip),%rax        # 0x7ffff7dda978

0x00007ffff7bda6b2 :    mov    %rax,-0x8(%rbp)

0x00007ffff7bda6b6 :    mov    $0x0,%eax

0x00007ffff7bda6bb :    callq  0x7ffff7bda590

0x00007ffff7bda6c0 :    mov    $0x0,%eax

0x00007ffff7bda6c5 :    leaveq

0x00007ffff7bda6c6 :    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 :     jmpq   *0x2004f2(%rip)        # 0x600a58

0x0000000000400566 :     pushq  $0x2

0x000000000040056b :    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:

之后的每一項對應一個函數的重定向地址。初始時,如boo@got.plt所示,該值指向下一條匯編指令。重定向完成后,該值指向真正的函數地址。

(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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值