本文主要是解决大家对于应用程序使用 dll 的幕后细节进行讲解,其他很多细节在此不再多提,假设读者已经明白如何编写 dll ,若对此不甚明白可参看下文:
http://www.codeproject.com/KB/cpp/howto_export_cpp_classes.aspx
动态链接库的生产过程
1. 生成 lib 文件
2. 生成 dll 文件
应用程序载入 dll 过程
1. 链接
2. 载入 dll
3. 修复导入符号的地址
1. 生成 lib 文件
dll 生成的过程中会 将该 dll 要导出的符号,包括函数名,变量名,类等名称 输出到相应的 lib 文件的 Exports 段中,使用 visual studio 自带的 dumpbin, 使用 exports 参数输出 lib 的 exports 段,即可输出如下信息:
dumpbin –exports xyzLibrary.lib
图 1. Lib 文件 dump 输出的 Exports 段
该 lib 文件 exports 段显示,导出了两个符号,即 GetXyz 和 _GetXyz@0
2. 生成 dll 文件
当然动态连接库的实际内容都是包含在 dll 文件中的,而为了使用户能够使用 dll 中的函数,变量,以及类等信息, dll 会在文件的 exports 段中列出将该 dll 要导出的符号,包括函数名,变量名,类等名称及其相对偏移地址。
如 dumpbin -exports xyzLibrary.dll 输出信息如下:
图 2.dll 文件 dump 输出的 exports 段
输出信息显示,该 dll 导出了两个符号, GetXyz, 偏移地址为 00001030 ,以及 _GetXyz@0, 偏移地址为 00001030 ,两个符号偏移地址一样,说明这两个符号是同一个函数,只是导出了两个名字而已。
应用程序载入 dll 过程 :
1. 链接
链接过程中,编译器会根据符号去应用程序的依赖的 lib 文件中去找,若找到该符号就把相应的 dll 文件名和符号名写入 exe 的 imports 段中,若没找到,编译器就会报 unresolved extern symbols 之类的错误,最后编译成功的 exe 文件的 imports 段是该 exe 依赖的 dll 和符号列表, 使用 dumpbin 输出 xyzExecutable.exe 的 imports 段信息如下:
Dumpbin -imports xyzExecutable.exe
图 3.exe 文件的 imports 段信息
信息显示该 exe 从 xyzLibrary.dll 文件中导入了 _GetXyz@0 符号。
2. 载入 dll
当 exe 被装载程序载入内存,并创建进程后,装载程序会将 exe 依赖的 dll 也载入程序的虚拟地址空间中,先会按照一个顺序扫描寻找该 dll 文件,具体顺序大概是先系统目录,再当前目录,若扫描完找不到 dll 文件,则会弹出对话框报错,如下图所示:
图 4. 应用程序找不到依赖的 dll 出错窗口
当然这个载入过程是会嵌套的,比如程序 a 导入了 dll_b, 但是 dll_b 还可能依赖 dll_c, 这样载入器在载入 dll_b 时也会将 dll_c 也载入的。
3. 修复导入符号的地址
装载程序找到这个 dll 后,将其载入到相应进程的地址空间,然后就要去 dll 的文件的 exports 段搜寻所依赖的符号,然后计算该符号的绝对地址,即 dll 的地址 + 符号相对 dll 首地址的相对地址,再使用这个符号去修复 exe 中对该符号的所有引用,但是如果在 dll 中没有找到相应符号,则会弹出以下错误窗口:
图 5. 应用程序在相应 dll 中找到不到相应的符号出错窗口
dll 的生成和应用程序载入 dll 的过程大概就是这样了,也不知道对大家会不会有帮助,但是做为我自己的学习笔记是可以了。