读书-程序员的自我修养-链接、封装与库(19:第七章:动态链接(3)动态链接相关结构


读书-程序员的自我修养-链接、封装与库(19:第七章:动态链接(3)动态链接相关结构)

  • 1. 静态链接和动态链接的可执行文件执行过程对比
    • 1.1 静态链接的可执行文件执行过程回顾
    • 1.2 动态链接的可执行文件执行过程
      • 1.2.1 ld.so 也是一个共享对象
  • 2. .interp 字段
    • 2.1 objdump -s a.out 查看 interp 段
  • 3. .dynamic 段
    • 3.1 readelf -d a.out // -d dynamic 显示动态段的信息
    • 3.2 ldd a.out //查看程序依赖哪些共享库
  • 4. 动态符号表
    • 4.1 静态链接和 .symtab
    • 4.2 导出函数(Export function)和导入函数(Import function)
    • 4.3 动态链接和 .dynsym
      • 4.3.1 辅助表
      • 4.3.2 readelf -sD libtest.so 查看动态链接库的动态符号
    • 4.4 symtab 和 dynsym 关系
  • 5. 动态链接重定位表
    • 5.1 静态链接和动态链接对导入符号的处理时机
    • 5.2 动态链接重定位相关结构
      • 5.2.1 静态链接的 .rel.text .rel.data
      • 5.2.2 动态连接的 .rel.dyn rel.plt
      • 5.2.3 readelf -r libtest.so // -r relocs显示可重定位段的信息
      • 5.2.4 readelf -S libtest.so 显示头结点信息
  • 6. 动态链接时进程堆栈初始化信息

动态链接在不同的系统上有不同的实现方式,ELF比PE简单些。这里只介绍ELF。
1. 静态链接和动态链接的可执行文件执行过程对比 1.1 静态链接的可执行文件执行过程回顾 第一,操作系统读取可执行文件的头部,检查文件的合法性,
第二,从头部中的program header 中读取每个 segment的虚拟地址,文件地址和属性
并将它们映射到进程虚拟空间相应位置
第三,操作系统把控制权交给可执行文件的入口,然后执行。
1.2 动态链接的可执行文件执行过程 【读书-程序员的自我修养-链接、封装与库(19:第七章:动态链接(3)动态链接相关结构】动态链接情况下,操作系统还不能在装载完可执行文件后,就把控制权交给
可执行文件。
原因:可执行文件依赖很多共享对象。
即可执行文件里对于很多外部符号的引用还处于无效地址的状态,
就是还没有跟相应的共享对象中的实际位置连接起来。
所以,在影射完可执行文件之后,操作系统会先启动一个动态连接器。
1.2.1 ld.so 也是一个共享对象
操作系统加载完动态连接器之后,就将控制权交给动态连接器的入口地址
(与可执行文件一样,共享对象也有入口地址)
2. .interp 字段 系统中哪个是动态链接库?
它的位置谁决定?
ELF中有个专门的段.interp (interpreter解释器)段。
它指向连接器。
2.1 objdump -s a.out 查看 interp 段
root@ubuntu-admin-a1:/home/6Chapter# objdump -s a.out a.out:file format elf64-x86-64Contents of section .interp: 400238 2f6c6962 36342f6c 642d6c69 6e75782d/lib64/ld-linux- 400248 7838362d 36342e73 6f2e3200x86-64.so.2.

这里 interp 指向的连接器是: ld-linux-x86-64.so.2.
这里是ubuntu的宿主机查看的结果。
linux下,动态连接器的路径几乎都是 /lib/ld-linux.so.2
3. .dynamic 段 dynamic 段是最重要的段。
它保持了动态连接器需要的基本信息。
比如依赖哪些共享对象,共享对象初始化代码的地址,动态链接符号表的位置,重定位表的位置
3.1 readelf -d a.out // -d dynamic 显示动态段的信息
root@ubuntu-admin-a1:/home/6Chapter# readelf -d a.out Dynamic section at offset 0xe28 contains 24 entries: TagTypeName/Value 0x0000000000000001 (NEEDED)Shared library: [libc.so.6] 0x000000000000000c (INIT)0x400498 0x000000000000000d (FINI)0x400774 0x0000000000000019 (INIT_ARRAY)0x600e10 0x000000000000001b (INIT_ARRAYSZ)8 (bytes) 0x000000000000001a (FINI_ARRAY)0x600e18 0x000000000000001c (FINI_ARRAYSZ)8 (bytes) 0x000000006ffffef5 (GNU_HASH)0x400298 0x0000000000000005 (STRTAB)0x400360 0x0000000000000006 (SYMTAB)0x4002b8 0x000000000000000a (STRSZ)102 (bytes) 0x000000000000000b (SYMENT)24 (bytes) 0x0000000000000015 (DEBUG)0x0 0x0000000000000003 (PLTGOT)0x601000 0x0000000000000002 (PLTRELSZ)120 (bytes) 0x0000000000000014 (PLTREL)RELA 0x0000000000000017 (JMPREL)0x400420 0x0000000000000007 (RELA)0x400408 0x0000000000000008 (RELASZ)24 (bytes) 0x0000000000000009 (RELAENT)24 (bytes) 0x000000006ffffffe (VERNEED)0x4003d8 0x000000006fffffff (VERNEEDNUM)1 0x000000006ffffff0 (VERSYM)0x4003c6 0x0000000000000000 (NULL)0x0 root@ubuntu-admin-a1:/home/6Chapter#

3.2 ldd a.out //查看程序依赖哪些共享库
root@ubuntu-admin-a1:/home/6Chapter# ldd a.out linux-vdso.so.1 =>(0x00007ffc379b8000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa425b86000) /lib64/ld-linux-x86-64.so.2 (0x000055910f911000) root@ubuntu-admin-a1:/home/6Chapter#

4. 动态符号表 为了完成动态链接,最关键的还是所依赖的符号和相关文件的信息。
4.1 静态链接和 .symtab 静态链接中,有个专门的段叫做 .symtab(symbol table),里面保存了所有关于该
目标文件的符号的定义和引用。
4.2 导出函数(Export function)和导入函数(Import function) 如程序pro1使用了hello.so 里面的 printHello 函数
我们就称 printHello 是 pro1 的导入函数;
printHello 是 hello.so 的导出函数;
4.3 动态链接和 .dynsym 为了表示动态链接这些模块之间的符号导入导出关系,ELF专门有个段来保存这些信息。
这个段就是:动态符号表(Dynamic Symbol Table), .dynsym。
4.3.1 辅助表
与 symtab 类似,动态符号表也需要一些辅助的表。
如保存符号名的字符串表 symtab 中叫做:.strtab (String Table)
dynsym 中叫做 .dynstr (Dynamic String Table) 动态符号字符串表。
还有符号哈希表 .hash
4.3.2 readelf -sD libtest.so 查看动态链接库的动态符号
root@ubuntu-admin-a1:/home/6Chapter# readelf -sD libtest.so Symbol table of `.gnu.hash' for image: Num Buc:ValueSizeTypeBind VisNdx Name 80: 00000000002010280 NOTYPEGLOBAL DEFAULT23 _edata 90: 00000000000006f319 FUNCGLOBAL DEFAULT12 test_b 100: 00000000002010300 NOTYPEGLOBAL DEFAULT24 _end 111: 000000000000070619 FUNCGLOBAL DEFAULT12 test_c 121: 00000000002010280 NOTYPEGLOBAL DEFAULT24 __bss_start 131: 00000000000005900 FUNCGLOBAL DEFAULT9 _init 142: 00000000000006e019 FUNCGLOBAL DEFAULT12 test_a 152: 000000000000071c0 FUNCGLOBAL DEFAULT13 _fini root@ubuntu-admin-a1:/home/6Chapter#

参考:
https://www.cnblogs.com/Burgess-Fan/p/6138687.html
4.4 symtab 和 dynsym 关系 dynsym 它只保持了与动态链接相关的符号。
symtab 保存了所有的符号,包括 dynsym 中的符号。
5. 动态链接重定位表 共享对象需要重定位的原因是:导入符号的存在。
5.1 静态链接和动态链接对导入符号的处理时机 在编译时,这些导入符号的地址是未知的,静态连接中,
这些未知的地址引用在最终链接时被修正。
动态连接中,导入符号的地址在运行时才确定,所以需要在运行时将这些
导入符号的引用修正,即需要重定位。
5.2 动态链接重定位相关结构 5.2.1 静态链接的 .rel.text .rel.data
静态连接中,目标文件里包含有专门用于表示重定位信息的重定位表,
如: .rel.text 表示代码段的重定位表
.rel.data 是数据段的重定位表。
5.2.2 动态连接的 .rel.dyn rel.plt
动态连接的目标文件中,也有类似的重定位表叫做: .rel.dyn 和 rel.plt。
  1. .rel.dyn 类似于 静态链接的 .rel.text。
    它实际上是对数据引用的修正。修正的位置位于 .got以及数据段。
  2. rel.plt 类似于 静态链接的 .rel.data
    它是对函数引用的修正。
5.2.3 readelf -r libtest.so // -r relocs显示可重定位段的信息
root@ubuntu-admin-a1:/home/6Chapter# readelf -r libtest.so Relocation section '.rela.dyn' at offset 0x4b8 contains 8 entries: OffsetInfoTypeSym. ValueSym. Name + Addend 000000200e00000000000008 R_X86_64_RELATIVE6b0 000000200e08000000000008 R_X86_64_RELATIVE670 000000201020000000000008 R_X86_64_RELATIVE201020 000000200fd8000200000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0 000000200fe0000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0 000000200fe8000500000006 R_X86_64_GLOB_DAT 0000000000000000 _Jv_RegisterClasses + 0 000000200ff0000600000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0 000000200ff8000700000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0Relocation section '.rela.plt' at offset 0x578 contains 1 entries: OffsetInfoTypeSym. ValueSym. Name + Addend 000000201018000300000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0 root@ubuntu-admin-a1:/home/6Chapter#

5.2.4 readelf -S libtest.so 显示头结点信息
root@ubuntu-admin-a1:/home/6Chapter# readelf -S libtest.so There are 29 section headers, starting at offset 0x18f8: Section Headers: [Nr] NameTypeAddressOffset SizeEntSizeFlagsLinkInfoAlign …… [21] .gotPROGBITS0000000000200fd800000fd8 00000000000000280000000000000008WA008 [22] .got.pltPROGBITS000000000020100000001000 00000000000000200000000000000008WA008 [23] .dataPROGBITS000000000020102000001020 00000000000000080000000000000000WA008 ……

6. 动态链接时进程堆栈初始化信息 打印堆栈中的初始化信息
参考: https://blog.csdn.net/hs794502825/article/details/16940105
auxiliary_vestor.c
#include #includeint main(int argc, char * argv[]) { int *p = (int *)argv; int i; Elf32_auxv_t *aux; printf("Argument count:%d\n", *(p-1) ); for(i=0; i<*(p-1); i++) { printf("Argument %d : %s\n", i, *(p+i) ); } p += i; p++; printf("Environment:\n"); while(*p) { printf("%s\n", *p); p++; } p++; printf("Auxiliary Vectors:\n"); aux = (Elf32_auxv_t *)p; while(aux->a_type != AT_NULL) { printf("Type: %02d Value: %x\n", aux->a_type, aux->a_un.a_val); aux++; } return 0; }

    推荐阅读