8. Windows下的动态链接【《链接、装载与库》学习笔记】

DLL

DLL(Dynamic-Link Library),windows系统中大量采用dll机制,包括windows内核的结构也很大程度依赖dll机制。

dll其实和exe时一个概念,都是有PE格式的二进制文件。dll的后缀名也可能是.ocx(OCX控件)或CPL(控制面板程序)。

当一个PE文件被装载时,其进程地址空间中的起始地址就是基地址。对于任何一个PE文件来说,都有一个优先装载的基地址,也就是PE文件头中的Image Base。如果基地址被占用,会选用其他空闲地址,产生一个相对地址(RVA,对基地址的偏移)。

DLL共享数据段

windows提供了一系列系统API可以实现IPC,其中一种方法是使用dll。可以设置dll为共享的(更常见的是设一个数据段作为进程共享的),但是这种方法有安全问题,应避免。

elf默认导出所有的全局符号,但是DLL需要声明导出内容,告诉程序dll导出的符号的这个过程叫导入

在MSVC中,使用__declspec(dllexport)表示该符号是从本DLL导出的,__declspec(dllimport)表示该符号是从别的DLL导入的符号。也可以使用.def文件直接声明。

可以使用dumpbin看到DLL的导出符号:

1
dumpbin /EXPORTS Math.dll

.lib文件是一组目标文件的集合,它并不真正包含代码和数据,用来描述dll的导出符号,和一些胶水代码。这样的文件被称为导入库

DLL运行时链接

windows提供了3个API:

  • LoadLibrary:装载一个dll到进程的地址空间,类似dlopen
  • GetProcAddress:用来查找某个符号的地址,类似dlsym
  • FreeLibrary:用来卸载某个已加载的模块,类似dlclose

符号导出导入表

当一个PE需要将一些函数或变量提供给其他PE使用,这种行为叫符号导出。所有被导出的符号被集中放在导出表中。导出表结构中,最后的3个成岩指向3个数组:

  • 导出地址表(EAT):存放各个导出函数的RVA
  • 符号名表(Name Table):保存名字
  • 名字序号对应表(Name-Ordinal Table):“序号“(Ordinals):函数名的下标,省去函数名查找过程,节省内存。

exp文件时链接器在创建dll的临时文件,是一个标准的PE/COFF目标文件。

DLL 优化

重定基地址(Rebasing)

windows采用装载时重定位的方法:在dll模块被装载时,如果目标地址被占用,操作系统为他分配一块新的空间,更改RVA信息。重定位信息放在.reloc段。

windows会在进程空间中专门划出一块区域用于映射常用的系统dll。

现在的dll中,导出函数表的函数名是经过排序的,查找可以二分。

导入函数绑定

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

同时使用时间戳和校验和检查是否失效。

C++与动态链接

为了解决C++编写DLL的兼容性问题,微软开始组件对象模型(COM,Component object model)

需要尽量遵循以下指导意见:

  • 所有接口函数都应该是抽象的,所有的方法都应该是纯虚的
  • 所有全局函数都应该使用extern "C"
  • 不要使用stl
  • 不要再dll内申请内存,而是在dll外释放。

DLL Hell

dll的不兼容问题,加上windows缺乏dll版本控制机制,引发了dll噩梦。

可以使用防止dll覆盖,避免dll冲突等方法避免。

在.NET框架下。一个程序集(Assembly)有两种类型:应用程序程序集(exe)以及库程序集(dll)。Manifest文件是用来描述程序集的清单文件,是一个xml的描述文件,每个dll都有。


8. Windows下的动态链接【《链接、装载与库》学习笔记】
http://blog.bluspace.ren/2025/12/08/8. Windows下的动态链接【《链接、装载与库》学习笔记】/
作者
Blauter
发布于
2025年12月8日
许可协议