装载和动态链接

来源:互联网 发布:画原型的软件 编辑:程序博客网 时间:2024/05/01 06:10

一、进程虚拟地址空间

1、程序是一个静态的概念,他是一些预编译好的指令数据集合的一个文件。进程是一个动态的概念是一个程序运行时的过程。​

每个进程都有自己独立的虚拟地址空间,虚拟地址空间的大小有硬件平台决定,具体说是CPU位数决定的,32位的平台具体大小是0-4G。从程序员的角度来讲可以通过判断指针所占用的空间来判断虚拟地址空间,C语言指针大小的位数与虚拟地址空间的位数相同。

2、装载

程序运行​时所需要的数据和指令必须要在内存中才能够正常运行。动态装载就是程序用到那部分数据就将其装载到内存中,不用的部分留在磁盘中。动态装载的方法分为覆盖装入和页映射。思想是程序用到那个模块就将哪个模块装入内存,如果不用就暂时不装入,存放在磁盘中。

覆盖装载是将数据和指令按照内存使用的大小进行分类,将不需要同时使用的部分装入相同的内存地址同时根据大小合理分类保证程序能正常运行。

页映射是将内存和磁盘中的数据按照页为单位划分为若干页,以后装载和操作的单位就是页​

二、进程的建立

1、创建一个独立的虚拟地址空间

通过一组页映射函数将虚拟地址空间的各个页映射至物理地址空间。​

2、读取可执行文件的文件头,并建立可执行文件和虚拟地址空间的映射关系。

建立虚拟地址空间和可执行文件之间的关系。​

3、将CPU的指令寄存器设置成CPU的可执行程序的入口地址,启动程序。

​至此进程建立完成,程序开始执行。但是可执行程序的指令和数据并没有真正的装载到内存中,操作系统只是根据可执行文件头部的信息建立了可执行文件和进程的虚拟内存之间的关系而已。随着程序的执行,页错误不断产生,操作系统会分配物理内存来满足进程执行的需要。

6、内核加载ELF文件的过程

(1)检查ELF可执行文件格式的有效性,比如魔数,程序头表中的段的数量

(2)寻找动态链接的“.interp”段,设置动态链接器路径

(3)根据ELF可执行文件的程序头表的描述,对ELF文件进行映射,比如代码,数据,只读数据

(4)初始化ELF进程环境,比如寄存器的地址

(5)将系统调用的返回地址修改成ELF可执行文件的入口点,静态链接的ELF文件就是文件头中的e_entry所指地址,动态链接的文件程序的入口点就是动态链接器。

Q:可执行文件启动时如何确定动态文件时动态链接还是动态加载?

A:对可执行文件而言,只需要查看“.interp”“.dynamic”段确定需要动态链接的库。若没有这个段,操作系统在执行时会认为这个文件不需要动态链接器的支持,因而会直接执行这个文件。通过下面的命令可以查看fun.so文件没有.interp段:

readelf -l ./fun.so | grep interp

动态链接器的的位置由ELF文件中的“.interp”段决定,而段“.dynamic”为动态链接提供了:依赖哪些共享对象、动态链接符号表的位置,动态链接重定位表的位置、共享对象初始化代码的地址等。动态链接过程需要动态符号表来确定函数的定义和引用关系,还需要重定位表来修正导入符号的引用。初始化完成后堆栈中保存了动态连接器所需要的一些辅助信息数组(其中包括程序入口地址,程序表头地址,程序表头项数及大小)。动态链接库最后被映射到进程地址空间的共享库区域段。完成重定位和初始化后,所有准备工作结束,所需要的共享对象也都已经装载并且链接完成。最后将进程的控制权转交给dyn程序的入口并开始执行。至于编译可执行文件时如何判断是否需要生产“.interp”“.dynamic”段,则根据代码中符号的引用,是否有导入符号。所以动态链接的可执行文件编译时需要有动态库的头文件对库中的符号进行声明,然后编译时需要指明库的路径(当然也可以让编译器在默认路径查找)。调用动态加载的动态库在编译可执行文件时不需要头文件也不需要指明库路径。

PS:静态库链接时按目标文件进行的(.o文件),因为静态库只是一些目标文件.o文件的集合,而动态库链接时整个库都会先映射到进行虚拟地址空间的共享库区域段。

三、进程虚拟地址空间的分配

1、将目标文件链接成可执行文件的时候,链接器会把相同权限属性的段链接在同一空间,在ELF中这些属性相同的有连在一起的段(section)叫做“segment”​,而系统正是按照segment来映射可执行文件的。从链接角度看,ELF文件按“section”存储的,从装载的角度看,ELF文件又可以按“segment”划分,链接器会尽量把相同权限属性的section分配到同一个segment中。

​使用readelf -S a 可以查看可执行文件a的段section

​使用readelf -l a 可以查看可执行文件a的段segment​

在ELF文件装载是段指segment,在其他时候段​指section

2、堆与栈

在Linux下面可以通过cat /proc查看进程的虚拟空间分配,如(123为进程号):

cat /proc/123/maps

​ps

PID USER       VSZ STAT COMMAND

    1 root      1388 S    init

    2 root         0 SW   [kthreadd]

    3 root         0 SW   [ksoftirqd/0]

    。。。

    264 root      5952 S    /home/process/daemon_fsp_app

    268 root     22660 S    /home/process/net_process

    281 root      305m S    /home/davinci

# cat /proc/281/maps

00008000-00bea000 r-xp 00000000 00:0d 1062       /home/davinci (deleted)---代码段

00bf2000-00cc5000 rwxp 00be2000 00:0d 1062       /home/davinci (deleted)---数据段

00cc5000-011c2000 rwxp 00000000 00:00 0

022cb000-024e1000 rwxp 00000000 00:00 0          [heap]

40013000-40014000 rwxp 00000000 00:00 0

4008e000-40095000 r-xp 00000000 00:01 164        /lib/ld-uClibc-0.9.32.1.so

4009c000-4009d000 rwxp 00006000 00:01 164        /lib/ld-uClibc-0.9.32.1.so

4009d000-400b7000 r-xp 00000000 00:0d 1056       /home/applib/libfsp_base.so

400b7000-400be000 ---p 00000000 00:00 0

400be000-400bf000 rwxp 00019000 00:0d 1056       /home/applib/libfsp_base.so

...

4023a000-402d2000 r-xp 00000000 00:0d 1052       /home/applib/libsjkd.so (deleted)---代码段

402d2000-402d8000 rwxp 00098000 00:0d 1052       /home/applib/libsjkd.so (deleted)---数据段

402d8000-402da000 rwxp 00000000 00:00 0

...

be9ce000-be9ef000 rwxp 00000000 00:00 0          [stack]

ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]

#

查看进程的虚拟地址空间是如何使用的。

该文件有6列,分别为:

地址:库在进程里地址范围

权限:虚拟内存的权限,r=读,w=写,x=,s=共享,p=私有;

偏移量:库在进程里地址范围

设备:映像文件的主设备号和次设备号;

节点:映像文件的节点号;

路径: 映像文件的路径

每项都与一个vm_area_struct结构成员对应

0 0