Windows下的动态链接
DLL简介
DLL和EXE都是PE格式的二进制文件,不同的是PE头部有符号位表示该文件是EXE还是DLL。
DLL文件扩展名不一定是.dll,也可能是别的比如.ocx或.CPL
进程地址空间和内存管理
在32位Windows中开始支持进程拥有独立地址空间,一个DLL在不同的进程中拥有不同的私有数据副本。
ELF中代码是地址无关,所以他可以实现多个进程之间共享一份代码,但DLL的代码并不是地址无关,所以只能在某些情况下可以被多个进程间共享。
基地址和RVA
基地址就是进程的起始地址。对于PE来说,它都有优先正在基地址,这个值就是PE文件头中的Image Base。
RVA就是偏移地址。
DLL共享数据段
Win32下,系统提供给了一系列API可以实现进程间的通信。其中有一种方法是使用DLL来实现进程间通信。
正常情况下每个DLL的数据段在各个进程中都是独立的,每个进程都拥有自己的副本。但是windows允许将DLL的数据段设置成共享的。
DLL导入和导出
在ELF中共享库中所有的全局函数和变量默认可以被其他模块使用,就是说ELF默认导出所有的全局符号。但在DLL中不同,我们需要显示的告诉编译器我们需要导出某个符号,否则编译器默认所有符号都不导出。
当我们程序中使用DLL导出的符号时,这个过程被称为导入(Import)。
在DLL代码中声明:
__declspec(dllexport) 表示要导出该函数或变量。
在用户代码中声明:
__declspec(dllimport) 表示该符号是从DLL导入的。
DLL创建
创建一个我们自己的DLL:
testdll.c
1 | __declspec(dllexport) double Add(double a,double b) |
将其编译成.dll后缀文件:
1 | cl /LDd testdll.c |
DLL使用
调用dll中的函数:
1 | #include <stdio.h> |
编译和链接
测试:
DLL导入库
在静态链接时.lib文件是静态库,里面保存了数据和代码,用于和其他模块组合起来链接成EXE文件。
但在动态链接时.lib是导入库,它内部并不包含dll的代码和数据,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。
DLL 显示运行时链接
windows 提供了3个API:
- LoadLibrary 用于装载一个DLL到进程的地址空间。
- GetProcAddress 用来查找某个符号的地址。
- FreeLibrary 用来卸载某个已加载的模块。
导出表
当一个PE需要将一些函数或变量提供给其他PE文件使用时,我们把这种行为叫做符号导出(Symbol Exporting),最典型的情况就是一个DLL将符号导出给EXE文件使用。
在Windows PE中,所有导出的符号被集中存放在了被称为导出表的结构中。导出表从简单的结构上来看,它提供了一个符号名与符号地址的映射关系,可以通过某个符号查找相应的地址。