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

动态链接在不同的系统上有不同的实现方式,ELF比PE简单些。这里只介绍ELF。

1. 静态链接和动态链接的可执行文件执行过程对比

1.1 静态链接的可执行文件执行过程回顾

第一,操作系统读取可执行文件的头部,检查文件的合法性,
第二,从头部中的program header 中读取每个 segment的虚拟地址,文件地址和属性
并将它们映射到进程虚拟空间相应位置
第三,操作系统把控制权交给可执行文件的入口,然后执行。

1.2 动态链接的可执行文件执行过程

动态链接情况下,操作系统还不能在装载完可执行文件后,就把控制权交给
可执行文件。

原因:可执行文件依赖很多共享对象。
即可执行文件里对于很多外部符号的引用还处于无效地址的状态,
就是还没有跟相应的共享对象中的实际位置连接起来。
所以,在影射完可执行文件之后,操作系统会先启动一个动态连接器。

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-64

Contents of section .interp:
400238 2f6c6962 36342f6c 642d6c69 6e75782d  /lib64/ld-linux-
400248 7838362d 36342e73 6f2e3200           x86-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:
Tag        Type                         Name/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:    Value          Size   Type   Bind Vis      Ndx Name
8    0: 0000000000201028     0 NOTYPE  GLOBAL DEFAULT  23 _edata
9    0: 00000000000006f3    19 FUNC    GLOBAL DEFAULT  12 test_b
10   0: 0000000000201030     0 NOTYPE  GLOBAL DEFAULT  24 _end
11   1: 0000000000000706    19 FUNC    GLOBAL DEFAULT  12 test_c
12   1: 0000000000201028     0 NOTYPE  GLOBAL DEFAULT  24 __bss_start
13   1: 0000000000000590     0 FUNC    GLOBAL DEFAULT   9 _init
14   2: 00000000000006e0    19 FUNC    GLOBAL DEFAULT  12 test_a
15   2: 000000000000071c     0 FUNC    GLOBAL DEFAULT  13 _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:
Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000200e00  000000000008 R_X86_64_RELATIVE                    6b0
000000200e08  000000000008 R_X86_64_RELATIVE                    670
000000201020  000000000008 R_X86_64_RELATIVE                    201020
000000200fd8  000200000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000200fe0  000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000200fe8  000500000006 R_X86_64_GLOB_DAT 0000000000000000 _Jv_RegisterClasses + 0
000000200ff0  000600000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000200ff8  000700000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0

Relocation section '.rela.plt' at offset 0x578 contains 1 entries:
Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000201018  000300000007 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] Name              Type             Address           Offset
	Size              EntSize          Flags  Link  Info  Align
……
[21] .got              PROGBITS         0000000000200fd8  00000fd8
	0000000000000028  0000000000000008  WA       0     0     8
[22] .got.plt          PROGBITS         0000000000201000  00001000
	0000000000000020  0000000000000008  WA       0     0     8
[23] .data             PROGBITS         0000000000201020  00001020
	0000000000000008  0000000000000000  WA       0     0     8
……

6. 动态链接时进程堆栈初始化信息

打印堆栈中的初始化信息
参考: https://blog.csdn.net/hs794502825/article/details/16940105
auxiliary_vestor.c

#include<stdio.h>
#include<elf.h>

int 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值