首页 > 解决方案 > 如果从 .dll 导入函数,为什么我们需要 .lib 文件?

问题描述

你能帮我理解一下,为什么我们在从 dll 中导入函数和数据时需要 .lib 文件?

我听说,它包含一个从相应 dll 导出的函数和数据元素的列表,但是当我使用 CFF Explorer 探索我的 dll 时,我发现 dll 已经有导出函数的地址,所以我理论上可以链接我的带有 .dll 的程序,无需任何附加文件。

您能否更详细地解释一下 .lib 文件中存储了哪些类型的数据。
而且,是的,我知道,Visual Studio 强制我们将 .lib 文件添加到附加依赖项部分,但为什么它真的需要它们呢?

标签: c++visual-studiodlllib

解决方案


当您的源代码静态调用导出的 DLL 函数或静态访问导出的 DLL 变量时,这些引用将作为指针编译到可执行文件的中间对象文件中,其值在运行时填充。

当链接器组合编译器生成的目标文件以生成最终的可执行文件时,它必须弄清楚所有编译器生成的引用实际上指的是什么。如果它无法匹配对可执行文件中某些代码的给定引用,则需要将其与外部 DLL 匹配。所以它需要知道要查看哪些 DLL,以及这些 DLL 如何导出内容。DLL 可以通过名称或序号导出给定的函数/变量,因此链接器需要一种方法来将代码引用使用的标识符映射到EXPORTS特定文件表中的特定条目.dll(尤其是在事物由序数)。静态链接.lib文件为链接器提供映射信息(即映射到DLLFunctionA中的 Ordinal ,123XYZ.dllFunctionB映射到_FunctionB@4DLL中的名称ABC.dll等)。

然后,链接器可以IMPORTS使用有关所需适当EXPORTS条目的信息填充可执行文件的表,然后使代码中的 DLL 引用指向正确的IMPORTS条目(如果链接器无法解析编译器生成的对一段代码的引用在您的可执行文件或特定的 DLL 导出中,它会因“未解决的外部”错误而中止)。

然后,当您的可执行文件在运行时加载时,OS 加载程序会查看IMPORTS表以了解需要哪些 DLL 导出,因此它可以将适当的 DLL 加载到内存中,并IMPORTS使用实际内存地址更新表中的条目基于每个 DLL 的EXPORTS表(如果引用的 DLL 加载失败,或者引用的导出失败,OS Loader 将中止加载您的可执行文件)。这样,当您的代码调用 DLL 函数或访问 DLL 变量时,这些访问会转到正确的位置。

如果您的源代码在运行时通过显式调用动态访问 DLL 函数/变量,情况就大不相同了。在这种情况下,这些访问不需要GetProcAddress()静态链接文件,因为您自己的代码正在处理将 DLL 加载到内存中并定位它想要使用的导出。.lib

但是,还有第三个选项将上述场景混合在一起:您可以编写代码以静态访问 DLL 函数/变量,但使用链接器的延迟加载功能(如果有的话)。.lib在这种情况下,您仍然需要为您访问的每个延迟加载的 DLL 提供静态链接文件,但链接器DELAYLOAD会在可执行文件中使用对 DLL 导出的引用填充一个单独的表,而不是填充该IMPORTS表。它将编译器生成的 DLL 引用指向编译器 RTL 中的存根,这些存根将用来自GetProcAddress()当在运行时第一次访问存根时,从而避免了在加载时由 OS 加载器填充引用的需要。这使您的可执行文件即使在加载时不存在 DLL 导出也可以正常运行,并且如果它们从未使用过,甚至可能根本不需要加载 DLL(当然,如果您的可执行文件确实尝试访问 DLL 导出动态并且无法加载,您的代码可能会崩溃,但这是一个单独的问题)。


推荐阅读