本文主要是解决大家对于应用程序使用 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 的过程大概就是这样了,也不知道对大家会不会有帮助,但是做为我自己的学习笔记是可以了。