关于C#:获取LoadLibrary无法加载DLL的原因 | 珊瑚贝

Get reason that LoadLibrary cannot load DLL


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

1
dlopen failed: cannot locate symbol“foo”

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

  • 简短的回答 – 你不能,因为 Windows 根本不提供那种细节。
  • 你知道 Dependency Walker 吗?
  • 细粒度的加载程序诊断可用,但默认情况下未启用。它们只对程序员有用,对最终用户没有用。谷歌”windows show loader snaps”了解如何操作。顺便说一句,几乎所有关于这个错误的半体面的问答都记录了这个,一定要多搜索一些。
  • @HansPassant 你能发布一个链接到”关于这个错误的半像样的问答”吗?如果它存在,我可能会找到它。


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

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

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

1
‘/c/Program Files (x86)/Windows Kits/10/Debuggers/x64/gflags.exe’ i main.exe +sls

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

1
2
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 并键入 run 或 r。
搜索类似于以下内容的部分。

1
2
3
4
5
6
7
8
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。


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

符号:

When a DLL is compiled, it’s functions are referenced by symbols.
These symbols directly relate to the functions name (the symbols are
represented by visible and readable strings), its return type, and
it’s parameters. The symbols can actually be read directly through a
text editor although difficult to find in large DLLs.DLL Symbols – C++ Forum

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

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


来源:https://www.codenong.com/62049362/

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?