动态链接学习的一些简单总结

一、源码编写编译

经过几天的学习,对动态链接进行一些简单的摸索,并将学习总结记录如下。

首先编写两个测试源码文件:

[ main.c ]

 1 #include <stdio.h>
 2 
 3 extern int subVal;
 4 void SubFunc(void);
 5 int mainVal = 3;
 6 
 7 void MainFunc(void)
 8 {
 9 
10 }
11 
12 int main()
13 {
14     puts("In Main.");
15 
16     mainVal = 6;
17     subVal = 6;
18 
19     MainFunc();
20     SubFunc();
21 
22     return 0;
23 }

[ subFunc.c ]

 1 #include <stdio.h>
 2 
 3 static int staticSubVal = 3;
 4 int subVal = 3;
 5 
 6 static void StaticTestFunc(void)
 7 {
 8 
 9 }
10 
11 void TestFunc(void)
12 {
13 
14 }
15 
16 void SubFunc()
17 {
18     puts("In SubFunc.");
19 
20     staticSubVal = 8;
21     subVal = 8;
22 
23     StaticTestFunc();
24     TestFunc();
25 }

编译源代码:

gcc -shared -fPIC -o libsubFunc.so subFunc.c
gcc -o main main.c -lsubFunc -L.

 二、查看对应的ELF文件及反汇编代码

① main ELF:

      ...
[11] .init PROGBITS 0000000000000630 00000630
   0000000000000017 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000000650 00000650
   0000000000000030 0000000000000010 AX 0 0 16
[13] .plt.got PROGBITS 0000000000000680 00000680
   0000000000000008 0000000000000008 AX 0 0 8
[14] .text PROGBITS 0000000000000690 00000690
   00000000000001c2 0000000000000000 AX 0 0 16
[22] .got PROGBITS 0000000000200fb0 00000fb0
   0000000000000050 0000000000000008 WA 0 0 8
[23] .data PROGBITS 0000000000201000 00001000
   0000000000000014 0000000000000000 WA 0 0 8
[24] .bss NOBITS 0000000000201014 00001014
   000000000000000c 0000000000000000 WA 0 0 4
[25] .comment PROGBITS 0000000000000000 00001014
   000000000000002b 0000000000000001 MS 0 0 1
      ...    

 ② main DISASM:

00000000000007a1 <main>:
  7a1: 55 push %rbp
  7a2: 48 89 e5 mov %rsp,%rbp
  7a5: 48 8d 3d b8 00 00 00 lea 0xb8(%rip),%rdi # 864 <_IO_stdin_used+0x4>
  7ac: e8 af fe ff ff callq 660 <puts@plt>
  7b1: c7 05 55 08 20 00 06 movl $0x6,0x200855(%rip) # 201010 <mainVal>
  7b8: 00 00 00 
  7bb: c7 05 4f 08 20 00 06 movl $0x6,0x20084f(%rip) # 201014 <subVal>
  7c2: 00 00 00 
  7c5: e8 d0 ff ff ff callq 79a <MainFunc>
  7ca: e8 a1 fe ff ff callq 670 <SubFunc@plt>
  7cf: b8 00 00 00 00 mov $0x0,%eax
  7d4: 5d pop %rbp
  7d5: c3 retq 
③ libsubFunc.so ELF:
      ...        
[ 9] .init PROGBITS 0000000000000560 00000560
   0000000000000017 0000000000000000 AX 0 0 4
[10] .plt PROGBITS 0000000000000580 00000580
   0000000000000030 0000000000000010 AX 0 0 16
[11] .plt.got PROGBITS 00000000000005b0 000005b0
   0000000000000008 0000000000000008 AX 0 0 8
[12] .text PROGBITS 00000000000005c0 000005c0
   000000000000011c 0000000000000000 AX 0 0 16
[20] .got PROGBITS 0000000000200fd8 00000fd8
   0000000000000028 0000000000000008 WA 0 0 8
[21] .got.plt PROGBITS 0000000000201000 00001000
   0000000000000028 0000000000000008 WA 0 0 8
[22] .data PROGBITS 0000000000201028 00001028
   0000000000000010 0000000000000000 WA 0 0 8
[23] .bss NOBITS 0000000000201038 00001038
   0000000000000008 0000000000000000 WA 0 0 1
      ...
④ libsubFunc.so DISASM:
00000000000006a8 <SubFunc>:
  6a8: 55 push %rbp
  6a9: 48 89 e5 mov %rsp,%rbp
  6ac: 48 8d 3d 32 00 00 00 lea 0x32(%rip),%rdi # 6e5 <_fini+0x9>
  6b3: e8 d8 fe ff ff callq 590 <puts@plt>
  6b8: c7 05 6e 09 20 00 08 movl $0x8,0x20096e(%rip) # 201030 <staticSubVal>
  6bf: 00 00 00 
  6c2: 48 8b 05 17 09 20 00 mov 0x200917(%rip),%rax # 200fe0 <subVal@@Base-0x54>
  6c9: c7 00 08 00 00 00 movl $0x8,(%rax)
  6cf: e8 c6 ff ff ff callq 69a <StaticTestFunc>
  6d4: e8 c7 fe ff ff callq 5a0 <TestFunc@plt>
  6d9: 90 nop
  6da: 5d pop %rbp
  6db: c3 retq

学习总结一:

(1)与动态链接不同,静态链接在编译生成可执行程序后,程序中所有的符号地址都已经确定了下来,在运行时系统可以直接指向程序的Entry Point,默认情况则为_start,并将运行权限完全交给可执行程序。

(2)而动态链接时,由于动态库是共享的且其地址由系统分配至合适的区域,其地址是不能确定的。在运行可执行程序时,系统会将执行权限先交至链接器(例如:libc.so.6),所以动态链接的符号时在运行时进行链接的。为了增快速度,链接器会采用Lazy的绑定方式,即函数的调用地址会在在第一次调用时才进行绑定。

  ①第一次调用时,实际的调用地址还未绑定,会执行解析绑定程序:

       

   ②绑定程序结束后,函数的调用地址将填入GOT相应位置,下次调用时可直接跳转:

           

学习总结二:
(1)动态链接时,如果调用 static 的变量及函数,会采用相对寻址的方式,数据段会创建一份副本。
(2)调用非static的变量及函数时,都会通过PLT(procedure linkage table)间接调用。链接器链接时,为了增快速度,函数的调用地址会在在第一次调用时才进行绑定,解析绑定函数为_dl_runtime_resolve_xsave,参数需要两个,分别是重定位索引下标和所在模块ID,具体过程如下:

重定位表在ELF文件中可以找到:

  

三、运行调试验证

具体调用过程可如下调试所示:

───────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────
    0x7ffff7bd36b3 <SubFunc+11> call puts@plt <0x7ffff7bd3590>
    0x7ffff7bd36b8 <SubFunc+16> mov dword ptr [rip + 0x20096e], 8 <0x7ffff7dd4030>
    0x7ffff7bd36c2 <SubFunc+26> mov rax, qword ptr [rip + 0x200917]
    0x7ffff7bd36c9 <SubFunc+33> mov dword ptr [rax], 8
    0x7ffff7bd36cf <SubFunc+39> call StaticTestFunc <0x7ffff7bd369a>0x7ffff7bd36d4 <SubFunc+44> call TestFunc@plt <0x7ffff7bd35a0>

进入:
───────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────
►   0x7ffff7bd35a0 <TestFunc@plt> jmp qword ptr [rip + 0x200a7a] <0x7ffff7dd4020>
    0x7ffff7bd35a6 <TestFunc@plt+6> push 1
    0x7ffff7bd35ab <TestFunc@plt+11> jmp 0x7ffff7bd35800x7ffff7bd3580 push qword ptr [rip + 0x200a82] <0x7ffff7dd4008>
    0x7ffff7bd3586 jmp qword ptr [rip + 0x200a84] <0x7ffff7dec680>0x7ffff7dec680 <_dl_runtime_resolve_xsave> push rbx
    0x7ffff7dec681 <_dl_runtime_resolve_xsave+1> mov rbx, rsp

执行到 0x7ffff7bd3586 时,栈如下所示:
────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────
    00:0000│ rsp 0x7fffffffe3a8 —▸ 0x7ffff7ff5000 —▸ 0x7ffff7bd3000 ◂— jg 0x7ffff7bd3047
    01:00080x7fffffffe3b0 ◂— 0x1
    02:00100x7fffffffe3b8 —▸ 0x7ffff7bd36d9 (SubFunc+49) ◂— nop 
    03:0018│ rbp 0x7fffffffe3c0 —▸ 0x7fffffffe3d0 —▸ 0x5555555547e0 (__libc_csu_init) ◂— push r15
    04:00200x7fffffffe3c8 —▸ 0x5555555547cf (main+46) ◂— mov eax, 0

由实际调用过程清楚可见,在调用 jmp qword ptr [rip + 0x200a84] <0x7ffff7dec680> 解析绑定函数前,的确传入两个参数,重写位下标 <TestFunc@plt+6> push 1 以及模块的ID push qword ptr [rip + 0x200a82] <0x7ffff7dd4008> 

动态库使用时应编译成PIC形式,进程在使用时可使用相对寻址的方式,而无需修改代码中的地址从而产生副本代码浪费了内存,并且共享库所占物理内存可直接进行映射。关于数据部分,可执行程序程序模块在链接时就要将访问到时的数据地址确定下来,如果在可执行程序模块中使用了动态库中的全局变量,那么将在.bss段中创建一个副本,运行时则使用动态库中的初始值进行初始化,而动态库中GOT中该变量的指针也会指向此副本。

DSO的优点:1)动态链接库的好处是可以节省内存占用,资源共享;

       2)团队之间成果物升级方便,无需重新编译;

刚接触该部分内容,理解不够透彻,如总结有问题,日后会维护修改。

本文图片摘自:zhuanlan.zhihu.com/p/25892385 感谢博主

更为详细的博文推荐:blog.csdn.net/taocr/article/details/52433614

猜你喜欢

转载自www.cnblogs.com/GyForever1004/p/11749169.html