【问题标题】:Get reason that LoadLibrary cannot load DLL获取 LoadLibrary 无法加载 DLL 的原因
【发布时间】:2020-05-27 17:35:16
【问题描述】:

在 Linux 和 Mac 上,当使用 dlopen() 加载链接到另一个库的共享库时,如果由于缺少符号而导致链接失败,您可以使用 dlerror() 获取缺少符号的名称。它说类似

dlopen failed: cannot locate symbol "foo"

在 Windows 上,当使用 LoadLibrary() 加载缺少符号的 DLL 时,您只能从 GetLastError() 获取错误代码,对于此类问题,该代码始终为 127。我如何确定哪个符号是缺少,或者来自LoadLibrary() 的更详细的错误消息解释了函数失败的原因?

【问题讨论】:

  • 简短回答 - 你不能,因为 Windows 根本不提供那种细节。
  • 你知道Dependency Walker吗?
  • 提供细粒度的加载程序诊断,但默认情况下不启用。它们只对程序员有用,对最终用户没有用。谷歌“windows show loader snaps”来了解如何做到这一点。顺便说一句,几乎所有关于此错误的半体面的 Q+A 都记录了这一点,请务必在 Google 上搜索更多内容。
  • @HansPassant 你能发布一个链接到“关于这个错误的半体面问答”吗?如果它存在,我可能会找到它。

标签: c++ winapi loadlibrary


【解决方案1】:

我想出了一个使用MSYS2 终端的方法。其他方法可能适用于 GUI 软件。 一个主要的警告是,这不能在纯 C/C++ 中完成并为最终用户发布。它仅供开发人员使用,但总比没有好。

通过下载 Windows SDK 并取消选中除调试工具以外的所有内容来安装 Debugging Tools for Windows。 我可能是错的,但似乎安装此软件会在 Windows 内核中安装一个挂钩,以允许 LoadLibrary() 将详细信息写入标准错误。

以管理员身份打开MSYS2 Mingw64终端并运行

'/c/Program Files (x86)/Windows Kits/10/Debuggers/x64/gflags.exe' -i main.exe +sls

这会将以下内容打印到终端以确认注册表已更改。

Current Registry Settings for main.exe executable are: 00000002
    sls - Show Loader Snaps

如果您需要撤消,请使用 -sls 而不是 +sls,因为我相信更改会发生在全局 Windows 中所有名为 main.exe 的程序上,而不仅仅是您的文件。

然后运行main.exe 应该将调试信息打印到stderr,但由于我正在调试-mwindows 应用程序,它不适合我。

但由于某种原因,使用 MSYS2 的 gdb 运行二进制文件允许将此调试信息打印到 stderr。 使用 MSYS2 安装 mingw-w64-x86_64-gdb 并运行 gdb ./main.exe 并键入 runr。 搜索类似于以下内容的部分。

warning: 1ec8:43a0 @ 764081125 - LdrpNameToOrdinal - WARNING: Procedure "foo" could not be located in DLL at base 0x000000006FC40000.
warning: 1ec8:43a0 @ 764081125 - LdrpReportError - ERROR: Locating export "foo" for DLL "C:\whatever\plugin.dll" failed with status: 0xc0000139.
warning: 1ec8:43a0 @ 764081125 - LdrpGenericExceptionFilter - ERROR: Function LdrpSnapModule raised exception 0xc0000139
    Exception record: .exr 00000000050BE5F0
    Context record: .cxr 00000000050BE100
warning: 1ec8:43a0 @ 764081125 - LdrpProcessWork - ERROR: Unable to load DLL: "C:\whatever\plugin.dll", Parent Module: "(null)", Status: 0xc0000139
warning: 1ec8:43a0 @ 764081171 - LdrpLoadDllInternal - RETURN: Status: 0xc0000139
warning: 1ec8:43a0 @ 764081171 - LdrLoadDll - RETURN: Status: 0xc0000139

太棒了!它说Procedure "foo" could not be located in DLL,所以我们缺少符号,就像在 POSIX/UNIX 的dlopen() 中一样。

【讨论】:

  • 这需要使用gdb 可以处理的格式的调试符号编译目标EXE/DLL。不幸的是,并非所有适用于 Windows 的第 3 方 C/C++ 编译器都使用这种格式。
  • 加载程序快照诊断不会打印到标准错误。转到调试输出 (OutputDebugString),可以使用 Visual Studio 等调试器或DebugView 等独立工具进行观察。
  • 此功能并非 MSYS 终端独有。您可以从任何类型的命令提示符配置 gflags。您可以在调试器上观察任何类型的输出,而不仅仅是 GDB。
【解决方案2】:

虽然 Remy Lebeau 的答案在技术上是正确的,但在 Windows 平台上仍然可以通过 GetLastError() 确定丢失的符号。要了解究竟缺少什么,了解术语至关重要。

符号:

当一个 DLL 被编译时,它的函数被符号引用。 这些符号与函数名称直接相关(符号为 由可见和可读的字符串表示),它的返回类型,和 它的参数。这些符号实际上可以通过 文本编辑器虽然在大型 DLL 中很难找到。DLL Symbols - C++ Forum

缺少符号意味着无法找到其中的函数。如果在使用 GetProcAddress() 之前发生此错误,则可能由于缺少先决条件而无法加载任意数量的函数。这意味着您尝试加载的库可能还需要第一个无法加载的库。这些依赖级别可能会持续到未知数量的层,但 GetLastError() 可以确定的唯一答案是缺少符号。一种这样的方法是使用Dependency Walker 来确定第一个库所需的缺失库。一旦所有需要的库都可用并且可以被该库找到(可以是它自己的蠕虫罐),就可以通过 LoadLibrary() 加载该库。

【讨论】:

  • 您能否详细说明如何设置 Dependency Walker 来说明缺少哪个符号? “Dependency Walker 缺少符号”在 Google 中没有返回相关结果,并且 Dependency Walker 的手册中也没有提到“符号”这个词。
  • 我更新了我的评论以扩展符号的概念。符号代表函数,如果这些函数缺少它们所依赖的库,则无法加载这些符号。基本上,您可能缺少加载的库所需的库,以便提供符号表中列出的所有符号。 Dependency Walker 可帮助您确定您的库需要哪些库。
  • Dependency Walker 在引入 Windows 8 后在很大程度上变得无用,至少静态导入解决方案是如此。我不知道动态(分析)模式是否受到同样的影响。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多