在32位Windows中,DLL是如何导出的?

32 位 Windows 的设计者不必担心将所有内容压缩到 256KB 的内存中。由于 Win32 中的模块基于需求分页,因此你所要做的就是将整个映像映射到内存中,然后运行访问所需的部分。

DLL中的常驻名(resident name)和非常驻名(non-resident name)之间没有区别,导出函数的名称仅存储在 DLL 文件中,并带有指向导出表中存储的名称的指针(即相对虚拟地址)。

与 16 位序号导出表不同,32 位序号导出表不是稀疏的。

例如,如果 DLL 导出两个函数,一个作为序号 10,一个作为序号 1000,你将有一个 991 条目表,该表由两个实际函数指针和大量零组成。
因此,应尽量不要在序号导出中出现较大的间隙,否则将浪费 DLL 的导出表中的空间。

导出的名称表的功能与 16 位 Windows 中导出的名称表相同,将名称映射到序号。
与顺序无关的 16 位命名导出表不同,32 位 Windows 中的导出名称表保持排序,以便可以使用更有效的二进制搜索来查找函数。
与 16 位 Windows 一样,每个命名函数都分配有一个序号。

如果程序员未在模块定义文件中分配一个序号,则链接器将为你创建一个,与 16 位 Windows 一样,链接器构成的值可能因生成而异。

但是,这两种模型之间存在主要区别:回想一下,我们不鼓励在 16 位 Windows 中进行命名导出(出于效率原因),因此,每个导出的函数都被显式分配了一个序号,这是链接到函数的首选方式。

另一方面,32 位 Windows 中的命名导出是常态,没有显式序号分配。这意味着命名导出的序号不是固定的。

例如,让我们看一下早年分配给 kernel32 函数 LocalAlloc 的序数:

>> 请移步至 topomel.com 以查看图片 <<

现在,有些人习惯于对导入库进行逆向工程,可能是因为他们懒得下载平台SDK并获取真正的导入库。

手动生成导入库的问题在于,你无法判断分配给 LoadLibrary 函数的序号是由模块定义文件分配的(因此不会在生成之间更改)还是仅由链接器自动生成(在这种情况下,序号将更改)。

导入库生成工具可以安全地使用命名导出,因为这在这两种情况下都有效,但由于某种原因,它们使用序号导出。(这可能是 16 位 Windows 的遗留物,正如我们之前看到的,序数优先于名称。)

导入库生成工具的这种设计方式,给 DirectX 团队带来了兼容性问题。(我不知道为什么 DirectX 比其他团队受到的打击更大。也许是因为游戏开发人员没有时间学习 Win32 的细节,他们只是想写他们的游戏吧)

由于他们使用了这些工具之一,他们最终通过序号而不是名称链接到 DirectX 函数,如 DirectDrawCreate,然后当下一个版本的 DirectX 出现并且链接器为名称分配了不同的序号时,他们的程序就会崩溃。

DirectX 团队必须返回到旧的 DLL,记下链接器随机分配的所有序号,并在模块定义文件中显式分配这些序号,以便它们将来不会再修改。

无法从 DLL 生成导入库还有其他原因,稍后,当我更详细地讨论导入库时,我将讨论这些主题。

总结

如果你是在 32 位 Windows 平台上做开发,则始终使用名称,而不是序号进行 DLL 调用,这样会免去很多麻烦。
毕竟,写入到 SDK 中的名称,不太可能随随便便的改变。

最后

Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《How are DLL functions exported in 32-bit Windows?》

在32位Windows中,DLL是如何导出的?_第1张图片

你可能感兴趣的:(OldNewThing,windows,软件开发,系统设计)