《程序员的自我修养》笔记七:Windows动态链接

背景

DLL 相当于 Linux 下的共享对象。PE 格式的二进制文件。

Windows 系统中大量采用 DLL 机制,包括内核、软件更新包、ActiveX 技术等。


DLL (Dynamic-Link Library)

DLL 即动态链接库的缩写,

进程拥有独立的地址空间,当某个 DLL 被加载到地址空间中,所有的程序都可以共享这个 DLL。一个 DLL 在不同的进程中拥用不同的私有数据副本。

基地址和 RVA

DLL 的代码段不是地址无关的

  • 基地址

PE 文件被装载时,其进程地址空间中的起始地址就是基地址。每个 PE 文件都有一个优先装载的基地址。

  • 相对地址 RVA

Windows 装载 DLL 时,先尝试把他装载到基地址,若该地址区域被占用,选择其他空闲地址。相对地址就是一个地址相对于基地址的偏移

DLL 共享数据段

Windows 允许将 DLL 数据段设置成共享,即任何进程都可以共享该 DLL 的同一份数据段

  • DLL 共享数据段可以实现进程间通信
  • 安全漏洞很大
  • 尽量避免使用 DLL 共享数据段来实现进程间的通信

导出/导入

与 ELF 文件不同,DLL 默认情况下所有的符号都不导出,需要显式的指明要导出的符号,在应用程序中使用 DLL 导出的符号过程,被称为导入。

  • 『__declspec(dllexport)』 导出关键字
  • 『__declspec(dllimport)』 导入关键字
  • 使用『.def』 文件来声明导入导出符号
符号导出导入表

当一个 PE 文件需要将一些函数或变量提供给其他 PE 文件使用时,这种行为叫做符号导出。

  • 导出表

所有导出的符号被击中存放在导出表中,提供符号名与符号地址的映射关系。

  • 序号
  • EXP 文件

创建 DLL 的同事也会得到一个 EXP 文件,是链接器在创建 DLL 时的临时文件。

  • 导入表

某个程序使用到了 DLL 的函数或变量,叫做符号导入

  • 延迟载入

等你链接一个支持延迟载入的 DLL 时,链接器会产生与普通 DLL 导入非常类似的数据,但操作系统会忽略这些数据。


DLL 优化

影响 DLL 性能的两个原因:

  1. 频繁的 Rebase 地址(基地址被占用)
  2. 大量的符号解析

优化方式:

  • 装载时重定位(与 ELF 相比,空间换时间)

在 DLL 模块装载时,如果目标地址被占用,操作系统为它分配新的空间,DLL 所涉及到的绝对地址引用都进行重定位。(需要重定位的地址只要加上一个固定差值即可)

  • 系统 DLL

Windows 系统在进程空间中专门划分一块区域,映射常用的系统 DLL,装载它们时不需要重定位了

  • 序号

一个 DLL 中每个导出的函数都有一个相应的序号,序号标示被导出函数地址在 DLL 导出表中的位置。导入时可以使用函数名也可以使用函数序号。(不推荐使用函数序号)

  • 导入函数绑定

大多数情况下, DLL 会以同样的顺序被装载到同样的内存地址,导出符号的地址不变,通过将导出函数的地址保存到模块的导入表中,可以省去每次启动时符号解析过程,被称为 DLL 绑定 (DLL Bingding)。​

editbin 工具

对被绑定的程序的导入符号进行遍历查找,找到后把符号的运行时目标地址写入到被绑定程序的导入表内。


C++ 与动态链接

C++ 的动态链接库是一场噩梦,根源是 C++ 的标准值规定了语言层面的规则,对二进制级别没有任何规定

DLL HELL (DLL 噩梦)

由于 DLL 数量、版本众多,相互之间的调用极容易发生问题:

  • 使用旧版的 DLL 替换原来一个新版的 DLL 引起的
  • 新版 DLL 中的函数无意发生改变引起的,未能保证向下兼容
  • 新版 DLL 的安装引入一个新的 BUG

解决方法

  • 静态链接(逗逼)
  • 防止 DLL 覆盖
  • 避免 DLL 冲突

让每个应用程序拥有自己依赖的 DLL,把问题 DLL 的不同版本放到该应用程序的文件夹中,而不是系统文件夹中。装置 DLL 时,先从自己文件夹下寻找,再去系统文件中寻找

  • .NET 下 DLL Hell 解决方案

使用 Manifest 清单文件,描述程序集的名子,版本号和各种资源,运行依赖的所有资源。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《程序员的自我修养:链,装载与库》是一本由林锐、郭晓东、郑蕾等人合著的计算机技术书籍,在该书中,作者从程序员的视角出发,对链、装载与库等概念进行了深入的阐述和解析。 在计算机编程中,链是指将各个源文件中的代码模块组合成一个可执行的程序的过程。链可以分为静态链动态两种方式。静态链是在编译时将所有代码模块合并成一个独立的可执行文件,而动态是在运行时根据需要加载相应的代码模块。 装载是指将一个程序从磁盘上加载到内存中准备执行的过程。在装载过程中,操作系统会为程序分配内存空间,并将程序中的各个模块加载到相应的内存地址上。装载过程中还包括解析模块之间的引用关系,以及进行地址重定位等操作。 库是指一组可重用的代码模块,通过链和装载的方式被程序调用。库可以分为静态库和动态库。静态库是在编译时将库的代码链到程序中,使程序与库的代码合并为一个可执行文件。动态库则是在运行时通过动态的方式加载并调用。 《程序员的自我修养:链,装载与库》对于理解链、装载和库的原理和机制具有极大的帮助。通过学习这些概念,程序员可以更好地优化代码结构和组织,提高程序的性能和可维护性。同时,了解链、装载和库的工作原理也对于进行调试和故障排除具有重要意义。 总之,链、装载与库是计算机编程中的重要概念,对于程序员来说掌握这些知识是非常必要的。《程序员的自我修养:链,装载与库》这本书提供了深入浅出的解释和实例,对于想要学习和掌握这些知识的程序员来说是一本非常有价值的参考书籍。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值