【问题标题】:LoadLibrary seemingly loading wrong DLLLoadLibrary 貌似加载了错误的 DLL
【发布时间】:2012-06-19 12:24:46
【问题描述】:

我在使用 Windows 上的 LoadLibrary 时遇到了一个奇怪的问题。首先是一些背景。此应用程序依赖于 Qt,并且 Qt 被拆分为多个库。我正在尝试升级 Qt 的版本,但不会破坏任何人。较新的 Qt 库向后兼容旧的库。这意味着使用旧版本构建的应用程序可以在加载新版本时运行。反之则不然——如果加载旧版本,使用新版本构建的应用程序将缺少符号。

Qt DLL 位于特定于版本的目录中(例如 c:\qt\qt-4.5.2\libc:\qt\qt-4.8.1\lib 示例)。大多数开发人员在他们的 PATH 中还有一个公共目录,其中包含我们使用的所有第三方库的“当前”版本(称为 c:\common\lib)。这是运行应用程序时通常可以找到 Qt 库的地方。

我将新的 Qt 版本库放在公共位置,一切似乎都工作正常,除了一个案例。有问题的应用程序分为多个库,其中一些是通过调用LoadLibrary() 加载的。其中一些运行时加载的 DLL 依赖于 Qt 库。在一种情况下,加载的 DLL 依赖于 QtXml,而 QtXml 本身又依赖于 QtCore

这就是奇怪的地方。一个应用程序依赖于QtCore,还加载了一个依赖于QtXml 的库。应用程序和库是与旧版本的 Qt 链接构建的。如果此应用程序仅使用 PATH 中的公共目录运行,则一切正常,因为新的 Qt 版本 DLL 是从公共目录加载的。但是,如果 PATH 包含存储旧 Qt 版本 DLL 的目录公共目录之前,则加载运行时 DLL 会失败并丢失符号。

(在进行自动化单元测试时会出现这种情况,脚本显式设置 PATH 以使用特定的库版本。)

据我所知,应用程序正在加载旧版本的 QtCore.dll,而运行时加载的 DLL 正在(不知何故)加载新版本的 QtXml.dll,因为已经加载的 QtCore 没有没有它需要的符号。

但这似乎是不可能的,因为 PATH 类似于 c:\qt\qt-4.5.2\lib;c:\common\lib(加上其他不相关的路径)。如果我删除较新的QtXml 从公共 lib 目录(不替换为旧版本,只需将其删除),那么 LoadLibrary() 成功,因为它加载了所有的 4.5.2 版本Qt 库。但这不是一个好的长期解决方案,因为在 PATH(常见)中没有 Qt 特定版本目录的情况下运行将无法找到 QtXml

这怎么可能? LoadLibrary()(或任何它递归调用以解决库的依赖项)如何从PATH 中的稍后 加载库?我找不到任何表明公共库目录被特别考虑的东西(它不是一个设置的 DLL 目录)。在构建过程中没有提到它,只是为了方便开发人员在他们的PATH 中提供的。

顺便说一句,LD_LIBRARY_PATHdlopen() 在 Linux 上也存在类似的情况,它在那里工作得很好。这是 Windows 正在做的不同的事情,我不明白。有没有人知道可能出了什么问题?

【问题讨论】:

  • 每个 DLL Hell 问题都是在您将 DLL 存储在一个公共目录中时产生的,在您开始依赖 PATH 环境变量时长大,并在您开始混合版本时使您的房子着火。不要共享 DLL。
  • 在已安装的系统中,这不会是问题,因为构建版本将与应用程序位于同一位置。这实际上只是一个开发问题,可以同时使用多个版本的第三方库。

标签: c++ windows loadlibrary


【解决方案1】:

LoadLibrary 有很多令人惊讶的行为。确保您完全了解 MSDN 中的所有 Remarks

如果已经加载了同名的库(任何版本),LoadLibrary 只会返回已经加载的 DLL 的句柄。这可能会在您的场景中发挥作用。

接下来,如果您指定了相对路径或仅指定了文件名,LoadLibrary 将应用 arcane search rules。您的 PATH 变量通常是最后搜索的地方。其他一些规则很可能在检查 PATH 之前就找到了“错误”的 DLL。一个好的指导原则是始终使用要加载的文件的绝对路径,以确保其搜索规则不会抓取错误的文件。一个常见的安全漏洞是无法控制 LoadLibrary 搜索的位置,并且攻击者会诱使您的应用程序加载经过修改的 DLL。

最后,安装程序可以应用DLL redirection,它可以覆盖您的要求以及可能在哪里找到它。我不确定这对于 Qt DLL 是否常见,但您可能需要检查您的注册表。

我偶尔使用ProcMon from SysInternals 来观察加载DLL 的程序。您可以看到它检查的每个地方,这可能会为您提供有关它为什么会找到错误版本的线索。

【讨论】:

  • “如果已经加载了同名的库(任何版本),LoadLibrary 只会返回已加载 DLL 的句柄。这可能会在您的场景中发挥作用。” - 哇,你能指出这是记录在哪里吗?我没有看到它在备注中明确列出。有没有可用的解决方法?难道只是用文件名来匹配吗?
  • 我猜它确实说:“如果 lpFileName 不包含路径并且有多个加载的模块具有相同的基本名称和扩展名,则该函数返回第一个加载的模块的句柄。”然而,这同样适用于完整路径。如果加载了完整路径,即使 dll 被移动并替换为新版本,也会返回现有句柄。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多