如果从.dll 导入函数,为什么我们需要.lib 文件?

Why do we need .lib file in case of importing functions from .dll?

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

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

能否请您更详细地解释一下.lib文件中存储了什么样的数据。
而且,是的,我知道,visual studio 迫使我们将 .lib 文件添加到附加依赖项部分,但为什么它真的需要它们?

当您的源代码静态调用导出的 DLL 函数,或静态访问导出的 DLL 变量时,这些引用将编译到您的 executable 的中间目标文件作为指针,其值填充在 run-time.

当链接器组合 compiler-generated 目标文件以生成最终的 executable 时,它必须弄清楚所有 compiler-generated 引用实际指的是什么。如果它无法匹配对您的 executable 中某段代码的给定引用,则需要将其与外部 DLL 匹配。所以它需要知道要查看哪些 DLL,以及这些 DLL 如何导出内容。 DLL 可以按名称或按序号导出给定的 function/variable,因此链接器需要一种方法将代码引用使用的标识符映射到 EXPORTS table 中的特定条目特定的 .dll 文件(尤其是在按序号导出内容的情况下)。 Static-link .lib 文件为链接器提供映射信息(即 FunctionA 映射到 DLL XYZ.dll 中的序号 123FunctionB 映射到名称 _FunctionB@4 在 DLL ABC.dll 等)。

链接器然后可以使用有关所需的适当 EXPORTS 条目的信息填充您的 executable 的 IMPORTS table,然后在您的代码指向正确的 IMPORTS 条目(如果链接器无法解析对您的 executable 中的一段代码的 compiler-generate 引用,或者特定的 DLL 导出,它会中止"unresolved external" 错误)。

然后,当您的 executable 在 run-time 加载时,OS 加载程序查看 IMPORTS table 以了解哪些 DLL 导出是需要,因此它可以将适当的 DLL 加载到内存中,并使用基于每个 DLL 的 EXPORTS table 的实际内存地址更新 IMPORTS table 中的条目(如果引用的 DLL 无法加载,或者如果找不到引用的导出,OS 加载程序将中止加载您的 executable)。这样,当您的代码调用 DLL 函数或访问 DLL 变量时,这些访问会到达正确的位置。

如果您的源代码动态 通过显式调用 run-time 处的 GetProcAddress() 访问 DLL functions/variables,情况就会大不相同。在这种情况下,这些访问不需要 static-link .lib 文件,因为您自己的代码正在处理将 DLL 加载到内存中并定位它想要使用的导出。

但是,还有第三个选项将上述情况混合在一起:您可以编写代码来访问 DLL functions/variables 静态 但使用链接器的 delay-load 特征(如果有的话)。在这种情况下,对于您访问的每个 delay-loaded DLL,您仍然需要 static-link .lib 文件,但是链接器会在您的 executable 引用 DLL 导出,而不是填充 IMPORTS table。它将 compiler-generated DLL 引用指向编译器 RTL 中的存根,当在 run-time 首次访问存根时,它将用 GetProcAddress() 中的地址替换引用,从而避免需要OS Loader 在 load-time 填充的引用。这允许您的 executable 正常地 运行 即使 DLL 导出不存在于 load-time,并且如果从未使用过它们甚至可能根本不需要加载 DLL(当然,如果您的 executable 确实尝试动态访问 DLL 导出但加载失败,您的代码可能会崩溃,但这是一个单独的问题)。

I've heard, that it contains a list of the exported functions and data elements from the corresponding dll, but when I used CFF Explorer to explore my dll, I found out that dll already has addresses of exporting functions so I theoretically can link my program with .dll without any additional files.

作为为什么这不能总是起作用的一个简单例子,考虑一个访问两个 DLL 的可执行文件,一个用于 Winsock 过滤器,另一个用于分配器。并假设在这台特定的机器上,Winsock 过滤器 DLL 恰好也实现了具有相同 API 的分配器,并且分配器 DLL 恰好也实现了具有相同 API 的 Winsock 过滤器。编译器如何知道从哪个 DLL 访问哪个 API 函数?库文件包含访问DLL的intent,即API和你要访问的函数

重要的是,没有"The corresponding DLL"这样的东西。不同系统上可能有不同的 DLL 文件。链接器需要知道的是它可以依赖的 DLL 应该是什么样子,而不是您可能碰巧在某些特定系统上使用的 DLL 可能碰巧是什么。

例如,假设 DLL 文件包含一个分配器。您可能有一个用于调试的分配器的 DLL 文件,一个用于针对特定 CPU 版本进行优化的分配器,一个用于使用新的实验算法的分配器。链接器需要知道的是所有这些DLL文件实现的API,而不是任何一个文件中的具体实现。

您可以 produce a LIB file from a DLL file 但您最终可能会构建一个在使用其他版本的 DLL 文件时无法运行的可执行文件。您必须假设这个特定的 DLL 碰巧做的事情恰恰是实现相同 API 的所有其他 DLL 碰巧做的事情。