深入理解计算机系统:链接(第三章:动态链接、运行时加载共享库、库打桩)

版权声明:转载请说明来源 https://blog.csdn.net/weixin_39640298/article/details/88937349

1、概述

前面整理了,静态连接库、可重定位目标文件、可执行文件等,还有动态库的部分没有整理。今天就把欠的账给补上。

静态库前面整理了很多,但是它有明显的缺点:其一是如果静态库更新,则使用它的用户也需要跟着更新;其二如果多个程序都使用这个静态库,那么需要把静态库的内容复制到程序中,造成了系统资源的浪费。为了解决这个问题,而产生了共享库。

2、共享库(动态连接库)

共享库是为了解决静态库缺陷的一个现代创新产物。共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接起来。这个过程叫做动态链接,是由一个叫做 动态链接器 的程序来执行的。在Linux系统中通常用.so后缀来表示,在Windows系统中,用.dll后缀来表示,叫做动态链接库。

共享库是以两种不同的方式来 “共享” 的。首先,在任何给定的文件系统中,对于一个库只有一个.so文件。所有引用该库的可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库的内容那样被复制和嵌入到引用它们的可执行文件中。其次,在内存中,一个共享库的.text节的一个副本可以被不同的正在运行的进程共享。

使用动态库生成可执行文件的过程中,静态的执行一部分链接,然后在程序加载时,动态完成剩余部分的链接过程。没有任何的动态库代码和数据节真的被复制到可执行文件中,而是,复制了一些重定位和符号表信息,它们使得运行时可以解析对动态库中代码和数据的引用。

3、从应用程序中加载和链接共享库

应用程序还可以在它运行时要求动态链接器加载和链接某个共享库,而无需在编译时将那些库链接到应用中。

动态链接是一项强大有用的技术。下面是一些现实世界中的例子:
分发软件。微软Windows应用的开发者常常利用共享库来分发软件更新。它们生成一个共享库的新版本,然后用户可以下载,并用它替代当前的版本。下一次它们运行应用程序时,应用将自动链接和加载新的共享库。
构建高性能Web服务器。许多Web服务器生成动态内容。比如个性化的Web页面、账户余额和广告标语。早期的Web服务器通过使用fork 和 execve 创建一个子进程,并在该子进程的上下文运行CGI 程序来生成动态内容。然而,现代高性能的Web 服务器可以使用基于动态链接的更有效和完善的方法来生成动态内容。
其思路是将每个生成动态内容的函数打包在共享库中。当一个来自Web浏览器的请求到达时,服务器动态地加载和链接适当的函数,然后直接调用它,而不是使用 fork 和 execve 在子进程的上下文中运行函数。函数会一直缓存在服务器的地址空间中,所以只要一个简单的函数调用的开销就可以处理随后的请求了。这对一个繁忙的网站来说是由很大影响的。更进一步说,在运行时无需停止服务器,就可以更新已存在的函数,以及添加新的函数。

Linux为运行时加载动态链接库提供了接口,分别是:dlopen、dlsym、dlclose
Windows为运行时加载动态库提供的接口,分别是:LoadLibrary、GetProcAddress、FreeLibrary

4、位置无关代码

多个进程是如何共享一个动态库的副本呢?现代系统使用一种方法来编译共享模块的代码段,使得可以把它们加载到内存的任何位置而无需链接器修改。

这种加载而无需重定位的代码称为 位置无关代码(Position-Independent Code, PIC)。用户对GCC使用 -fpic 选项只是 GNU 编译系统生成 PIC 代码。共享库的编译必须总是使用该选项。

5、库打桩机制

Linux链接器支持一个很强大的技术,称为 库打桩 ,它允许你截获对共享库函数的调用,取而代之执行自己的代码。使用打桩机制,你可以追踪对某个特殊库函数的调用次数,验证和追踪它的输入和输出值,或者甚至把它替换成一个完全不同的实现。

下面是它的基本思路:给定一个需要打桩的目标函数,创建一个包装函数,它的原型与目标函数完全一样。使用某种特殊的打桩机制,你就可以欺骗系统调用包装函数而不是目标函数了。包装函数通常会执行它自己的逻辑,然后调用目标函数,再将目标函数的返回值传递给调用者。

打桩可以发生在编译时、链接时或当程序被加载和执行的运行时。

至于怎么使用,需要各位自己去发现了,我也不知道,但是感觉有点像windows下的钩子函数。

感谢大家,我是假装很努力的YoungYangD(小羊)

参考资料:
《深入理解计算机系统》

猜你喜欢

转载自blog.csdn.net/weixin_39640298/article/details/88937349