Windows 下的动态链接
普及:Windows 下的 DLL 文件和 EXE 文件实际上是一个概念,都是 PE 格式 的二进制文件,扩展名并不唯一:.dll 和 .ocx 或是 .cpl 都是 dll 文件,软件更新包(Service Packs)机制就是通过升级 DLL 的形式进行自我完善
普及:当一个 dll 被装载到内存中之后,通常有两个地址概念分别是:基地址(Base Address) 和 相对地址(RVA, Relative Virtual Address),基地址就是被装载的起始地址,RVA地址就是 地址 + 基地址
普及:dll 实现进程共享数据段,不推荐,存在一定的安全风险,应该尽量 避免 这种 dll 共享数据段来实现进程间通信
普及:声明 dll 中的某个函数为 导出函数 的办法有两种:一种是使用 __declspec(dllexport) 扩展,另一种是采用模块定义(.def)文件声明
普及:dll 支持显示运行时链接,提供了 3 个 API 分别是:LoadLibrary,GerProcAddress,FreeLibrary
符号导入导出表
- 导出表,EXP文件,导出重定向,导入表,导入函数调用,这些都是 dll 中存在的结构和方式,均与符号和函数相关
DLL优化
应用程序在加载 dll 导致启动速度变慢,这里主要有两个原因:1. dll 的代码段和数据段本身并不是地址无关的,当被装载的目标地址被占用时,整个 dll 便会引起 rebase,频繁的 rebase 情况更加糟糕; 2. 导入函数的符号在运行时需要被 逐个查找解析,这里用的是二分查找,整个过程随着 dll 数量的增加,也是非常耗时的
重定基地址(Rebasing):只需要重新定位基地址即可,其余的 RVA 只是的偏移量,不受影响,当且仅当 dll 被装载时,基地址被占用的情况下,才会发生 重定位,所以 windows 系统为本身自带的许多库单独划分了一段空间区域(0x70000000 ~ 0x80000000),用于映射这些系统常用的 dll
dll 绑定:把导出函数的地址保存到模块的导入表中,省去了每次启动时符号解析的过程,根本优化在于:当程序每次运行时,所有被依赖的 dll 都会装载,并且一些列的导入导出符号依赖关系都会 被重新解析,大多数情况下,这些 dll 都会 以同样的顺序 被装载到 同样的内存地址,所以他们的到处符号的地址都是不变的
在使用 C++ 时需要注意的动态链接事项
所有接口函数都应该是抽象的,所有的方法都应该是纯虚的
所有的全局函数都应该是用 extern “C” 来防止名字修饰的不兼容,并且导出函数都是应该是 __stdcall 调用规范的
不要使用 C++ 标准的 STL
不要使用异常
不要使用虚析构函数
不要在 dll 里面申请内存,而且在 dll 外释放(或者相反)
不要再接口中使用重载方法
DLL HELL:主要是更新过程中的兼容性问题,解决方法如下:
静态链接
防止 dll 覆盖(DLL Stomping)
避免 dll 冲突(Conflicting DLLs)
应用程序使用 Manifest 文件来打包,控制自身依赖的 dll