C/C++程序编译与链接(三) 动态库的概念


动态库的根本目标与静态库一样,是为了达到代码重用。但是它们方式有本质区别。
静态库是在程序生成期间被直接链接进程序,所有需要的符号都归为可执行程序,所以链接了静态的可执行程序会变大,并且执行程序运行时不再需要额外的库。
动态库重用的阶段是在执行程序加载期,动态库是与执行程序是分离的,所以动态库并不影响执行程序的大小,动态库可以更新而不影响客户程序(只要符号未变)。可执行程序在被加载到内存时,由加载器将执行程序的符号解析到正确的地址上,这个地址是动态库所映射到进程内存映射中的地址。

动态链接

构建动态库

相比于静态库的构建,构建动态库的过程是一个完整的构建过程,包含编译,链接。
构建动态库生成的二进制文件本质上与可执行文件是相同的,唯一的区别在与动态库缺少了让其独立执行的启动程例程。

生成动态库1.png

Linux中libc(C运行时库),就可以直接运行。在Linux,我们可以修改动态库以使其可以独立运行。

链接到客户程序

链接动态库时,链接器不会对动态库的二进制接口(符号)进行任何检查,它不会查找节或节的大小,也不会将节拼接到最终的二进制文件中。
链接器只会检查二进制文件中所需的符号是否都在动态库中找到。一旦找到了所有的符号,链接器就会完成任务并创建可执行文件。所谓只是认为"所有的符号都能够被正确解析"。

运行时装载和符号解析

程序装载过程中所执行的操作非常重要,因为在这个阶段将检验经过链接器链接的动态库能否正常工作。
在之前的构建过程中已经对动态库二进制文件的副本中可执行文件所需的符号进行了校验。
现在在运行时需完成下面的操作:

  1. 可执行程序要先找到动态库二进制文件的位置。每一种操作系统都有一组规则来规定装载器查找动态库的二进制文件的路径。

  2. 进程需要将动态库载入内存映射中,这时须确保构建阶段时链接中的承诺能在运行时完全满足。事实上,在运行时加载动态库就必须包含"相同"的构建阶段所需的符号。更准确地说,对于函数符号而言"相同"是指运行时动态库中的函数符号必须与构建阶段中的完整函数签名(从属关系,函数名,参数列表和链接与调用惯例)相同。

  3. 运行时需要将可执行程序的符号正确解析到正确的地址上,这个地址是动态库所映射到进程内存映射中的地址。

动态库使用

动态库是操作系统的基石,在操作系统中很多功能模块被组织成了动态库,在程序运行时,被加载器按需加载进程序内存空间。
但是动态库的使用比静态库麻烦些,包括基本的两点:

  • 动态库的位置

对一些核心动态库(比如libc.so,libstd++.so等),这些都不是问题,因为这些库的位置,都已经被预先内置到系统中。加载器都会优先去这些固定的路径去寻找。
但是对我们自己依赖动态库的程序,要想运行必须有两点:1. 操作系统中有程序需要的动态库。2. 为加载器指定动态库所在的路径。
当然加载器查找动态库的路径是有规则的。

  • 动态库的版本

版本的区别对应客户程序来说就是体现在符号的差异上。
与路径一样,对操作系统自身的功能动态库,版本都是适配于操作系统的最合适的版本。
动态库的版本命名也有规则,加载器按照此规则,加载对应版本的动态库。如果操作系统内有多个版本的库,了解此规则至关重要。

猜你喜欢

转载自blog.csdn.net/mo4776/article/details/129354093